Do not check for non NULL pointer before HeapFree'ing it. It's
[wine] / dlls / kernel / locale.c
1 /*
2  * Locale support
3  *
4  * Copyright 1995 Martin von Loewis
5  * Copyright 1998 David Lee Lambert
6  * Copyright 2000 Julio César Gázquez
7  * Copyright 2002 Alexandre Julliard for CodeWeavers
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <assert.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <stdlib.h>
33
34 #include "ntstatus.h"
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winuser.h"  /* for RT_STRINGW */
38 #include "winreg.h"
39 #include "winternl.h"
40 #include "wine/unicode.h"
41 #include "winnls.h"
42 #include "winerror.h"
43 #include "winver.h"
44 #include "thread.h"
45 #include "kernel_private.h"
46 #include "wine/debug.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(nls);
49
50 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|LOCALE_RETURN_NUMBER)
51
52 /* current code pages */
53 static const union cptable *ansi_cptable;
54 static const union cptable *oem_cptable;
55 static const union cptable *mac_cptable;
56 static const union cptable *unix_cptable;  /* NULL if UTF8 */
57
58 static HKEY NLS_RegOpenKey(HKEY hRootKey, LPCWSTR szKeyName);
59 static HKEY NLS_RegOpenSubKey(HKEY hRootKey, LPCWSTR szKeyName);
60
61 static const WCHAR szNlsKeyName[] = {
62     'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
63     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
64     'C','o','n','t','r','o','l','\\','N','l','s','\0'
65 };
66
67 /* Charset to codepage map, sorted by name. */
68 static const struct charset_entry
69 {
70     const char *charset_name;
71     UINT        codepage;
72 } charset_names[] =
73 {
74     { "BIG5", 950 },
75     { "CP1250", 1250 },
76     { "CP1251", 1251 },
77     { "CP1252", 1252 },
78     { "CP1253", 1253 },
79     { "CP1254", 1254 },
80     { "CP1255", 1255 },
81     { "CP1256", 1256 },
82     { "CP1257", 1257 },
83     { "CP1258", 1258 },
84     { "CP932", 932 },
85     { "CP936", 936 },
86     { "CP949", 949 },
87     { "CP950", 950 },
88     { "EUCJP", 20932 },
89     { "GB2312", 936 },
90     { "IBM037", 37 },
91     { "IBM1026", 1026 },
92     { "IBM424", 424 },
93     { "IBM437", 437 },
94     { "IBM500", 500 },
95     { "IBM850", 850 },
96     { "IBM852", 852 },
97     { "IBM855", 855 },
98     { "IBM857", 857 },
99     { "IBM860", 860 },
100     { "IBM861", 861 },
101     { "IBM862", 862 },
102     { "IBM863", 863 },
103     { "IBM864", 864 },
104     { "IBM865", 865 },
105     { "IBM866", 866 },
106     { "IBM869", 869 },
107     { "IBM874", 874 },
108     { "IBM875", 875 },
109     { "ISO88591", 28591 },
110     { "ISO885910", 28600 },
111     { "ISO885913", 28603 },
112     { "ISO885914", 28604 },
113     { "ISO885915", 28605 },
114     { "ISO885916", 28606 },
115     { "ISO88592", 28592 },
116     { "ISO88593", 28593 },
117     { "ISO88594", 28594 },
118     { "ISO88595", 28595 },
119     { "ISO88596", 28596 },
120     { "ISO88597", 28597 },
121     { "ISO88598", 28598 },
122     { "ISO88599", 28599 },
123     { "KOI8R", 20866 },
124     { "KOI8U", 20866 },
125     { "UTF8", CP_UTF8 }
126 };
127
128 #define NLS_MAX_LANGUAGES 20
129 typedef struct {
130     WCHAR lang[128];
131     WCHAR country[4];
132     LANGID found_lang_id[NLS_MAX_LANGUAGES];
133     WCHAR found_language[NLS_MAX_LANGUAGES][3];
134     WCHAR found_country[NLS_MAX_LANGUAGES][3];
135     int n_found;
136 } LANG_FIND_DATA;
137
138
139 /* copy Unicode string to Ascii without using codepages */
140 static inline void strcpyWtoA( char *dst, const WCHAR *src )
141 {
142     while ((*dst++ = *src++));
143 }
144
145 /* Copy Ascii string to Unicode without using codepages */
146 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
147 {
148     while (n > 1 && *src)
149     {
150         *dst++ = (unsigned char)*src++;
151         n--;
152     }
153     if (n) *dst = 0;
154 }
155
156 /* return a printable string for a language id */
157 static const char *debugstr_lang( LANGID lang )
158 {
159     WCHAR langW[4], countryW[4];
160     char buffer[8];
161     LCID lcid = MAKELCID( lang, SORT_DEFAULT );
162
163     GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME|LOCALE_NOUSEROVERRIDE, langW, sizeof(langW)/sizeof(WCHAR));
164     GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE, countryW, sizeof(countryW)/sizeof(WCHAR));
165     strcpyWtoA( buffer, langW );
166     strcat( buffer, "_" );
167     strcpyWtoA( buffer + strlen(buffer), countryW );
168     return wine_dbg_sprintf( "%s", buffer );
169 }
170
171 /***********************************************************************
172  *              get_lcid_codepage
173  *
174  * Retrieve the ANSI codepage for a given locale.
175  */
176 inline static UINT get_lcid_codepage( LCID lcid )
177 {
178     UINT ret;
179     if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
180                          sizeof(ret)/sizeof(WCHAR) )) ret = 0;
181     return ret;
182 }
183
184
185 /***********************************************************************
186  *              get_codepage_table
187  *
188  * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
189  */
190 static const union cptable *get_codepage_table( unsigned int codepage )
191 {
192     const union cptable *ret = NULL;
193
194     assert( ansi_cptable );  /* init must have been done already */
195
196     switch(codepage)
197     {
198     case CP_ACP:
199         return ansi_cptable;
200     case CP_OEMCP:
201         return oem_cptable;
202     case CP_MACCP:
203         return mac_cptable;
204     case CP_UTF7:
205     case CP_UTF8:
206         break;
207     case CP_THREAD_ACP:
208         if (!(codepage = NtCurrentTeb()->code_page)) return ansi_cptable;
209         /* fall through */
210     default:
211         if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
212         if (codepage == oem_cptable->info.codepage) return oem_cptable;
213         if (codepage == mac_cptable->info.codepage) return mac_cptable;
214         ret = wine_cp_get_table( codepage );
215         break;
216     }
217     return ret;
218 }
219
220 /***********************************************************************
221  *              create_registry_key
222  *
223  * Create the Control Panel\\International registry key.
224  */
225 inline static HKEY create_registry_key(void)
226 {
227     static const WCHAR intlW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
228                                   'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
229     OBJECT_ATTRIBUTES attr;
230     UNICODE_STRING nameW;
231     HKEY hkey;
232
233     if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
234
235     attr.Length = sizeof(attr);
236     attr.RootDirectory = hkey;
237     attr.ObjectName = &nameW;
238     attr.Attributes = 0;
239     attr.SecurityDescriptor = NULL;
240     attr.SecurityQualityOfService = NULL;
241     RtlInitUnicodeString( &nameW, intlW );
242
243     if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) hkey = 0;
244     NtClose( attr.RootDirectory );
245     return hkey;
246 }
247
248
249 /***********************************************************************
250  *              LOCALE_InitRegistry
251  *
252  * Update registry contents on startup if the user locale has changed.
253  * This simulates the action of the Windows control panel.
254  */
255 void LOCALE_InitRegistry(void)
256 {
257     static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
258     static const WCHAR acpW[] = {'A','C','P',0};
259     static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
260     static const WCHAR maccpW[] = {'M','A','C','C','P',0};
261     static const struct
262     {
263         LPCWSTR name;
264         USHORT value;
265     } update_cp_values[] = {
266         { acpW, LOCALE_IDEFAULTANSICODEPAGE },
267         { oemcpW, LOCALE_IDEFAULTCODEPAGE },
268         { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
269     };
270     static const USHORT updateValues[] = {
271       LOCALE_SLANGUAGE,
272       LOCALE_SCOUNTRY, LOCALE_ICOUNTRY,
273       LOCALE_S1159, LOCALE_S2359,
274       LOCALE_STIME, LOCALE_ITIME,
275       LOCALE_ITLZERO,
276       LOCALE_SSHORTDATE,
277       LOCALE_SLONGDATE,
278       LOCALE_SDATE,
279       LOCALE_SCURRENCY, LOCALE_ICURRENCY,
280       LOCALE_INEGCURR,
281       LOCALE_ICURRDIGITS,
282       LOCALE_SDECIMAL,
283       LOCALE_SLIST,
284       LOCALE_STHOUSAND,
285       LOCALE_IDIGITS,
286       LOCALE_IDIGITSUBSTITUTION,
287       LOCALE_SNATIVEDIGITS,
288       LOCALE_ITIMEMARKPOSN,
289       LOCALE_ICALENDARTYPE,
290       LOCALE_ILZERO,
291       LOCALE_IMEASURE
292     };
293     static const WCHAR LocaleW[] = {'L','o','c','a','l','e',0};
294     UNICODE_STRING nameW;
295     char buffer[20];
296     WCHAR bufferW[80];
297     DWORD count, i;
298     HKEY hkey;
299     LCID lcid = GetUserDefaultLCID();
300
301     if (!(hkey = create_registry_key()))
302         return;  /* don't do anything if we can't create the registry key */
303
304     RtlInitUnicodeString( &nameW, LocaleW );
305     count = sizeof(bufferW);
306     if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, (LPBYTE)bufferW, count, &count))
307     {
308         const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
309         LPCWSTR szValueText = (LPCWSTR)info->Data;
310
311         if (strtoulW( szValueText, NULL, 16 ) == lcid)  /* already set correctly */
312         {
313             NtClose( hkey );
314             return;
315         }
316         TRACE( "updating registry, locale changed %s -> %08lx\n", debugstr_w(szValueText), lcid );
317     }
318     else TRACE( "updating registry, locale changed none -> %08lx\n", lcid );
319
320     sprintf( buffer, "%08lx", lcid );
321     /* Note: '9' constant below is strlen(buffer) + 1 */
322     RtlMultiByteToUnicodeN( bufferW, sizeof(bufferW), NULL, buffer, 9 );
323     NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, 9 * sizeof(WCHAR) );
324     NtClose( hkey );
325
326     for (i = 0; i < sizeof(updateValues)/sizeof(updateValues[0]); i++)
327     {
328         GetLocaleInfoW( lcid, updateValues[i] | LOCALE_NOUSEROVERRIDE, bufferW,
329                         sizeof(bufferW)/sizeof(WCHAR) );
330         SetLocaleInfoW( lcid, updateValues[i], bufferW );
331     }
332
333     hkey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), CodepageW );
334
335     for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
336     {
337         count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
338                                 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
339         RtlInitUnicodeString( &nameW, update_cp_values[i].name );
340         NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
341     }
342
343     NtClose( hkey );
344 }
345
346
347 /***********************************************************************
348  *           find_language_id_proc
349  */
350 static BOOL CALLBACK find_language_id_proc( HMODULE hModule, LPCWSTR type,
351                                             LPCWSTR name, WORD LangID, LPARAM lParam )
352 {
353     LANG_FIND_DATA *l_data = (LANG_FIND_DATA *)lParam;
354     LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
355     WCHAR buf_language[128];
356     WCHAR buf_country[128];
357     WCHAR buf_en_language[128];
358
359     if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
360         return TRUE; /* continue search */
361
362     buf_language[0] = 0;
363     buf_country[0] = 0;
364
365     GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME|LOCALE_NOUSEROVERRIDE,
366                    buf_language, sizeof(buf_language)/sizeof(WCHAR));
367     GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
368                    buf_country, sizeof(buf_country)/sizeof(WCHAR));
369
370     if(l_data->lang[0] && !strcmpiW(l_data->lang, buf_language))
371     {
372         if(l_data->country[0])
373         {
374             if(!strcmpiW(l_data->country, buf_country))
375             {
376                 l_data->found_lang_id[0] = LangID;
377                 l_data->n_found = 1;
378                 TRACE("Found id %04X for lang %s country %s\n",
379                       LangID, debugstr_w(l_data->lang), debugstr_w(l_data->country));
380                 return FALSE; /* stop enumeration */
381             }
382         }
383         else goto found; /* l_data->country not specified */
384     }
385
386     /* Just in case, check LOCALE_SENGLANGUAGE too,
387      * in hope that possible alias name might have that value.
388      */
389     buf_en_language[0] = 0;
390     GetLocaleInfoW(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE,
391                    buf_en_language, sizeof(buf_en_language)/sizeof(WCHAR));
392
393     if(l_data->lang[0] && !strcmpiW(l_data->lang, buf_en_language)) goto found;
394     return TRUE;  /* not found, continue search */
395
396 found:
397     l_data->found_lang_id[l_data->n_found] = LangID;
398     strncpyW(l_data->found_country[l_data->n_found], buf_country, 3);
399     strncpyW(l_data->found_language[l_data->n_found], buf_language, 3);
400     l_data->n_found++;
401     TRACE("Found id %04X for lang %s\n", LangID, debugstr_w(l_data->lang));
402     return (l_data->n_found < NLS_MAX_LANGUAGES); /* continue search, unless we have enough */
403 }
404
405
406 /***********************************************************************
407  *           get_language_id
408  *
409  * INPUT:
410  *      Lang: a string whose two first chars are the iso name of a language.
411  *      Country: a string whose two first chars are the iso name of country
412  *      Charset: a string defining the chosen charset encoding
413  *      Dialect: a string defining a variation of the locale
414  *
415  *      all those values are from the standardized format of locale
416  *      name in unix which is: Lang[_Country][.Charset][@Dialect]
417  *
418  * RETURNS:
419  *      the numeric code of the language used by Windows
420  *
421  * FIXME: Charset and Dialect are not handled
422  */
423 static LANGID get_language_id(LPCSTR Lang, LPCSTR Country, LPCSTR Charset, LPCSTR Dialect)
424 {
425     LANG_FIND_DATA l_data;
426
427     if(!Lang)
428     {
429         l_data.found_lang_id[0] = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
430         goto END;
431     }
432
433     l_data.n_found = 0;
434     strcpynAtoW(l_data.lang, Lang, sizeof(l_data.lang));
435
436     if (Country) strcpynAtoW(l_data.country, Country, sizeof(l_data.country));
437     else l_data.country[0] = 0;
438
439     EnumResourceLanguagesW(kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
440                            find_language_id_proc, (LPARAM)&l_data);
441
442     if (l_data.n_found == 1) goto END;
443
444     if(!l_data.n_found)
445     {
446         if(l_data.country[0])
447         {
448             /* retry without country name */
449             l_data.country[0] = 0;
450             EnumResourceLanguagesW(kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
451                                    find_language_id_proc, (LONG)&l_data);
452             if (!l_data.n_found)
453             {
454                 MESSAGE("Warning: Language '%s_%s' was not recognized, defaulting to English.\n",
455                         Lang, Country);
456                 l_data.found_lang_id[0] = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
457             }
458             else MESSAGE("Warning: Language '%s_%s' was not recognized, defaulting to '%s'.\n",
459                          Lang, Country, debugstr_lang(l_data.found_lang_id[0]) );
460         }
461         else
462         {
463             MESSAGE("Warning: Language '%s' was not recognized, defaulting to English.\n", Lang);
464             l_data.found_lang_id[0] = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
465         }
466     }
467     else
468     {
469         int i;
470
471         if (Country && Country[0])
472             MESSAGE("For language '%s_%s' several language ids were found:\n", Lang, Country);
473         else
474             MESSAGE("For language '%s' several language ids were found:\n", Lang);
475
476         /* print a list of languages with their description */
477         for (i = 0; i < l_data.n_found; i++)
478         {
479             WCHAR buffW[128];
480             char buffA[128];
481             GetLocaleInfoW( MAKELCID( l_data.found_lang_id[i], SORT_DEFAULT ),
482                            LOCALE_SLANGUAGE|LOCALE_NOUSEROVERRIDE, buffW, sizeof(buffW)/sizeof(WCHAR));
483             strcpyWtoA( buffA, buffW );
484             MESSAGE( "   %s (%04X) - %s\n", debugstr_lang(l_data.found_lang_id[i]),
485                      l_data.found_lang_id[i], buffA );
486         }
487         MESSAGE("Defaulting to '%s'. You should specify the exact language you want\n"
488                 "by defining your LANG environment variable like this: LANG=%s\n",
489                 debugstr_lang(l_data.found_lang_id[0]), debugstr_lang(l_data.found_lang_id[0]) );
490     }
491 END:
492     TRACE("Returning %04X (%s)\n", l_data.found_lang_id[0], debugstr_lang(l_data.found_lang_id[0]));
493     return l_data.found_lang_id[0];
494 }
495
496
497 /***********************************************************************
498  *              charset_cmp (internal)
499  */
500 static int charset_cmp( const void *name, const void *entry )
501 {
502     const struct charset_entry *charset = (const struct charset_entry *)entry;
503     return strcasecmp( (const char *)name, charset->charset_name );
504 }
505
506 /***********************************************************************
507  *              get_env_lcid
508  */
509 static LCID get_env_lcid( UINT *unix_cp, const char *env_str )
510 {
511     char *buf, *lang,*country,*charset,*dialect,*next;
512     LCID ret = 0;
513
514     if ((lang = getenv( "LC_ALL" )) ||
515         (env_str && (lang = getenv( env_str ))) ||
516         (lang = getenv( "LANG" )))
517     {
518         if (!strcmp(lang,"POSIX") || !strcmp(lang,"C")) goto done;
519
520         buf = RtlAllocateHeap( GetProcessHeap(), 0, strlen(lang) + 1 );
521         strcpy( buf, lang );
522         lang=buf;
523
524         do {
525             next=strchr(lang,':'); if (next) *next++='\0';
526             dialect=strchr(lang,'@'); if (dialect) *dialect++='\0';
527             charset=strchr(lang,'.'); if (charset) *charset++='\0';
528             country=strchr(lang,'_'); if (country) *country++='\0';
529
530             ret = get_language_id(lang, country, charset, dialect);
531             if (ret && charset && unix_cp)
532             {
533                 const struct charset_entry *entry;
534                 char charset_name[16];
535                 size_t i, j;
536
537                 /* remove punctuation characters from charset name */
538                 for (i = j = 0; charset[i] && j < sizeof(charset_name)-1; i++)
539                     if (isalnum(charset[i])) charset_name[j++] = charset[i];
540                 charset_name[j] = 0;
541
542                 entry = bsearch( charset_name, charset_names,
543                                  sizeof(charset_names)/sizeof(charset_names[0]),
544                                  sizeof(charset_names[0]), charset_cmp );
545                 if (entry)
546                 {
547                     *unix_cp = entry->codepage;
548                     TRACE("charset %s was mapped to cp %u\n", charset, *unix_cp);
549                 }
550                 else
551                     FIXME("charset %s was not recognized\n", charset);
552             }
553
554             lang=next;
555         } while (lang && !ret);
556
557         if (!ret) MESSAGE("Warning: language '%s' not recognized, defaulting to English\n", buf);
558         RtlFreeHeap( GetProcessHeap(), 0, buf );
559     }
560
561  done:
562     if (!ret) ret = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT) ;
563     return ret;
564 }
565
566
567 /***********************************************************************
568  *              GetUserDefaultLangID (KERNEL32.@)
569  *
570  * Get the default language Id for the current user.
571  *
572  * PARAMS
573  *  None.
574  *
575  * RETURNS
576  *  The current LANGID of the default language for the current user.
577  */
578 LANGID WINAPI GetUserDefaultLangID(void)
579 {
580     return LANGIDFROMLCID(GetUserDefaultLCID());
581 }
582
583
584 /***********************************************************************
585  *              GetSystemDefaultLangID (KERNEL32.@)
586  *
587  * Get the default language Id for the system.
588  *
589  * PARAMS
590  *  None.
591  *
592  * RETURNS
593  *  The current LANGID of the default language for the system.
594  */
595 LANGID WINAPI GetSystemDefaultLangID(void)
596 {
597     return LANGIDFROMLCID(GetSystemDefaultLCID());
598 }
599
600
601 /***********************************************************************
602  *              GetUserDefaultLCID (KERNEL32.@)
603  *
604  * Get the default locale Id for the current user.
605  *
606  * PARAMS
607  *  None.
608  *
609  * RETURNS
610  *  The current LCID of the default locale for the current user.
611  */
612 LCID WINAPI GetUserDefaultLCID(void)
613 {
614     LCID lcid;
615     NtQueryDefaultLocale( TRUE, &lcid );
616     return lcid;
617 }
618
619
620 /***********************************************************************
621  *              GetSystemDefaultLCID (KERNEL32.@)
622  *
623  * Get the default locale Id for the system.
624  *
625  * PARAMS
626  *  None.
627  *
628  * RETURNS
629  *  The current LCID of the default locale for the system.
630  */
631 LCID WINAPI GetSystemDefaultLCID(void)
632 {
633     LCID lcid;
634     NtQueryDefaultLocale( FALSE, &lcid );
635     return lcid;
636 }
637
638
639 /***********************************************************************
640  *              GetUserDefaultUILanguage (KERNEL32.@)
641  *
642  * Get the default user interface language Id for the current user.
643  *
644  * PARAMS
645  *  None.
646  *
647  * RETURNS
648  *  The current LANGID of the default UI language for the current user.
649  */
650 LANGID WINAPI GetUserDefaultUILanguage(void)
651 {
652     LANGID lang;
653     NtQueryDefaultUILanguage( &lang );
654     return lang;
655 }
656
657
658 /***********************************************************************
659  *              GetSystemDefaultUILanguage (KERNEL32.@)
660  *
661  * Get the default user interface language Id for the system.
662  *
663  * PARAMS
664  *  None.
665  *
666  * RETURNS
667  *  The current LANGID of the default UI language for the system. This is
668  *  typically the same language used during the installation process.
669  */
670 LANGID WINAPI GetSystemDefaultUILanguage(void)
671 {
672     LANGID lang;
673     NtQueryInstallUILanguage( &lang );
674     return lang;
675 }
676
677
678 /******************************************************************************
679  *              get_locale_value_name
680  *
681  * Gets the registry value name for a given lctype.
682  */
683 static const WCHAR *get_locale_value_name( DWORD lctype )
684 {
685     static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
686     static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
687     static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
688     static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
689     static const WCHAR iDateW[] = {'i','D','a','t','e',0};
690     static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
691     static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
692     static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
693     static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
694     static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
695     static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
696     static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
697     static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
698     static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
699     static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
700     static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
701     static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
702     static const WCHAR s1159W[] = {'s','1','1','5','9',0};
703     static const WCHAR s2359W[] = {'s','2','3','5','9',0};
704     static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
705     static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
706     static const WCHAR sDateW[] = {'s','D','a','t','e',0};
707     static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
708     static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
709     static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
710     static const WCHAR sListW[] = {'s','L','i','s','t',0};
711     static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
712     static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
713     static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
714     static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
715     static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
716     static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
717     static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
718     static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
719     static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
720     static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
721     static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
722     static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
723     static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
724
725     switch (lctype)
726     {
727     /* These values are used by SetLocaleInfo and GetLocaleInfo, and
728      * the values are stored in the registry, confirmed under Windows.
729      */
730     case LOCALE_ICALENDARTYPE:    return iCalendarTypeW;
731     case LOCALE_ICURRDIGITS:      return iCurrDigitsW;
732     case LOCALE_ICURRENCY:        return iCurrencyW;
733     case LOCALE_IDIGITS:          return iDigitsW;
734     case LOCALE_IFIRSTDAYOFWEEK:  return iFirstDayOfWeekW;
735     case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
736     case LOCALE_ILZERO:           return iLZeroW;
737     case LOCALE_IMEASURE:         return iMeasureW;
738     case LOCALE_INEGCURR:         return iNegCurrW;
739     case LOCALE_INEGNUMBER:       return iNegNumberW;
740     case LOCALE_IPAPERSIZE:       return iPaperSizeW;
741     case LOCALE_ITIME:            return iTimeW;
742     case LOCALE_S1159:            return s1159W;
743     case LOCALE_S2359:            return s2359W;
744     case LOCALE_SCURRENCY:        return sCurrencyW;
745     case LOCALE_SDATE:            return sDateW;
746     case LOCALE_SDECIMAL:         return sDecimalW;
747     case LOCALE_SGROUPING:        return sGroupingW;
748     case LOCALE_SLIST:            return sListW;
749     case LOCALE_SLONGDATE:        return sLongDateW;
750     case LOCALE_SMONDECIMALSEP:   return sMonDecimalSepW;
751     case LOCALE_SMONGROUPING:     return sMonGroupingW;
752     case LOCALE_SMONTHOUSANDSEP:  return sMonThousandSepW;
753     case LOCALE_SNEGATIVESIGN:    return sNegativeSignW;
754     case LOCALE_SPOSITIVESIGN:    return sPositiveSignW;
755     case LOCALE_SSHORTDATE:       return sShortDateW;
756     case LOCALE_STHOUSAND:        return sThousandW;
757     case LOCALE_STIME:            return sTimeW;
758     case LOCALE_STIMEFORMAT:      return sTimeFormatW;
759     case LOCALE_SYEARMONTH:       return sYearMonthW;
760
761     /* The following are not listed under MSDN as supported,
762      * but seem to be used and also stored in the registry.
763      */
764     case LOCALE_ICOUNTRY:         return iCountryW;
765     case LOCALE_IDATE:            return iDateW;
766     case LOCALE_ILDATE:           return iLDateW;
767     case LOCALE_ITLZERO:          return iTLZeroW;
768     case LOCALE_SCOUNTRY:         return sCountryW;
769     case LOCALE_SLANGUAGE:        return sLanguageW;
770
771     /* The following are used in XP and later */
772     case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
773     case LOCALE_SNATIVEDIGITS:      return sNativeDigitsW;
774     case LOCALE_ITIMEMARKPOSN:      return iTimePrefixW;
775     }
776     return NULL;
777 }
778
779
780 /******************************************************************************
781  *              get_registry_locale_info
782  *
783  * Retrieve user-modified locale info from the registry.
784  * Return length, 0 on error, -1 if not found.
785  */
786 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
787 {
788     DWORD size;
789     INT ret;
790     HKEY hkey;
791     NTSTATUS status;
792     UNICODE_STRING nameW;
793     KEY_VALUE_PARTIAL_INFORMATION *info;
794     static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
795
796     if (!(hkey = create_registry_key())) return -1;
797
798     RtlInitUnicodeString( &nameW, value );
799     size = info_size + len * sizeof(WCHAR);
800
801     if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
802     {
803         NtClose( hkey );
804         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
805         return 0;
806     }
807
808     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
809     if (status == STATUS_BUFFER_OVERFLOW && !buffer) status = 0;
810
811     if (!status)
812     {
813         ret = (size - info_size) / sizeof(WCHAR);
814         /* append terminating null if needed */
815         if (!ret || ((WCHAR *)info->Data)[ret-1])
816         {
817             if (ret < len || !buffer) ret++;
818             else
819             {
820                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
821                 ret = 0;
822             }
823         }
824         if (ret && buffer)
825         {
826             memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
827             buffer[ret-1] = 0;
828         }
829     }
830     else
831     {
832         if (status == STATUS_OBJECT_NAME_NOT_FOUND) ret = -1;
833         else
834         {
835             SetLastError( RtlNtStatusToDosError(status) );
836             ret = 0;
837         }
838     }
839     NtClose( hkey );
840     HeapFree( GetProcessHeap(), 0, info );
841     return ret;
842 }
843
844
845 /******************************************************************************
846  *              GetLocaleInfoA (KERNEL32.@)
847  *
848  * Get information about an aspect of a locale.
849  *
850  * PARAMS
851  *  lcid   [I] LCID of the locale
852  *  lctype [I] LCTYPE_ flags from "winnls.h"
853  *  buffer [O] Destination for the information
854  *  len    [I] Length of buffer in characters
855  *
856  * RETURNS
857  *  Success: The size of the data requested. If buffer is non-NULL, it is filled
858  *           with the information.
859  *  Failure: 0. Use GetLastError() to determine the cause.
860  *
861  * NOTES
862  *  - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
863  *  - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
864  *    which is a bit string.
865  */
866 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
867 {
868     WCHAR *bufferW;
869     INT lenW, ret;
870
871     TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
872
873     if (len < 0 || (len && !buffer))
874     {
875         SetLastError( ERROR_INVALID_PARAMETER );
876         return 0;
877     }
878     if (!len) buffer = NULL;
879
880     if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
881
882     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
883     {
884         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
885         return 0;
886     }
887     if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
888     {
889         if ((lctype & LOCALE_RETURN_NUMBER) ||
890             ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
891         {
892             /* it's not an ASCII string, just bytes */
893             ret *= sizeof(WCHAR);
894             if (buffer)
895             {
896                 if (ret <= len) memcpy( buffer, bufferW, ret );
897                 else
898                 {
899                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
900                     ret = 0;
901                 }
902             }
903         }
904         else
905         {
906             UINT codepage = CP_ACP;
907             if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
908             ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
909         }
910     }
911     HeapFree( GetProcessHeap(), 0, bufferW );
912     return ret;
913 }
914
915
916 /******************************************************************************
917  *              GetLocaleInfoW (KERNEL32.@)
918  *
919  * See GetLocaleInfoA.
920  */
921 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
922 {
923     LANGID lang_id;
924     HRSRC hrsrc;
925     HGLOBAL hmem;
926     INT ret;
927     UINT lcflags;
928     const WCHAR *p;
929     unsigned int i;
930
931     if (len < 0 || (len && !buffer))
932     {
933         SetLastError( ERROR_INVALID_PARAMETER );
934         return 0;
935     }
936     if (!len) buffer = NULL;
937
938     lcid = ConvertDefaultLocale(lcid);
939
940     lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
941     lctype &= 0xffff;
942
943     TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len );
944
945     /* first check for overrides in the registry */
946
947     if (!(lcflags & LOCALE_NOUSEROVERRIDE) && lcid == GetUserDefaultLCID())
948     {
949         const WCHAR *value = get_locale_value_name(lctype);
950
951         if (value)
952         {
953             if (lcflags & LOCALE_RETURN_NUMBER)
954             {
955                 WCHAR tmp[16];
956                 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
957                 if (ret > 0)
958                 {
959                     WCHAR *end;
960                     UINT number = strtolW( tmp, &end, 10 );
961                     if (*end)  /* invalid number */
962                     {
963                         SetLastError( ERROR_INVALID_FLAGS );
964                         return 0;
965                     }
966                     ret = sizeof(UINT)/sizeof(WCHAR);
967                     if (!buffer) return ret;
968                     if (ret > len)
969                     {
970                         SetLastError( ERROR_INSUFFICIENT_BUFFER );
971                         return 0;
972                     }
973                     memcpy( buffer, &number, sizeof(number) );
974                 }
975             }
976             else ret = get_registry_locale_info( value, buffer, len );
977
978             if (ret != -1) return ret;
979         }
980     }
981
982     /* now load it from kernel resources */
983
984     lang_id = LANGIDFROMLCID( lcid );
985
986     /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
987     if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
988         lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
989
990     if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
991                                    (LPCWSTR)((lctype >> 4) + 1), lang_id )))
992     {
993         SetLastError( ERROR_INVALID_FLAGS );  /* no such lctype */
994         return 0;
995     }
996     if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
997         return 0;
998
999     p = LockResource( hmem );
1000     for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1001
1002     if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1003     else ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1004
1005     if (!buffer) return ret;
1006
1007     if (ret > len)
1008     {
1009         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1010         return 0;
1011     }
1012
1013     if (lcflags & LOCALE_RETURN_NUMBER)
1014     {
1015         UINT number;
1016         WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1017         if (!tmp) return 0;
1018         memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1019         tmp[*p] = 0;
1020         number = strtolW( tmp, &end, 10 );
1021         if (!*end)
1022             memcpy( buffer, &number, sizeof(number) );
1023         else  /* invalid number */
1024         {
1025             SetLastError( ERROR_INVALID_FLAGS );
1026             ret = 0;
1027         }
1028         HeapFree( GetProcessHeap(), 0, tmp );
1029
1030         TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning number %d\n",
1031                lcid, lctype, buffer, len, number );
1032     }
1033     else
1034     {
1035         memcpy( buffer, p + 1, *p * sizeof(WCHAR) );
1036         if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1037
1038         TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning %d %s\n",
1039                lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1040     }
1041     return ret;
1042 }
1043
1044
1045 /******************************************************************************
1046  *              SetLocaleInfoA  [KERNEL32.@]
1047  *
1048  * Set information about an aspect of a locale.
1049  *
1050  * PARAMS
1051  *  lcid   [I] LCID of the locale
1052  *  lctype [I] LCTYPE_ flags from "winnls.h"
1053  *  data   [I] Information to set
1054  *
1055  * RETURNS
1056  *  Success: TRUE. The information given will be returned by GetLocaleInfoA()
1057  *           whenever it is called without LOCALE_NOUSEROVERRIDE.
1058  *  Failure: FALSE. Use GetLastError() to determine the cause.
1059  *
1060  * NOTES
1061  *  - Values are only be set for the current user locale; the system locale
1062  *  settings cannot be changed.
1063  *  - Any settings changed by this call are lost when the locale is changed by
1064  *  the control panel (in Wine, this happens every time you change LANG).
1065  *  - The native implementation of this function does not check that lcid matches
1066  *  the current user locale, and simply sets the new values. Wine warns you in
1067  *  this case, but behaves the same.
1068  */
1069 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1070 {
1071     UINT codepage = CP_ACP;
1072     WCHAR *strW;
1073     DWORD len;
1074     BOOL ret;
1075
1076     lcid = ConvertDefaultLocale(lcid);
1077
1078     if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1079
1080     if (!data)
1081     {
1082         SetLastError( ERROR_INVALID_PARAMETER );
1083         return FALSE;
1084     }
1085     len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1086     if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1087     {
1088         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1089         return FALSE;
1090     }
1091     MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1092     ret = SetLocaleInfoW( lcid, lctype, strW );
1093     HeapFree( GetProcessHeap(), 0, strW );
1094     return ret;
1095 }
1096
1097
1098 /******************************************************************************
1099  *              SetLocaleInfoW  (KERNEL32.@)
1100  *
1101  * See SetLocaleInfoA.
1102  */
1103 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1104 {
1105     const WCHAR *value;
1106     static const WCHAR intlW[] = {'i','n','t','l',0 };
1107     UNICODE_STRING valueW;
1108     NTSTATUS status;
1109     HKEY hkey;
1110
1111     lcid = ConvertDefaultLocale(lcid);
1112
1113     lctype &= 0xffff;
1114     value = get_locale_value_name( lctype );
1115
1116     if (!data || !value)
1117     {
1118         SetLastError( ERROR_INVALID_PARAMETER );
1119         return FALSE;
1120     }
1121
1122     if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1123     {
1124         SetLastError( ERROR_INVALID_FLAGS );
1125         return FALSE;
1126     }
1127
1128     if (lcid != GetUserDefaultLCID())
1129     {
1130         /* Windows does not check that the lcid matches the current lcid */
1131         WARN("locale 0x%08lx isn't the current locale (0x%08lx), setting anyway!\n",
1132              lcid, GetUserDefaultLCID());
1133     }
1134
1135     TRACE("setting %lx (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1136
1137     /* FIXME: should check that data to set is sane */
1138
1139     /* FIXME: profile functions should map to registry */
1140     WriteProfileStringW( intlW, value, data );
1141
1142     if (!(hkey = create_registry_key())) return FALSE;
1143     RtlInitUnicodeString( &valueW, value );
1144     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1145
1146     if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1147     {
1148       /* Set I-value from S value */
1149       WCHAR *lpD, *lpM, *lpY;
1150       WCHAR szBuff[2];
1151
1152       lpD = strrchrW(data, 'd');
1153       lpM = strrchrW(data, 'M');
1154       lpY = strrchrW(data, 'y');
1155
1156       if (lpD <= lpM)
1157       {
1158         szBuff[0] = '1'; /* D-M-Y */
1159       }
1160       else
1161       {
1162         if (lpY <= lpM)
1163           szBuff[0] = '2'; /* Y-M-D */
1164         else
1165           szBuff[0] = '0'; /* M-D-Y */
1166       }
1167
1168       szBuff[1] = '\0';
1169
1170       if (lctype == LOCALE_SSHORTDATE)
1171         lctype = LOCALE_IDATE;
1172       else
1173         lctype = LOCALE_ILDATE;
1174
1175       value = get_locale_value_name( lctype );
1176
1177       WriteProfileStringW( intlW, value, szBuff );
1178
1179       RtlInitUnicodeString( &valueW, value );
1180       status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1181     }
1182
1183     NtClose( hkey );
1184
1185     if (status) SetLastError( RtlNtStatusToDosError(status) );
1186     return !status;
1187 }
1188
1189
1190 /******************************************************************************
1191  *              GetACP   (KERNEL32.@)
1192  *
1193  * Get the current Ansi code page Id for the system.
1194  *
1195  * PARAMS
1196  *  None.
1197  *
1198  * RETURNS
1199  *    The current Ansi code page identifier for the system.
1200  */
1201 UINT WINAPI GetACP(void)
1202 {
1203     assert( ansi_cptable );
1204     return ansi_cptable->info.codepage;
1205 }
1206
1207
1208 /***********************************************************************
1209  *              GetOEMCP   (KERNEL32.@)
1210  *
1211  * Get the current OEM code page Id for the system.
1212  *
1213  * PARAMS
1214  *  None.
1215  *
1216  * RETURNS
1217  *    The current OEM code page identifier for the system.
1218  */
1219 UINT WINAPI GetOEMCP(void)
1220 {
1221     assert( oem_cptable );
1222     return oem_cptable->info.codepage;
1223 }
1224
1225
1226 /***********************************************************************
1227  *           IsValidCodePage   (KERNEL32.@)
1228  *
1229  * Determine if a given code page identifier is valid.
1230  *
1231  * PARAMS
1232  *  codepage [I] Code page Id to verify.
1233  *
1234  * RETURNS
1235  *  TRUE, If codepage is valid and available on the system,
1236  *  FALSE otherwise.
1237  */
1238 BOOL WINAPI IsValidCodePage( UINT codepage )
1239 {
1240     switch(codepage) {
1241     case CP_UTF7:
1242     case CP_UTF8:
1243         return TRUE;
1244     default:
1245         return wine_cp_get_table( codepage ) != NULL;
1246     }
1247 }
1248
1249
1250 /***********************************************************************
1251  *           IsDBCSLeadByteEx   (KERNEL32.@)
1252  *
1253  * Determine if a character is a lead byte in a given code page.
1254  *
1255  * PARAMS
1256  *  codepage [I] Code page for the test.
1257  *  testchar [I] Character to test
1258  *
1259  * RETURNS
1260  *  TRUE, if testchar is a lead byte in codepage,
1261  *  FALSE otherwise.
1262  */
1263 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1264 {
1265     const union cptable *table = get_codepage_table( codepage );
1266     return table && is_dbcs_leadbyte( table, testchar );
1267 }
1268
1269
1270 /***********************************************************************
1271  *           IsDBCSLeadByte   (KERNEL32.@)
1272  *           IsDBCSLeadByte   (KERNEL.207)
1273  *
1274  * Determine if a character is a lead byte.
1275  *
1276  * PARAMS
1277  *  testchar [I] Character to test
1278  *
1279  * RETURNS
1280  *  TRUE, if testchar is a lead byte in the Ansii code page,
1281  *  FALSE otherwise.
1282  */
1283 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1284 {
1285     if (!ansi_cptable) return FALSE;
1286     return is_dbcs_leadbyte( ansi_cptable, testchar );
1287 }
1288
1289
1290 /***********************************************************************
1291  *           GetCPInfo   (KERNEL32.@)
1292  *
1293  * Get information about a code page.
1294  *
1295  * PARAMS
1296  *  codepage [I] Code page number
1297  *  cpinfo   [O] Destination for code page information
1298  *
1299  * RETURNS
1300  *  Success: TRUE. cpinfo is updated with the information about codepage.
1301  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1302  */
1303 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1304 {
1305     const union cptable *table = get_codepage_table( codepage );
1306
1307     if (!table)
1308     {
1309         switch(codepage)
1310         {
1311             case CP_UTF7:
1312             case CP_UTF8:
1313                 cpinfo->DefaultChar[0] = 0x3f;
1314                 cpinfo->DefaultChar[1] = 0;
1315                 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1316                 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1317                 return TRUE;
1318         }
1319
1320         SetLastError( ERROR_INVALID_PARAMETER );
1321         return FALSE;
1322     }
1323     if (table->info.def_char & 0xff00)
1324     {
1325         cpinfo->DefaultChar[0] = table->info.def_char & 0xff00;
1326         cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1327     }
1328     else
1329     {
1330         cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1331         cpinfo->DefaultChar[1] = 0;
1332     }
1333     if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1334         memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1335     else
1336         cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1337
1338     return TRUE;
1339 }
1340
1341 /***********************************************************************
1342  *           GetCPInfoExA   (KERNEL32.@)
1343  *
1344  * Get extended information about a code page.
1345  *
1346  * PARAMS
1347  *  codepage [I] Code page number
1348  *  dwFlags  [I] Reserved, must to 0.
1349  *  cpinfo   [O] Destination for code page information
1350  *
1351  * RETURNS
1352  *  Success: TRUE. cpinfo is updated with the information about codepage.
1353  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1354  */
1355 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1356 {
1357     CPINFOEXW cpinfoW;
1358
1359     if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1360       return FALSE;
1361
1362     /* the layout is the same except for CodePageName */
1363     memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1364     WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1365     return TRUE;
1366 }
1367
1368 /***********************************************************************
1369  *           GetCPInfoExW   (KERNEL32.@)
1370  *
1371  * Unicode version of GetCPInfoExA.
1372  */
1373 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1374 {
1375     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1376       return FALSE;
1377
1378     switch(codepage)
1379     {
1380         case CP_UTF7:
1381         {
1382             static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1383
1384             cpinfo->CodePage = CP_UTF7;
1385             cpinfo->UnicodeDefaultChar = 0x3f;
1386             strcpyW(cpinfo->CodePageName, utf7);
1387             break;
1388         }
1389
1390         case CP_UTF8:
1391         {
1392             static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1393
1394             cpinfo->CodePage = CP_UTF8;
1395             cpinfo->UnicodeDefaultChar = 0x3f;
1396             strcpyW(cpinfo->CodePageName, utf8);
1397             break;
1398         }
1399
1400         default:
1401         {
1402             const union cptable *table = get_codepage_table( codepage );
1403
1404             cpinfo->CodePage = table->info.codepage;
1405             cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1406             MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1407                                  sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1408             break;
1409         }
1410     }
1411     return TRUE;
1412 }
1413
1414 /***********************************************************************
1415  *              EnumSystemCodePagesA   (KERNEL32.@)
1416  *
1417  * Call a user defined function for every code page installed on the system.
1418  *
1419  * PARAMS
1420  *   lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1421  *   flags            [I] Reserved, set to 0.
1422  *
1423  * RETURNS
1424  *  TRUE, If all code pages have been enumerated, or
1425  *  FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1426  */
1427 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1428 {
1429     const union cptable *table;
1430     char buffer[10];
1431     int index = 0;
1432
1433     for (;;)
1434     {
1435         if (!(table = wine_cp_enum_table( index++ ))) break;
1436         sprintf( buffer, "%d", table->info.codepage );
1437         if (!lpfnCodePageEnum( buffer )) break;
1438     }
1439     return TRUE;
1440 }
1441
1442
1443 /***********************************************************************
1444  *              EnumSystemCodePagesW   (KERNEL32.@)
1445  *
1446  * See EnumSystemCodePagesA.
1447  */
1448 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1449 {
1450     const union cptable *table;
1451     WCHAR buffer[10], *p;
1452     int page, index = 0;
1453
1454     for (;;)
1455     {
1456         if (!(table = wine_cp_enum_table( index++ ))) break;
1457         p = buffer + sizeof(buffer)/sizeof(WCHAR);
1458         *--p = 0;
1459         page = table->info.codepage;
1460         do
1461         {
1462             *--p = '0' + (page % 10);
1463             page /= 10;
1464         } while( page );
1465         if (!lpfnCodePageEnum( p )) break;
1466     }
1467     return TRUE;
1468 }
1469
1470
1471 /***********************************************************************
1472  *              MultiByteToWideChar   (KERNEL32.@)
1473  *
1474  * Convert a multibyte character string into a Unicode string.
1475  *
1476  * PARAMS
1477  *   page   [I] Codepage character set to convert from
1478  *   flags  [I] Character mapping flags
1479  *   src    [I] Source string buffer
1480  *   srclen [I] Length of src, or -1 if src is NUL terminated
1481  *   dst    [O] Destination buffer
1482  *   dstlen [I] Length of dst, or 0 to compute the required length
1483  *
1484  * RETURNS
1485  *   Success: If dstlen > 0, the number of characters written to dst.
1486  *            If dstlen == 0, the number of characters needed to perform the
1487  *            conversion. In both cases the count includes the terminating NUL.
1488  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1489  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1490  *            and dstlen != 0; ERROR_INVALID_PARAMETER,  if an invalid parameter
1491  *            is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1492  *            possible for src.
1493  */
1494 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1495                                 LPWSTR dst, INT dstlen )
1496 {
1497     const union cptable *table;
1498     int ret;
1499
1500     if (!src || (!dst && dstlen))
1501     {
1502         SetLastError( ERROR_INVALID_PARAMETER );
1503         return 0;
1504     }
1505
1506     if (srclen < 0) srclen = strlen(src) + 1;
1507
1508     if (flags & MB_USEGLYPHCHARS) FIXME("MB_USEGLYPHCHARS not supported\n");
1509
1510     switch(page)
1511     {
1512     case CP_SYMBOL:
1513         if( flags)
1514         {
1515             SetLastError( ERROR_INVALID_PARAMETER );
1516             return 0;
1517         }
1518         ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1519         break;
1520     case CP_UTF7:
1521         FIXME("UTF-7 not supported\n");
1522         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1523         return 0;
1524     case CP_UNIXCP:
1525         if (unix_cptable)
1526         {
1527             ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1528             break;
1529         }
1530         /* fall through */
1531     case CP_UTF8:
1532         ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1533         break;
1534     default:
1535         if (!(table = get_codepage_table( page )))
1536         {
1537             SetLastError( ERROR_INVALID_PARAMETER );
1538             return 0;
1539         }
1540         ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1541         break;
1542     }
1543
1544     if (ret < 0)
1545     {
1546         switch(ret)
1547         {
1548         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1549         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1550         }
1551         ret = 0;
1552     }
1553     return ret;
1554 }
1555
1556
1557 /***********************************************************************
1558  *              WideCharToMultiByte   (KERNEL32.@)
1559  *
1560  * Convert a Unicode character string into a multibyte string.
1561  *
1562  * PARAMS
1563  *   page    [I] Code page character set to convert to
1564  *   flags   [I] Mapping Flags (MB_ constants from "winnls.h").
1565  *   src     [I] Source string buffer
1566  *   srclen  [I] Length of src, or -1 if src is NUL terminated
1567  *   dst     [O] Destination buffer
1568  *   dstlen  [I] Length of dst, or 0 to compute the required length
1569  *   defchar [I] Default character to use for conversion if no exact
1570  *                  conversion can be made
1571  *   used    [O] Set if default character was used in the conversion
1572  *
1573  * RETURNS
1574  *   Success: If dstlen > 0, the number of characters written to dst.
1575  *            If dstlen == 0, number of characters needed to perform the
1576  *            conversion. In both cases the count includes the terminating NUL.
1577  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1578  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1579  *            and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1580  *            parameter was given.
1581  */
1582 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1583                                 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1584 {
1585     const union cptable *table;
1586     int ret, used_tmp;
1587
1588     if (!src || (!dst && dstlen))
1589     {
1590         SetLastError( ERROR_INVALID_PARAMETER );
1591         return 0;
1592     }
1593
1594     if (srclen < 0) srclen = strlenW(src) + 1;
1595
1596     switch(page)
1597     {
1598     case CP_SYMBOL:
1599         if( flags || defchar || used)
1600         {
1601             SetLastError( ERROR_INVALID_PARAMETER );
1602             return 0;
1603         }
1604         ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
1605         break;
1606     case CP_UTF7:
1607         FIXME("UTF-7 not supported\n");
1608         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1609         return 0;
1610     case CP_UNIXCP:
1611         if (unix_cptable)
1612         {
1613             ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
1614                                     defchar, used ? &used_tmp : NULL );
1615             break;
1616         }
1617         /* fall through */
1618     case CP_UTF8:
1619         if (used) *used = FALSE;  /* all chars are valid for UTF-8 */
1620         ret = wine_utf8_wcstombs( src, srclen, dst, dstlen );
1621         break;
1622     default:
1623         if (!(table = get_codepage_table( page )))
1624         {
1625             SetLastError( ERROR_INVALID_PARAMETER );
1626             return 0;
1627         }
1628         ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
1629                                 defchar, used ? &used_tmp : NULL );
1630         if (used) *used = used_tmp;
1631         break;
1632     }
1633
1634     if (ret < 0)
1635     {
1636         switch(ret)
1637         {
1638         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1639         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1640         }
1641         ret = 0;
1642     }
1643     TRACE("cp %d %s -> %s\n", page, debugstr_w(src), debugstr_a(dst));
1644     return ret;
1645 }
1646
1647
1648 /***********************************************************************
1649  *           GetThreadLocale    (KERNEL32.@)
1650  *
1651  * Get the current threads locale.
1652  *
1653  * PARAMS
1654  *  None.
1655  *
1656  * RETURNS
1657  *  The LCID currently assocated with the calling thread.
1658  */
1659 LCID WINAPI GetThreadLocale(void)
1660 {
1661     LCID ret = NtCurrentTeb()->CurrentLocale;
1662     if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
1663     return ret;
1664 }
1665
1666 /**********************************************************************
1667  *           SetThreadLocale    (KERNEL32.@)
1668  *
1669  * Set the current threads locale.
1670  *
1671  * PARAMS
1672  *  lcid [I] LCID of the locale to set
1673  *
1674  * RETURNS
1675  *  Success: TRUE. The threads locale is set to lcid.
1676  *  Failure: FALSE. Use GetLastError() to determine the cause.
1677  */
1678 BOOL WINAPI SetThreadLocale( LCID lcid )
1679 {
1680     TRACE("(0x%04lX)\n", lcid);
1681
1682     lcid = ConvertDefaultLocale(lcid);
1683
1684     if (lcid != GetThreadLocale())
1685     {
1686         if (!IsValidLocale(lcid, LCID_SUPPORTED))
1687         {
1688             SetLastError(ERROR_INVALID_PARAMETER);
1689             return FALSE;
1690         }
1691
1692         NtCurrentTeb()->CurrentLocale = lcid;
1693         NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
1694     }
1695     return TRUE;
1696 }
1697
1698 /******************************************************************************
1699  *              ConvertDefaultLocale (KERNEL32.@)
1700  *
1701  * Convert a default locale identifier into a real identifier.
1702  *
1703  * PARAMS
1704  *  lcid [I] LCID identifier of the locale to convert
1705  *
1706  * RETURNS
1707  *  lcid unchanged, if not a default locale or its sublanguage is
1708  *   not SUBLANG_NEUTRAL.
1709  *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
1710  *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
1711  *  Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
1712  */
1713 LCID WINAPI ConvertDefaultLocale( LCID lcid )
1714 {
1715     LANGID langid;
1716
1717     switch (lcid)
1718     {
1719     case LOCALE_SYSTEM_DEFAULT:
1720         lcid = GetSystemDefaultLCID();
1721         break;
1722     case LOCALE_USER_DEFAULT:
1723     case LOCALE_NEUTRAL:
1724         lcid = GetUserDefaultLCID();
1725         break;
1726     default:
1727         /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
1728         langid = LANGIDFROMLCID(lcid);
1729         if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
1730         {
1731           langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
1732           lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
1733         }
1734     }
1735     return lcid;
1736 }
1737
1738
1739 /******************************************************************************
1740  *           IsValidLocale   (KERNEL32.@)
1741  *
1742  * Determine if a locale is valid.
1743  *
1744  * PARAMS
1745  *  lcid  [I] LCID of the locale to check
1746  *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
1747  *
1748  * RETURN
1749  *  TRUE,  if lcid is valid,
1750  *  FALSE, otherwise.
1751  *
1752  * NOTES
1753  *  Wine does not currently make the distinction between supported and installed. All
1754  *  languages supported are installed by default.
1755  */
1756 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
1757 {
1758     /* check if language is registered in the kernel32 resources */
1759     return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1760                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
1761 }
1762
1763
1764 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
1765                                        LPCSTR name, WORD LangID, LONG lParam )
1766 {
1767     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
1768     char buf[20];
1769
1770     sprintf(buf, "%08x", (UINT)LangID);
1771     return lpfnLocaleEnum( buf );
1772 }
1773
1774 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
1775                                        LPCWSTR name, WORD LangID, LONG lParam )
1776 {
1777     static const WCHAR formatW[] = {'%','0','8','x',0};
1778     LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
1779     WCHAR buf[20];
1780     sprintfW( buf, formatW, (UINT)LangID );
1781     return lpfnLocaleEnum( buf );
1782 }
1783
1784 /******************************************************************************
1785  *           EnumSystemLocalesA  (KERNEL32.@)
1786  *
1787  * Call a users function for each locale available on the system.
1788  *
1789  * PARAMS
1790  *  lpfnLocaleEnum [I] Callback function to call for each locale
1791  *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
1792  *
1793  * RETURNS
1794  *  Success: TRUE.
1795  *  Failure: FALSE. Use GetLastError() to determine the cause.
1796  */
1797 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
1798 {
1799     TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
1800     EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
1801                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
1802                             (LONG)lpfnLocaleEnum);
1803     return TRUE;
1804 }
1805
1806
1807 /******************************************************************************
1808  *           EnumSystemLocalesW  (KERNEL32.@)
1809  *
1810  * See EnumSystemLocalesA.
1811  */
1812 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
1813 {
1814     TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
1815     EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
1816                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
1817                             (LONG)lpfnLocaleEnum);
1818     return TRUE;
1819 }
1820
1821
1822 /***********************************************************************
1823  *           VerLanguageNameA  (KERNEL32.@)
1824  *
1825  * Get the name of a language.
1826  *
1827  * PARAMS
1828  *  wLang  [I] LANGID of the language
1829  *  szLang [O] Destination for the language name
1830  *
1831  * RETURNS
1832  *  Success: The size of the language name. If szLang is non-NULL, it is filled
1833  *           with the name.
1834  *  Failure: 0. Use GetLastError() to determine the cause.
1835  *
1836  */
1837 DWORD WINAPI VerLanguageNameA( UINT wLang, LPSTR szLang, UINT nSize )
1838 {
1839     return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
1840 }
1841
1842
1843 /***********************************************************************
1844  *           VerLanguageNameW  (KERNEL32.@)
1845  *
1846  * See VerLanguageNameA.
1847  */
1848 DWORD WINAPI VerLanguageNameW( UINT wLang, LPWSTR szLang, UINT nSize )
1849 {
1850     return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
1851 }
1852
1853
1854 /******************************************************************************
1855  *           GetStringTypeW    (KERNEL32.@)
1856  *
1857  * See GetStringTypeA.
1858  */
1859 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
1860 {
1861     if (count == -1) count = strlenW(src) + 1;
1862     switch(type)
1863     {
1864     case CT_CTYPE1:
1865         while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
1866         break;
1867     case CT_CTYPE2:
1868         while (count--) *chartype++ = get_char_typeW( *src++ ) >> 12;
1869         break;
1870     case CT_CTYPE3:
1871     {
1872         WARN("CT_CTYPE3: semi-stub.\n");
1873         while (count--)
1874         {
1875             int c = *src;
1876             WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
1877
1878             type1 = get_char_typeW( *src++ ) & 0xfff;
1879             /* try to construct type3 from type1 */
1880             if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
1881             if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
1882             if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
1883             if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
1884             if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
1885             if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
1886             if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
1887
1888             if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
1889             if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
1890             if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
1891             if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
1892             if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
1893             if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
1894             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
1895             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
1896
1897             if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
1898             if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
1899             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
1900             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
1901             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
1902             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
1903             *chartype++ = type3;
1904         }
1905         break;
1906     }
1907     default:
1908         SetLastError( ERROR_INVALID_PARAMETER );
1909         return FALSE;
1910     }
1911     return TRUE;
1912 }
1913
1914
1915 /******************************************************************************
1916  *           GetStringTypeExW    (KERNEL32.@)
1917  *
1918  * See GetStringTypeExA.
1919  */
1920 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
1921 {
1922     /* locale is ignored for Unicode */
1923     return GetStringTypeW( type, src, count, chartype );
1924 }
1925
1926
1927 /******************************************************************************
1928  *           GetStringTypeA    (KERNEL32.@)
1929  *
1930  * Get characteristics of the characters making up a string.
1931  *
1932  * PARAMS
1933  *  locale   [I] Locale Id for the string
1934  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
1935  *  src      [I] String to analyse
1936  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
1937  *  chartype [O] Destination for the calculated characteristics
1938  *
1939  * RETURNS
1940  *  Success: TRUE. chartype is filled with the requested characteristics of each char
1941  *           in src.
1942  *  Failure: FALSE. Use GetLastError() to determine the cause.
1943  */
1944 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
1945 {
1946     UINT cp;
1947     INT countW;
1948     LPWSTR srcW;
1949     BOOL ret = FALSE;
1950
1951     if(count == -1) count = strlen(src) + 1;
1952
1953     if (!(cp = get_lcid_codepage( locale )))
1954     {
1955         FIXME("For locale %04lx using current ANSI code page\n", locale);
1956         cp = GetACP();
1957     }
1958
1959     countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
1960     if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
1961     {
1962         MultiByteToWideChar(cp, 0, src, count, srcW, countW);
1963     /*
1964      * NOTE: the target buffer has 1 word for each CHARACTER in the source
1965      * string, with multibyte characters there maybe be more bytes in count
1966      * than character space in the buffer!
1967      */
1968         ret = GetStringTypeW(type, srcW, countW, chartype);
1969         HeapFree(GetProcessHeap(), 0, srcW);
1970     }
1971     return ret;
1972 }
1973
1974 /******************************************************************************
1975  *           GetStringTypeExA    (KERNEL32.@)
1976  *
1977  * Get characteristics of the characters making up a string.
1978  *
1979  * PARAMS
1980  *  locale   [I] Locale Id for the string
1981  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
1982  *  src      [I] String to analyse
1983  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
1984  *  chartype [O] Destination for the calculated characteristics
1985  *
1986  * RETURNS
1987  *  Success: TRUE. chartype is filled with the requested characteristics of each char
1988  *           in src.
1989  *  Failure: FALSE. Use GetLastError() to determine the cause.
1990  */
1991 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
1992 {
1993     return GetStringTypeA(locale, type, src, count, chartype);
1994 }
1995
1996
1997 /*************************************************************************
1998  *           LCMapStringW    (KERNEL32.@)
1999  *
2000  * See LCMapStringA.
2001  */
2002 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2003                         LPWSTR dst, INT dstlen)
2004 {
2005     LPWSTR dst_ptr;
2006
2007     if (!src || !srclen || dstlen < 0)
2008     {
2009         SetLastError(ERROR_INVALID_PARAMETER);
2010         return 0;
2011     }
2012
2013     /* mutually exclusive flags */
2014     if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2015         (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2016         (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2017         (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2018     {
2019         SetLastError(ERROR_INVALID_FLAGS);
2020         return 0;
2021     }
2022
2023     if (!dstlen) dst = NULL;
2024
2025     lcid = ConvertDefaultLocale(lcid);
2026
2027     if (flags & LCMAP_SORTKEY)
2028     {
2029         if (src == dst)
2030         {
2031             SetLastError(ERROR_INVALID_FLAGS);
2032             return 0;
2033         }
2034
2035         if (srclen < 0) srclen = strlenW(src);
2036
2037         TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
2038               lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2039
2040         return wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2041     }
2042
2043     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2044     if (flags & SORT_STRINGSORT)
2045     {
2046         SetLastError(ERROR_INVALID_FLAGS);
2047         return 0;
2048     }
2049
2050     if (srclen < 0) srclen = strlenW(src) + 1;
2051
2052     TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
2053           lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2054
2055     if (!dst) /* return required string length */
2056     {
2057         INT len;
2058
2059         for (len = 0; srclen; src++, srclen--)
2060         {
2061             WCHAR wch = *src;
2062             /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2063              * and skips white space and punctuation characters for
2064              * NORM_IGNORESYMBOLS.
2065              */
2066             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2067                 continue;
2068             len++;
2069         }
2070         return len;
2071     }
2072
2073     if (flags & LCMAP_UPPERCASE)
2074     {
2075         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2076         {
2077             WCHAR wch = *src;
2078             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2079                 continue;
2080             *dst_ptr++ = toupperW(wch);
2081             dstlen--;
2082         }
2083     }
2084     else if (flags & LCMAP_LOWERCASE)
2085     {
2086         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2087         {
2088             WCHAR wch = *src;
2089             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2090                 continue;
2091             *dst_ptr++ = tolowerW(wch);
2092             dstlen--;
2093         }
2094     }
2095     else
2096     {
2097         if (src == dst)
2098         {
2099             SetLastError(ERROR_INVALID_FLAGS);
2100             return 0;
2101         }
2102         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2103         {
2104             WCHAR wch = *src;
2105             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2106                 continue;
2107             *dst_ptr++ = wch;
2108             dstlen--;
2109         }
2110     }
2111
2112     if (srclen)
2113     {
2114         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2115         return 0;
2116     }
2117
2118     return dst_ptr - dst;
2119 }
2120
2121 /*************************************************************************
2122  *           LCMapStringA    (KERNEL32.@)
2123  *
2124  * Map characters in a locale sensitive string.
2125  *
2126  * PARAMS
2127  *  lcid   [I] LCID for the conversion.
2128  *  flags  [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2129  *  src    [I] String to map
2130  *  srclen [I] Length of src in chars, or -1 if src is NUL terminated
2131  *  dst    [O] Destination for mapped string
2132  *  dstlen [I] Length of dst in characters
2133  *
2134  * RETURNS
2135  *  Success: The length of the mapped string in dst, including the NUL terminator.
2136  *  Failure: 0. Use GetLastError() to determine the cause.
2137  */
2138 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2139                         LPSTR dst, INT dstlen)
2140 {
2141     WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2142     LPWSTR srcW, dstW;
2143     INT ret = 0, srclenW, dstlenW;
2144     UINT locale_cp = CP_ACP;
2145
2146     if (!src || !srclen || dstlen < 0)
2147     {
2148         SetLastError(ERROR_INVALID_PARAMETER);
2149         return 0;
2150     }
2151
2152     if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2153
2154     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2155     if (srclenW)
2156         srcW = bufW;
2157     else
2158     {
2159         srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2160         srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2161         if (!srcW)
2162         {
2163             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2164             return 0;
2165         }
2166         MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2167     }
2168
2169     if (flags & LCMAP_SORTKEY)
2170     {
2171         if (src == dst)
2172         {
2173             SetLastError(ERROR_INVALID_FLAGS);
2174             goto map_string_exit;
2175         }
2176         ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2177         goto map_string_exit;
2178     }
2179
2180     if (flags & SORT_STRINGSORT)
2181     {
2182         SetLastError(ERROR_INVALID_FLAGS);
2183         goto map_string_exit;
2184     }
2185
2186     dstlenW = LCMapStringW(lcid, flags, srcW, srclenW, NULL, 0);
2187     if (!dstlenW)
2188         goto map_string_exit;
2189
2190     dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2191     if (!dstW)
2192     {
2193         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2194         goto map_string_exit;
2195     }
2196
2197     LCMapStringW(lcid, flags, srcW, srclenW, dstW, dstlenW);
2198     ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2199     HeapFree(GetProcessHeap(), 0, dstW);
2200
2201 map_string_exit:
2202     if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2203     return ret;
2204 }
2205
2206 /*************************************************************************
2207  *           FoldStringA    (KERNEL32.@)
2208  *
2209  * Map characters in a string.
2210  *
2211  * PARAMS
2212  *  dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2213  *  src     [I] String to map
2214  *  srclen  [I] Length of src, or -1 if src is NUL terminated
2215  *  dst     [O] Destination for mapped string
2216  *  dstlen  [I] Length of dst, or 0 to find the required length for the mapped string
2217  *
2218  * RETURNS
2219  *  Success: The length of the string written to dst, including the terminating NUL. If
2220  *           dstlen is 0, the value returned is the same, but nothing is written to dst,
2221  *           and dst may be NULL.
2222  *  Failure: 0. Use GetLastError() to determine the cause.
2223  */
2224 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2225                        LPSTR dst, INT dstlen)
2226 {
2227     INT ret = 0, srclenW = 0;
2228     WCHAR *srcW = NULL, *dstW = NULL;
2229
2230     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2231     {
2232         SetLastError(ERROR_INVALID_PARAMETER);
2233         return 0;
2234     }
2235
2236     srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2237                                   src, srclen, NULL, 0);
2238     srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2239
2240     if (!srcW)
2241     {
2242         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2243         goto FoldStringA_exit;
2244     }
2245
2246     MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2247                         src, srclen, srcW, srclenW);
2248
2249     dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2250
2251     ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2252     if (ret && dstlen)
2253     {
2254         dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2255
2256         if (!dstW)
2257         {
2258             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2259             goto FoldStringA_exit;
2260         }
2261
2262         ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2263         if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2264         {
2265             ret = 0;
2266             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2267         }
2268     }
2269
2270     HeapFree(GetProcessHeap(), 0, dstW);
2271
2272 FoldStringA_exit:
2273     HeapFree(GetProcessHeap(), 0, srcW);
2274     return ret;
2275 }
2276
2277 /*************************************************************************
2278  *           FoldStringW    (KERNEL32.@)
2279  *
2280  * See FoldStringA.
2281  */
2282 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2283                        LPWSTR dst, INT dstlen)
2284 {
2285     int ret;
2286
2287     switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2288     {
2289     case 0:
2290         if (dwFlags)
2291           break;
2292         /* Fall through for dwFlags == 0 */
2293     case MAP_PRECOMPOSED|MAP_COMPOSITE:
2294     case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2295     case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2296         SetLastError(ERROR_INVALID_FLAGS);
2297         return 0;
2298     }
2299
2300     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2301     {
2302         SetLastError(ERROR_INVALID_PARAMETER);
2303         return 0;
2304     }
2305
2306     ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2307     if (!ret)
2308         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2309     return ret;
2310 }
2311
2312 /******************************************************************************
2313  *           CompareStringW    (KERNEL32.@)
2314  *
2315  * See CompareStringA.
2316  */
2317 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2318                           LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2319 {
2320     INT ret;
2321
2322     if (!str1 || !str2)
2323     {
2324         SetLastError(ERROR_INVALID_PARAMETER);
2325         return 0;
2326     }
2327
2328     if( style & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2329         SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2330     {
2331         SetLastError(ERROR_INVALID_FLAGS);
2332         return 0;
2333     }
2334
2335     if (style & 0x10000000)
2336         FIXME("Ignoring unknown style 0x10000000\n");
2337
2338     if (len1 < 0) len1 = strlenW(str1);
2339     if (len2 < 0) len2 = strlenW(str2);
2340
2341     ret = wine_compare_string(style, str1, len1, str2, len2);
2342
2343     if (ret) /* need to translate result */
2344         return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2345     return CSTR_EQUAL;
2346 }
2347
2348 /******************************************************************************
2349  *           CompareStringA    (KERNEL32.@)
2350  *
2351  * Compare two locale sensitive strings.
2352  *
2353  * PARAMS
2354  *  lcid  [I] LCID for the comparison
2355  *  style [I] Flags for the comparison (NORM_ constants from "winnls.h").
2356  *  str1  [I] First string to compare
2357  *  len1  [I] Length of str1, or -1 if str1 is NUL terminated
2358  *  str2  [I] Second string to compare
2359  *  len2  [I] Length of str2, or -1 if str2 is NUL terminated
2360  *
2361  * RETURNS
2362  *  Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2363  *           str2 is less than, equal to or greater than str1 respectively.
2364  *  Failure: FALSE. Use GetLastError() to determine the cause.
2365  */
2366 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2367                           LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2368 {
2369     WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2370     WCHAR *buf2W = buf1W + 130;
2371     LPWSTR str1W, str2W;
2372     INT len1W, len2W, ret;
2373     UINT locale_cp = CP_ACP;
2374
2375     if (!str1 || !str2)
2376     {
2377         SetLastError(ERROR_INVALID_PARAMETER);
2378         return 0;
2379     }
2380     if (len1 < 0) len1 = strlen(str1);
2381     if (len2 < 0) len2 = strlen(str2);
2382
2383     if (!(style & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2384
2385     len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2386     if (len1W)
2387         str1W = buf1W;
2388     else
2389     {
2390         len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2391         str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2392         if (!str1W)
2393         {
2394             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2395             return 0;
2396         }
2397         MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2398     }
2399     len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2400     if (len2W)
2401         str2W = buf2W;
2402     else
2403     {
2404         len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2405         str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2406         if (!str2W)
2407         {
2408             if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2409             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2410             return 0;
2411         }
2412         MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2413     }
2414
2415     ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2416
2417     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2418     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2419     return ret;
2420 }
2421
2422 /*************************************************************************
2423  *           lstrcmp     (KERNEL32.@)
2424  *           lstrcmpA    (KERNEL32.@)
2425  *
2426  * Compare two strings using the current thread locale.
2427  *
2428  * PARAMS
2429  *  str1  [I] First string to compare
2430  *  str2  [I] Second string to compare
2431  *
2432  * RETURNS
2433  *  Success: A number less than, equal to or greater than 0 depending on whether
2434  *           str2 is less than, equal to or greater than str1 respectively.
2435  *  Failure: FALSE. Use GetLastError() to determine the cause.
2436  */
2437 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
2438 {
2439     int ret;
2440     
2441     if ((str1 == NULL) && (str2 == NULL)) return 0;
2442     if (str1 == NULL) return -1;
2443     if (str2 == NULL) return 1;
2444
2445     ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2446     if (ret) ret -= 2;
2447     
2448     return ret;
2449 }
2450
2451 /*************************************************************************
2452  *           lstrcmpi     (KERNEL32.@)
2453  *           lstrcmpiA    (KERNEL32.@)
2454  *
2455  * Compare two strings using the current thread locale, ignoring case.
2456  *
2457  * PARAMS
2458  *  str1  [I] First string to compare
2459  *  str2  [I] Second string to compare
2460  *
2461  * RETURNS
2462  *  Success: A number less than, equal to or greater than 0 depending on whether
2463  *           str2 is less than, equal to or greater than str1 respectively.
2464  *  Failure: FALSE. Use GetLastError() to determine the cause.
2465  */
2466 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
2467 {
2468     int ret;
2469     
2470     if ((str1 == NULL) && (str2 == NULL)) return 0;
2471     if (str1 == NULL) return -1;
2472     if (str2 == NULL) return 1;
2473
2474     ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2475     if (ret) ret -= 2;
2476     
2477     return ret;
2478 }
2479
2480 /*************************************************************************
2481  *           lstrcmpW    (KERNEL32.@)
2482  *
2483  * See lstrcmpA.
2484  */
2485 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
2486 {
2487     int ret;
2488
2489     if ((str1 == NULL) && (str2 == NULL)) return 0;
2490     if (str1 == NULL) return -1;
2491     if (str2 == NULL) return 1;
2492
2493     ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
2494     if (ret) ret -= 2;
2495     
2496     return ret;
2497 }
2498
2499 /*************************************************************************
2500  *           lstrcmpiW    (KERNEL32.@)
2501  *
2502  * See lstrcmpiA.
2503  */
2504 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
2505 {
2506     int ret;
2507     
2508     if ((str1 == NULL) && (str2 == NULL)) return 0;
2509     if (str1 == NULL) return -1;
2510     if (str2 == NULL) return 1;
2511
2512     ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
2513     if (ret) ret -= 2;
2514     
2515     return ret;
2516 }
2517
2518 /******************************************************************************
2519  *              LOCALE_Init
2520  */
2521 void LOCALE_Init(void)
2522 {
2523     extern void __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
2524                                        const union cptable *unix_cp );
2525
2526     UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp = ~0U;
2527     LCID lcid;
2528
2529     lcid = get_env_lcid( NULL, NULL );
2530     NtSetDefaultLocale( TRUE, lcid );
2531
2532     lcid = get_env_lcid( NULL, "LC_MESSAGES" );
2533     NtSetDefaultUILanguage( LANGIDFROMLCID(lcid) );
2534
2535     lcid = get_env_lcid( &unix_cp, "LC_CTYPE" );
2536     NtSetDefaultLocale( FALSE, lcid );
2537
2538     ansi_cp = get_lcid_codepage(lcid);
2539     GetLocaleInfoW( lcid, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
2540                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
2541     GetLocaleInfoW( lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
2542                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
2543     if (unix_cp == ~0U)
2544         GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
2545                     (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
2546
2547     if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
2548         ansi_cptable = wine_cp_get_table( 1252 );
2549     if (!(oem_cptable = wine_cp_get_table( oem_cp )))
2550         oem_cptable  = wine_cp_get_table( 437 );
2551     if (!(mac_cptable = wine_cp_get_table( mac_cp )))
2552         mac_cptable  = wine_cp_get_table( 10000 );
2553     if (unix_cp != CP_UTF8)
2554     {
2555         if (!(unix_cptable = wine_cp_get_table( unix_cp )))
2556             unix_cptable  = wine_cp_get_table( 28591 );
2557     }
2558
2559     __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
2560
2561     TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
2562            ansi_cptable->info.codepage, oem_cptable->info.codepage,
2563            mac_cptable->info.codepage, unix_cp );
2564 }
2565
2566 static HKEY NLS_RegOpenKey(HKEY hRootKey, LPCWSTR szKeyName)
2567 {
2568     UNICODE_STRING keyName;
2569     OBJECT_ATTRIBUTES attr;
2570     HKEY hkey;
2571
2572     RtlInitUnicodeString( &keyName, szKeyName );
2573     InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
2574
2575     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS)
2576         hkey = 0;
2577
2578     return hkey;
2579 }
2580
2581 static HKEY NLS_RegOpenSubKey(HKEY hRootKey, LPCWSTR szKeyName)
2582 {
2583     HKEY hKey = NLS_RegOpenKey(hRootKey, szKeyName);
2584
2585     if (hRootKey)
2586         NtClose( hRootKey );
2587
2588     return hKey;
2589 }
2590
2591 static BOOL NLS_RegEnumSubKey(HKEY hKey, UINT ulIndex, LPWSTR szKeyName,
2592                               ULONG keyNameSize)
2593 {
2594     BYTE buffer[80];
2595     KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
2596     DWORD dwLen;
2597
2598     if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
2599                         sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
2600         info->NameLength > keyNameSize)
2601     {
2602         return FALSE;
2603     }
2604
2605     TRACE("info->Name %s info->NameLength %ld\n", debugstr_w(info->Name), info->NameLength);
2606
2607     memcpy( szKeyName, info->Name, info->NameLength);
2608     szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
2609
2610     TRACE("returning %s\n", debugstr_w(szKeyName));
2611     return TRUE;
2612 }
2613
2614 static BOOL NLS_RegEnumValue(HKEY hKey, UINT ulIndex,
2615                              LPWSTR szValueName, ULONG valueNameSize,
2616                              LPWSTR szValueData, ULONG valueDataSize)
2617 {
2618     BYTE buffer[80];
2619     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
2620     DWORD dwLen;
2621
2622     if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
2623         buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
2624         info->NameLength > valueNameSize ||
2625         info->DataLength > valueDataSize)
2626     {
2627         return FALSE;
2628     }
2629
2630     TRACE("info->Name %s info->DataLength %ld\n", debugstr_w(info->Name), info->DataLength);
2631
2632     memcpy( szValueName, info->Name, info->NameLength);
2633     szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
2634     memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
2635     szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
2636
2637     TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
2638     return TRUE;
2639 }
2640
2641 static BOOL NLS_RegGetDword(HKEY hKey, LPCWSTR szValueName, DWORD *lpVal)
2642 {
2643     BYTE buffer[128];
2644     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
2645     DWORD dwSize = sizeof(buffer);
2646     UNICODE_STRING valueName;
2647
2648     RtlInitUnicodeString( &valueName, szValueName );
2649
2650     TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
2651     if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
2652                          buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
2653         info->DataLength == sizeof(DWORD))
2654     {
2655         memcpy(lpVal, info->Data, sizeof(DWORD));
2656         return TRUE;
2657     }
2658
2659     return FALSE;
2660 }
2661
2662 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
2663 {
2664     LANGID  langId;
2665     LPCWSTR szResourceName = (LPCWSTR)(((lgrpid + 0x2000) >> 4) + 1);
2666     HRSRC   hResource;
2667     BOOL    bRet = FALSE;
2668
2669     /* FIXME: Is it correct to use the system default langid? */
2670     langId = GetSystemDefaultLangID();
2671
2672     if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
2673         langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
2674
2675     hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
2676
2677     if (hResource)
2678     {
2679         HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
2680
2681         if (hResDir)
2682         {
2683             ULONG   iResourceIndex = lgrpid & 0xf;
2684             LPCWSTR lpResEntry = LockResource( hResDir );
2685             ULONG   i;
2686
2687             for (i = 0; i < iResourceIndex; i++)
2688                 lpResEntry += *lpResEntry + 1;
2689
2690             if (*lpResEntry < nameSize)
2691             {
2692                 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
2693                 szName[*lpResEntry] = '\0';
2694                 bRet = TRUE;
2695             }
2696
2697         }
2698         FreeResource( hResource );
2699     }
2700     return bRet;
2701 }
2702
2703 /* Registry keys for NLS related information */
2704 static const WCHAR szLangGroupsKeyName[] = {
2705     'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s','\0'
2706 };
2707
2708 static const WCHAR szCountryListName[] = {
2709     'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
2710     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
2711     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2712     'T','e','l','e','p','h','o','n','y','\\',
2713     'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
2714 };
2715
2716
2717 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
2718 typedef struct
2719 {
2720   LANGUAGEGROUP_ENUMPROCA procA;
2721   LANGUAGEGROUP_ENUMPROCW procW;
2722   DWORD    dwFlags;
2723   LONG_PTR lParam;
2724 } ENUMLANGUAGEGROUP_CALLBACKS;
2725
2726 /* Internal implementation of EnumSystemLanguageGroupsA/W */
2727 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
2728 {
2729     WCHAR szNumber[10], szValue[4];
2730     HKEY hKey;
2731     BOOL bContinue = TRUE;
2732     ULONG ulIndex = 0;
2733
2734     if (!lpProcs)
2735     {
2736         SetLastError(ERROR_INVALID_PARAMETER);
2737         return FALSE;
2738     }
2739
2740     switch (lpProcs->dwFlags)
2741     {
2742     case 0:
2743         /* Default to LGRPID_INSTALLED */
2744         lpProcs->dwFlags = LGRPID_INSTALLED;
2745         /* Fall through... */
2746     case LGRPID_INSTALLED:
2747     case LGRPID_SUPPORTED:
2748         break;
2749     default:
2750         SetLastError(ERROR_INVALID_FLAGS);
2751         return FALSE;
2752     }
2753
2754     hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
2755
2756     if (!hKey)
2757         FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
2758
2759     while (bContinue)
2760     {
2761         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
2762                               szValue, sizeof(szValue) ))
2763         {
2764             BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
2765             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
2766
2767             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
2768                    bInstalled ? "" : "not ");
2769
2770             if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
2771             {
2772                 WCHAR szGrpName[48];
2773
2774                 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
2775                     szGrpName[0] = '\0';
2776
2777                 if (lpProcs->procW)
2778                     bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
2779                                                 lpProcs->lParam );
2780                 else
2781                 {
2782                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
2783                     char szGrpNameA[48];
2784
2785                     /* FIXME: MSDN doesn't say which code page the W->A translation uses,
2786                      *        or whether the language names are ever localised. Assume CP_ACP.
2787                      */
2788
2789                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
2790                     WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
2791
2792                     bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
2793                                                 lpProcs->lParam );
2794                 }
2795             }
2796
2797             ulIndex++;
2798         }
2799         else
2800             bContinue = FALSE;
2801
2802         if (!bContinue)
2803             break;
2804     }
2805
2806     if (hKey)
2807         NtClose( hKey );
2808
2809     return TRUE;
2810 }
2811
2812 /******************************************************************************
2813  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
2814  *
2815  * Call a users function for each language group available on the system.
2816  *
2817  * PARAMS
2818  *  pLangGrpEnumProc [I] Callback function to call for each language group
2819  *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
2820  *  lParam           [I] User parameter to pass to pLangGrpEnumProc
2821  *
2822  * RETURNS
2823  *  Success: TRUE.
2824  *  Failure: FALSE. Use GetLastError() to determine the cause.
2825  */
2826 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
2827                                       DWORD dwFlags, LONG_PTR lParam)
2828 {
2829     ENUMLANGUAGEGROUP_CALLBACKS procs;
2830
2831     TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
2832
2833     procs.procA = pLangGrpEnumProc;
2834     procs.procW = NULL;
2835     procs.dwFlags = dwFlags;
2836     procs.lParam = lParam;
2837
2838     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
2839 }
2840
2841 /******************************************************************************
2842  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
2843  *
2844  * See EnumSystemLanguageGroupsA.
2845  */
2846 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
2847                                       DWORD dwFlags, LONG_PTR lParam)
2848 {
2849     ENUMLANGUAGEGROUP_CALLBACKS procs;
2850
2851     TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
2852
2853     procs.procA = NULL;
2854     procs.procW = pLangGrpEnumProc;
2855     procs.dwFlags = dwFlags;
2856     procs.lParam = lParam;
2857
2858     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
2859 }
2860
2861 /******************************************************************************
2862  *           IsValidLanguageGroup    (KERNEL32.@)
2863  *
2864  * Determine if a language group is supported and/or installed.
2865  *
2866  * PARAMS
2867  *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
2868  *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
2869  *
2870  * RETURNS
2871  *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
2872  *  FALSE otherwise.
2873  */
2874 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
2875 {
2876     static const WCHAR szFormat[] = { '%','x','\0' };
2877     WCHAR szValueName[16], szValue[2];
2878     BOOL bSupported = FALSE, bInstalled = FALSE;
2879     HKEY hKey;
2880
2881
2882     switch (dwFlags)
2883     {
2884     case LGRPID_INSTALLED:
2885     case LGRPID_SUPPORTED:
2886
2887         hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
2888
2889         sprintfW( szValueName, szFormat, lgrpid );
2890
2891         if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)&szValue ))
2892         {
2893             bSupported = TRUE;
2894
2895             if (szValue[0] == '1')
2896                 bInstalled = TRUE;
2897         }
2898
2899         if (hKey)
2900             NtClose( hKey );
2901
2902         break;
2903     }
2904
2905     if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
2906         (dwFlags == LGRPID_INSTALLED && bInstalled))
2907         return TRUE;
2908
2909     return FALSE;
2910 }
2911
2912 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
2913 typedef struct
2914 {
2915   LANGGROUPLOCALE_ENUMPROCA procA;
2916   LANGGROUPLOCALE_ENUMPROCW procW;
2917   DWORD    dwFlags;
2918   LGRPID   lgrpid;
2919   LONG_PTR lParam;
2920 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
2921
2922 /* Internal implementation of EnumLanguageGrouplocalesA/W */
2923 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
2924 {
2925     static const WCHAR szLocaleKeyName[] = {
2926       'L','o','c','a','l','e','\0'
2927     };
2928     static const WCHAR szAlternateSortsKeyName[] = {
2929       'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
2930     };
2931     WCHAR szNumber[10], szValue[4];
2932     HKEY hKey;
2933     BOOL bContinue = TRUE, bAlternate = FALSE;
2934     LGRPID lgrpid;
2935     ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
2936
2937     if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
2938     {
2939         SetLastError(ERROR_INVALID_PARAMETER);
2940         return FALSE;
2941     }
2942
2943     if (lpProcs->dwFlags)
2944     {
2945         SetLastError(ERROR_INVALID_FLAGS);
2946         return FALSE;
2947     }
2948
2949     hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLocaleKeyName );
2950
2951     if (!hKey)
2952         WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
2953
2954     while (bContinue)
2955     {
2956         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
2957                               szValue, sizeof(szValue) ))
2958         {
2959             lgrpid = strtoulW( szValue, NULL, 16 );
2960
2961             TRACE("lcid %s, grpid %ld (%smatched)\n", debugstr_w(szNumber),
2962                    lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
2963
2964             if (lgrpid == lpProcs->lgrpid)
2965             {
2966                 LCID lcid;
2967
2968                 lcid = strtoulW( szNumber, NULL, 16 );
2969
2970                 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
2971                  * '00000437          ;Georgian'
2972                  * At present we only pass the LCID string.
2973                  */
2974
2975                 if (lpProcs->procW)
2976                     bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
2977                 else
2978                 {
2979                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
2980
2981                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
2982
2983                     bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
2984                 }
2985             }
2986
2987             ulIndex++;
2988         }
2989         else
2990         {
2991             /* Finished enumerating this key */
2992             if (!bAlternate)
2993             {
2994                 /* Enumerate alternate sorts also */
2995                 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
2996                 bAlternate = TRUE;
2997                 ulIndex = 0;
2998             }
2999             else
3000                 bContinue = FALSE; /* Finished both keys */
3001         }
3002
3003         if (!bContinue)
3004             break;
3005     }
3006
3007     if (hKey)
3008         NtClose( hKey );
3009
3010     return TRUE;
3011 }
3012
3013 /******************************************************************************
3014  *           EnumLanguageGroupLocalesA    (KERNEL32.@)
3015  *
3016  * Call a users function for every locale in a language group available on the system.
3017  *
3018  * PARAMS
3019  *  pLangGrpLcEnumProc [I] Callback function to call for each locale
3020  *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
3021  *  dwFlags            [I] Reserved, set to 0
3022  *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
3023  *
3024  * RETURNS
3025  *  Success: TRUE.
3026  *  Failure: FALSE. Use GetLastError() to determine the cause.
3027  */
3028 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3029                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3030 {
3031     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3032
3033     TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3034
3035     callbacks.procA   = pLangGrpLcEnumProc;
3036     callbacks.procW   = NULL;
3037     callbacks.dwFlags = dwFlags;
3038     callbacks.lgrpid  = lgrpid;
3039     callbacks.lParam  = lParam;
3040
3041     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3042 }
3043
3044 /******************************************************************************
3045  *           EnumLanguageGroupLocalesW    (KERNEL32.@)
3046  *
3047  * See EnumLanguageGroupLocalesA.
3048  */
3049 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3050                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3051 {
3052     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3053
3054     TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3055
3056     callbacks.procA   = NULL;
3057     callbacks.procW   = pLangGrpLcEnumProc;
3058     callbacks.dwFlags = dwFlags;
3059     callbacks.lgrpid  = lgrpid;
3060     callbacks.lParam  = lParam;
3061
3062     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3063 }
3064
3065 /******************************************************************************
3066  *           EnumSystemGeoID    (KERNEL32.@)
3067  *
3068  * Call a users function for every location available on the system.
3069  *
3070  * PARAMS
3071  *  geoclass     [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3072  *  reserved     [I] Reserved, set to 0
3073  *  pGeoEnumProc [I] Callback function to call for each location
3074  *
3075  * RETURNS
3076  *  Success: TRUE.
3077  *  Failure: FALSE. Use GetLastError() to determine the cause.
3078  */
3079 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3080 {
3081     static const WCHAR szCountryCodeValueName[] = {
3082       'C','o','u','n','t','r','y','C','o','d','e','\0'
3083     };
3084     WCHAR szNumber[10];
3085     HKEY hKey;
3086     ULONG ulIndex = 0;
3087
3088     TRACE("(0x%08lX,0x%08lX,%p)\n", geoclass, reserved, pGeoEnumProc);
3089
3090     if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3091     {
3092         SetLastError(ERROR_INVALID_PARAMETER);
3093         return FALSE;
3094     }
3095
3096     hKey = NLS_RegOpenKey( 0, szCountryListName );
3097
3098     while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3099     {
3100         BOOL bContinue = TRUE;
3101         DWORD dwGeoId;
3102         HKEY hSubKey = NLS_RegOpenKey( hKey, szNumber );
3103
3104         if (hSubKey)
3105         {
3106             if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3107             {
3108                 TRACE("Got geoid %ld\n", dwGeoId);
3109
3110                 if (!pGeoEnumProc( dwGeoId ))
3111                     bContinue = FALSE;
3112             }
3113
3114             NtClose( hSubKey );
3115         }
3116
3117         if (!bContinue)
3118             break;
3119
3120         ulIndex++;
3121     }
3122
3123     if (hKey)
3124         NtClose( hKey );
3125
3126     return TRUE;
3127 }
3128
3129 /******************************************************************************
3130  *           InvalidateNLSCache           (KERNEL32.@)
3131  *
3132  * Invalidate the cache of NLS values.
3133  *
3134  * PARAMS
3135  *  None.
3136  *
3137  * RETURNS
3138  *  Success: TRUE.
3139  *  Failure: FALSE.
3140  */
3141 BOOL WINAPI InvalidateNLSCache(void)
3142 {
3143   FIXME("() stub\n");
3144   return FALSE;
3145 }
3146
3147 /******************************************************************************
3148  *           GetUserGeoID (KERNEL32.@)
3149  */
3150 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3151 {
3152     FIXME("%ld\n",GeoClass);
3153     return GEOID_NOT_AVAILABLE;
3154 }
3155
3156 /******************************************************************************
3157  *           SetUserGeoID (KERNEL32.@)
3158  */
3159 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3160 {
3161     FIXME("%ld\n",GeoID);
3162     return FALSE;
3163 }
3164
3165 /******************************************************************************
3166  *           EnumUILanguagesA (KERNEL32.@)
3167  */
3168 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3169 {
3170     static char value[] = "0409";
3171
3172     if(!pUILangEnumProc) {
3173         SetLastError(ERROR_INVALID_PARAMETER);
3174         return FALSE;
3175     }
3176     if(dwFlags) {
3177         SetLastError(ERROR_INVALID_FLAGS);
3178         return FALSE;
3179     }
3180
3181     FIXME("%p, %lx, %lx calling pUILangEnumProc with %s\n",
3182           pUILangEnumProc, dwFlags, lParam, debugstr_a(value));
3183
3184     pUILangEnumProc( value, lParam );
3185     return(TRUE);
3186 }
3187
3188 /******************************************************************************
3189  *           EnumUILanguagesW (KERNEL32.@)
3190  */
3191 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3192 {
3193     static WCHAR value[] = {'0','4','0','9',0};
3194
3195     if(!pUILangEnumProc) {
3196         SetLastError(ERROR_INVALID_PARAMETER);
3197         return FALSE;
3198     }
3199     if(dwFlags) {
3200         SetLastError(ERROR_INVALID_FLAGS);
3201         return FALSE;
3202     }
3203
3204     FIXME("%p, %lx, %lx calling pUILangEnumProc with %s\n",
3205           pUILangEnumProc, dwFlags, lParam, debugstr_w(value));
3206
3207     pUILangEnumProc( value, lParam );
3208     return(TRUE);
3209 }