Release 1.4.1.
[wine] / dlls / kernel32 / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFBundle.h>
37 # include <CoreFoundation/CFLocale.h>
38 # include <CoreFoundation/CFString.h>
39 #endif
40
41 #include "ntstatus.h"
42 #define WIN32_NO_STATUS
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winuser.h"  /* for RT_STRINGW */
46 #include "winternl.h"
47 #include "wine/unicode.h"
48 #include "winnls.h"
49 #include "winerror.h"
50 #include "winver.h"
51 #include "kernel_private.h"
52 #include "wine/debug.h"
53
54 WINE_DEFAULT_DEBUG_CHANNEL(nls);
55
56 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
57                                     LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
58
59 /* current code pages */
60 static const union cptable *ansi_cptable;
61 static const union cptable *oem_cptable;
62 static const union cptable *mac_cptable;
63 static const union cptable *unix_cptable;  /* NULL if UTF8 */
64
65 static const WCHAR szLocaleKeyName[] = {
66     'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
67     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
68     'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
69 };
70
71 static const WCHAR szLangGroupsKeyName[] = {
72     'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
73     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
74     'C','o','n','t','r','o','l','\\','N','l','s','\\',
75     'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
76 };
77
78 /* Charset to codepage map, sorted by name. */
79 static const struct charset_entry
80 {
81     const char *charset_name;
82     UINT        codepage;
83 } charset_names[] =
84 {
85     { "BIG5", 950 },
86     { "CP1250", 1250 },
87     { "CP1251", 1251 },
88     { "CP1252", 1252 },
89     { "CP1253", 1253 },
90     { "CP1254", 1254 },
91     { "CP1255", 1255 },
92     { "CP1256", 1256 },
93     { "CP1257", 1257 },
94     { "CP1258", 1258 },
95     { "CP932", 932 },
96     { "CP936", 936 },
97     { "CP949", 949 },
98     { "CP950", 950 },
99     { "EUCJP", 20932 },
100     { "GB2312", 936 },
101     { "IBM037", 37 },
102     { "IBM1026", 1026 },
103     { "IBM424", 424 },
104     { "IBM437", 437 },
105     { "IBM500", 500 },
106     { "IBM850", 850 },
107     { "IBM852", 852 },
108     { "IBM855", 855 },
109     { "IBM857", 857 },
110     { "IBM860", 860 },
111     { "IBM861", 861 },
112     { "IBM862", 862 },
113     { "IBM863", 863 },
114     { "IBM864", 864 },
115     { "IBM865", 865 },
116     { "IBM866", 866 },
117     { "IBM869", 869 },
118     { "IBM874", 874 },
119     { "IBM875", 875 },
120     { "ISO88591", 28591 },
121     { "ISO885910", 28600 },
122     { "ISO885913", 28603 },
123     { "ISO885914", 28604 },
124     { "ISO885915", 28605 },
125     { "ISO885916", 28606 },
126     { "ISO88592", 28592 },
127     { "ISO88593", 28593 },
128     { "ISO88594", 28594 },
129     { "ISO88595", 28595 },
130     { "ISO88596", 28596 },
131     { "ISO88597", 28597 },
132     { "ISO88598", 28598 },
133     { "ISO88599", 28599 },
134     { "KOI8R", 20866 },
135     { "KOI8U", 21866 },
136     { "UTF8", CP_UTF8 }
137 };
138
139
140 struct locale_name
141 {
142     WCHAR  win_name[128];   /* Windows name ("en-US") */
143     WCHAR  lang[128];       /* language ("en") (note: buffer contains the other strings too) */
144     WCHAR *country;         /* country ("US") */
145     WCHAR *charset;         /* charset ("UTF-8") for Unix format only */
146     WCHAR *script;          /* script ("Latn") for Windows format only */
147     WCHAR *modifier;        /* modifier or sort order */
148     LCID   lcid;            /* corresponding LCID */
149     int    matches;         /* number of elements matching LCID (0..4) */
150     UINT   codepage;        /* codepage corresponding to charset */
151 };
152
153 /* locale ids corresponding to the various Unix locale parameters */
154 static LCID lcid_LC_COLLATE;
155 static LCID lcid_LC_CTYPE;
156 static LCID lcid_LC_MESSAGES;
157 static LCID lcid_LC_MONETARY;
158 static LCID lcid_LC_NUMERIC;
159 static LCID lcid_LC_TIME;
160 static LCID lcid_LC_PAPER;
161 static LCID lcid_LC_MEASUREMENT;
162 static LCID lcid_LC_TELEPHONE;
163
164 /* Copy Ascii string to Unicode without using codepages */
165 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
166 {
167     while (n > 1 && *src)
168     {
169         *dst++ = (unsigned char)*src++;
170         n--;
171     }
172     if (n) *dst = 0;
173 }
174
175
176 /***********************************************************************
177  *              get_lcid_codepage
178  *
179  * Retrieve the ANSI codepage for a given locale.
180  */
181 static inline UINT get_lcid_codepage( LCID lcid )
182 {
183     UINT ret;
184     if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
185                          sizeof(ret)/sizeof(WCHAR) )) ret = 0;
186     return ret;
187 }
188
189
190 /***********************************************************************
191  *              get_codepage_table
192  *
193  * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
194  */
195 static const union cptable *get_codepage_table( unsigned int codepage )
196 {
197     const union cptable *ret = NULL;
198
199     assert( ansi_cptable );  /* init must have been done already */
200
201     switch(codepage)
202     {
203     case CP_ACP:
204         return ansi_cptable;
205     case CP_OEMCP:
206         return oem_cptable;
207     case CP_MACCP:
208         return mac_cptable;
209     case CP_UTF7:
210     case CP_UTF8:
211         break;
212     case CP_THREAD_ACP:
213         if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
214         codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
215         /* fall through */
216     default:
217         if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
218         if (codepage == oem_cptable->info.codepage) return oem_cptable;
219         if (codepage == mac_cptable->info.codepage) return mac_cptable;
220         ret = wine_cp_get_table( codepage );
221         break;
222     }
223     return ret;
224 }
225
226
227 /***********************************************************************
228  *              charset_cmp (internal)
229  */
230 static int charset_cmp( const void *name, const void *entry )
231 {
232     const struct charset_entry *charset = entry;
233     return strcasecmp( name, charset->charset_name );
234 }
235
236 /***********************************************************************
237  *              find_charset
238  */
239 static UINT find_charset( const WCHAR *name )
240 {
241     const struct charset_entry *entry;
242     char charset_name[16];
243     size_t i, j;
244
245     /* remove punctuation characters from charset name */
246     for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
247         if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
248     charset_name[j] = 0;
249
250     entry = bsearch( charset_name, charset_names,
251                      sizeof(charset_names)/sizeof(charset_names[0]),
252                      sizeof(charset_names[0]), charset_cmp );
253     if (entry) return entry->codepage;
254     return 0;
255 }
256
257
258 /***********************************************************************
259  *           find_locale_id_callback
260  */
261 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
262                                               LPCWSTR name, WORD LangID, LPARAM lParam )
263 {
264     struct locale_name *data = (struct locale_name *)lParam;
265     WCHAR buffer[128];
266     int matches = 0;
267     LCID lcid = MAKELCID( LangID, SORT_DEFAULT );  /* FIXME: handle sort order */
268
269     if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
270
271     /* first check exact name */
272     if (data->win_name[0] &&
273         GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
274                         buffer, sizeof(buffer)/sizeof(WCHAR) ))
275     {
276         if (!strcmpW( data->win_name, buffer ))
277         {
278             matches = 4;  /* everything matches */
279             goto done;
280         }
281     }
282
283     if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
284                          buffer, sizeof(buffer)/sizeof(WCHAR) ))
285         return TRUE;
286     if (strcmpW( buffer, data->lang )) return TRUE;
287     matches++;  /* language name matched */
288
289     if (data->country)
290     {
291         if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
292                             buffer, sizeof(buffer)/sizeof(WCHAR) ))
293         {
294             if (strcmpW( buffer, data->country )) goto done;
295             matches++;  /* country name matched */
296         }
297     }
298     else  /* match default language */
299     {
300         if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
301     }
302
303     if (data->codepage)
304     {
305         UINT unix_cp;
306         if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
307                             (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
308         {
309             if (unix_cp == data->codepage) matches++;
310         }
311     }
312
313     /* FIXME: check sort order */
314
315 done:
316     if (matches > data->matches)
317     {
318         data->lcid = lcid;
319         data->matches = matches;
320     }
321     return (data->matches < 4);  /* no need to continue for perfect match */
322 }
323
324
325 /***********************************************************************
326  *              parse_locale_name
327  *
328  * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
329  * Unix format is: lang[_country][.charset][@modifier]
330  * Windows format is: lang[-script][-country][_modifier]
331  */
332 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
333 {
334     static const WCHAR sepW[] = {'-','_','.','@',0};
335     static const WCHAR winsepW[] = {'-','_',0};
336     static const WCHAR posixW[] = {'P','O','S','I','X',0};
337     static const WCHAR cW[] = {'C',0};
338     static const WCHAR latinW[] = {'l','a','t','i','n',0};
339     static const WCHAR latnW[] = {'-','L','a','t','n',0};
340     WCHAR *p;
341
342     TRACE("%s\n", debugstr_w(str));
343
344     name->country = name->charset = name->script = name->modifier = NULL;
345     name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
346     name->matches = 0;
347     name->codepage = 0;
348     name->win_name[0] = 0;
349     lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
350
351     if (!(p = strpbrkW( name->lang, sepW )))
352     {
353         if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
354         {
355             name->matches = 4;  /* perfect match for default English lcid */
356             return;
357         }
358         strcpyW( name->win_name, name->lang );
359     }
360     else if (*p == '-')  /* Windows format */
361     {
362         strcpyW( name->win_name, name->lang );
363         *p++ = 0;
364         name->country = p;
365         if (!(p = strpbrkW( p, winsepW ))) goto done;
366         if (*p == '-')
367         {
368             *p++ = 0;
369             name->script = name->country;
370             name->country = p;
371             if (!(p = strpbrkW( p, winsepW ))) goto done;
372         }
373         *p++ = 0;
374         name->modifier = p;
375     }
376     else  /* Unix format */
377     {
378         if (*p == '_')
379         {
380             *p++ = 0;
381             name->country = p;
382             p = strpbrkW( p, sepW + 2 );
383         }
384         if (p && *p == '.')
385         {
386             *p++ = 0;
387             name->charset = p;
388             p = strchrW( p, '@' );
389         }
390         if (p)
391         {
392             *p++ = 0;
393             name->modifier = p;
394         }
395
396         if (name->charset)
397             name->codepage = find_charset( name->charset );
398
399         /* rebuild a Windows name if possible */
400
401         if (name->charset) goto done;  /* can't specify charset in Windows format */
402         if (name->modifier && strcmpW( name->modifier, latinW ))
403             goto done;  /* only Latn script supported for now */
404         strcpyW( name->win_name, name->lang );
405         if (name->modifier) strcatW( name->win_name, latnW );
406         if (name->country)
407         {
408             p = name->win_name + strlenW(name->win_name);
409             *p++ = '-';
410             strcpyW( p, name->country );
411         }
412     }
413 done:
414     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
415                             find_locale_id_callback, (LPARAM)name );
416 }
417
418
419 /***********************************************************************
420  *           convert_default_lcid
421  *
422  * Get the default LCID to use for a given lctype in GetLocaleInfo.
423  */
424 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
425 {
426     if (lcid == LOCALE_SYSTEM_DEFAULT ||
427         lcid == LOCALE_USER_DEFAULT ||
428         lcid == LOCALE_NEUTRAL)
429     {
430         LCID default_id = 0;
431
432         switch(lctype & 0xffff)
433         {
434         case LOCALE_SSORTNAME:
435             default_id = lcid_LC_COLLATE;
436             break;
437
438         case LOCALE_FONTSIGNATURE:
439         case LOCALE_IDEFAULTANSICODEPAGE:
440         case LOCALE_IDEFAULTCODEPAGE:
441         case LOCALE_IDEFAULTEBCDICCODEPAGE:
442         case LOCALE_IDEFAULTMACCODEPAGE:
443         case LOCALE_IDEFAULTUNIXCODEPAGE:
444             default_id = lcid_LC_CTYPE;
445             break;
446
447         case LOCALE_ICURRDIGITS:
448         case LOCALE_ICURRENCY:
449         case LOCALE_IINTLCURRDIGITS:
450         case LOCALE_INEGCURR:
451         case LOCALE_INEGSEPBYSPACE:
452         case LOCALE_INEGSIGNPOSN:
453         case LOCALE_INEGSYMPRECEDES:
454         case LOCALE_IPOSSEPBYSPACE:
455         case LOCALE_IPOSSIGNPOSN:
456         case LOCALE_IPOSSYMPRECEDES:
457         case LOCALE_SCURRENCY:
458         case LOCALE_SINTLSYMBOL:
459         case LOCALE_SMONDECIMALSEP:
460         case LOCALE_SMONGROUPING:
461         case LOCALE_SMONTHOUSANDSEP:
462         case LOCALE_SNATIVECURRNAME:
463             default_id = lcid_LC_MONETARY;
464             break;
465
466         case LOCALE_IDIGITS:
467         case LOCALE_IDIGITSUBSTITUTION:
468         case LOCALE_ILZERO:
469         case LOCALE_INEGNUMBER:
470         case LOCALE_SDECIMAL:
471         case LOCALE_SGROUPING:
472         case LOCALE_SNAN:
473         case LOCALE_SNATIVEDIGITS:
474         case LOCALE_SNEGATIVESIGN:
475         case LOCALE_SNEGINFINITY:
476         case LOCALE_SPOSINFINITY:
477         case LOCALE_SPOSITIVESIGN:
478         case LOCALE_STHOUSAND:
479             default_id = lcid_LC_NUMERIC;
480             break;
481
482         case LOCALE_ICALENDARTYPE:
483         case LOCALE_ICENTURY:
484         case LOCALE_IDATE:
485         case LOCALE_IDAYLZERO:
486         case LOCALE_IFIRSTDAYOFWEEK:
487         case LOCALE_IFIRSTWEEKOFYEAR:
488         case LOCALE_ILDATE:
489         case LOCALE_IMONLZERO:
490         case LOCALE_IOPTIONALCALENDAR:
491         case LOCALE_ITIME:
492         case LOCALE_ITIMEMARKPOSN:
493         case LOCALE_ITLZERO:
494         case LOCALE_S1159:
495         case LOCALE_S2359:
496         case LOCALE_SABBREVDAYNAME1:
497         case LOCALE_SABBREVDAYNAME2:
498         case LOCALE_SABBREVDAYNAME3:
499         case LOCALE_SABBREVDAYNAME4:
500         case LOCALE_SABBREVDAYNAME5:
501         case LOCALE_SABBREVDAYNAME6:
502         case LOCALE_SABBREVDAYNAME7:
503         case LOCALE_SABBREVMONTHNAME1:
504         case LOCALE_SABBREVMONTHNAME2:
505         case LOCALE_SABBREVMONTHNAME3:
506         case LOCALE_SABBREVMONTHNAME4:
507         case LOCALE_SABBREVMONTHNAME5:
508         case LOCALE_SABBREVMONTHNAME6:
509         case LOCALE_SABBREVMONTHNAME7:
510         case LOCALE_SABBREVMONTHNAME8:
511         case LOCALE_SABBREVMONTHNAME9:
512         case LOCALE_SABBREVMONTHNAME10:
513         case LOCALE_SABBREVMONTHNAME11:
514         case LOCALE_SABBREVMONTHNAME12:
515         case LOCALE_SABBREVMONTHNAME13:
516         case LOCALE_SDATE:
517         case LOCALE_SDAYNAME1:
518         case LOCALE_SDAYNAME2:
519         case LOCALE_SDAYNAME3:
520         case LOCALE_SDAYNAME4:
521         case LOCALE_SDAYNAME5:
522         case LOCALE_SDAYNAME6:
523         case LOCALE_SDAYNAME7:
524         case LOCALE_SDURATION:
525         case LOCALE_SLONGDATE:
526         case LOCALE_SMONTHNAME1:
527         case LOCALE_SMONTHNAME2:
528         case LOCALE_SMONTHNAME3:
529         case LOCALE_SMONTHNAME4:
530         case LOCALE_SMONTHNAME5:
531         case LOCALE_SMONTHNAME6:
532         case LOCALE_SMONTHNAME7:
533         case LOCALE_SMONTHNAME8:
534         case LOCALE_SMONTHNAME9:
535         case LOCALE_SMONTHNAME10:
536         case LOCALE_SMONTHNAME11:
537         case LOCALE_SMONTHNAME12:
538         case LOCALE_SMONTHNAME13:
539         case LOCALE_SSHORTDATE:
540         case LOCALE_SSHORTESTDAYNAME1:
541         case LOCALE_SSHORTESTDAYNAME2:
542         case LOCALE_SSHORTESTDAYNAME3:
543         case LOCALE_SSHORTESTDAYNAME4:
544         case LOCALE_SSHORTESTDAYNAME5:
545         case LOCALE_SSHORTESTDAYNAME6:
546         case LOCALE_SSHORTESTDAYNAME7:
547         case LOCALE_STIME:
548         case LOCALE_STIMEFORMAT:
549         case LOCALE_SYEARMONTH:
550             default_id = lcid_LC_TIME;
551             break;
552
553         case LOCALE_IPAPERSIZE:
554             default_id = lcid_LC_PAPER;
555             break;
556
557         case LOCALE_IMEASURE:
558             default_id = lcid_LC_MEASUREMENT;
559             break;
560
561         case LOCALE_ICOUNTRY:
562             default_id = lcid_LC_TELEPHONE;
563             break;
564         }
565         if (default_id) lcid = default_id;
566     }
567     return ConvertDefaultLocale( lcid );
568 }
569
570 /***********************************************************************
571  *           is_genitive_name_supported
572  *
573  * Determine could LCTYPE basically support genitive name form or not.
574  */
575 static BOOL is_genitive_name_supported( LCTYPE lctype )
576 {
577     switch(lctype & 0xffff)
578     {
579     case LOCALE_SMONTHNAME1:
580     case LOCALE_SMONTHNAME2:
581     case LOCALE_SMONTHNAME3:
582     case LOCALE_SMONTHNAME4:
583     case LOCALE_SMONTHNAME5:
584     case LOCALE_SMONTHNAME6:
585     case LOCALE_SMONTHNAME7:
586     case LOCALE_SMONTHNAME8:
587     case LOCALE_SMONTHNAME9:
588     case LOCALE_SMONTHNAME10:
589     case LOCALE_SMONTHNAME11:
590     case LOCALE_SMONTHNAME12:
591     case LOCALE_SMONTHNAME13:
592          return TRUE;
593     default:
594          return FALSE;
595     }
596 }
597
598 /***********************************************************************
599  *              create_registry_key
600  *
601  * Create the Control Panel\\International registry key.
602  */
603 static inline HANDLE create_registry_key(void)
604 {
605     static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
606     static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
607     OBJECT_ATTRIBUTES attr;
608     UNICODE_STRING nameW;
609     HANDLE cpl_key, hkey = 0;
610
611     if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
612
613     attr.Length = sizeof(attr);
614     attr.RootDirectory = hkey;
615     attr.ObjectName = &nameW;
616     attr.Attributes = 0;
617     attr.SecurityDescriptor = NULL;
618     attr.SecurityQualityOfService = NULL;
619     RtlInitUnicodeString( &nameW, cplW );
620
621     if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
622     {
623         NtClose( attr.RootDirectory );
624         attr.RootDirectory = cpl_key;
625         RtlInitUnicodeString( &nameW, intlW );
626         if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
627     }
628     NtClose( attr.RootDirectory );
629     return hkey;
630 }
631
632
633 /* update the registry settings for a given locale parameter */
634 /* return TRUE if an update was needed */
635 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
636                                     const LCTYPE *values, UINT nb_values )
637 {
638     static const WCHAR formatW[] = { '%','0','8','x',0 };
639     WCHAR bufferW[40];
640     UNICODE_STRING nameW;
641     DWORD count, i;
642
643     RtlInitUnicodeString( &nameW, name );
644     count = sizeof(bufferW);
645     if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
646     {
647         const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
648         LPCWSTR text = (LPCWSTR)info->Data;
649
650         if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
651         TRACE( "updating registry, locale %s changed %s -> %08x\n",
652                debugstr_w(name), debugstr_w(text), lcid );
653     }
654     else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
655     sprintfW( bufferW, formatW, lcid );
656     NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
657
658     for (i = 0; i < nb_values; i++)
659     {
660         GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
661                         sizeof(bufferW)/sizeof(WCHAR) );
662         SetLocaleInfoW( lcid, values[i], bufferW );
663     }
664     return TRUE;
665 }
666
667
668 /***********************************************************************
669  *              LOCALE_InitRegistry
670  *
671  * Update registry contents on startup if the user locale has changed.
672  * This simulates the action of the Windows control panel.
673  */
674 void LOCALE_InitRegistry(void)
675 {
676     static const WCHAR acpW[] = {'A','C','P',0};
677     static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
678     static const WCHAR maccpW[] = {'M','A','C','C','P',0};
679     static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
680     static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
681     static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
682     static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
683     static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
684     static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
685     static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
686     static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
687     static const struct
688     {
689         LPCWSTR name;
690         USHORT value;
691     } update_cp_values[] = {
692         { acpW, LOCALE_IDEFAULTANSICODEPAGE },
693         { oemcpW, LOCALE_IDEFAULTCODEPAGE },
694         { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
695     };
696     static const LCTYPE lc_messages_values[] = {
697       LOCALE_SABBREVLANGNAME,
698       LOCALE_SCOUNTRY,
699       LOCALE_SLIST };
700     static const LCTYPE lc_monetary_values[] = {
701       LOCALE_SCURRENCY,
702       LOCALE_ICURRENCY,
703       LOCALE_INEGCURR,
704       LOCALE_ICURRDIGITS,
705       LOCALE_ILZERO,
706       LOCALE_SMONDECIMALSEP,
707       LOCALE_SMONGROUPING,
708       LOCALE_SMONTHOUSANDSEP };
709     static const LCTYPE lc_numeric_values[] = {
710       LOCALE_SDECIMAL,
711       LOCALE_STHOUSAND,
712       LOCALE_IDIGITS,
713       LOCALE_IDIGITSUBSTITUTION,
714       LOCALE_SNATIVEDIGITS,
715       LOCALE_INEGNUMBER,
716       LOCALE_SNEGATIVESIGN,
717       LOCALE_SPOSITIVESIGN,
718       LOCALE_SGROUPING };
719     static const LCTYPE lc_time_values[] = {
720       LOCALE_S1159,
721       LOCALE_S2359,
722       LOCALE_STIME,
723       LOCALE_ITIME,
724       LOCALE_ITLZERO,
725       LOCALE_SSHORTDATE,
726       LOCALE_SLONGDATE,
727       LOCALE_SDATE,
728       LOCALE_ITIMEMARKPOSN,
729       LOCALE_ICALENDARTYPE,
730       LOCALE_IFIRSTDAYOFWEEK,
731       LOCALE_IFIRSTWEEKOFYEAR,
732       LOCALE_STIMEFORMAT,
733       LOCALE_SYEARMONTH,
734       LOCALE_IDATE };
735     static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
736     static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
737     static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
738
739     UNICODE_STRING nameW;
740     WCHAR bufferW[80];
741     DWORD count, i;
742     HANDLE hkey;
743     LCID lcid = GetUserDefaultLCID();
744
745     if (!(hkey = create_registry_key()))
746         return;  /* don't do anything if we can't create the registry key */
747
748     locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
749                             sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
750     locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
751                             sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
752     locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
753                             sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
754     locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
755                             sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
756     locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
757                             sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
758     locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
759                             sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
760     locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
761                             sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
762
763     if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
764     {
765         static const WCHAR codepageW[] =
766             {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
767              'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
768              'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
769
770         OBJECT_ATTRIBUTES attr;
771         HANDLE nls_key;
772         DWORD len = 14;
773
774         RtlInitUnicodeString( &nameW, codepageW );
775         InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
776         while (codepageW[len])
777         {
778             nameW.Length = len * sizeof(WCHAR);
779             if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
780             NtClose( nls_key );
781             len++;
782             while (codepageW[len] && codepageW[len] != '\\') len++;
783         }
784         nameW.Length = len * sizeof(WCHAR);
785         if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
786         {
787             for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
788             {
789                 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
790                                         bufferW, sizeof(bufferW)/sizeof(WCHAR) );
791                 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
792                 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
793             }
794             NtClose( nls_key );
795         }
796     }
797
798     NtClose( hkey );
799 }
800
801
802 /***********************************************************************
803  *           setup_unix_locales
804  */
805 static UINT setup_unix_locales(void)
806 {
807     struct locale_name locale_name;
808     WCHAR buffer[128], ctype_buff[128];
809     char *locale;
810     UINT unix_cp = 0;
811
812     if ((locale = setlocale( LC_CTYPE, NULL )))
813     {
814         strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
815         parse_locale_name( ctype_buff, &locale_name );
816         lcid_LC_CTYPE = locale_name.lcid;
817         unix_cp = locale_name.codepage;
818     }
819     if (!lcid_LC_CTYPE)  /* this one needs a default value */
820         lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
821
822     TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
823            locale_name.lcid, locale_name.matches, debugstr_a(locale) );
824
825 #define GET_UNIX_LOCALE(cat) do \
826     if ((locale = setlocale( cat, NULL ))) \
827     { \
828         strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
829         if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
830         else { \
831             parse_locale_name( buffer, &locale_name );  \
832             lcid_##cat = locale_name.lcid; \
833             TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n",        \
834                    locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
835         } \
836     } while (0)
837
838     GET_UNIX_LOCALE( LC_COLLATE );
839     GET_UNIX_LOCALE( LC_MESSAGES );
840     GET_UNIX_LOCALE( LC_MONETARY );
841     GET_UNIX_LOCALE( LC_NUMERIC );
842     GET_UNIX_LOCALE( LC_TIME );
843 #ifdef LC_PAPER
844     GET_UNIX_LOCALE( LC_PAPER );
845 #endif
846 #ifdef LC_MEASUREMENT
847     GET_UNIX_LOCALE( LC_MEASUREMENT );
848 #endif
849 #ifdef LC_TELEPHONE
850     GET_UNIX_LOCALE( LC_TELEPHONE );
851 #endif
852
853 #undef GET_UNIX_LOCALE
854
855     return unix_cp;
856 }
857
858
859 /***********************************************************************
860  *              GetUserDefaultLangID (KERNEL32.@)
861  *
862  * Get the default language Id for the current user.
863  *
864  * PARAMS
865  *  None.
866  *
867  * RETURNS
868  *  The current LANGID of the default language for the current user.
869  */
870 LANGID WINAPI GetUserDefaultLangID(void)
871 {
872     return LANGIDFROMLCID(GetUserDefaultLCID());
873 }
874
875
876 /***********************************************************************
877  *              GetSystemDefaultLangID (KERNEL32.@)
878  *
879  * Get the default language Id for the system.
880  *
881  * PARAMS
882  *  None.
883  *
884  * RETURNS
885  *  The current LANGID of the default language for the system.
886  */
887 LANGID WINAPI GetSystemDefaultLangID(void)
888 {
889     return LANGIDFROMLCID(GetSystemDefaultLCID());
890 }
891
892
893 /***********************************************************************
894  *              GetUserDefaultLCID (KERNEL32.@)
895  *
896  * Get the default locale Id for the current user.
897  *
898  * PARAMS
899  *  None.
900  *
901  * RETURNS
902  *  The current LCID of the default locale for the current user.
903  */
904 LCID WINAPI GetUserDefaultLCID(void)
905 {
906     LCID lcid;
907     NtQueryDefaultLocale( TRUE, &lcid );
908     return lcid;
909 }
910
911
912 /***********************************************************************
913  *              GetSystemDefaultLCID (KERNEL32.@)
914  *
915  * Get the default locale Id for the system.
916  *
917  * PARAMS
918  *  None.
919  *
920  * RETURNS
921  *  The current LCID of the default locale for the system.
922  */
923 LCID WINAPI GetSystemDefaultLCID(void)
924 {
925     LCID lcid;
926     NtQueryDefaultLocale( FALSE, &lcid );
927     return lcid;
928 }
929
930
931 /***********************************************************************
932  *              GetUserDefaultUILanguage (KERNEL32.@)
933  *
934  * Get the default user interface language Id for the current user.
935  *
936  * PARAMS
937  *  None.
938  *
939  * RETURNS
940  *  The current LANGID of the default UI language for the current user.
941  */
942 LANGID WINAPI GetUserDefaultUILanguage(void)
943 {
944     LANGID lang;
945     NtQueryDefaultUILanguage( &lang );
946     return lang;
947 }
948
949
950 /***********************************************************************
951  *              GetSystemDefaultUILanguage (KERNEL32.@)
952  *
953  * Get the default user interface language Id for the system.
954  *
955  * PARAMS
956  *  None.
957  *
958  * RETURNS
959  *  The current LANGID of the default UI language for the system. This is
960  *  typically the same language used during the installation process.
961  */
962 LANGID WINAPI GetSystemDefaultUILanguage(void)
963 {
964     LANGID lang;
965     NtQueryInstallUILanguage( &lang );
966     return lang;
967 }
968
969
970 /***********************************************************************
971  *           LocaleNameToLCID  (KERNEL32.@)
972  */
973 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
974 {
975     struct locale_name locale_name;
976
977     if (flags) FIXME( "unsupported flags %x\n", flags );
978
979     if (name == LOCALE_NAME_USER_DEFAULT)
980         return GetUserDefaultLCID();
981
982     /* string parsing */
983     parse_locale_name( name, &locale_name );
984
985     TRACE( "found lcid %x for %s, matches %d\n",
986            locale_name.lcid, debugstr_w(name), locale_name.matches );
987
988     if (!locale_name.matches)
989         WARN( "locale %s not recognized, defaulting to English\n", debugstr_w(name) );
990     else if (locale_name.matches == 1)
991         WARN( "locale %s not recognized, defaulting to %s\n",
992               debugstr_w(name), debugstr_w(locale_name.lang) );
993
994     return locale_name.lcid;
995 }
996
997
998 /***********************************************************************
999  *           LCIDToLocaleName  (KERNEL32.@)
1000  */
1001 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1002 {
1003     if (flags) FIXME( "unsupported flags %x\n", flags );
1004
1005     return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1006 }
1007
1008
1009 /******************************************************************************
1010  *              get_locale_value_name
1011  *
1012  * Gets the registry value name for a given lctype.
1013  */
1014 static const WCHAR *get_locale_value_name( DWORD lctype )
1015 {
1016     static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1017     static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1018     static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1019     static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1020     static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1021     static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1022     static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1023     static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1024     static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1025     static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1026     static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1027     static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1028     static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1029     static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1030     static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1031     static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1032     static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1033     static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1034     static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1035     static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1036     static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1037     static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1038     static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1039     static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1040     static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1041     static const WCHAR sListW[] = {'s','L','i','s','t',0};
1042     static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1043     static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1044     static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1045     static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1046     static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1047     static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1048     static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1049     static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1050     static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1051     static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1052     static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1053     static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1054     static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1055
1056     switch (lctype)
1057     {
1058     /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1059      * the values are stored in the registry, confirmed under Windows.
1060      */
1061     case LOCALE_ICALENDARTYPE:    return iCalendarTypeW;
1062     case LOCALE_ICURRDIGITS:      return iCurrDigitsW;
1063     case LOCALE_ICURRENCY:        return iCurrencyW;
1064     case LOCALE_IDIGITS:          return iDigitsW;
1065     case LOCALE_IFIRSTDAYOFWEEK:  return iFirstDayOfWeekW;
1066     case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1067     case LOCALE_ILZERO:           return iLZeroW;
1068     case LOCALE_IMEASURE:         return iMeasureW;
1069     case LOCALE_INEGCURR:         return iNegCurrW;
1070     case LOCALE_INEGNUMBER:       return iNegNumberW;
1071     case LOCALE_IPAPERSIZE:       return iPaperSizeW;
1072     case LOCALE_ITIME:            return iTimeW;
1073     case LOCALE_S1159:            return s1159W;
1074     case LOCALE_S2359:            return s2359W;
1075     case LOCALE_SCURRENCY:        return sCurrencyW;
1076     case LOCALE_SDATE:            return sDateW;
1077     case LOCALE_SDECIMAL:         return sDecimalW;
1078     case LOCALE_SGROUPING:        return sGroupingW;
1079     case LOCALE_SLIST:            return sListW;
1080     case LOCALE_SLONGDATE:        return sLongDateW;
1081     case LOCALE_SMONDECIMALSEP:   return sMonDecimalSepW;
1082     case LOCALE_SMONGROUPING:     return sMonGroupingW;
1083     case LOCALE_SMONTHOUSANDSEP:  return sMonThousandSepW;
1084     case LOCALE_SNEGATIVESIGN:    return sNegativeSignW;
1085     case LOCALE_SPOSITIVESIGN:    return sPositiveSignW;
1086     case LOCALE_SSHORTDATE:       return sShortDateW;
1087     case LOCALE_STHOUSAND:        return sThousandW;
1088     case LOCALE_STIME:            return sTimeW;
1089     case LOCALE_STIMEFORMAT:      return sTimeFormatW;
1090     case LOCALE_SYEARMONTH:       return sYearMonthW;
1091
1092     /* The following are not listed under MSDN as supported,
1093      * but seem to be used and also stored in the registry.
1094      */
1095     case LOCALE_ICOUNTRY:         return iCountryW;
1096     case LOCALE_IDATE:            return iDateW;
1097     case LOCALE_ILDATE:           return iLDateW;
1098     case LOCALE_ITLZERO:          return iTLZeroW;
1099     case LOCALE_SCOUNTRY:         return sCountryW;
1100     case LOCALE_SABBREVLANGNAME:  return sLanguageW;
1101
1102     /* The following are used in XP and later */
1103     case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1104     case LOCALE_SNATIVEDIGITS:      return sNativeDigitsW;
1105     case LOCALE_ITIMEMARKPOSN:      return iTimePrefixW;
1106     }
1107     return NULL;
1108 }
1109
1110
1111 /******************************************************************************
1112  *              get_registry_locale_info
1113  *
1114  * Retrieve user-modified locale info from the registry.
1115  * Return length, 0 on error, -1 if not found.
1116  */
1117 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1118 {
1119     DWORD size;
1120     INT ret;
1121     HANDLE hkey;
1122     NTSTATUS status;
1123     UNICODE_STRING nameW;
1124     KEY_VALUE_PARTIAL_INFORMATION *info;
1125     static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1126
1127     if (!(hkey = create_registry_key())) return -1;
1128
1129     RtlInitUnicodeString( &nameW, value );
1130     size = info_size + len * sizeof(WCHAR);
1131
1132     if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1133     {
1134         NtClose( hkey );
1135         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1136         return 0;
1137     }
1138
1139     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1140
1141     if (!status)
1142     {
1143         ret = (size - info_size) / sizeof(WCHAR);
1144         /* append terminating null if needed */
1145         if (!ret || ((WCHAR *)info->Data)[ret-1])
1146         {
1147             if (ret < len || !buffer) ret++;
1148             else
1149             {
1150                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1151                 ret = 0;
1152             }
1153         }
1154         if (ret && buffer)
1155         {
1156             memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1157             buffer[ret-1] = 0;
1158         }
1159     }
1160     else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1161     {
1162         ret = (size - info_size) / sizeof(WCHAR) + 1;
1163     }
1164     else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1165     {
1166         ret = -1;
1167     }
1168     else
1169     {
1170         SetLastError( RtlNtStatusToDosError(status) );
1171         ret = 0;
1172     }
1173     NtClose( hkey );
1174     HeapFree( GetProcessHeap(), 0, info );
1175     return ret;
1176 }
1177
1178
1179 /******************************************************************************
1180  *              GetLocaleInfoA (KERNEL32.@)
1181  *
1182  * Get information about an aspect of a locale.
1183  *
1184  * PARAMS
1185  *  lcid   [I] LCID of the locale
1186  *  lctype [I] LCTYPE_ flags from "winnls.h"
1187  *  buffer [O] Destination for the information
1188  *  len    [I] Length of buffer in characters
1189  *
1190  * RETURNS
1191  *  Success: The size of the data requested. If buffer is non-NULL, it is filled
1192  *           with the information.
1193  *  Failure: 0. Use GetLastError() to determine the cause.
1194  *
1195  * NOTES
1196  *  - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1197  *  - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1198  *    which is a bit string.
1199  */
1200 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1201 {
1202     WCHAR *bufferW;
1203     INT lenW, ret;
1204
1205     TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1206
1207     if (len < 0 || (len && !buffer))
1208     {
1209         SetLastError( ERROR_INVALID_PARAMETER );
1210         return 0;
1211     }
1212     if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1213     {
1214         SetLastError( ERROR_INVALID_FLAGS );
1215         return 0;
1216     }
1217
1218     if (!len) buffer = NULL;
1219
1220     if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1221
1222     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1223     {
1224         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1225         return 0;
1226     }
1227     if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1228     {
1229         if ((lctype & LOCALE_RETURN_NUMBER) ||
1230             ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1231         {
1232             /* it's not an ASCII string, just bytes */
1233             ret *= sizeof(WCHAR);
1234             if (buffer)
1235             {
1236                 if (ret <= len) memcpy( buffer, bufferW, ret );
1237                 else
1238                 {
1239                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1240                     ret = 0;
1241                 }
1242             }
1243         }
1244         else
1245         {
1246             UINT codepage = CP_ACP;
1247             if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1248             ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1249         }
1250     }
1251     HeapFree( GetProcessHeap(), 0, bufferW );
1252     return ret;
1253 }
1254
1255
1256 /******************************************************************************
1257  *              GetLocaleInfoW (KERNEL32.@)
1258  *
1259  * See GetLocaleInfoA.
1260  */
1261 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1262 {
1263     LANGID lang_id;
1264     HRSRC hrsrc;
1265     HGLOBAL hmem;
1266     INT ret;
1267     UINT lcflags;
1268     const WCHAR *p;
1269     unsigned int i;
1270
1271     if (len < 0 || (len && !buffer))
1272     {
1273         SetLastError( ERROR_INVALID_PARAMETER );
1274         return 0;
1275     }
1276     if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1277        !is_genitive_name_supported( lctype ))
1278     {
1279         SetLastError( ERROR_INVALID_FLAGS );
1280         return 0;
1281     }
1282
1283     if (!len) buffer = NULL;
1284
1285     lcid = convert_default_lcid( lcid, lctype );
1286
1287     lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1288     lctype &= 0xffff;
1289
1290     TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1291
1292     /* first check for overrides in the registry */
1293
1294     if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1295         lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1296     {
1297         const WCHAR *value = get_locale_value_name(lctype);
1298
1299         if (value)
1300         {
1301             if (lcflags & LOCALE_RETURN_NUMBER)
1302             {
1303                 WCHAR tmp[16];
1304                 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1305                 if (ret > 0)
1306                 {
1307                     WCHAR *end;
1308                     UINT number = strtolW( tmp, &end, 10 );
1309                     if (*end)  /* invalid number */
1310                     {
1311                         SetLastError( ERROR_INVALID_FLAGS );
1312                         return 0;
1313                     }
1314                     ret = sizeof(UINT)/sizeof(WCHAR);
1315                     if (!buffer) return ret;
1316                     if (ret > len)
1317                     {
1318                         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1319                         return 0;
1320                     }
1321                     memcpy( buffer, &number, sizeof(number) );
1322                 }
1323             }
1324             else ret = get_registry_locale_info( value, buffer, len );
1325
1326             if (ret != -1) return ret;
1327         }
1328     }
1329
1330     /* now load it from kernel resources */
1331
1332     lang_id = LANGIDFROMLCID( lcid );
1333
1334     /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1335     if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1336         lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1337
1338     if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1339                                    ULongToPtr((lctype >> 4) + 1), lang_id )))
1340     {
1341         SetLastError( ERROR_INVALID_FLAGS );  /* no such lctype */
1342         return 0;
1343     }
1344     if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1345         return 0;
1346
1347     p = LockResource( hmem );
1348     for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1349
1350     if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1351     else if (is_genitive_name_supported( lctype ) && *p)
1352     {
1353         /* genitive form's stored after a null separator from a nominative */
1354         for (i = 1; i <= *p; i++) if (!p[i]) break;
1355
1356         if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1357         {
1358             ret = *p - i + 1;
1359             p += i;
1360         }
1361         else ret = i;
1362     }
1363     else
1364         ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1365
1366     if (!buffer) return ret;
1367
1368     if (ret > len)
1369     {
1370         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1371         return 0;
1372     }
1373
1374     if (lcflags & LOCALE_RETURN_NUMBER)
1375     {
1376         UINT number;
1377         WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1378         if (!tmp) return 0;
1379         memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1380         tmp[*p] = 0;
1381         number = strtolW( tmp, &end, 10 );
1382         if (!*end)
1383             memcpy( buffer, &number, sizeof(number) );
1384         else  /* invalid number */
1385         {
1386             SetLastError( ERROR_INVALID_FLAGS );
1387             ret = 0;
1388         }
1389         HeapFree( GetProcessHeap(), 0, tmp );
1390
1391         TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1392                lcid, lctype, buffer, len, number );
1393     }
1394     else
1395     {
1396         memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1397         if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1398
1399         TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1400                lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1401     }
1402     return ret;
1403 }
1404
1405
1406 /******************************************************************************
1407  *              SetLocaleInfoA  [KERNEL32.@]
1408  *
1409  * Set information about an aspect of a locale.
1410  *
1411  * PARAMS
1412  *  lcid   [I] LCID of the locale
1413  *  lctype [I] LCTYPE_ flags from "winnls.h"
1414  *  data   [I] Information to set
1415  *
1416  * RETURNS
1417  *  Success: TRUE. The information given will be returned by GetLocaleInfoA()
1418  *           whenever it is called without LOCALE_NOUSEROVERRIDE.
1419  *  Failure: FALSE. Use GetLastError() to determine the cause.
1420  *
1421  * NOTES
1422  *  - Values are only be set for the current user locale; the system locale
1423  *  settings cannot be changed.
1424  *  - Any settings changed by this call are lost when the locale is changed by
1425  *  the control panel (in Wine, this happens every time you change LANG).
1426  *  - The native implementation of this function does not check that lcid matches
1427  *  the current user locale, and simply sets the new values. Wine warns you in
1428  *  this case, but behaves the same.
1429  */
1430 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1431 {
1432     UINT codepage = CP_ACP;
1433     WCHAR *strW;
1434     DWORD len;
1435     BOOL ret;
1436
1437     if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1438
1439     if (!data)
1440     {
1441         SetLastError( ERROR_INVALID_PARAMETER );
1442         return FALSE;
1443     }
1444     len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1445     if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1446     {
1447         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1448         return FALSE;
1449     }
1450     MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1451     ret = SetLocaleInfoW( lcid, lctype, strW );
1452     HeapFree( GetProcessHeap(), 0, strW );
1453     return ret;
1454 }
1455
1456
1457 /******************************************************************************
1458  *              SetLocaleInfoW  (KERNEL32.@)
1459  *
1460  * See SetLocaleInfoA.
1461  */
1462 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1463 {
1464     const WCHAR *value;
1465     static const WCHAR intlW[] = {'i','n','t','l',0 };
1466     UNICODE_STRING valueW;
1467     NTSTATUS status;
1468     HANDLE hkey;
1469
1470     lctype &= 0xffff;
1471     value = get_locale_value_name( lctype );
1472
1473     if (!data || !value)
1474     {
1475         SetLastError( ERROR_INVALID_PARAMETER );
1476         return FALSE;
1477     }
1478
1479     if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1480     {
1481         SetLastError( ERROR_INVALID_FLAGS );
1482         return FALSE;
1483     }
1484
1485     TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1486
1487     /* FIXME: should check that data to set is sane */
1488
1489     /* FIXME: profile functions should map to registry */
1490     WriteProfileStringW( intlW, value, data );
1491
1492     if (!(hkey = create_registry_key())) return FALSE;
1493     RtlInitUnicodeString( &valueW, value );
1494     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1495
1496     if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1497     {
1498       /* Set I-value from S value */
1499       WCHAR *lpD, *lpM, *lpY;
1500       WCHAR szBuff[2];
1501
1502       lpD = strrchrW(data, 'd');
1503       lpM = strrchrW(data, 'M');
1504       lpY = strrchrW(data, 'y');
1505
1506       if (lpD <= lpM)
1507       {
1508         szBuff[0] = '1'; /* D-M-Y */
1509       }
1510       else
1511       {
1512         if (lpY <= lpM)
1513           szBuff[0] = '2'; /* Y-M-D */
1514         else
1515           szBuff[0] = '0'; /* M-D-Y */
1516       }
1517
1518       szBuff[1] = '\0';
1519
1520       if (lctype == LOCALE_SSHORTDATE)
1521         lctype = LOCALE_IDATE;
1522       else
1523         lctype = LOCALE_ILDATE;
1524
1525       value = get_locale_value_name( lctype );
1526
1527       WriteProfileStringW( intlW, value, szBuff );
1528
1529       RtlInitUnicodeString( &valueW, value );
1530       status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1531     }
1532
1533     NtClose( hkey );
1534
1535     if (status) SetLastError( RtlNtStatusToDosError(status) );
1536     return !status;
1537 }
1538
1539
1540 /******************************************************************************
1541  *              GetACP   (KERNEL32.@)
1542  *
1543  * Get the current Ansi code page Id for the system.
1544  *
1545  * PARAMS
1546  *  None.
1547  *
1548  * RETURNS
1549  *    The current Ansi code page identifier for the system.
1550  */
1551 UINT WINAPI GetACP(void)
1552 {
1553     assert( ansi_cptable );
1554     return ansi_cptable->info.codepage;
1555 }
1556
1557
1558 /******************************************************************************
1559  *              SetCPGlobal   (KERNEL32.@)
1560  *
1561  * Set the current Ansi code page Id for the system.
1562  *
1563  * PARAMS
1564  *    acp [I] code page ID to be the new ACP.
1565  *
1566  * RETURNS
1567  *    The previous ACP.
1568  */
1569 UINT WINAPI SetCPGlobal( UINT acp )
1570 {
1571     UINT ret = GetACP();
1572     const union cptable *new_cptable = wine_cp_get_table( acp );
1573
1574     if (new_cptable) ansi_cptable = new_cptable;
1575     return ret;
1576 }
1577
1578
1579 /***********************************************************************
1580  *              GetOEMCP   (KERNEL32.@)
1581  *
1582  * Get the current OEM code page Id for the system.
1583  *
1584  * PARAMS
1585  *  None.
1586  *
1587  * RETURNS
1588  *    The current OEM code page identifier for the system.
1589  */
1590 UINT WINAPI GetOEMCP(void)
1591 {
1592     assert( oem_cptable );
1593     return oem_cptable->info.codepage;
1594 }
1595
1596
1597 /***********************************************************************
1598  *           IsValidCodePage   (KERNEL32.@)
1599  *
1600  * Determine if a given code page identifier is valid.
1601  *
1602  * PARAMS
1603  *  codepage [I] Code page Id to verify.
1604  *
1605  * RETURNS
1606  *  TRUE, If codepage is valid and available on the system,
1607  *  FALSE otherwise.
1608  */
1609 BOOL WINAPI IsValidCodePage( UINT codepage )
1610 {
1611     switch(codepage) {
1612     case CP_UTF7:
1613     case CP_UTF8:
1614         return TRUE;
1615     default:
1616         return wine_cp_get_table( codepage ) != NULL;
1617     }
1618 }
1619
1620
1621 /***********************************************************************
1622  *           IsDBCSLeadByteEx   (KERNEL32.@)
1623  *
1624  * Determine if a character is a lead byte in a given code page.
1625  *
1626  * PARAMS
1627  *  codepage [I] Code page for the test.
1628  *  testchar [I] Character to test
1629  *
1630  * RETURNS
1631  *  TRUE, if testchar is a lead byte in codepage,
1632  *  FALSE otherwise.
1633  */
1634 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1635 {
1636     const union cptable *table = get_codepage_table( codepage );
1637     return table && wine_is_dbcs_leadbyte( table, testchar );
1638 }
1639
1640
1641 /***********************************************************************
1642  *           IsDBCSLeadByte   (KERNEL32.@)
1643  *           IsDBCSLeadByte   (KERNEL.207)
1644  *
1645  * Determine if a character is a lead byte.
1646  *
1647  * PARAMS
1648  *  testchar [I] Character to test
1649  *
1650  * RETURNS
1651  *  TRUE, if testchar is a lead byte in the ANSI code page,
1652  *  FALSE otherwise.
1653  */
1654 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1655 {
1656     if (!ansi_cptable) return FALSE;
1657     return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1658 }
1659
1660
1661 /***********************************************************************
1662  *           GetCPInfo   (KERNEL32.@)
1663  *
1664  * Get information about a code page.
1665  *
1666  * PARAMS
1667  *  codepage [I] Code page number
1668  *  cpinfo   [O] Destination for code page information
1669  *
1670  * RETURNS
1671  *  Success: TRUE. cpinfo is updated with the information about codepage.
1672  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1673  */
1674 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1675 {
1676     const union cptable *table;
1677
1678     if (!cpinfo)
1679     {
1680         SetLastError( ERROR_INVALID_PARAMETER );
1681         return FALSE;
1682     }
1683
1684     if (!(table = get_codepage_table( codepage )))
1685     {
1686         switch(codepage)
1687         {
1688             case CP_UTF7:
1689             case CP_UTF8:
1690                 cpinfo->DefaultChar[0] = 0x3f;
1691                 cpinfo->DefaultChar[1] = 0;
1692                 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1693                 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1694                 return TRUE;
1695         }
1696
1697         SetLastError( ERROR_INVALID_PARAMETER );
1698         return FALSE;
1699     }
1700     if (table->info.def_char & 0xff00)
1701     {
1702         cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1703         cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1704     }
1705     else
1706     {
1707         cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1708         cpinfo->DefaultChar[1] = 0;
1709     }
1710     if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1711         memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1712     else
1713         cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1714
1715     return TRUE;
1716 }
1717
1718 /***********************************************************************
1719  *           GetCPInfoExA   (KERNEL32.@)
1720  *
1721  * Get extended information about a code page.
1722  *
1723  * PARAMS
1724  *  codepage [I] Code page number
1725  *  dwFlags  [I] Reserved, must to 0.
1726  *  cpinfo   [O] Destination for code page information
1727  *
1728  * RETURNS
1729  *  Success: TRUE. cpinfo is updated with the information about codepage.
1730  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1731  */
1732 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1733 {
1734     CPINFOEXW cpinfoW;
1735
1736     if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1737       return FALSE;
1738
1739     /* the layout is the same except for CodePageName */
1740     memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1741     WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1742     return TRUE;
1743 }
1744
1745 /***********************************************************************
1746  *           GetCPInfoExW   (KERNEL32.@)
1747  *
1748  * Unicode version of GetCPInfoExA.
1749  */
1750 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1751 {
1752     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1753       return FALSE;
1754
1755     switch(codepage)
1756     {
1757         case CP_UTF7:
1758         {
1759             static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1760
1761             cpinfo->CodePage = CP_UTF7;
1762             cpinfo->UnicodeDefaultChar = 0x3f;
1763             strcpyW(cpinfo->CodePageName, utf7);
1764             break;
1765         }
1766
1767         case CP_UTF8:
1768         {
1769             static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1770
1771             cpinfo->CodePage = CP_UTF8;
1772             cpinfo->UnicodeDefaultChar = 0x3f;
1773             strcpyW(cpinfo->CodePageName, utf8);
1774             break;
1775         }
1776
1777         default:
1778         {
1779             const union cptable *table = get_codepage_table( codepage );
1780
1781             cpinfo->CodePage = table->info.codepage;
1782             cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1783             MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1784                                  sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1785             break;
1786         }
1787     }
1788     return TRUE;
1789 }
1790
1791 /***********************************************************************
1792  *              EnumSystemCodePagesA   (KERNEL32.@)
1793  *
1794  * Call a user defined function for every code page installed on the system.
1795  *
1796  * PARAMS
1797  *   lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1798  *   flags            [I] Reserved, set to 0.
1799  *
1800  * RETURNS
1801  *  TRUE, If all code pages have been enumerated, or
1802  *  FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1803  */
1804 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1805 {
1806     const union cptable *table;
1807     char buffer[10];
1808     int index = 0;
1809
1810     for (;;)
1811     {
1812         if (!(table = wine_cp_enum_table( index++ ))) break;
1813         sprintf( buffer, "%d", table->info.codepage );
1814         if (!lpfnCodePageEnum( buffer )) break;
1815     }
1816     return TRUE;
1817 }
1818
1819
1820 /***********************************************************************
1821  *              EnumSystemCodePagesW   (KERNEL32.@)
1822  *
1823  * See EnumSystemCodePagesA.
1824  */
1825 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1826 {
1827     const union cptable *table;
1828     WCHAR buffer[10], *p;
1829     int page, index = 0;
1830
1831     for (;;)
1832     {
1833         if (!(table = wine_cp_enum_table( index++ ))) break;
1834         p = buffer + sizeof(buffer)/sizeof(WCHAR);
1835         *--p = 0;
1836         page = table->info.codepage;
1837         do
1838         {
1839             *--p = '0' + (page % 10);
1840             page /= 10;
1841         } while( page );
1842         if (!lpfnCodePageEnum( p )) break;
1843     }
1844     return TRUE;
1845 }
1846
1847
1848 /***********************************************************************
1849  *              MultiByteToWideChar   (KERNEL32.@)
1850  *
1851  * Convert a multibyte character string into a Unicode string.
1852  *
1853  * PARAMS
1854  *   page   [I] Codepage character set to convert from
1855  *   flags  [I] Character mapping flags
1856  *   src    [I] Source string buffer
1857  *   srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1858  *   dst    [O] Destination buffer
1859  *   dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1860  *
1861  * RETURNS
1862  *   Success: If dstlen > 0, the number of characters written to dst.
1863  *            If dstlen == 0, the number of characters needed to perform the
1864  *            conversion. In both cases the count includes the terminating NUL.
1865  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1866  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1867  *            and dstlen != 0; ERROR_INVALID_PARAMETER,  if an invalid parameter
1868  *            is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1869  *            possible for src.
1870  */
1871 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1872                                 LPWSTR dst, INT dstlen )
1873 {
1874     const union cptable *table;
1875     int ret;
1876
1877     if (!src || (!dst && dstlen))
1878     {
1879         SetLastError( ERROR_INVALID_PARAMETER );
1880         return 0;
1881     }
1882
1883     if (srclen < 0) srclen = strlen(src) + 1;
1884
1885     switch(page)
1886     {
1887     case CP_SYMBOL:
1888         if( flags)
1889         {
1890             SetLastError( ERROR_INVALID_PARAMETER );
1891             return 0;
1892         }
1893         ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1894         break;
1895     case CP_UTF7:
1896         FIXME("UTF-7 not supported\n");
1897         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1898         return 0;
1899     case CP_UNIXCP:
1900         if (unix_cptable)
1901         {
1902             ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1903             break;
1904         }
1905 #ifdef __APPLE__
1906         flags |= MB_COMPOSITE;  /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1907 #endif
1908         /* fall through */
1909     case CP_UTF8:
1910         ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1911         break;
1912     default:
1913         if (!(table = get_codepage_table( page )))
1914         {
1915             SetLastError( ERROR_INVALID_PARAMETER );
1916             return 0;
1917         }
1918         ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1919         break;
1920     }
1921
1922     if (ret < 0)
1923     {
1924         switch(ret)
1925         {
1926         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1927         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1928         }
1929         ret = 0;
1930     }
1931     TRACE("cp %d %s -> %s, ret = %d\n",
1932           page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1933     return ret;
1934 }
1935
1936
1937 /***********************************************************************
1938  *              WideCharToMultiByte   (KERNEL32.@)
1939  *
1940  * Convert a Unicode character string into a multibyte string.
1941  *
1942  * PARAMS
1943  *   page    [I] Code page character set to convert to
1944  *   flags   [I] Mapping Flags (MB_ constants from "winnls.h").
1945  *   src     [I] Source string buffer
1946  *   srclen  [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1947  *   dst     [O] Destination buffer
1948  *   dstlen  [I] Length of dst (in bytes), or 0 to compute the required length
1949  *   defchar [I] Default character to use for conversion if no exact
1950  *                  conversion can be made
1951  *   used    [O] Set if default character was used in the conversion
1952  *
1953  * RETURNS
1954  *   Success: If dstlen > 0, the number of characters written to dst.
1955  *            If dstlen == 0, number of characters needed to perform the
1956  *            conversion. In both cases the count includes the terminating NUL.
1957  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1958  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1959  *            and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1960  *            parameter was given.
1961  */
1962 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1963                                 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1964 {
1965     const union cptable *table;
1966     int ret, used_tmp;
1967
1968     if (!src || (!dst && dstlen))
1969     {
1970         SetLastError( ERROR_INVALID_PARAMETER );
1971         return 0;
1972     }
1973
1974     if (srclen < 0) srclen = strlenW(src) + 1;
1975
1976     switch(page)
1977     {
1978     case CP_SYMBOL:
1979         if( flags || defchar || used)
1980         {
1981             SetLastError( ERROR_INVALID_PARAMETER );
1982             return 0;
1983         }
1984         ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
1985         break;
1986     case CP_UTF7:
1987         FIXME("UTF-7 not supported\n");
1988         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1989         return 0;
1990     case CP_UNIXCP:
1991         if (unix_cptable)
1992         {
1993             ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
1994                                     defchar, used ? &used_tmp : NULL );
1995             break;
1996         }
1997         /* fall through */
1998     case CP_UTF8:
1999         if (used) *used = FALSE;  /* all chars are valid for UTF-8 */
2000         ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2001         break;
2002     default:
2003         if (!(table = get_codepage_table( page )))
2004         {
2005             SetLastError( ERROR_INVALID_PARAMETER );
2006             return 0;
2007         }
2008         ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2009                                 defchar, used ? &used_tmp : NULL );
2010         if (used) *used = used_tmp;
2011         break;
2012     }
2013
2014     if (ret < 0)
2015     {
2016         switch(ret)
2017         {
2018         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2019         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2020         }
2021         ret = 0;
2022     }
2023     TRACE("cp %d %s -> %s, ret = %d\n",
2024           page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2025     return ret;
2026 }
2027
2028
2029 /***********************************************************************
2030  *           GetThreadLocale    (KERNEL32.@)
2031  *
2032  * Get the current threads locale.
2033  *
2034  * PARAMS
2035  *  None.
2036  *
2037  * RETURNS
2038  *  The LCID currently associated with the calling thread.
2039  */
2040 LCID WINAPI GetThreadLocale(void)
2041 {
2042     LCID ret = NtCurrentTeb()->CurrentLocale;
2043     if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2044     return ret;
2045 }
2046
2047 /**********************************************************************
2048  *           SetThreadLocale    (KERNEL32.@)
2049  *
2050  * Set the current threads locale.
2051  *
2052  * PARAMS
2053  *  lcid [I] LCID of the locale to set
2054  *
2055  * RETURNS
2056  *  Success: TRUE. The threads locale is set to lcid.
2057  *  Failure: FALSE. Use GetLastError() to determine the cause.
2058  */
2059 BOOL WINAPI SetThreadLocale( LCID lcid )
2060 {
2061     TRACE("(0x%04X)\n", lcid);
2062
2063     lcid = ConvertDefaultLocale(lcid);
2064
2065     if (lcid != GetThreadLocale())
2066     {
2067         if (!IsValidLocale(lcid, LCID_SUPPORTED))
2068         {
2069             SetLastError(ERROR_INVALID_PARAMETER);
2070             return FALSE;
2071         }
2072
2073         NtCurrentTeb()->CurrentLocale = lcid;
2074     }
2075     return TRUE;
2076 }
2077
2078 /**********************************************************************
2079  *           SetThreadUILanguage    (KERNEL32.@)
2080  *
2081  * Set the current threads UI language.
2082  *
2083  * PARAMS
2084  *  langid [I] LANGID of the language to set, or 0 to use
2085  *             the available language which is best supported
2086  *             for console applications
2087  *
2088  * RETURNS
2089  *  Success: The return value is the same as the input value.
2090  *  Failure: The return value differs from the input value.
2091  *           Use GetLastError() to determine the cause.
2092  */
2093 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2094 {
2095     TRACE("(0x%04x) stub - returning success\n", langid);
2096     return langid;
2097 }
2098
2099 /******************************************************************************
2100  *              ConvertDefaultLocale (KERNEL32.@)
2101  *
2102  * Convert a default locale identifier into a real identifier.
2103  *
2104  * PARAMS
2105  *  lcid [I] LCID identifier of the locale to convert
2106  *
2107  * RETURNS
2108  *  lcid unchanged, if not a default locale or its sublanguage is
2109  *   not SUBLANG_NEUTRAL.
2110  *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2111  *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2112  *  Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2113  */
2114 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2115 {
2116     LANGID langid;
2117
2118     switch (lcid)
2119     {
2120     case LOCALE_SYSTEM_DEFAULT:
2121         lcid = GetSystemDefaultLCID();
2122         break;
2123     case LOCALE_USER_DEFAULT:
2124     case LOCALE_NEUTRAL:
2125         lcid = GetUserDefaultLCID();
2126         break;
2127     default:
2128         /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2129         langid = LANGIDFROMLCID(lcid);
2130         if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2131         {
2132           langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2133           lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2134         }
2135     }
2136     return lcid;
2137 }
2138
2139
2140 /******************************************************************************
2141  *           IsValidLocale   (KERNEL32.@)
2142  *
2143  * Determine if a locale is valid.
2144  *
2145  * PARAMS
2146  *  lcid  [I] LCID of the locale to check
2147  *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2148  *
2149  * RETURNS
2150  *  TRUE,  if lcid is valid,
2151  *  FALSE, otherwise.
2152  *
2153  * NOTES
2154  *  Wine does not currently make the distinction between supported and installed. All
2155  *  languages supported are installed by default.
2156  */
2157 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2158 {
2159     /* check if language is registered in the kernel32 resources */
2160     return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2161                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2162 }
2163
2164
2165 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2166                                        LPCSTR name, WORD LangID, LONG_PTR lParam )
2167 {
2168     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2169     char buf[20];
2170
2171     sprintf(buf, "%08x", (UINT)LangID);
2172     return lpfnLocaleEnum( buf );
2173 }
2174
2175 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2176                                        LPCWSTR name, WORD LangID, LONG_PTR lParam )
2177 {
2178     static const WCHAR formatW[] = {'%','0','8','x',0};
2179     LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2180     WCHAR buf[20];
2181     sprintfW( buf, formatW, (UINT)LangID );
2182     return lpfnLocaleEnum( buf );
2183 }
2184
2185 /******************************************************************************
2186  *           EnumSystemLocalesA  (KERNEL32.@)
2187  *
2188  * Call a users function for each locale available on the system.
2189  *
2190  * PARAMS
2191  *  lpfnLocaleEnum [I] Callback function to call for each locale
2192  *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2193  *
2194  * RETURNS
2195  *  Success: TRUE.
2196  *  Failure: FALSE. Use GetLastError() to determine the cause.
2197  */
2198 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2199 {
2200     TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2201     EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2202                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2203                             (LONG_PTR)lpfnLocaleEnum);
2204     return TRUE;
2205 }
2206
2207
2208 /******************************************************************************
2209  *           EnumSystemLocalesW  (KERNEL32.@)
2210  *
2211  * See EnumSystemLocalesA.
2212  */
2213 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2214 {
2215     TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2216     EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2217                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2218                             (LONG_PTR)lpfnLocaleEnum);
2219     return TRUE;
2220 }
2221
2222
2223 struct enum_locale_ex_data
2224 {
2225     LOCALE_ENUMPROCEX proc;
2226     DWORD             flags;
2227     LPARAM            lparam;
2228 };
2229
2230 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2231                                           LPCWSTR name, WORD lang, LONG_PTR lparam )
2232 {
2233     struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2234     WCHAR buffer[256];
2235     DWORD neutral;
2236     unsigned int flags;
2237
2238     GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2239                     buffer, sizeof(buffer) / sizeof(WCHAR) );
2240     if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2241                          LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2242                          (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2243         neutral = 0;
2244     flags = LOCALE_WINDOWS;
2245     flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2246     if (data->flags && ~(data->flags & flags)) return TRUE;
2247     return data->proc( buffer, flags, data->lparam );
2248 }
2249
2250 /******************************************************************************
2251  *           EnumSystemLocalesEx  (KERNEL32.@)
2252  */
2253 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2254 {
2255     struct enum_locale_ex_data data;
2256
2257     if (reserved)
2258     {
2259         SetLastError( ERROR_INVALID_PARAMETER );
2260         return FALSE;
2261     }
2262     data.proc   = proc;
2263     data.flags  = flags;
2264     data.lparam = lparam;
2265     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2266                             (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2267                             enum_locale_ex_proc, (LONG_PTR)&data );
2268     return TRUE;
2269 }
2270
2271
2272 /***********************************************************************
2273  *           VerLanguageNameA  (KERNEL32.@)
2274  *
2275  * Get the name of a language.
2276  *
2277  * PARAMS
2278  *  wLang  [I] LANGID of the language
2279  *  szLang [O] Destination for the language name
2280  *
2281  * RETURNS
2282  *  Success: The size of the language name. If szLang is non-NULL, it is filled
2283  *           with the name.
2284  *  Failure: 0. Use GetLastError() to determine the cause.
2285  *
2286  */
2287 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2288 {
2289     return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2290 }
2291
2292
2293 /***********************************************************************
2294  *           VerLanguageNameW  (KERNEL32.@)
2295  *
2296  * See VerLanguageNameA.
2297  */
2298 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2299 {
2300     return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2301 }
2302
2303
2304 /******************************************************************************
2305  *           GetStringTypeW    (KERNEL32.@)
2306  *
2307  * See GetStringTypeA.
2308  */
2309 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2310 {
2311     static const unsigned char type2_map[16] =
2312     {
2313         C2_NOTAPPLICABLE,      /* unassigned */
2314         C2_LEFTTORIGHT,        /* L */
2315         C2_RIGHTTOLEFT,        /* R */
2316         C2_EUROPENUMBER,       /* EN */
2317         C2_EUROPESEPARATOR,    /* ES */
2318         C2_EUROPETERMINATOR,   /* ET */
2319         C2_ARABICNUMBER,       /* AN */
2320         C2_COMMONSEPARATOR,    /* CS */
2321         C2_BLOCKSEPARATOR,     /* B */
2322         C2_SEGMENTSEPARATOR,   /* S */
2323         C2_WHITESPACE,         /* WS */
2324         C2_OTHERNEUTRAL,       /* ON */
2325         C2_RIGHTTOLEFT,        /* AL */
2326         C2_NOTAPPLICABLE,      /* NSM */
2327         C2_NOTAPPLICABLE,      /* BN */
2328         C2_OTHERNEUTRAL        /* LRE, LRO, RLE, RLO, PDF */
2329     };
2330
2331     if (count == -1) count = strlenW(src) + 1;
2332     switch(type)
2333     {
2334     case CT_CTYPE1:
2335         while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2336         break;
2337     case CT_CTYPE2:
2338         while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2339         break;
2340     case CT_CTYPE3:
2341     {
2342         WARN("CT_CTYPE3: semi-stub.\n");
2343         while (count--)
2344         {
2345             int c = *src;
2346             WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2347
2348             type1 = get_char_typeW( *src++ ) & 0xfff;
2349             /* try to construct type3 from type1 */
2350             if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2351             if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2352             if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2353             if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2354             if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2355             if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2356             if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2357
2358             if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2359             if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2360             if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2361             if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2362             if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2363             if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2364             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2365             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2366
2367             if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2368             if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2369             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2370             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2371             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2372             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2373             *chartype++ = type3;
2374         }
2375         break;
2376     }
2377     default:
2378         SetLastError( ERROR_INVALID_PARAMETER );
2379         return FALSE;
2380     }
2381     return TRUE;
2382 }
2383
2384
2385 /******************************************************************************
2386  *           GetStringTypeExW    (KERNEL32.@)
2387  *
2388  * See GetStringTypeExA.
2389  */
2390 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2391 {
2392     /* locale is ignored for Unicode */
2393     return GetStringTypeW( type, src, count, chartype );
2394 }
2395
2396
2397 /******************************************************************************
2398  *           GetStringTypeA    (KERNEL32.@)
2399  *
2400  * Get characteristics of the characters making up a string.
2401  *
2402  * PARAMS
2403  *  locale   [I] Locale Id for the string
2404  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2405  *  src      [I] String to analyse
2406  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
2407  *  chartype [O] Destination for the calculated characteristics
2408  *
2409  * RETURNS
2410  *  Success: TRUE. chartype is filled with the requested characteristics of each char
2411  *           in src.
2412  *  Failure: FALSE. Use GetLastError() to determine the cause.
2413  */
2414 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2415 {
2416     UINT cp;
2417     INT countW;
2418     LPWSTR srcW;
2419     BOOL ret = FALSE;
2420
2421     if(count == -1) count = strlen(src) + 1;
2422
2423     if (!(cp = get_lcid_codepage( locale )))
2424     {
2425         FIXME("For locale %04x using current ANSI code page\n", locale);
2426         cp = GetACP();
2427     }
2428
2429     countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2430     if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2431     {
2432         MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2433     /*
2434      * NOTE: the target buffer has 1 word for each CHARACTER in the source
2435      * string, with multibyte characters there maybe be more bytes in count
2436      * than character space in the buffer!
2437      */
2438         ret = GetStringTypeW(type, srcW, countW, chartype);
2439         HeapFree(GetProcessHeap(), 0, srcW);
2440     }
2441     return ret;
2442 }
2443
2444 /******************************************************************************
2445  *           GetStringTypeExA    (KERNEL32.@)
2446  *
2447  * Get characteristics of the characters making up a string.
2448  *
2449  * PARAMS
2450  *  locale   [I] Locale Id for the string
2451  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2452  *  src      [I] String to analyse
2453  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
2454  *  chartype [O] Destination for the calculated characteristics
2455  *
2456  * RETURNS
2457  *  Success: TRUE. chartype is filled with the requested characteristics of each char
2458  *           in src.
2459  *  Failure: FALSE. Use GetLastError() to determine the cause.
2460  */
2461 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2462 {
2463     return GetStringTypeA(locale, type, src, count, chartype);
2464 }
2465
2466
2467 /*************************************************************************
2468  *           LCMapStringW    (KERNEL32.@)
2469  *
2470  * See LCMapStringA.
2471  */
2472 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2473                         LPWSTR dst, INT dstlen)
2474 {
2475     LPWSTR dst_ptr;
2476
2477     if (!src || !srclen || dstlen < 0)
2478     {
2479         SetLastError(ERROR_INVALID_PARAMETER);
2480         return 0;
2481     }
2482
2483     /* mutually exclusive flags */
2484     if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2485         (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2486         (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2487         (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2488     {
2489         SetLastError(ERROR_INVALID_FLAGS);
2490         return 0;
2491     }
2492
2493     if (!dstlen) dst = NULL;
2494
2495     lcid = ConvertDefaultLocale(lcid);
2496
2497     if (flags & LCMAP_SORTKEY)
2498     {
2499         INT ret;
2500         if (src == dst)
2501         {
2502             SetLastError(ERROR_INVALID_FLAGS);
2503             return 0;
2504         }
2505
2506         if (srclen < 0) srclen = strlenW(src);
2507
2508         TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2509               lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2510
2511         ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2512         if (ret == 0)
2513             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2514         else
2515             ret++;
2516         return ret;
2517     }
2518
2519     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2520     if (flags & SORT_STRINGSORT)
2521     {
2522         SetLastError(ERROR_INVALID_FLAGS);
2523         return 0;
2524     }
2525
2526     if (srclen < 0) srclen = strlenW(src) + 1;
2527
2528     TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2529           lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2530
2531     if (!dst) /* return required string length */
2532     {
2533         INT len;
2534
2535         for (len = 0; srclen; src++, srclen--)
2536         {
2537             WCHAR wch = *src;
2538             /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2539              * and skips white space and punctuation characters for
2540              * NORM_IGNORESYMBOLS.
2541              */
2542             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2543                 continue;
2544             len++;
2545         }
2546         return len;
2547     }
2548
2549     if (flags & LCMAP_UPPERCASE)
2550     {
2551         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2552         {
2553             WCHAR wch = *src;
2554             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2555                 continue;
2556             *dst_ptr++ = toupperW(wch);
2557             dstlen--;
2558         }
2559     }
2560     else if (flags & LCMAP_LOWERCASE)
2561     {
2562         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2563         {
2564             WCHAR wch = *src;
2565             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2566                 continue;
2567             *dst_ptr++ = tolowerW(wch);
2568             dstlen--;
2569         }
2570     }
2571     else
2572     {
2573         if (src == dst)
2574         {
2575             SetLastError(ERROR_INVALID_FLAGS);
2576             return 0;
2577         }
2578         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2579         {
2580             WCHAR wch = *src;
2581             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2582                 continue;
2583             *dst_ptr++ = wch;
2584             dstlen--;
2585         }
2586     }
2587
2588     if (srclen)
2589     {
2590         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2591         return 0;
2592     }
2593
2594     return dst_ptr - dst;
2595 }
2596
2597 /*************************************************************************
2598  *           LCMapStringA    (KERNEL32.@)
2599  *
2600  * Map characters in a locale sensitive string.
2601  *
2602  * PARAMS
2603  *  lcid   [I] LCID for the conversion.
2604  *  flags  [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2605  *  src    [I] String to map
2606  *  srclen [I] Length of src in chars, or -1 if src is NUL terminated
2607  *  dst    [O] Destination for mapped string
2608  *  dstlen [I] Length of dst in characters
2609  *
2610  * RETURNS
2611  *  Success: The length of the mapped string in dst, including the NUL terminator.
2612  *  Failure: 0. Use GetLastError() to determine the cause.
2613  */
2614 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2615                         LPSTR dst, INT dstlen)
2616 {
2617     WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2618     LPWSTR srcW, dstW;
2619     INT ret = 0, srclenW, dstlenW;
2620     UINT locale_cp = CP_ACP;
2621
2622     if (!src || !srclen || dstlen < 0)
2623     {
2624         SetLastError(ERROR_INVALID_PARAMETER);
2625         return 0;
2626     }
2627
2628     if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2629
2630     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2631     if (srclenW)
2632         srcW = bufW;
2633     else
2634     {
2635         srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2636         srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2637         if (!srcW)
2638         {
2639             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2640             return 0;
2641         }
2642         MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2643     }
2644
2645     if (flags & LCMAP_SORTKEY)
2646     {
2647         if (src == dst)
2648         {
2649             SetLastError(ERROR_INVALID_FLAGS);
2650             goto map_string_exit;
2651         }
2652         ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2653         if (ret == 0)
2654             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2655         else
2656             ret++;
2657         goto map_string_exit;
2658     }
2659
2660     if (flags & SORT_STRINGSORT)
2661     {
2662         SetLastError(ERROR_INVALID_FLAGS);
2663         goto map_string_exit;
2664     }
2665
2666     dstlenW = LCMapStringW(lcid, flags, srcW, srclenW, NULL, 0);
2667     if (!dstlenW)
2668         goto map_string_exit;
2669
2670     dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2671     if (!dstW)
2672     {
2673         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2674         goto map_string_exit;
2675     }
2676
2677     LCMapStringW(lcid, flags, srcW, srclenW, dstW, dstlenW);
2678     ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2679     HeapFree(GetProcessHeap(), 0, dstW);
2680
2681 map_string_exit:
2682     if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2683     return ret;
2684 }
2685
2686 /*************************************************************************
2687  *           FoldStringA    (KERNEL32.@)
2688  *
2689  * Map characters in a string.
2690  *
2691  * PARAMS
2692  *  dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2693  *  src     [I] String to map
2694  *  srclen  [I] Length of src, or -1 if src is NUL terminated
2695  *  dst     [O] Destination for mapped string
2696  *  dstlen  [I] Length of dst, or 0 to find the required length for the mapped string
2697  *
2698  * RETURNS
2699  *  Success: The length of the string written to dst, including the terminating NUL. If
2700  *           dstlen is 0, the value returned is the same, but nothing is written to dst,
2701  *           and dst may be NULL.
2702  *  Failure: 0. Use GetLastError() to determine the cause.
2703  */
2704 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2705                        LPSTR dst, INT dstlen)
2706 {
2707     INT ret = 0, srclenW = 0;
2708     WCHAR *srcW = NULL, *dstW = NULL;
2709
2710     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2711     {
2712         SetLastError(ERROR_INVALID_PARAMETER);
2713         return 0;
2714     }
2715
2716     srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2717                                   src, srclen, NULL, 0);
2718     srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2719
2720     if (!srcW)
2721     {
2722         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2723         goto FoldStringA_exit;
2724     }
2725
2726     MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2727                         src, srclen, srcW, srclenW);
2728
2729     dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2730
2731     ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2732     if (ret && dstlen)
2733     {
2734         dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2735
2736         if (!dstW)
2737         {
2738             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2739             goto FoldStringA_exit;
2740         }
2741
2742         ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2743         if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2744         {
2745             ret = 0;
2746             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2747         }
2748     }
2749
2750     HeapFree(GetProcessHeap(), 0, dstW);
2751
2752 FoldStringA_exit:
2753     HeapFree(GetProcessHeap(), 0, srcW);
2754     return ret;
2755 }
2756
2757 /*************************************************************************
2758  *           FoldStringW    (KERNEL32.@)
2759  *
2760  * See FoldStringA.
2761  */
2762 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2763                        LPWSTR dst, INT dstlen)
2764 {
2765     int ret;
2766
2767     switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2768     {
2769     case 0:
2770         if (dwFlags)
2771           break;
2772         /* Fall through for dwFlags == 0 */
2773     case MAP_PRECOMPOSED|MAP_COMPOSITE:
2774     case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2775     case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2776         SetLastError(ERROR_INVALID_FLAGS);
2777         return 0;
2778     }
2779
2780     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2781     {
2782         SetLastError(ERROR_INVALID_PARAMETER);
2783         return 0;
2784     }
2785
2786     ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2787     if (!ret)
2788         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2789     return ret;
2790 }
2791
2792 /******************************************************************************
2793  *           CompareStringW    (KERNEL32.@)
2794  *
2795  * See CompareStringA.
2796  */
2797 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2798                           LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2799 {
2800     INT ret;
2801
2802     if (!str1 || !str2)
2803     {
2804         SetLastError(ERROR_INVALID_PARAMETER);
2805         return 0;
2806     }
2807
2808     if( style & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2809         SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2810     {
2811         SetLastError(ERROR_INVALID_FLAGS);
2812         return 0;
2813     }
2814
2815     /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2816     if (style & 0x10000000)
2817         WARN("Ignoring unknown style 0x10000000\n");
2818
2819     if (len1 < 0) len1 = strlenW(str1);
2820     if (len2 < 0) len2 = strlenW(str2);
2821
2822     ret = wine_compare_string(style, str1, len1, str2, len2);
2823
2824     if (ret) /* need to translate result */
2825         return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2826     return CSTR_EQUAL;
2827 }
2828
2829 /******************************************************************************
2830  *           CompareStringA    (KERNEL32.@)
2831  *
2832  * Compare two locale sensitive strings.
2833  *
2834  * PARAMS
2835  *  lcid  [I] LCID for the comparison
2836  *  style [I] Flags for the comparison (NORM_ constants from "winnls.h").
2837  *  str1  [I] First string to compare
2838  *  len1  [I] Length of str1, or -1 if str1 is NUL terminated
2839  *  str2  [I] Second string to compare
2840  *  len2  [I] Length of str2, or -1 if str2 is NUL terminated
2841  *
2842  * RETURNS
2843  *  Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2844  *           str1 is less than, equal to or greater than str2 respectively.
2845  *  Failure: FALSE. Use GetLastError() to determine the cause.
2846  */
2847 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2848                           LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2849 {
2850     WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2851     WCHAR *buf2W = buf1W + 130;
2852     LPWSTR str1W, str2W;
2853     INT len1W, len2W, ret;
2854     UINT locale_cp = CP_ACP;
2855
2856     if (!str1 || !str2)
2857     {
2858         SetLastError(ERROR_INVALID_PARAMETER);
2859         return 0;
2860     }
2861     if (len1 < 0) len1 = strlen(str1);
2862     if (len2 < 0) len2 = strlen(str2);
2863
2864     if (!(style & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2865
2866     len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2867     if (len1W)
2868         str1W = buf1W;
2869     else
2870     {
2871         len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2872         str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2873         if (!str1W)
2874         {
2875             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2876             return 0;
2877         }
2878         MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2879     }
2880     len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2881     if (len2W)
2882         str2W = buf2W;
2883     else
2884     {
2885         len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2886         str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2887         if (!str2W)
2888         {
2889             if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2890             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2891             return 0;
2892         }
2893         MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2894     }
2895
2896     ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2897
2898     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2899     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2900     return ret;
2901 }
2902
2903 /*************************************************************************
2904  *           lstrcmp     (KERNEL32.@)
2905  *           lstrcmpA    (KERNEL32.@)
2906  *
2907  * Compare two strings using the current thread locale.
2908  *
2909  * PARAMS
2910  *  str1  [I] First string to compare
2911  *  str2  [I] Second string to compare
2912  *
2913  * RETURNS
2914  *  Success: A number less than, equal to or greater than 0 depending on whether
2915  *           str1 is less than, equal to or greater than str2 respectively.
2916  *  Failure: FALSE. Use GetLastError() to determine the cause.
2917  */
2918 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
2919 {
2920     int ret;
2921     
2922     if ((str1 == NULL) && (str2 == NULL)) return 0;
2923     if (str1 == NULL) return -1;
2924     if (str2 == NULL) return 1;
2925
2926     ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2927     if (ret) ret -= 2;
2928     
2929     return ret;
2930 }
2931
2932 /*************************************************************************
2933  *           lstrcmpi     (KERNEL32.@)
2934  *           lstrcmpiA    (KERNEL32.@)
2935  *
2936  * Compare two strings using the current thread locale, ignoring case.
2937  *
2938  * PARAMS
2939  *  str1  [I] First string to compare
2940  *  str2  [I] Second string to compare
2941  *
2942  * RETURNS
2943  *  Success: A number less than, equal to or greater than 0 depending on whether
2944  *           str2 is less than, equal to or greater than str1 respectively.
2945  *  Failure: FALSE. Use GetLastError() to determine the cause.
2946  */
2947 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
2948 {
2949     int ret;
2950     
2951     if ((str1 == NULL) && (str2 == NULL)) return 0;
2952     if (str1 == NULL) return -1;
2953     if (str2 == NULL) return 1;
2954
2955     ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2956     if (ret) ret -= 2;
2957     
2958     return ret;
2959 }
2960
2961 /*************************************************************************
2962  *           lstrcmpW    (KERNEL32.@)
2963  *
2964  * See lstrcmpA.
2965  */
2966 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
2967 {
2968     int ret;
2969
2970     if ((str1 == NULL) && (str2 == NULL)) return 0;
2971     if (str1 == NULL) return -1;
2972     if (str2 == NULL) return 1;
2973
2974     ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
2975     if (ret) ret -= 2;
2976     
2977     return ret;
2978 }
2979
2980 /*************************************************************************
2981  *           lstrcmpiW    (KERNEL32.@)
2982  *
2983  * See lstrcmpiA.
2984  */
2985 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
2986 {
2987     int ret;
2988     
2989     if ((str1 == NULL) && (str2 == NULL)) return 0;
2990     if (str1 == NULL) return -1;
2991     if (str2 == NULL) return 1;
2992
2993     ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
2994     if (ret) ret -= 2;
2995     
2996     return ret;
2997 }
2998
2999 /******************************************************************************
3000  *              LOCALE_Init
3001  */
3002 void LOCALE_Init(void)
3003 {
3004     extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3005                                              const union cptable *unix_cp );
3006
3007     UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3008
3009 #ifdef __APPLE__
3010     /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3011     char user_locale[50];
3012
3013     CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3014     CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3015     CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3016     CFStringRef user_locale_string_ref;
3017
3018     if (user_locale_country_ref)
3019     {
3020         user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3021             user_locale_lang_ref, user_locale_country_ref);
3022     }
3023     else
3024     {
3025         user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3026             user_locale_lang_ref);
3027     }
3028
3029     CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3030
3031     unix_cp = CP_UTF8;  /* default to utf-8 even if we don't get a valid locale */
3032     setenv( "LANG", user_locale, 0 );
3033     TRACE( "setting locale to '%s'\n", user_locale );
3034 #endif /* __APPLE__ */
3035
3036     setlocale( LC_ALL, "" );
3037
3038     unix_cp = setup_unix_locales();
3039     if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3040
3041 #ifdef __APPLE__
3042     /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3043     if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3044     {
3045         /* Retrieve the preferred language as chosen in System Preferences. */
3046         /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3047            leave things be. */
3048         CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3049         CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3050         CFStringRef user_language_string_ref;
3051         if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3052             (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3053             !CFEqual(user_language_string_ref, user_locale_lang_ref))
3054         {
3055             struct locale_name locale_name;
3056             WCHAR buffer[128];
3057             CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3058             strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3059             parse_locale_name( buffer, &locale_name );
3060             lcid_LC_MESSAGES = locale_name.lcid;
3061             TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3062         }
3063         CFRelease( all_locales );
3064         if (preferred_locales)
3065             CFRelease( preferred_locales );
3066     }
3067
3068     CFRelease( user_locale_ref );
3069     CFRelease( user_locale_string_ref );
3070 #endif
3071
3072     NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3073     NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3074     NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3075
3076     ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3077     GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3078                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3079     GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3080                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3081     if (!unix_cp)
3082         GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3083                         (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3084
3085     if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3086         ansi_cptable = wine_cp_get_table( 1252 );
3087     if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3088         oem_cptable  = wine_cp_get_table( 437 );
3089     if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3090         mac_cptable  = wine_cp_get_table( 10000 );
3091     if (unix_cp != CP_UTF8)
3092     {
3093         if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3094             unix_cptable  = wine_cp_get_table( 28591 );
3095     }
3096
3097     __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3098
3099     TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3100            ansi_cptable->info.codepage, oem_cptable->info.codepage,
3101            mac_cptable->info.codepage, unix_cp );
3102
3103     setlocale(LC_NUMERIC, "C");  /* FIXME: oleaut32 depends on this */
3104 }
3105
3106 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3107 {
3108     UNICODE_STRING keyName;
3109     OBJECT_ATTRIBUTES attr;
3110     HANDLE hkey;
3111
3112     RtlInitUnicodeString( &keyName, szKeyName );
3113     InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3114
3115     if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3116         hkey = 0;
3117
3118     return hkey;
3119 }
3120
3121 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3122                               ULONG keyNameSize)
3123 {
3124     BYTE buffer[80];
3125     KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3126     DWORD dwLen;
3127
3128     if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3129                         sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3130         info->NameLength > keyNameSize)
3131     {
3132         return FALSE;
3133     }
3134
3135     TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3136
3137     memcpy( szKeyName, info->Name, info->NameLength);
3138     szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3139
3140     TRACE("returning %s\n", debugstr_w(szKeyName));
3141     return TRUE;
3142 }
3143
3144 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3145                              LPWSTR szValueName, ULONG valueNameSize,
3146                              LPWSTR szValueData, ULONG valueDataSize)
3147 {
3148     BYTE buffer[80];
3149     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3150     DWORD dwLen;
3151
3152     if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3153         buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3154         info->NameLength > valueNameSize ||
3155         info->DataLength > valueDataSize)
3156     {
3157         return FALSE;
3158     }
3159
3160     TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3161
3162     memcpy( szValueName, info->Name, info->NameLength);
3163     szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3164     memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3165     szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3166
3167     TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3168     return TRUE;
3169 }
3170
3171 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3172 {
3173     BYTE buffer[128];
3174     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3175     DWORD dwSize = sizeof(buffer);
3176     UNICODE_STRING valueName;
3177
3178     RtlInitUnicodeString( &valueName, szValueName );
3179
3180     TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3181     if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3182                          buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3183         info->DataLength == sizeof(DWORD))
3184     {
3185         memcpy(lpVal, info->Data, sizeof(DWORD));
3186         return TRUE;
3187     }
3188
3189     return FALSE;
3190 }
3191
3192 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3193 {
3194     LANGID  langId;
3195     LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3196     HRSRC   hResource;
3197     BOOL    bRet = FALSE;
3198
3199     /* FIXME: Is it correct to use the system default langid? */
3200     langId = GetSystemDefaultLangID();
3201
3202     if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3203         langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3204
3205     hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3206
3207     if (hResource)
3208     {
3209         HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3210
3211         if (hResDir)
3212         {
3213             ULONG   iResourceIndex = lgrpid & 0xf;
3214             LPCWSTR lpResEntry = LockResource( hResDir );
3215             ULONG   i;
3216
3217             for (i = 0; i < iResourceIndex; i++)
3218                 lpResEntry += *lpResEntry + 1;
3219
3220             if (*lpResEntry < nameSize)
3221             {
3222                 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3223                 szName[*lpResEntry] = '\0';
3224                 bRet = TRUE;
3225             }
3226
3227         }
3228         FreeResource( hResource );
3229     }
3230     return bRet;
3231 }
3232
3233 /* Registry keys for NLS related information */
3234
3235 static const WCHAR szCountryListName[] = {
3236     'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3237     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3238     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3239     'T','e','l','e','p','h','o','n','y','\\',
3240     'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3241 };
3242
3243
3244 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3245 typedef struct
3246 {
3247   LANGUAGEGROUP_ENUMPROCA procA;
3248   LANGUAGEGROUP_ENUMPROCW procW;
3249   DWORD    dwFlags;
3250   LONG_PTR lParam;
3251 } ENUMLANGUAGEGROUP_CALLBACKS;
3252
3253 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3254 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3255 {
3256     WCHAR szNumber[10], szValue[4];
3257     HANDLE hKey;
3258     BOOL bContinue = TRUE;
3259     ULONG ulIndex = 0;
3260
3261     if (!lpProcs)
3262     {
3263         SetLastError(ERROR_INVALID_PARAMETER);
3264         return FALSE;
3265     }
3266
3267     switch (lpProcs->dwFlags)
3268     {
3269     case 0:
3270         /* Default to LGRPID_INSTALLED */
3271         lpProcs->dwFlags = LGRPID_INSTALLED;
3272         /* Fall through... */
3273     case LGRPID_INSTALLED:
3274     case LGRPID_SUPPORTED:
3275         break;
3276     default:
3277         SetLastError(ERROR_INVALID_FLAGS);
3278         return FALSE;
3279     }
3280
3281     hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3282
3283     if (!hKey)
3284         FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3285
3286     while (bContinue)
3287     {
3288         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3289                               szValue, sizeof(szValue) ))
3290         {
3291             BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
3292             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3293
3294             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3295                    bInstalled ? "" : "not ");
3296
3297             if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3298             {
3299                 WCHAR szGrpName[48];
3300
3301                 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3302                     szGrpName[0] = '\0';
3303
3304                 if (lpProcs->procW)
3305                     bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3306                                                 lpProcs->lParam );
3307                 else
3308                 {
3309                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3310                     char szGrpNameA[48];
3311
3312                     /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3313                      *        or whether the language names are ever localised. Assume CP_ACP.
3314                      */
3315
3316                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3317                     WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3318
3319                     bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3320                                                 lpProcs->lParam );
3321                 }
3322             }
3323
3324             ulIndex++;
3325         }
3326         else
3327             bContinue = FALSE;
3328
3329         if (!bContinue)
3330             break;
3331     }
3332
3333     if (hKey)
3334         NtClose( hKey );
3335
3336     return TRUE;
3337 }
3338
3339 /******************************************************************************
3340  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
3341  *
3342  * Call a users function for each language group available on the system.
3343  *
3344  * PARAMS
3345  *  pLangGrpEnumProc [I] Callback function to call for each language group
3346  *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3347  *  lParam           [I] User parameter to pass to pLangGrpEnumProc
3348  *
3349  * RETURNS
3350  *  Success: TRUE.
3351  *  Failure: FALSE. Use GetLastError() to determine the cause.
3352  */
3353 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3354                                       DWORD dwFlags, LONG_PTR lParam)
3355 {
3356     ENUMLANGUAGEGROUP_CALLBACKS procs;
3357
3358     TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3359
3360     procs.procA = pLangGrpEnumProc;
3361     procs.procW = NULL;
3362     procs.dwFlags = dwFlags;
3363     procs.lParam = lParam;
3364
3365     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3366 }
3367
3368 /******************************************************************************
3369  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
3370  *
3371  * See EnumSystemLanguageGroupsA.
3372  */
3373 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3374                                       DWORD dwFlags, LONG_PTR lParam)
3375 {
3376     ENUMLANGUAGEGROUP_CALLBACKS procs;
3377
3378     TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3379
3380     procs.procA = NULL;
3381     procs.procW = pLangGrpEnumProc;
3382     procs.dwFlags = dwFlags;
3383     procs.lParam = lParam;
3384
3385     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3386 }
3387
3388 /******************************************************************************
3389  *           IsValidLanguageGroup    (KERNEL32.@)
3390  *
3391  * Determine if a language group is supported and/or installed.
3392  *
3393  * PARAMS
3394  *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
3395  *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3396  *
3397  * RETURNS
3398  *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3399  *  FALSE otherwise.
3400  */
3401 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3402 {
3403     static const WCHAR szFormat[] = { '%','x','\0' };
3404     WCHAR szValueName[16], szValue[2];
3405     BOOL bSupported = FALSE, bInstalled = FALSE;
3406     HANDLE hKey;
3407
3408
3409     switch (dwFlags)
3410     {
3411     case LGRPID_INSTALLED:
3412     case LGRPID_SUPPORTED:
3413
3414         hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3415
3416         sprintfW( szValueName, szFormat, lgrpid );
3417
3418         if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3419         {
3420             bSupported = TRUE;
3421
3422             if (szValue[0] == '1')
3423                 bInstalled = TRUE;
3424         }
3425
3426         if (hKey)
3427             NtClose( hKey );
3428
3429         break;
3430     }
3431
3432     if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3433         (dwFlags == LGRPID_INSTALLED && bInstalled))
3434         return TRUE;
3435
3436     return FALSE;
3437 }
3438
3439 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3440 typedef struct
3441 {
3442   LANGGROUPLOCALE_ENUMPROCA procA;
3443   LANGGROUPLOCALE_ENUMPROCW procW;
3444   DWORD    dwFlags;
3445   LGRPID   lgrpid;
3446   LONG_PTR lParam;
3447 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3448
3449 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3450 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3451 {
3452     static const WCHAR szAlternateSortsKeyName[] = {
3453       'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3454     };
3455     WCHAR szNumber[10], szValue[4];
3456     HANDLE hKey;
3457     BOOL bContinue = TRUE, bAlternate = FALSE;
3458     LGRPID lgrpid;
3459     ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
3460
3461     if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3462     {
3463         SetLastError(ERROR_INVALID_PARAMETER);
3464         return FALSE;
3465     }
3466
3467     if (lpProcs->dwFlags)
3468     {
3469         SetLastError(ERROR_INVALID_FLAGS);
3470         return FALSE;
3471     }
3472
3473     hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3474
3475     if (!hKey)
3476         WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3477
3478     while (bContinue)
3479     {
3480         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3481                               szValue, sizeof(szValue) ))
3482         {
3483             lgrpid = strtoulW( szValue, NULL, 16 );
3484
3485             TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3486                    lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3487
3488             if (lgrpid == lpProcs->lgrpid)
3489             {
3490                 LCID lcid;
3491
3492                 lcid = strtoulW( szNumber, NULL, 16 );
3493
3494                 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3495                  * '00000437          ;Georgian'
3496                  * At present we only pass the LCID string.
3497                  */
3498
3499                 if (lpProcs->procW)
3500                     bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3501                 else
3502                 {
3503                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3504
3505                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3506
3507                     bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3508                 }
3509             }
3510
3511             ulIndex++;
3512         }
3513         else
3514         {
3515             /* Finished enumerating this key */
3516             if (!bAlternate)
3517             {
3518                 /* Enumerate alternate sorts also */
3519                 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3520                 bAlternate = TRUE;
3521                 ulIndex = 0;
3522             }
3523             else
3524                 bContinue = FALSE; /* Finished both keys */
3525         }
3526
3527         if (!bContinue)
3528             break;
3529     }
3530
3531     if (hKey)
3532         NtClose( hKey );
3533
3534     return TRUE;
3535 }
3536
3537 /******************************************************************************
3538  *           EnumLanguageGroupLocalesA    (KERNEL32.@)
3539  *
3540  * Call a users function for every locale in a language group available on the system.
3541  *
3542  * PARAMS
3543  *  pLangGrpLcEnumProc [I] Callback function to call for each locale
3544  *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
3545  *  dwFlags            [I] Reserved, set to 0
3546  *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
3547  *
3548  * RETURNS
3549  *  Success: TRUE.
3550  *  Failure: FALSE. Use GetLastError() to determine the cause.
3551  */
3552 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3553                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3554 {
3555     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3556
3557     TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3558
3559     callbacks.procA   = pLangGrpLcEnumProc;
3560     callbacks.procW   = NULL;
3561     callbacks.dwFlags = dwFlags;
3562     callbacks.lgrpid  = lgrpid;
3563     callbacks.lParam  = lParam;
3564
3565     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3566 }
3567
3568 /******************************************************************************
3569  *           EnumLanguageGroupLocalesW    (KERNEL32.@)
3570  *
3571  * See EnumLanguageGroupLocalesA.
3572  */
3573 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3574                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3575 {
3576     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3577
3578     TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3579
3580     callbacks.procA   = NULL;
3581     callbacks.procW   = pLangGrpLcEnumProc;
3582     callbacks.dwFlags = dwFlags;
3583     callbacks.lgrpid  = lgrpid;
3584     callbacks.lParam  = lParam;
3585
3586     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3587 }
3588
3589 /******************************************************************************
3590  *           EnumSystemGeoID    (KERNEL32.@)
3591  *
3592  * Call a users function for every location available on the system.
3593  *
3594  * PARAMS
3595  *  geoclass     [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3596  *  reserved     [I] Reserved, set to 0
3597  *  pGeoEnumProc [I] Callback function to call for each location
3598  *
3599  * RETURNS
3600  *  Success: TRUE.
3601  *  Failure: FALSE. Use GetLastError() to determine the cause.
3602  */
3603 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3604 {
3605     static const WCHAR szCountryCodeValueName[] = {
3606       'C','o','u','n','t','r','y','C','o','d','e','\0'
3607     };
3608     WCHAR szNumber[10];
3609     HANDLE hKey;
3610     ULONG ulIndex = 0;
3611
3612     TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3613
3614     if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3615     {
3616         SetLastError(ERROR_INVALID_PARAMETER);
3617         return FALSE;
3618     }
3619
3620     hKey = NLS_RegOpenKey( 0, szCountryListName );
3621
3622     while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3623     {
3624         BOOL bContinue = TRUE;
3625         DWORD dwGeoId;
3626         HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3627
3628         if (hSubKey)
3629         {
3630             if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3631             {
3632                 TRACE("Got geoid %d\n", dwGeoId);
3633
3634                 if (!pGeoEnumProc( dwGeoId ))
3635                     bContinue = FALSE;
3636             }
3637
3638             NtClose( hSubKey );
3639         }
3640
3641         if (!bContinue)
3642             break;
3643
3644         ulIndex++;
3645     }
3646
3647     if (hKey)
3648         NtClose( hKey );
3649
3650     return TRUE;
3651 }
3652
3653 /******************************************************************************
3654  *           InvalidateNLSCache           (KERNEL32.@)
3655  *
3656  * Invalidate the cache of NLS values.
3657  *
3658  * PARAMS
3659  *  None.
3660  *
3661  * RETURNS
3662  *  Success: TRUE.
3663  *  Failure: FALSE.
3664  */
3665 BOOL WINAPI InvalidateNLSCache(void)
3666 {
3667   FIXME("() stub\n");
3668   return FALSE;
3669 }
3670
3671 /******************************************************************************
3672  *           GetUserGeoID (KERNEL32.@)
3673  */
3674 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3675 {
3676     GEOID ret = GEOID_NOT_AVAILABLE;
3677     static const WCHAR geoW[] = {'G','e','o',0};
3678     static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3679     WCHAR bufferW[40], *end;
3680     DWORD count;
3681     HANDLE hkey, hSubkey = 0;
3682     UNICODE_STRING keyW;
3683     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3684     RtlInitUnicodeString( &keyW, nationW );
3685     count = sizeof(bufferW);
3686
3687     if(!(hkey = create_registry_key())) return ret;
3688
3689     switch( GeoClass ){
3690     case GEOCLASS_NATION:
3691         if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3692         {
3693             if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3694                                 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3695                 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3696         }
3697         break;
3698     case GEOCLASS_REGION:
3699         FIXME("GEOCLASS_REGION not handled yet\n");
3700         break;
3701     }
3702
3703     NtClose(hkey);
3704     if (hSubkey) NtClose(hSubkey);
3705     return ret;
3706 }
3707
3708 /******************************************************************************
3709  *           SetUserGeoID (KERNEL32.@)
3710  */
3711 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3712 {
3713     static const WCHAR geoW[] = {'G','e','o',0};
3714     static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3715     static const WCHAR formatW[] = {'%','i',0};
3716     UNICODE_STRING nameW,keyW;
3717     WCHAR bufferW[10];
3718     OBJECT_ATTRIBUTES attr;
3719     HANDLE hkey;
3720
3721     if(!(hkey = create_registry_key())) return FALSE;
3722
3723     attr.Length = sizeof(attr);
3724     attr.RootDirectory = hkey;
3725     attr.ObjectName = &nameW;
3726     attr.Attributes = 0;
3727     attr.SecurityDescriptor = NULL;
3728     attr.SecurityQualityOfService = NULL;
3729     RtlInitUnicodeString( &nameW, geoW );
3730     RtlInitUnicodeString( &keyW, nationW );
3731
3732     if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3733
3734     {
3735         NtClose(attr.RootDirectory);
3736         return FALSE;
3737     }
3738
3739     sprintfW(bufferW, formatW, GeoID);
3740     NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3741     NtClose(attr.RootDirectory);
3742     NtClose(hkey);
3743     return TRUE;
3744 }
3745
3746 typedef struct
3747 {
3748     union
3749     {
3750         UILANGUAGE_ENUMPROCA procA;
3751         UILANGUAGE_ENUMPROCW procW;
3752     } u;
3753     DWORD flags;
3754     LONG_PTR param;
3755 } ENUM_UILANG_CALLBACK;
3756
3757 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3758                                          LPCSTR name, WORD LangID, LONG_PTR lParam )
3759 {
3760     ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3761     char buf[20];
3762
3763     sprintf(buf, "%08x", (UINT)LangID);
3764     return enum_uilang->u.procA( buf, enum_uilang->param );
3765 }
3766
3767 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3768                                          LPCWSTR name, WORD LangID, LONG_PTR lParam )
3769 {
3770     static const WCHAR formatW[] = {'%','0','8','x',0};
3771     ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3772     WCHAR buf[20];
3773
3774     sprintfW( buf, formatW, (UINT)LangID );
3775     return enum_uilang->u.procW( buf, enum_uilang->param );
3776 }
3777
3778 /******************************************************************************
3779  *           EnumUILanguagesA (KERNEL32.@)
3780  */
3781 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3782 {
3783     ENUM_UILANG_CALLBACK enum_uilang;
3784
3785     TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3786
3787     if(!pUILangEnumProc) {
3788         SetLastError(ERROR_INVALID_PARAMETER);
3789         return FALSE;
3790     }
3791     if(dwFlags) {
3792         SetLastError(ERROR_INVALID_FLAGS);
3793         return FALSE;
3794     }
3795
3796     enum_uilang.u.procA = pUILangEnumProc;
3797     enum_uilang.flags = dwFlags;
3798     enum_uilang.param = lParam;
3799
3800     EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3801                             (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3802                             (LONG_PTR)&enum_uilang);
3803     return TRUE;
3804 }
3805
3806 /******************************************************************************
3807  *           EnumUILanguagesW (KERNEL32.@)
3808  */
3809 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3810 {
3811     ENUM_UILANG_CALLBACK enum_uilang;
3812
3813     TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3814
3815
3816     if(!pUILangEnumProc) {
3817         SetLastError(ERROR_INVALID_PARAMETER);
3818         return FALSE;
3819     }
3820     if(dwFlags) {
3821         SetLastError(ERROR_INVALID_FLAGS);
3822         return FALSE;
3823     }
3824
3825     enum_uilang.u.procW = pUILangEnumProc;
3826     enum_uilang.flags = dwFlags;
3827     enum_uilang.param = lParam;
3828
3829     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3830                             (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3831                             (LONG_PTR)&enum_uilang);
3832     return TRUE;
3833 }
3834
3835 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData, 
3836                 int cchData, LANGID language)
3837 {
3838     FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3839     return 0;
3840 }
3841
3842 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData, 
3843                 int cchData, LANGID language)
3844 {
3845     FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3846     return 0;
3847 }
3848
3849 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
3850 {
3851     LCID userlcid;
3852
3853     TRACE("%p, %d\n", localename,  buffersize);
3854     
3855     userlcid = GetUserDefaultLCID();
3856     return LCIDToLocaleName(userlcid, localename, buffersize, 0);
3857 }
3858
3859 /******************************************************************************
3860  *           NormalizeString (KERNEL32.@)
3861  */
3862 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
3863                            LPWSTR lpDstString, INT cwDstLength)
3864 {
3865     FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
3866     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3867     return 0;
3868 }
3869
3870 /******************************************************************************
3871  *           IsNormalizedString (KERNEL32.@)
3872  */
3873 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
3874 {
3875     FIXME("%x %p %d\n", NormForm, lpString, cwLength);
3876     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3877     return FALSE;
3878 }
3879
3880 /******************************************************************************
3881  *           IdnToAscii (KERNEL32.@)
3882  */
3883 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
3884                       LPWSTR lpASCIICharStr, INT cchASCIIChar)
3885 {
3886     FIXME("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
3887         lpASCIICharStr, cchASCIIChar);
3888     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3889     return 0;
3890 }
3891
3892 /******************************************************************************
3893  *           IdnToNameprepUnicode (KERNEL32.@)
3894  */
3895 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
3896                                 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
3897 {
3898     FIXME("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
3899         lpNameprepCharStr, cchNameprepChar);
3900     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3901     return 0;
3902 }
3903
3904 /******************************************************************************
3905  *           IdnToUnicode (KERNEL32.@)
3906  */
3907 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
3908                         LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
3909 {
3910     FIXME("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
3911         lpUnicodeCharStr, cchUnicodeChar);
3912     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3913     return 0;
3914 }