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