Stubs for job control.
[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  * Get the default language Id for the current user.
531  *
532  * PARAMS
533  *  None.
534  *
535  * RETURNS
536  *  The current LANGID of the default language for the current user.
537  */
538 LANGID WINAPI GetUserDefaultLangID(void)
539 {
540     return LANGIDFROMLCID(GetUserDefaultLCID());
541 }
542
543
544 /***********************************************************************
545  *              GetSystemDefaultLangID (KERNEL32.@)
546  *
547  * Get the default language Id for the system.
548  *
549  * PARAMS
550  *  None.
551  *
552  * RETURNS
553  *  The current LANGID of the default language for the system.
554  */
555 LANGID WINAPI GetSystemDefaultLangID(void)
556 {
557     return GetUserDefaultLangID();
558 }
559
560
561 /***********************************************************************
562  *              GetUserDefaultLCID (KERNEL32.@)
563  *
564  * Get the default locale Id for the current user.
565  *
566  * PARAMS
567  *  None.
568  *
569  * RETURNS
570  *  The current LCID of the default locale for the current user.
571  */
572 LCID WINAPI GetUserDefaultLCID(void)
573 {
574     LCID lcid;
575     NtQueryDefaultLocale( TRUE, &lcid );
576     return lcid;
577 }
578
579
580 /***********************************************************************
581  *              GetSystemDefaultLCID (KERNEL32.@)
582  *
583  * Get the default locale Id for the system.
584  *
585  * PARAMS
586  *  None.
587  *
588  * RETURNS
589  *  The current LCID of the default locale for the system.
590  */
591 LCID WINAPI GetSystemDefaultLCID(void)
592 {
593     LCID lcid;
594     NtQueryDefaultLocale( FALSE, &lcid );
595     return lcid;
596 }
597
598
599 /***********************************************************************
600  *              GetUserDefaultUILanguage (KERNEL32.@)
601  *
602  * Get the default user interface language Id for the current user.
603  *
604  * PARAMS
605  *  None.
606  *
607  * RETURNS
608  *  The current LANGID of the default UI language for the current user.
609  */
610 LANGID WINAPI GetUserDefaultUILanguage(void)
611 {
612     return GetUserDefaultLangID();
613 }
614
615
616 /***********************************************************************
617  *              GetSystemDefaultUILanguage (KERNEL32.@)
618  *
619  * Get the default user interface language Id for the system.
620  *
621  * PARAMS
622  *  None.
623  *
624  * RETURNS
625  *  The current LANGID of the default UI language for the system. This is
626  *  typically the same language used during the installation process.
627  */
628 LANGID WINAPI GetSystemDefaultUILanguage(void)
629 {
630     return GetSystemDefaultLangID();
631 }
632
633
634 /******************************************************************************
635  *              get_locale_value_name
636  *
637  * Gets the registry value name for a given lctype.
638  */
639 static const WCHAR *get_locale_value_name( DWORD lctype )
640 {
641     static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
642     static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
643     static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
644     static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
645     static const WCHAR iDateW[] = {'i','D','a','t','e',0};
646     static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
647     static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
648     static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
649     static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
650     static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
651     static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
652     static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
653     static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
654     static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
655     static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
656     static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
657     static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
658     static const WCHAR s1159W[] = {'s','1','1','5','9',0};
659     static const WCHAR s2359W[] = {'s','2','3','5','9',0};
660     static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
661     static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
662     static const WCHAR sDateW[] = {'s','D','a','t','e',0};
663     static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
664     static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
665     static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
666     static const WCHAR sListW[] = {'s','L','i','s','t',0};
667     static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
668     static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
669     static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
670     static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
671     static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
672     static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
673     static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
674     static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
675     static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
676     static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
677     static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
678     static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
679     static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
680
681     switch (lctype & ~LOCALE_LOCALEINFOFLAGSMASK)
682     {
683     /* These values are used by SetLocaleInfo and GetLocaleInfo, and
684      * the values are stored in the registry, confirmed under Windows.
685      */
686     case LOCALE_ICALENDARTYPE:    return iCalendarTypeW;
687     case LOCALE_ICURRDIGITS:      return iCurrDigitsW;
688     case LOCALE_ICURRENCY:        return iCurrencyW;
689     case LOCALE_IDIGITS:          return iDigitsW;
690     case LOCALE_IFIRSTDAYOFWEEK:  return iFirstDayOfWeekW;
691     case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
692     case LOCALE_ILZERO:           return iLZeroW;
693     case LOCALE_IMEASURE:         return iMeasureW;
694     case LOCALE_INEGCURR:         return iNegCurrW;
695     case LOCALE_INEGNUMBER:       return iNegNumberW;
696     case LOCALE_IPAPERSIZE:       return iPaperSizeW;
697     case LOCALE_ITIME:            return iTimeW;
698     case LOCALE_S1159:            return s1159W;
699     case LOCALE_S2359:            return s2359W;
700     case LOCALE_SCURRENCY:        return sCurrencyW;
701     case LOCALE_SDATE:            return sDateW;
702     case LOCALE_SDECIMAL:         return sDecimalW;
703     case LOCALE_SGROUPING:        return sGroupingW;
704     case LOCALE_SLIST:            return sListW;
705     case LOCALE_SLONGDATE:        return sLongDateW;
706     case LOCALE_SMONDECIMALSEP:   return sMonDecimalSepW;
707     case LOCALE_SMONGROUPING:     return sMonGroupingW;
708     case LOCALE_SMONTHOUSANDSEP:  return sMonThousandSepW;
709     case LOCALE_SNEGATIVESIGN:    return sNegativeSignW;
710     case LOCALE_SPOSITIVESIGN:    return sPositiveSignW;
711     case LOCALE_SSHORTDATE:       return sShortDateW;
712     case LOCALE_STHOUSAND:        return sThousandW;
713     case LOCALE_STIME:            return sTimeW;
714     case LOCALE_STIMEFORMAT:      return sTimeFormatW;
715     case LOCALE_SYEARMONTH:       return sYearMonthW;
716
717     /* The following are not listed under MSDN as supported,
718      * but seem to be used and also stored in the registry.
719      */
720     case LOCALE_ICOUNTRY:         return iCountryW;
721     case LOCALE_IDATE:            return iDateW;
722     case LOCALE_ILDATE:           return iLDateW;
723     case LOCALE_ITLZERO:          return iTLZeroW;
724     case LOCALE_SCOUNTRY:         return sCountryW;
725     case LOCALE_SLANGUAGE:        return sLanguageW;
726
727     /* The following are used in XP and later */
728     case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
729     case LOCALE_SNATIVEDIGITS:      return sNativeDigitsW;
730     case LOCALE_ITIMEMARKPOSN:      return iTimePrefixW;
731     }
732     return NULL;
733 }
734
735
736 /******************************************************************************
737  *              get_registry_locale_info
738  *
739  * Retrieve user-modified locale info from the registry.
740  * Return length, 0 on error, -1 if not found.
741  */
742 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
743 {
744     DWORD size;
745     INT ret;
746     HKEY hkey;
747     NTSTATUS status;
748     UNICODE_STRING nameW;
749     KEY_VALUE_PARTIAL_INFORMATION *info;
750     static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
751
752     if (!(hkey = create_registry_key())) return -1;
753
754     RtlInitUnicodeString( &nameW, value );
755     size = info_size + len * sizeof(WCHAR);
756
757     if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
758     {
759         NtClose( hkey );
760         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
761         return 0;
762     }
763
764     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
765     if (status == STATUS_BUFFER_OVERFLOW && !buffer) status = 0;
766
767     if (!status)
768     {
769         ret = (size - info_size) / sizeof(WCHAR);
770         /* append terminating null if needed */
771         if (!ret || ((WCHAR *)info->Data)[ret-1])
772         {
773             if (ret < len || !buffer) ret++;
774             else
775             {
776                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
777                 ret = 0;
778             }
779         }
780         if (ret && buffer)
781         {
782             memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
783             buffer[ret-1] = 0;
784         }
785     }
786     else
787     {
788         if (status == STATUS_OBJECT_NAME_NOT_FOUND) ret = -1;
789         else
790         {
791             SetLastError( RtlNtStatusToDosError(status) );
792             ret = 0;
793         }
794     }
795     NtClose( hkey );
796     HeapFree( GetProcessHeap(), 0, info );
797     return ret;
798 }
799
800
801 /******************************************************************************
802  *              GetLocaleInfoA (KERNEL32.@)
803  *
804  * Get information about an aspect of a locale.
805  *
806  * PARAMS
807  *  lcid   [I] LCID of the locale
808  *  lctype [I] LCTYPE_ flags from "winnls.h"
809  *  buffer [O] Destination for the information
810  *  len    [I] Length of buffer in characters
811  *
812  * RETURNS
813  *  Success: The size of the data requested. If buffer is non-NULL, it is filled
814  *           with the information.
815  *  Failure: 0. Use GetLastError() to determine the cause.
816  *
817  * NOTES
818  *  - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
819  *  - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
820  *    which is a bit string.
821  */
822 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
823 {
824     WCHAR *bufferW;
825     INT lenW, ret;
826
827     if (len < 0 || (len && !buffer))
828     {
829         SetLastError( ERROR_INVALID_PARAMETER );
830         return 0;
831     }
832     if (!len) buffer = NULL;
833
834     if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
835
836     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
837     {
838         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
839         return 0;
840     }
841     if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
842     {
843         if ((lctype & LOCALE_RETURN_NUMBER) ||
844             ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
845         {
846             /* it's not an ASCII string, just bytes */
847             ret *= sizeof(WCHAR);
848             if (buffer)
849             {
850                 if (ret <= len) memcpy( buffer, bufferW, ret );
851                 else
852                 {
853                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
854                     ret = 0;
855                 }
856             }
857         }
858         else
859         {
860             UINT codepage = CP_ACP;
861             if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
862             ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
863         }
864     }
865     HeapFree( GetProcessHeap(), 0, bufferW );
866     return ret;
867 }
868
869
870 /******************************************************************************
871  *              GetLocaleInfoW (KERNEL32.@)
872  *
873  * See GetLocaleInfoA.
874  */
875 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
876 {
877     LANGID lang_id;
878     HRSRC hrsrc;
879     HGLOBAL hmem;
880     HMODULE hModule;
881     INT ret;
882     UINT lcflags;
883     const WCHAR *p;
884     unsigned int i;
885
886     if (len < 0 || (len && !buffer))
887     {
888         SetLastError( ERROR_INVALID_PARAMETER );
889         return 0;
890     }
891     if (!len) buffer = NULL;
892
893     lcid = ConvertDefaultLocale(lcid);
894
895     lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
896     lctype &= ~LOCALE_LOCALEINFOFLAGSMASK;
897
898     /* first check for overrides in the registry */
899
900     if (!(lcflags & LOCALE_NOUSEROVERRIDE) && lcid == GetUserDefaultLCID())
901     {
902         const WCHAR *value = get_locale_value_name(lctype);
903
904         if (value && ((ret = get_registry_locale_info( value, buffer, len )) != -1)) return ret;
905     }
906
907     /* now load it from kernel resources */
908
909     lang_id = LANGIDFROMLCID( lcid );
910
911     /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
912     if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
913         lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
914
915     hModule = GetModuleHandleW( kernel32W );
916     if (!(hrsrc = FindResourceExW( hModule, (LPWSTR)RT_STRING, (LPCWSTR)((lctype >> 4) + 1), lang_id )))
917     {
918         SetLastError( ERROR_INVALID_FLAGS );  /* no such lctype */
919         return 0;
920     }
921     if (!(hmem = LoadResource( hModule, hrsrc )))
922         return 0;
923
924     p = LockResource( hmem );
925     for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
926
927     if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
928     else ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
929
930     if (!buffer) return ret;
931
932     if (ret > len)
933     {
934         SetLastError( ERROR_INSUFFICIENT_BUFFER );
935         return 0;
936     }
937
938     if (lcflags & LOCALE_RETURN_NUMBER)
939     {
940         UINT number;
941         WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
942         if (!tmp) return 0;
943         memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
944         tmp[*p] = 0;
945         number = strtolW( tmp, &end, 10 );
946         if (!*end)
947             memcpy( buffer, &number, sizeof(number) );
948         else  /* invalid number */
949         {
950             SetLastError( ERROR_INVALID_FLAGS );
951             ret = 0;
952         }
953         HeapFree( GetProcessHeap(), 0, tmp );
954
955         TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning number %d\n",
956                lcid, lctype, buffer, len, number );
957     }
958     else
959     {
960         memcpy( buffer, p + 1, *p * sizeof(WCHAR) );
961         if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
962
963         TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning %d %s\n",
964                lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
965     }
966     return ret;
967 }
968
969
970 /******************************************************************************
971  *              SetLocaleInfoA  [KERNEL32.@]
972  *
973  * Set information about an aspect of a locale.
974  *
975  * PARAMS
976  *  lcid   [I] LCID of the locale
977  *  lctype [I] LCTYPE_ flags from "winnls.h"
978  *  data   [I] Information to set
979  *
980  * RETURNS
981  *  Success: TRUE. The information given will be returned by GetLocaleInfoA()
982  *           whenever it is called without LOCALE_NOUSEROVERRIDE.
983  *  Failure: FALSE. Use GetLastError() to determine the cause.
984  *
985  * NOTES
986  *  - Values are only be set for the current user locale; the system locale
987  *  settings cannot be changed.
988  *  - Any settings changed by this call are lost when the locale is changed by
989  *  the control panel (in Wine, this happens every time you change LANG).
990  *  - The native implementation of this function does not check that lcid matches
991  *  the current user locale, and simply sets the new values. Wine warns you in
992  *  this case, but behaves the same.
993  */
994 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
995 {
996     UINT codepage = CP_ACP;
997     WCHAR *strW;
998     DWORD len;
999     BOOL ret;
1000
1001     lcid = ConvertDefaultLocale(lcid);
1002
1003     if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1004     len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1005     if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1006     {
1007         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1008         return FALSE;
1009     }
1010     MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1011     ret = SetLocaleInfoW( lcid, lctype, strW );
1012     HeapFree( GetProcessHeap(), 0, strW );
1013     return ret;
1014 }
1015
1016
1017 /******************************************************************************
1018  *              SetLocaleInfoW  (KERNEL32.@)
1019  *
1020  * See SetLocaleInfoA.
1021  */
1022 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1023 {
1024     const WCHAR *value;
1025     const WCHAR intlW[] = {'i','n','t','l',0 };
1026     UNICODE_STRING valueW;
1027     NTSTATUS status;
1028     HKEY hkey;
1029
1030     lcid = ConvertDefaultLocale(lcid);
1031
1032     lctype &= ~LOCALE_LOCALEINFOFLAGSMASK;
1033     value = get_locale_value_name( lctype );
1034
1035     if (!data || !value)
1036     {
1037         SetLastError( ERROR_INVALID_PARAMETER );
1038         return FALSE;
1039     }
1040
1041     if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1042     {
1043         SetLastError( ERROR_INVALID_FLAGS );
1044         return FALSE;
1045     }
1046
1047     if (lcid != GetUserDefaultLCID())
1048     {
1049         /* Windows does not check that the lcid matches the current lcid */
1050         WARN("locale 0x%08lx isn't the current locale (0x%08lx), setting anyway!\n",
1051              lcid, GetUserDefaultLCID());
1052     }
1053
1054     TRACE("setting %lx to %s\n", lctype, debugstr_w(data) );
1055
1056     /* FIXME: should check that data to set is sane */
1057
1058     /* FIXME: profile functions should map to registry */
1059     WriteProfileStringW( intlW, value, data );
1060
1061     if (!(hkey = create_registry_key())) return FALSE;
1062     RtlInitUnicodeString( &valueW, value );
1063     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1064
1065     if (lctype == LOCALE_SDATE || lctype == LOCALE_SLONGDATE)
1066     {
1067       /* Set I-value from S value */
1068       WCHAR *lpD, *lpM, *lpY;
1069       WCHAR szBuff[2];
1070
1071       lpD = strchrW(data, 'd');
1072       lpM = strchrW(data, 'M');
1073       lpY = strchrW(data, 'y');
1074
1075       if (lpD <= lpM)
1076       {
1077         szBuff[0] = '1'; /* D-M-Y */
1078       }
1079       else
1080       {
1081         if (lpY <= lpM)
1082           szBuff[0] = '2'; /* Y-M-D */
1083         else
1084           szBuff[0] = '0'; /* M-D-Y */
1085       }
1086
1087       szBuff[1] = '\0';
1088
1089       if (lctype == LOCALE_SDATE)
1090         lctype = LOCALE_IDATE;
1091       else
1092         lctype = LOCALE_ILDATE;
1093
1094       value = get_locale_value_name( lctype );
1095
1096       WriteProfileStringW( intlW, value, szBuff );
1097
1098       RtlInitUnicodeString( &valueW, value );
1099       status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1100     }
1101
1102     NtClose( hkey );
1103
1104     if (status) SetLastError( RtlNtStatusToDosError(status) );
1105     return !status;
1106 }
1107
1108
1109 /******************************************************************************
1110  *              GetACP   (KERNEL32.@)
1111  *
1112  * Get the current Ansi code page Id for the system.
1113  *
1114  * PARAMS
1115  *  None.
1116  *
1117  * RETURNS
1118  *    The current Ansi code page identifier for the system.
1119  */
1120 UINT WINAPI GetACP(void)
1121 {
1122     assert( ansi_cptable );
1123     return ansi_cptable->info.codepage;
1124 }
1125
1126
1127 /***********************************************************************
1128  *              GetOEMCP   (KERNEL32.@)
1129  *
1130  * Get the current OEM code page Id for the system.
1131  *
1132  * PARAMS
1133  *  None.
1134  *
1135  * RETURNS
1136  *    The current OEM code page identifier for the system.
1137  */
1138 UINT WINAPI GetOEMCP(void)
1139 {
1140     assert( oem_cptable );
1141     return oem_cptable->info.codepage;
1142 }
1143
1144
1145 /***********************************************************************
1146  *           IsValidCodePage   (KERNEL32.@)
1147  *
1148  * Determine if a given code page identifier is valid.
1149  *
1150  * PARAMS
1151  *  codepage [I] Code page Id to verify.
1152  *
1153  * RETURNS
1154  *  TRUE, If codepage is valid and available on the system,
1155  *  FALSE otherwise.
1156  */
1157 BOOL WINAPI IsValidCodePage( UINT codepage )
1158 {
1159     switch(codepage) {
1160     case CP_SYMBOL:
1161         return FALSE;
1162     case CP_UTF7:
1163     case CP_UTF8:
1164         return TRUE;
1165     default:
1166         return wine_cp_get_table( codepage ) != NULL;
1167     }
1168 }
1169
1170
1171 /***********************************************************************
1172  *           IsDBCSLeadByteEx   (KERNEL32.@)
1173  *
1174  * Determine if a character is a lead byte in a given code page.
1175  *
1176  * PARAMS
1177  *  codepage [I] Code page for the test.
1178  *  testchar [I] Character to test
1179  *
1180  * RETURNS
1181  *  TRUE, if testchar is a lead byte in codepage,
1182  *  FALSE otherwise.
1183  */
1184 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1185 {
1186     const union cptable *table = get_codepage_table( codepage );
1187     return table && is_dbcs_leadbyte( table, testchar );
1188 }
1189
1190
1191 /***********************************************************************
1192  *           IsDBCSLeadByte   (KERNEL32.@)
1193  *           IsDBCSLeadByte   (KERNEL.207)
1194  *
1195  * Determine if a character is a lead byte.
1196  *
1197  * PARAMS
1198  *  testchar [I] Character to test
1199  *
1200  * RETURNS
1201  *  TRUE, if testchar is a lead byte in the Ansii code page,
1202  *  FALSE otherwise.
1203  */
1204 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1205 {
1206     if (!ansi_cptable) return FALSE;
1207     return is_dbcs_leadbyte( ansi_cptable, testchar );
1208 }
1209
1210
1211 /***********************************************************************
1212  *           GetCPInfo   (KERNEL32.@)
1213  *
1214  * Get information about a code page.
1215  *
1216  * PARAMS
1217  *  codepage [I] Code page number
1218  *  cpinfo   [O] Destination for code page information
1219  *
1220  * RETURNS
1221  *  Success: TRUE. cpinfo is updated with the information about codepage.
1222  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1223  */
1224 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1225 {
1226     const union cptable *table = get_codepage_table( codepage );
1227
1228     if (!table)
1229     {
1230         SetLastError( ERROR_INVALID_PARAMETER );
1231         return FALSE;
1232     }
1233     if (table->info.def_char & 0xff00)
1234     {
1235         cpinfo->DefaultChar[0] = table->info.def_char & 0xff00;
1236         cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1237     }
1238     else
1239     {
1240         cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1241         cpinfo->DefaultChar[1] = 0;
1242     }
1243     if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1244         memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1245     else
1246         cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1247
1248     return TRUE;
1249 }
1250
1251 /***********************************************************************
1252  *           GetCPInfoExA   (KERNEL32.@)
1253  *
1254  * Get extended information about a code page.
1255  *
1256  * PARAMS
1257  *  codepage [I] Code page number
1258  *  dwFlags  [I] Reserved, must to 0.
1259  *  cpinfo   [O] Destination for code page information
1260  *
1261  * RETURNS
1262  *  Success: TRUE. cpinfo is updated with the information about codepage.
1263  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1264  */
1265 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1266 {
1267     const union cptable *table = get_codepage_table( codepage );
1268
1269     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1270       return FALSE;
1271
1272     cpinfo->CodePage = codepage;
1273     cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1274     strcpy(cpinfo->CodePageName, table->info.name);
1275     return TRUE;
1276 }
1277
1278 /***********************************************************************
1279  *           GetCPInfoExW   (KERNEL32.@)
1280  *
1281  * Unicode version of GetCPInfoExA.
1282  */
1283 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1284 {
1285     const union cptable *table = get_codepage_table( codepage );
1286
1287     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1288       return FALSE;
1289
1290     cpinfo->CodePage = codepage;
1291     cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1292     MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1293                          sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1294     return TRUE;
1295 }
1296
1297 /***********************************************************************
1298  *              EnumSystemCodePagesA   (KERNEL32.@)
1299  *
1300  * Call a user defined function for every code page installed on the system.
1301  *
1302  * PARAMS
1303  *   lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1304  *   flags            [I] Reserved, set to 0.
1305  *
1306  * RETURNS
1307  *  TRUE, If all code pages have been enumerated, or
1308  *  FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1309  */
1310 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1311 {
1312     const union cptable *table;
1313     char buffer[10];
1314     int index = 0;
1315
1316     for (;;)
1317     {
1318         if (!(table = wine_cp_enum_table( index++ ))) break;
1319         sprintf( buffer, "%d", table->info.codepage );
1320         if (!lpfnCodePageEnum( buffer )) break;
1321     }
1322     return TRUE;
1323 }
1324
1325
1326 /***********************************************************************
1327  *              EnumSystemCodePagesW   (KERNEL32.@)
1328  *
1329  * See EnumSystemCodePagesA.
1330  */
1331 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1332 {
1333     const union cptable *table;
1334     WCHAR buffer[10], *p;
1335     int page, index = 0;
1336
1337     for (;;)
1338     {
1339         if (!(table = wine_cp_enum_table( index++ ))) break;
1340         p = buffer + sizeof(buffer)/sizeof(WCHAR);
1341         *--p = 0;
1342         page = table->info.codepage;
1343         do
1344         {
1345             *--p = '0' + (page % 10);
1346             page /= 10;
1347         } while( page );
1348         if (!lpfnCodePageEnum( p )) break;
1349     }
1350     return TRUE;
1351 }
1352
1353
1354 /***********************************************************************
1355  *              MultiByteToWideChar   (KERNEL32.@)
1356  *
1357  * Convert a multibyte character string into a Unicode string.
1358  *
1359  * PARAMS
1360  *   page   [I] Codepage character set to convert from
1361  *   flags  [I] Character mapping flags
1362  *   src    [I] Source string buffer
1363  *   srclen [I] Length of src, or -1 if src is NUL terminated
1364  *   dst    [O] Destination buffer
1365  *   dstlen [I] Length of dst, or 0 to compute the required length
1366  *
1367  * RETURNS
1368  *   Success: If dstlen > 0, the number of characters written to dst.
1369  *            If dstlen == 0, the number of characters needed to perform the
1370  *            conversion. In both cases the count includes the terminating NUL.
1371  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1372  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1373  *            and dstlen != 0; ERROR_INVALID_PARAMETER,  if an invalid parameter
1374  *            is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1375  *            possible for src.
1376  */
1377 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1378                                 LPWSTR dst, INT dstlen )
1379 {
1380     const union cptable *table;
1381     int ret;
1382
1383     if (!src || (!dst && dstlen))
1384     {
1385         SetLastError( ERROR_INVALID_PARAMETER );
1386         return 0;
1387     }
1388
1389     if (srclen < 0) srclen = strlen(src) + 1;
1390
1391     if (flags & MB_USEGLYPHCHARS) FIXME("MB_USEGLYPHCHARS not supported\n");
1392
1393     switch(page)
1394     {
1395     case CP_UTF7:
1396         FIXME("UTF-7 not supported\n");
1397         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1398         return 0;
1399     case CP_UNIXCP:
1400         if (unix_cptable)
1401         {
1402             ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1403             break;
1404         }
1405         /* fall through */
1406     case CP_UTF8:
1407         ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1408         break;
1409     default:
1410         if (!(table = get_codepage_table( page )))
1411         {
1412             SetLastError( ERROR_INVALID_PARAMETER );
1413             return 0;
1414         }
1415         ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1416         break;
1417     }
1418
1419     if (ret < 0)
1420     {
1421         switch(ret)
1422         {
1423         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1424         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1425         }
1426         ret = 0;
1427     }
1428     return ret;
1429 }
1430
1431
1432 /***********************************************************************
1433  *              WideCharToMultiByte   (KERNEL32.@)
1434  *
1435  * Convert a Unicode character string into a multibyte string.
1436  *
1437  * PARAMS
1438  *   page    [I] Code page character set to convert to
1439  *   flags   [I] Mapping Flags (MB_ constants from "winnls.h").
1440  *   src     [I] Source string buffer
1441  *   srclen  [I] Length of src, or -1 if src is NUL terminated
1442  *   dst     [O] Destination buffer
1443  *   dstlen  [I] Length of dst, or 0 to compute the required length
1444  *   defchar [I] Default character to use for conversion if no exact
1445  *                  conversion can be made
1446  *   used    [O] Set if default character was used in the conversion
1447  *
1448  * RETURNS
1449  *   Success: If dstlen > 0, the number of characters written to dst.
1450  *            If dstlen == 0, number of characters needed to perform the
1451  *            conversion. In both cases the count includes the terminating NUL.
1452  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1453  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1454  *            and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1455  *            parameter was given.
1456  */
1457 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1458                                 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1459 {
1460     const union cptable *table;
1461     int ret, used_tmp;
1462
1463     if (!src || (!dst && dstlen))
1464     {
1465         SetLastError( ERROR_INVALID_PARAMETER );
1466         return 0;
1467     }
1468
1469     if (srclen < 0) srclen = strlenW(src) + 1;
1470
1471     switch(page)
1472     {
1473     case CP_UTF7:
1474         FIXME("UTF-7 not supported\n");
1475         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1476         return 0;
1477     case CP_UNIXCP:
1478         if (unix_cptable)
1479         {
1480             ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
1481                                     defchar, used ? &used_tmp : NULL );
1482             break;
1483         }
1484         /* fall through */
1485     case CP_UTF8:
1486         ret = wine_utf8_wcstombs( src, srclen, dst, dstlen );
1487         break;
1488     default:
1489         if (!(table = get_codepage_table( page )))
1490         {
1491             SetLastError( ERROR_INVALID_PARAMETER );
1492             return 0;
1493         }
1494         ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
1495                                 defchar, used ? &used_tmp : NULL );
1496         if (used) *used = used_tmp;
1497         break;
1498     }
1499
1500     if (ret == -1)
1501     {
1502         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1503         ret = 0;
1504     }
1505     TRACE("cp %d %s -> %s\n", page, debugstr_w(src), debugstr_a(dst));
1506     return ret;
1507 }
1508
1509
1510 /***********************************************************************
1511  *           GetThreadLocale    (KERNEL32.@)
1512  *
1513  * Get the current threads locale.
1514  *
1515  * PARAMS
1516  *  None.
1517  *
1518  * RETURNS
1519  *  The LCID currently assocated with the calling thread.
1520  */
1521 LCID WINAPI GetThreadLocale(void)
1522 {
1523     LCID ret = NtCurrentTeb()->CurrentLocale;
1524     if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
1525     return ret;
1526 }
1527
1528 /**********************************************************************
1529  *           SetThreadLocale    (KERNEL32.@)
1530  *
1531  * Set the current threads locale.
1532  *
1533  * PARAMS
1534  *  lcid [I] LCID of the locale to set
1535  *
1536  * RETURNS
1537  *  Success: TRUE. The threads locale is set to lcid.
1538  *  Failure: FALSE. Use GetLastError() to determine the cause.
1539  */
1540 BOOL WINAPI SetThreadLocale( LCID lcid )
1541 {
1542     TRACE("(0x%04lX)\n", lcid);
1543
1544     lcid = ConvertDefaultLocale(lcid);
1545
1546     if (lcid != GetThreadLocale())
1547     {
1548         if (!IsValidLocale(lcid, LCID_SUPPORTED))
1549         {
1550             SetLastError(ERROR_INVALID_PARAMETER);
1551             return FALSE;
1552         }
1553
1554         NtCurrentTeb()->CurrentLocale = lcid;
1555         NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
1556     }
1557     return TRUE;
1558 }
1559
1560 /******************************************************************************
1561  *              ConvertDefaultLocale (KERNEL32.@)
1562  *
1563  * Convert a default locale identifier into a real identifier.
1564  *
1565  * PARAMS
1566  *  lcid [I] LCID identifier of the locale to convert
1567  *
1568  * RETURNS
1569  *  lcid unchanged, if not a default locale or its sublanguage is
1570  *   not SUBLANG_NEUTRAL.
1571  *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
1572  *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
1573  *  Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
1574  */
1575 LCID WINAPI ConvertDefaultLocale( LCID lcid )
1576 {
1577     LANGID langid;
1578
1579     switch (lcid)
1580     {
1581     case LOCALE_SYSTEM_DEFAULT:
1582         lcid = GetSystemDefaultLCID();
1583         break;
1584     case LOCALE_USER_DEFAULT:
1585     case LOCALE_NEUTRAL:
1586         lcid = GetUserDefaultLCID();
1587         break;
1588     default:
1589         /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
1590         langid = LANGIDFROMLCID(lcid);
1591         if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
1592         {
1593           langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
1594           lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
1595         }
1596     }
1597     return lcid;
1598 }
1599
1600
1601 /******************************************************************************
1602  *           IsValidLocale   (KERNEL32.@)
1603  *
1604  * Determine if a locale is valid.
1605  *
1606  * PARAMS
1607  *  lcid  [I] LCID of the locale to check
1608  *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
1609  *
1610  * RETURN
1611  *  TRUE,  if lcid is valid,
1612  *  FALSE, otherwise.
1613  *
1614  * NOTES
1615  *  Wine does not currently make the distinction between supported and installed. All
1616  *  languages supported are installed by default.
1617  */
1618 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
1619 {
1620     /* check if language is registered in the kernel32 resources */
1621     return FindResourceExW( GetModuleHandleW(kernel32W), (LPWSTR)RT_STRING,
1622                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
1623 }
1624
1625
1626 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
1627                                        LPCSTR name, WORD LangID, LONG lParam )
1628 {
1629     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
1630     char buf[20];
1631
1632     sprintf(buf, "%08x", (UINT)LangID);
1633     return lpfnLocaleEnum( buf );
1634 }
1635
1636 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
1637                                        LPCWSTR name, WORD LangID, LONG lParam )
1638 {
1639     static const WCHAR formatW[] = {'%','0','8','x',0};
1640     LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
1641     WCHAR buf[20];
1642     sprintfW( buf, formatW, (UINT)LangID );
1643     return lpfnLocaleEnum( buf );
1644 }
1645
1646 /******************************************************************************
1647  *           EnumSystemLocalesA  (KERNEL32.@)
1648  *
1649  * Call a users function for each locale available on the system.
1650  *
1651  * PARAMS
1652  *  lpfnLocaleEnum [I] Callback function to call for each locale
1653  *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
1654  *
1655  * RETURNS
1656  *  Success: TRUE.
1657  *  Failure: FALSE. Use GetLastError() to determine the cause.
1658  */
1659 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
1660 {
1661     TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
1662     EnumResourceLanguagesA( GetModuleHandleW(kernel32W), (LPSTR)RT_STRING,
1663                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
1664                             (LONG)lpfnLocaleEnum);
1665     return TRUE;
1666 }
1667
1668
1669 /******************************************************************************
1670  *           EnumSystemLocalesW  (KERNEL32.@)
1671  *
1672  * See EnumSystemLocalesA.
1673  */
1674 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
1675 {
1676     TRACE("(%p,%08lx)\n", lpfnLocaleEnum, dwFlags);
1677     EnumResourceLanguagesW( GetModuleHandleW(kernel32W), (LPWSTR)RT_STRING,
1678                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
1679                             (LONG)lpfnLocaleEnum);
1680     return TRUE;
1681 }
1682
1683
1684 /***********************************************************************
1685  *           VerLanguageNameA  (KERNEL32.@)
1686  *
1687  * Get the name of a language.
1688  *
1689  * PARAMS
1690  *  wLang  [I] LANGID of the language
1691  *  szLang [O] Destination for the language name
1692  *
1693  * RETURNS
1694  *  Success: The size of the language name. If szLang is non-NULL, it is filled
1695  *           with the name.
1696  *  Failure: 0. Use GetLastError() to determine the cause.
1697  *
1698  */
1699 DWORD WINAPI VerLanguageNameA( UINT wLang, LPSTR szLang, UINT nSize )
1700 {
1701     return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
1702 }
1703
1704
1705 /***********************************************************************
1706  *           VerLanguageNameW  (KERNEL32.@)
1707  *
1708  * See VerLanguageNameA.
1709  */
1710 DWORD WINAPI VerLanguageNameW( UINT wLang, LPWSTR szLang, UINT nSize )
1711 {
1712     return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
1713 }
1714
1715
1716 /******************************************************************************
1717  *           GetStringTypeW    (KERNEL32.@)
1718  *
1719  * See GetStringTypeA.
1720  */
1721 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
1722 {
1723     if (count == -1) count = strlenW(src) + 1;
1724     switch(type)
1725     {
1726     case CT_CTYPE1:
1727         while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
1728         break;
1729     case CT_CTYPE2:
1730         while (count--) *chartype++ = get_char_typeW( *src++ ) >> 12;
1731         break;
1732     case CT_CTYPE3:
1733     {
1734         WARN("CT_CTYPE3: semi-stub.\n");
1735         while (count--)
1736         {
1737             int c = *src;
1738             WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
1739
1740             type1 = get_char_typeW( *src++ ) & 0xfff;
1741             /* try to construct type3 from type1 */
1742             if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
1743             if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
1744             if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
1745             if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
1746             if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
1747             if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
1748             if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
1749
1750             if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
1751             if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
1752             if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
1753             if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
1754             if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
1755             if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
1756             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
1757             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
1758
1759             if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
1760             if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
1761             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
1762             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
1763             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
1764             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
1765             *chartype++ = type3;
1766         }
1767         break;
1768     }
1769     default:
1770         SetLastError( ERROR_INVALID_PARAMETER );
1771         return FALSE;
1772     }
1773     return TRUE;
1774 }
1775
1776
1777 /******************************************************************************
1778  *           GetStringTypeExW    (KERNEL32.@)
1779  *
1780  * See GetStringTypeExA.
1781  */
1782 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
1783 {
1784     /* locale is ignored for Unicode */
1785     return GetStringTypeW( type, src, count, chartype );
1786 }
1787
1788
1789 /******************************************************************************
1790  *           GetStringTypeA    (KERNEL32.@)
1791  *
1792  * Get characteristics of the characters making up a string.
1793  *
1794  * PARAMS
1795  *  locale   [I] Locale Id for the string
1796  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
1797  *  src      [I] String to analyse
1798  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
1799  *  chartype [O] Destination for the calculated characteristics
1800  *
1801  * RETURNS
1802  *  Success: TRUE. chartype is filled with the requested characteristics of each char
1803  *           in src.
1804  *  Failure: FALSE. Use GetLastError() to determine the cause.
1805  */
1806 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
1807 {
1808     UINT cp;
1809     INT countW;
1810     LPWSTR srcW;
1811     BOOL ret = FALSE;
1812
1813     if(count == -1) count = strlen(src) + 1;
1814
1815     if (!(cp = get_lcid_codepage( locale )))
1816     {
1817         FIXME("For locale %04lx using current ANSI code page\n", locale);
1818         cp = GetACP();
1819     }
1820
1821     countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
1822     if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
1823     {
1824         MultiByteToWideChar(cp, 0, src, count, srcW, countW);
1825     /*
1826      * NOTE: the target buffer has 1 word for each CHARACTER in the source
1827      * string, with multibyte characters there maybe be more bytes in count
1828      * than character space in the buffer!
1829      */
1830         ret = GetStringTypeW(type, srcW, countW, chartype);
1831         HeapFree(GetProcessHeap(), 0, srcW);
1832     }
1833     return ret;
1834 }
1835
1836 /******************************************************************************
1837  *           GetStringTypeExA    (KERNEL32.@)
1838  *
1839  * Get characteristics of the characters making up a string.
1840  *
1841  * PARAMS
1842  *  locale   [I] Locale Id for the string
1843  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
1844  *  src      [I] String to analyse
1845  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
1846  *  chartype [O] Destination for the calculated characteristics
1847  *
1848  * RETURNS
1849  *  Success: TRUE. chartype is filled with the requested characteristics of each char
1850  *           in src.
1851  *  Failure: FALSE. Use GetLastError() to determine the cause.
1852  */
1853 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
1854 {
1855     return GetStringTypeA(locale, type, src, count, chartype);
1856 }
1857
1858
1859 /*************************************************************************
1860  *           LCMapStringW    (KERNEL32.@)
1861  *
1862  * See LCMapStringA.
1863  */
1864 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
1865                         LPWSTR dst, INT dstlen)
1866 {
1867     LPWSTR dst_ptr;
1868
1869     if (!src || !srclen || dstlen < 0)
1870     {
1871         SetLastError(ERROR_INVALID_PARAMETER);
1872         return 0;
1873     }
1874
1875     /* mutually exclusive flags */
1876     if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
1877         (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
1878         (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
1879         (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
1880     {
1881         SetLastError(ERROR_INVALID_FLAGS);
1882         return 0;
1883     }
1884
1885     if (!dstlen) dst = NULL;
1886
1887     lcid = ConvertDefaultLocale(lcid);
1888
1889     if (flags & LCMAP_SORTKEY)
1890     {
1891         if (src == dst)
1892         {
1893             SetLastError(ERROR_INVALID_FLAGS);
1894             return 0;
1895         }
1896
1897         if (srclen < 0) srclen = strlenW(src);
1898
1899         TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
1900               lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
1901
1902         return wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
1903     }
1904
1905     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
1906     if (flags & SORT_STRINGSORT)
1907     {
1908         SetLastError(ERROR_INVALID_FLAGS);
1909         return 0;
1910     }
1911
1912     if (srclen < 0) srclen = strlenW(src) + 1;
1913
1914     TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
1915           lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
1916
1917     if (!dst) /* return required string length */
1918     {
1919         INT len;
1920
1921         for (len = 0; srclen; src++, srclen--)
1922         {
1923             WCHAR wch = *src;
1924             /* tests show that win2k just ignores NORM_IGNORENONSPACE,
1925              * and skips white space and punctuation characters for
1926              * NORM_IGNORESYMBOLS.
1927              */
1928             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
1929                 continue;
1930             len++;
1931         }
1932         return len;
1933     }
1934
1935     if (flags & LCMAP_UPPERCASE)
1936     {
1937         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
1938         {
1939             WCHAR wch = *src;
1940             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
1941                 continue;
1942             *dst_ptr++ = toupperW(wch);
1943             dstlen--;
1944         }
1945     }
1946     else if (flags & LCMAP_LOWERCASE)
1947     {
1948         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
1949         {
1950             WCHAR wch = *src;
1951             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
1952                 continue;
1953             *dst_ptr++ = tolowerW(wch);
1954             dstlen--;
1955         }
1956     }
1957     else
1958     {
1959         if (src == dst)
1960         {
1961             SetLastError(ERROR_INVALID_FLAGS);
1962             return 0;
1963         }
1964         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
1965         {
1966             WCHAR wch = *src;
1967             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
1968                 continue;
1969             *dst_ptr++ = wch;
1970             dstlen--;
1971         }
1972     }
1973
1974     return dst_ptr - dst;
1975 }
1976
1977 /*************************************************************************
1978  *           LCMapStringA    (KERNEL32.@)
1979  *
1980  * Map characters in a locale sensitive string.
1981  *
1982  * PARAMS
1983  *  lcid   [I] LCID for the conversion.
1984  *  flags  [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
1985  *  src    [I] String to map
1986  *  srclen [I] Length of src in chars, or -1 if src is NUL terminated
1987  *  dst    [O] Destination for mapped string
1988  *  dstlen [I] Length of dst in characters
1989  *
1990  * RETURNS
1991  *  Success: The length of the mapped string in dst, including the NUL terminator.
1992  *  Failure: 0. Use GetLastError() to determine the cause.
1993  */
1994 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
1995                         LPSTR dst, INT dstlen)
1996 {
1997     WCHAR bufW[128];
1998     LPWSTR srcW, dstW;
1999     INT ret = 0, srclenW, dstlenW;
2000     UINT locale_cp;
2001
2002     if (!src || !srclen || dstlen < 0)
2003     {
2004         SetLastError(ERROR_INVALID_PARAMETER);
2005         return 0;
2006     }
2007
2008     locale_cp = get_lcid_codepage(lcid);
2009
2010     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 128);
2011     if (srclenW)
2012         srcW = bufW;
2013     else
2014     {
2015         srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2016         srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2017         if (!srcW)
2018         {
2019             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2020             return 0;
2021         }
2022         MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2023     }
2024
2025     if (flags & LCMAP_SORTKEY)
2026     {
2027         if (src == dst)
2028         {
2029             SetLastError(ERROR_INVALID_FLAGS);
2030             goto map_string_exit;
2031         }
2032         ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2033         goto map_string_exit;
2034     }
2035
2036     if (flags & SORT_STRINGSORT)
2037     {
2038         SetLastError(ERROR_INVALID_FLAGS);
2039         goto map_string_exit;
2040     }
2041
2042     dstlenW = LCMapStringW(lcid, flags, srcW, srclenW, NULL, 0);
2043     dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2044     if (!dstW)
2045     {
2046         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2047         goto map_string_exit;
2048     }
2049
2050     LCMapStringW(lcid, flags, srcW, srclenW, dstW, dstlenW);
2051     ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2052     HeapFree(GetProcessHeap(), 0, dstW);
2053
2054 map_string_exit:
2055     if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2056     return ret;
2057 }
2058
2059 /*************************************************************************
2060  *           FoldStringA    (KERNEL32.@)
2061  *
2062  * Map characters in a string.
2063  *
2064  * PARAMS
2065  *  dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2066  *  src     [I] String to map
2067  *  srclen  [I] Length of src, or -1 if src is NUL terminated
2068  *  dst     [O] Destination for mapped string
2069  *  dstlen  [I] Length of dst, or 0 to find the required length for the mapped string
2070  *
2071  * RETURNS
2072  *  Success: The length of the string written to dst, including the terminating NUL. If
2073  *           dstlen is 0, the value returned is the same, but nothing is written to dst,
2074  *           and dst may be NULL.
2075  *  Failure: 0. Use GetLastError() to determine the cause.
2076  */
2077 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2078                        LPSTR dst, INT dstlen)
2079 {
2080     INT ret = 0, srclenW = 0;
2081     WCHAR *srcW = NULL, *dstW = NULL;
2082
2083     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2084     {
2085         SetLastError(ERROR_INVALID_PARAMETER);
2086         return 0;
2087     }
2088
2089     srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2090                                   src, srclen, NULL, 0);
2091     srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2092
2093     if (!srcW)
2094     {
2095         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2096         goto FoldStringA_exit;
2097     }
2098
2099     MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2100                         src, srclen, srcW, srclenW);
2101
2102     dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2103
2104     ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2105     if (ret && dstlen)
2106     {
2107         dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2108
2109         if (!dstW)
2110         {
2111             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2112             goto FoldStringA_exit;
2113         }
2114
2115         ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2116         if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2117         {
2118             ret = 0;
2119             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2120         }
2121     }
2122
2123     if (dstW)
2124         HeapFree(GetProcessHeap(), 0, dstW);
2125
2126 FoldStringA_exit:
2127     if (srcW)
2128         HeapFree(GetProcessHeap(), 0, srcW);
2129     return ret;
2130 }
2131
2132 /*************************************************************************
2133  *           FoldStringW    (KERNEL32.@)
2134  *
2135  * See FoldStringA.
2136  */
2137 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2138                        LPWSTR dst, INT dstlen)
2139 {
2140     int ret;
2141
2142     switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2143     {
2144     case 0:
2145         if (dwFlags)
2146           break;
2147         /* Fall through for dwFlags == 0 */
2148     case MAP_PRECOMPOSED|MAP_COMPOSITE:
2149     case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2150     case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2151         SetLastError(ERROR_INVALID_FLAGS);
2152         return 0;
2153     }
2154
2155     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2156     {
2157         SetLastError(ERROR_INVALID_PARAMETER);
2158         return 0;
2159     }
2160
2161     ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2162     if (!ret)
2163         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2164     return ret;
2165 }
2166
2167 /******************************************************************************
2168  *           CompareStringW    (KERNEL32.@)
2169  *
2170  * See CompareStringA.
2171  */
2172 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2173                           LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2174 {
2175     INT ret, len;
2176
2177     if (!str1 || !str2)
2178     {
2179         SetLastError(ERROR_INVALID_PARAMETER);
2180         return 0;
2181     }
2182
2183     if (len1 < 0) len1 = lstrlenW(str1);
2184     if (len2 < 0) len2 = lstrlenW(str2);
2185
2186     len = (len1 < len2) ? len1 : len2;
2187     ret = (style & NORM_IGNORECASE) ? strncmpiW(str1, str2, len) :
2188                                       strncmpW(str1, str2, len);
2189
2190     if (ret) /* need to translate result */
2191         return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2192
2193     if (len1 == len2) return CSTR_EQUAL;
2194     /* the longer one is lexically greater */
2195     return (len1 < len2) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2196 }
2197
2198 /******************************************************************************
2199  *           CompareStringA    (KERNEL32.@)
2200  *
2201  * Compare two locale sensitive strings.
2202  *
2203  * PARAMS
2204  *  lcid  [I] LCID for the comparason
2205  *  style [I] Flags for the comparason (NORM_ constants from "winnls.h").
2206  *  str1  [I] First string to compare
2207  *  len1  [I] Length of str1, or -1 if str1 is NUL terminated
2208  *  str2  [I] Second string to compare
2209  *  len2  [I] Length of str2, or -1 if str2 is NUL terminated
2210  *
2211  * RETURNS
2212  *  Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2213  *           str2 is less than, equal to or greater than str1 respectively.
2214  *  Failure: FALSE. Use GetLastError() to determine the cause.
2215  */
2216 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2217                           LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2218 {
2219     WCHAR buf1W[128], buf2W[128];
2220     LPWSTR str1W, str2W;
2221     INT len1W, len2W, ret;
2222     UINT locale_cp;
2223
2224     if (!str1 || !str2)
2225     {
2226         SetLastError(ERROR_INVALID_PARAMETER);
2227         return 0;
2228     }
2229
2230     if (len1 < 0) len1 = strlen(str1);
2231     if (len2 < 0) len2 = strlen(str2);
2232
2233     locale_cp = get_lcid_codepage(lcid);
2234
2235     len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 128);
2236     if (len1W)
2237         str1W = buf1W;
2238     else
2239     {
2240         len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2241         str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2242         if (!str1W)
2243         {
2244             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2245             return 0;
2246         }
2247         MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2248     }
2249     len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 128);
2250     if (len2W)
2251         str2W = buf2W;
2252     else
2253     {
2254         len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2255         str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2256         if (!str2W)
2257         {
2258             if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2259             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2260             return 0;
2261         }
2262         MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2263     }
2264
2265     ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2266
2267     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2268     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2269     return ret;
2270 }
2271
2272 /*************************************************************************
2273  *           lstrcmp     (KERNEL32.@)
2274  *           lstrcmpA    (KERNEL32.@)
2275  *
2276  * Compare two strings using the current thread locale.
2277  *
2278  * PARAMS
2279  *  str1  [I] First string to compare
2280  *  str2  [I] Second string to compare
2281  *
2282  * RETURNS
2283  *  Success: A number less than, equal to or greater than 0 depending on whether
2284  *           str2 is less than, equal to or greater than str1 respectively.
2285  *  Failure: FALSE. Use GetLastError() to determine the cause.
2286  */
2287 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
2288 {
2289     int ret = CompareStringA(GetThreadLocale(), 0, str1, -1, str2, -1);
2290     if (ret) ret -= 2;
2291     return ret;
2292 }
2293
2294 /*************************************************************************
2295  *           lstrcmpi     (KERNEL32.@)
2296  *           lstrcmpiA    (KERNEL32.@)
2297  *
2298  * Compare two strings using the current thread locale, ignoring case.
2299  *
2300  * PARAMS
2301  *  str1  [I] First string to compare
2302  *  str2  [I] Second string to compare
2303  *
2304  * RETURNS
2305  *  Success: A number less than, equal to or greater than 0 depending on whether
2306  *           str2 is less than, equal to or greater than str1 respectively.
2307  *  Failure: FALSE. Use GetLastError() to determine the cause.
2308  */
2309 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
2310 {
2311     int ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
2312     if (ret) ret -= 2;
2313     return ret;
2314 }
2315
2316 /*************************************************************************
2317  *           lstrcmpW    (KERNEL32.@)
2318  *
2319  * See lstrcmpA.
2320  */
2321 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
2322 {
2323     int ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
2324     if (ret) ret -= 2;
2325     return ret;
2326 }
2327
2328 /*************************************************************************
2329  *           lstrcmpiW    (KERNEL32.@)
2330  *
2331  * See lstrcmpiA.
2332  */
2333 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
2334 {
2335     int ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
2336     if (ret) ret -= 2;
2337     return ret;
2338 }
2339
2340 /******************************************************************************
2341  *              LOCALE_Init
2342  */
2343 void LOCALE_Init(void)
2344 {
2345     extern void __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp );
2346
2347     UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp = ~0U;
2348     LCID lcid = init_default_lcid( &unix_cp );
2349
2350     NtSetDefaultLocale( FALSE, lcid );
2351     NtSetDefaultLocale( TRUE, lcid );
2352
2353     ansi_cp = get_lcid_codepage(lcid);
2354     GetLocaleInfoW( lcid, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
2355                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
2356     GetLocaleInfoW( lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
2357                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
2358     if (unix_cp == ~0U)
2359         GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
2360                     (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
2361
2362     if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
2363         ansi_cptable = wine_cp_get_table( 1252 );
2364     if (!(oem_cptable = wine_cp_get_table( oem_cp )))
2365         oem_cptable  = wine_cp_get_table( 437 );
2366     if (!(mac_cptable = wine_cp_get_table( mac_cp )))
2367         mac_cptable  = wine_cp_get_table( 10000 );
2368     if (unix_cp != CP_UTF8)
2369     {
2370         if (!(unix_cptable = wine_cp_get_table( unix_cp )))
2371             unix_cptable  = wine_cp_get_table( 28591 );
2372     }
2373
2374     __wine_init_codepages( ansi_cptable, oem_cptable );
2375
2376     TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
2377            ansi_cptable->info.codepage, oem_cptable->info.codepage,
2378            mac_cptable->info.codepage, unix_cp );
2379 }
2380
2381 static HKEY NLS_RegOpenKey(HKEY hRootKey, LPCWSTR szKeyName)
2382 {
2383     UNICODE_STRING keyName;
2384     OBJECT_ATTRIBUTES attr;
2385     HKEY hkey;
2386
2387     RtlInitUnicodeString( &keyName, szKeyName );
2388     InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
2389
2390     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS)
2391         hkey = 0;
2392
2393     return hkey;
2394 }
2395
2396 static HKEY NLS_RegOpenSubKey(HKEY hRootKey, LPCWSTR szKeyName)
2397 {
2398     HKEY hKey = NLS_RegOpenKey(hRootKey, szKeyName);
2399
2400     if (hRootKey)
2401         NtClose( hRootKey );
2402
2403     return hKey;
2404 }
2405
2406 static BOOL NLS_RegEnumSubKey(HKEY hKey, UINT ulIndex, LPWSTR szKeyName,
2407                               ULONG keyNameSize)
2408 {
2409     BYTE buffer[80];
2410     KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
2411     DWORD dwLen;
2412
2413     if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
2414                         sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
2415         info->NameLength > keyNameSize)
2416     {
2417         return FALSE;
2418     }
2419
2420     TRACE("info->Name %s info->NameLength %ld\n", debugstr_w(info->Name), info->NameLength);
2421
2422     memcpy( szKeyName, info->Name, info->NameLength);
2423     szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
2424
2425     TRACE("returning %s\n", debugstr_w(szKeyName));
2426     return TRUE;
2427 }
2428
2429 static BOOL NLS_RegEnumValue(HKEY hKey, UINT ulIndex,
2430                              LPWSTR szValueName, ULONG valueNameSize,
2431                              LPWSTR szValueData, ULONG valueDataSize)
2432 {
2433     BYTE buffer[80];
2434     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
2435     DWORD dwLen;
2436
2437     if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
2438         buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
2439         info->NameLength > valueNameSize ||
2440         info->DataLength > valueDataSize)
2441     {
2442         return FALSE;
2443     }
2444
2445     TRACE("info->Name %s info->DataLength %ld\n", debugstr_w(info->Name), info->DataLength);
2446
2447     memcpy( szValueName, info->Name, info->NameLength);
2448     szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
2449     memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
2450     szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
2451
2452     TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
2453     return TRUE;
2454 }
2455
2456 static BOOL NLS_RegGetDword(HKEY hKey, LPCWSTR szValueName, DWORD *lpVal)
2457 {
2458     BYTE buffer[128];
2459     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
2460     DWORD dwSize = sizeof(buffer);
2461     UNICODE_STRING valueName;
2462
2463     RtlInitUnicodeString( &valueName, szValueName );
2464
2465     TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
2466     if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
2467                          buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
2468         info->DataLength == sizeof(DWORD))
2469     {
2470         memcpy(lpVal, info->Data, sizeof(DWORD));
2471         return TRUE;
2472     }
2473
2474     return FALSE;
2475 }
2476
2477 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
2478 {
2479     HMODULE hModule = GetModuleHandleW(kernel32W);
2480     LANGID  langId;
2481     LPCWSTR szResourceName = (LPCWSTR)(((lgrpid + 0x2000) >> 4) + 1);
2482     HRSRC   hResource;
2483     BOOL    bRet = FALSE;
2484
2485     /* FIXME: Is it correct to use the system default langid? */
2486     langId = GetSystemDefaultLangID();
2487
2488     if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
2489         langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
2490
2491     hResource = FindResourceExW( hModule, (LPWSTR)RT_STRING, szResourceName, langId );
2492
2493     if (hResource)
2494     {
2495         HGLOBAL hResDir = LoadResource( hModule, hResource );
2496
2497         if (hResDir)
2498         {
2499             ULONG   iResourceIndex = lgrpid & 0xf;
2500             LPCWSTR lpResEntry = LockResource( hResDir );
2501             ULONG   i;
2502
2503             for (i = 0; i < iResourceIndex; i++)
2504                 lpResEntry += *lpResEntry + 1;
2505
2506             if (*lpResEntry < nameSize)
2507             {
2508                 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
2509                 szName[*lpResEntry] = '\0';
2510                 bRet = TRUE;
2511             }
2512
2513         }
2514         FreeResource( hResource );
2515     }
2516     return bRet;
2517 }
2518
2519 /* Registry keys for NLS related information */
2520 static const WCHAR szNlsKeyName[] = {
2521     'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
2522     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
2523     'C','o','n','t','r','o','l','\\','N','l','s','\0'
2524 };
2525
2526 static const WCHAR szLangGroupsKeyName[] = {
2527     'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s','\0'
2528 };
2529
2530 static const WCHAR szCountryListName[] = {
2531     'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
2532     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
2533     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2534     'T','e','l','e','p','h','o','n','y','\\',
2535     'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
2536 };
2537
2538
2539 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
2540 typedef struct
2541 {
2542   LANGUAGEGROUP_ENUMPROCA procA;
2543   LANGUAGEGROUP_ENUMPROCW procW;
2544   DWORD    dwFlags;
2545   LONG_PTR lParam;
2546 } ENUMLANGUAGEGROUP_CALLBACKS;
2547
2548 /* Internal implementation of EnumSystemLanguageGroupsA/W */
2549 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
2550 {
2551     WCHAR szNumber[10], szValue[4];
2552     HKEY hKey;
2553     BOOL bContinue = TRUE;
2554     ULONG ulIndex = 0;
2555
2556     if (!lpProcs)
2557     {
2558         SetLastError(ERROR_INVALID_PARAMETER);
2559         return FALSE;
2560     }
2561
2562     switch (lpProcs->dwFlags)
2563     {
2564     case 0:
2565         /* Default to LGRPID_INSTALLED */
2566         lpProcs->dwFlags = LGRPID_INSTALLED;
2567         /* Fall through... */
2568     case LGRPID_INSTALLED:
2569     case LGRPID_SUPPORTED:
2570         break;
2571     default:
2572         SetLastError(ERROR_INVALID_FLAGS);
2573         return FALSE;
2574     }
2575
2576     hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
2577
2578     if (!hKey)
2579       WARN("NLS registry key not found. Please apply the default registry file 'winedefault.reg'\n");
2580
2581     while (bContinue)
2582     {
2583         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
2584                               szValue, sizeof(szValue) ))
2585         {
2586             BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
2587             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
2588
2589             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
2590                    bInstalled ? "" : "not ");
2591
2592             if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
2593             {
2594                 WCHAR szGrpName[48];
2595
2596                 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
2597                     szGrpName[0] = '\0';
2598
2599                 if (lpProcs->procW)
2600                     bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
2601                                                 lpProcs->lParam );
2602                 else
2603                 {
2604                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
2605                     char szGrpNameA[48];
2606
2607                     /* FIXME: MSDN doesn't say which code page the W->A translation uses,
2608                      *        or whether the language names are ever localised. Assume CP_ACP.
2609                      */
2610
2611                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
2612                     WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
2613
2614                     bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
2615                                                 lpProcs->lParam );
2616                 }
2617             }
2618
2619             ulIndex++;
2620         }
2621         else
2622             bContinue = FALSE;
2623
2624         if (!bContinue)
2625             break;
2626     }
2627
2628     if (hKey)
2629         NtClose( hKey );
2630
2631     return TRUE;
2632 }
2633
2634 /******************************************************************************
2635  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
2636  *
2637  * Call a users function for each language group available on the system.
2638  *
2639  * PARAMS
2640  *  pLangGrpEnumProc [I] Callback function to call for each language group
2641  *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
2642  *  lParam           [I] User parameter to pass to pLangGrpEnumProc
2643  *
2644  * RETURNS
2645  *  Success: TRUE.
2646  *  Failure: FALSE. Use GetLastError() to determine the cause.
2647  */
2648 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
2649                                       DWORD dwFlags, LONG_PTR lParam)
2650 {
2651     ENUMLANGUAGEGROUP_CALLBACKS procs;
2652
2653     TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
2654
2655     procs.procA = pLangGrpEnumProc;
2656     procs.procW = NULL;
2657     procs.dwFlags = dwFlags;
2658     procs.lParam = lParam;
2659
2660     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
2661 }
2662
2663 /******************************************************************************
2664  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
2665  *
2666  * See EnumSystemLanguageGroupsA.
2667  */
2668 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
2669                                       DWORD dwFlags, LONG_PTR lParam)
2670 {
2671     ENUMLANGUAGEGROUP_CALLBACKS procs;
2672
2673     TRACE("(%p,0x%08lX,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
2674
2675     procs.procA = NULL;
2676     procs.procW = pLangGrpEnumProc;
2677     procs.dwFlags = dwFlags;
2678     procs.lParam = lParam;
2679
2680     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
2681 }
2682
2683 /******************************************************************************
2684  *           IsValidLanguageGroup    (KERNEL32.@)
2685  *
2686  * Determine if a language group is supported and/or installed.
2687  *
2688  * PARAMS
2689  *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
2690  *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
2691  *
2692  * RETURNS
2693  *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
2694  *  FALSE otherwise.
2695  */
2696 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
2697 {
2698     static const WCHAR szFormat[] = { '%','x','\0' };
2699     WCHAR szValueName[16], szValue[2];
2700     BOOL bSupported = FALSE, bInstalled = FALSE;
2701     HKEY hKey;
2702
2703
2704     switch (dwFlags)
2705     {
2706     case LGRPID_INSTALLED:
2707     case LGRPID_SUPPORTED:
2708
2709         hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLangGroupsKeyName );
2710
2711         sprintfW( szValueName, szFormat, lgrpid );
2712
2713         if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)&szValue ))
2714         {
2715             bSupported = TRUE;
2716
2717             if (szValue[0] == '1')
2718                 bInstalled = TRUE;
2719         }
2720
2721         if (hKey)
2722             NtClose( hKey );
2723
2724         break;
2725     }
2726
2727     if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
2728         (dwFlags == LGRPID_INSTALLED && bInstalled))
2729         return TRUE;
2730
2731     return FALSE;
2732 }
2733
2734 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
2735 typedef struct
2736 {
2737   LANGGROUPLOCALE_ENUMPROCA procA;
2738   LANGGROUPLOCALE_ENUMPROCW procW;
2739   DWORD    dwFlags;
2740   LGRPID   lgrpid;
2741   LONG_PTR lParam;
2742 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
2743
2744 /* Internal implementation of EnumLanguageGrouplocalesA/W */
2745 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
2746 {
2747     static const WCHAR szLocaleKeyName[] = {
2748       'L','o','c','a','l','e','\0'
2749     };
2750     static const WCHAR szAlternateSortsKeyName[] = {
2751       'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
2752     };
2753     WCHAR szNumber[10], szValue[4];
2754     HKEY hKey;
2755     BOOL bContinue = TRUE, bAlternate = FALSE;
2756     LGRPID lgrpid;
2757     ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
2758
2759     if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
2760     {
2761         SetLastError(ERROR_INVALID_PARAMETER);
2762         return FALSE;
2763     }
2764
2765     if (lpProcs->dwFlags)
2766     {
2767         SetLastError(ERROR_INVALID_FLAGS);
2768         return FALSE;
2769     }
2770
2771     hKey = NLS_RegOpenSubKey( NLS_RegOpenKey( 0, szNlsKeyName ), szLocaleKeyName );
2772
2773     if (!hKey)
2774       WARN("NLS registry key not found. Please apply the default registry file 'winedefault.reg'\n");
2775
2776     while (bContinue)
2777     {
2778         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
2779                               szValue, sizeof(szValue) ))
2780         {
2781             lgrpid = strtoulW( szValue, NULL, 16 );
2782
2783             TRACE("lcid %s, grpid %ld (%smatched)\n", debugstr_w(szNumber),
2784                    lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
2785
2786             if (lgrpid == lpProcs->lgrpid)
2787             {
2788                 LCID lcid;
2789
2790                 lcid = strtoulW( szNumber, NULL, 16 );
2791
2792                 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
2793                  * '00000437          ;Georgian'
2794                  * At present we only pass the LCID string.
2795                  */
2796
2797                 if (lpProcs->procW)
2798                     bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
2799                 else
2800                 {
2801                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
2802
2803                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
2804
2805                     bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
2806                 }
2807             }
2808
2809             ulIndex++;
2810         }
2811         else
2812         {
2813             /* Finished enumerating this key */
2814             if (!bAlternate)
2815             {
2816                 /* Enumerate alternate sorts also */
2817                 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
2818                 bAlternate = TRUE;
2819                 ulIndex = 0;
2820             }
2821             else
2822                 bContinue = FALSE; /* Finished both keys */
2823         }
2824
2825         if (!bContinue)
2826             break;
2827     }
2828
2829     if (hKey)
2830         NtClose( hKey );
2831
2832     return TRUE;
2833 }
2834
2835 /******************************************************************************
2836  *           EnumLanguageGroupLocalesA    (KERNEL32.@)
2837  *
2838  * Call a users function for every locale in a language group available on the system.
2839  *
2840  * PARAMS
2841  *  pLangGrpLcEnumProc [I] Callback function to call for each locale
2842  *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
2843  *  dwFlags            [I] Reserved, set to 0
2844  *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
2845  *
2846  * RETURNS
2847  *  Success: TRUE.
2848  *  Failure: FALSE. Use GetLastError() to determine the cause.
2849  */
2850 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
2851                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
2852 {
2853     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
2854
2855     TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
2856
2857     callbacks.procA   = pLangGrpLcEnumProc;
2858     callbacks.procW   = NULL;
2859     callbacks.dwFlags = dwFlags;
2860     callbacks.lgrpid  = lgrpid;
2861     callbacks.lParam  = lParam;
2862
2863     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
2864 }
2865
2866 /******************************************************************************
2867  *           EnumLanguageGroupLocalesW    (KERNEL32.@)
2868  *
2869  * See EnumLanguageGroupLocalesA.
2870  */
2871 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
2872                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
2873 {
2874     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
2875
2876     TRACE("(%p,0x%08lX,0x%08lX,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
2877
2878     callbacks.procA   = NULL;
2879     callbacks.procW   = pLangGrpLcEnumProc;
2880     callbacks.dwFlags = dwFlags;
2881     callbacks.lgrpid  = lgrpid;
2882     callbacks.lParam  = lParam;
2883
2884     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
2885 }
2886
2887 /******************************************************************************
2888  *           EnumSystemGeoID    (KERNEL32.@)
2889  *
2890  * Call a users function for every location available on the system.
2891  *
2892  * PARAMS
2893  *  geoclass     [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
2894  *  reserved     [I] Reserved, set to 0
2895  *  pGeoEnumProc [I] Callback function to call for each location
2896  *
2897  * RETURNS
2898  *  Success: TRUE.
2899  *  Failure: FALSE. Use GetLastError() to determine the cause.
2900  */
2901 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
2902 {
2903     static const WCHAR szCountryCodeValueName[] = {
2904       'C','o','u','n','t','r','y','C','o','d','e','\0'
2905     };
2906     WCHAR szNumber[10];
2907     HKEY hKey;
2908     ULONG ulIndex = 0;
2909
2910     TRACE("(0x%08lX,0x%08lX,%p)\n", geoclass, reserved, pGeoEnumProc);
2911
2912     if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
2913     {
2914         SetLastError(ERROR_INVALID_PARAMETER);
2915         return FALSE;
2916     }
2917
2918     hKey = NLS_RegOpenKey( 0, szCountryListName );
2919
2920     while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
2921     {
2922         BOOL bContinue = TRUE;
2923         DWORD dwGeoId;
2924         HKEY hSubKey = NLS_RegOpenKey( hKey, szNumber );
2925
2926         if (hSubKey)
2927         {
2928             if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
2929             {
2930                 TRACE("Got geoid %ld\n", dwGeoId);
2931
2932                 if (!pGeoEnumProc( dwGeoId ))
2933                     bContinue = FALSE;
2934             }
2935
2936             NtClose( hSubKey );
2937         }
2938
2939         if (!bContinue)
2940             break;
2941
2942         ulIndex++;
2943     }
2944
2945     if (hKey)
2946         NtClose( hKey );
2947
2948     return TRUE;
2949 }
2950
2951 /******************************************************************************
2952  *           InvalidateNLSCache           (KERNEL32.@)
2953  *
2954  * Invalidate the cache of NLS values.
2955  *
2956  * PARAMS
2957  *  None.
2958  *
2959  * RETURNS
2960  *  Success: TRUE.
2961  *  Failure: FALSE.
2962  */
2963 BOOL WINAPI InvalidateNLSCache(void)
2964 {
2965   FIXME("() stub\n");
2966   return FALSE;
2967 }
2968
2969 /******************************************************************************
2970  *           GetUserGeoID (KERNEL32.@)
2971  */
2972 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
2973 {
2974     FIXME("%ld\n",GeoClass);
2975     return GEOID_NOT_AVAILABLE;
2976 }
2977
2978 /******************************************************************************
2979  *           SetUserGeoID (KERNEL32.@)
2980  */
2981 BOOL WINAPI SetUserGeoID( GEOID GeoID )
2982 {
2983     FIXME("%ld\n",GeoID);
2984     return FALSE;
2985 }