kernel32: Add stubs for ExpungeConsoleCommandHistoryA/W.
[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     parse_locale_name( name, &locale_name );
980
981     TRACE( "found lcid %x for %s, matches %d\n",
982            locale_name.lcid, debugstr_w(name), locale_name.matches );
983
984     if (!locale_name.matches)
985         WARN( "locale %s not recognized, defaulting to English\n", debugstr_w(name) );
986     else if (locale_name.matches == 1)
987         WARN( "locale %s not recognized, defaulting to %s\n",
988               debugstr_w(name), debugstr_w(locale_name.lang) );
989
990     return locale_name.lcid;
991 }
992
993
994 /***********************************************************************
995  *           LCIDToLocaleName  (KERNEL32.@)
996  */
997 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
998 {
999     if (flags) FIXME( "unsupported flags %x\n", flags );
1000
1001     return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1002 }
1003
1004
1005 /******************************************************************************
1006  *              get_locale_value_name
1007  *
1008  * Gets the registry value name for a given lctype.
1009  */
1010 static const WCHAR *get_locale_value_name( DWORD lctype )
1011 {
1012     static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1013     static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1014     static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1015     static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1016     static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1017     static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1018     static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1019     static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1020     static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1021     static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1022     static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1023     static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1024     static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1025     static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1026     static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1027     static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1028     static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1029     static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1030     static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1031     static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1032     static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1033     static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1034     static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1035     static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1036     static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1037     static const WCHAR sListW[] = {'s','L','i','s','t',0};
1038     static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1039     static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1040     static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1041     static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1042     static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1043     static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1044     static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1045     static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1046     static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1047     static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1048     static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1049     static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1050     static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1051
1052     switch (lctype)
1053     {
1054     /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1055      * the values are stored in the registry, confirmed under Windows.
1056      */
1057     case LOCALE_ICALENDARTYPE:    return iCalendarTypeW;
1058     case LOCALE_ICURRDIGITS:      return iCurrDigitsW;
1059     case LOCALE_ICURRENCY:        return iCurrencyW;
1060     case LOCALE_IDIGITS:          return iDigitsW;
1061     case LOCALE_IFIRSTDAYOFWEEK:  return iFirstDayOfWeekW;
1062     case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1063     case LOCALE_ILZERO:           return iLZeroW;
1064     case LOCALE_IMEASURE:         return iMeasureW;
1065     case LOCALE_INEGCURR:         return iNegCurrW;
1066     case LOCALE_INEGNUMBER:       return iNegNumberW;
1067     case LOCALE_IPAPERSIZE:       return iPaperSizeW;
1068     case LOCALE_ITIME:            return iTimeW;
1069     case LOCALE_S1159:            return s1159W;
1070     case LOCALE_S2359:            return s2359W;
1071     case LOCALE_SCURRENCY:        return sCurrencyW;
1072     case LOCALE_SDATE:            return sDateW;
1073     case LOCALE_SDECIMAL:         return sDecimalW;
1074     case LOCALE_SGROUPING:        return sGroupingW;
1075     case LOCALE_SLIST:            return sListW;
1076     case LOCALE_SLONGDATE:        return sLongDateW;
1077     case LOCALE_SMONDECIMALSEP:   return sMonDecimalSepW;
1078     case LOCALE_SMONGROUPING:     return sMonGroupingW;
1079     case LOCALE_SMONTHOUSANDSEP:  return sMonThousandSepW;
1080     case LOCALE_SNEGATIVESIGN:    return sNegativeSignW;
1081     case LOCALE_SPOSITIVESIGN:    return sPositiveSignW;
1082     case LOCALE_SSHORTDATE:       return sShortDateW;
1083     case LOCALE_STHOUSAND:        return sThousandW;
1084     case LOCALE_STIME:            return sTimeW;
1085     case LOCALE_STIMEFORMAT:      return sTimeFormatW;
1086     case LOCALE_SYEARMONTH:       return sYearMonthW;
1087
1088     /* The following are not listed under MSDN as supported,
1089      * but seem to be used and also stored in the registry.
1090      */
1091     case LOCALE_ICOUNTRY:         return iCountryW;
1092     case LOCALE_IDATE:            return iDateW;
1093     case LOCALE_ILDATE:           return iLDateW;
1094     case LOCALE_ITLZERO:          return iTLZeroW;
1095     case LOCALE_SCOUNTRY:         return sCountryW;
1096     case LOCALE_SABBREVLANGNAME:  return sLanguageW;
1097
1098     /* The following are used in XP and later */
1099     case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1100     case LOCALE_SNATIVEDIGITS:      return sNativeDigitsW;
1101     case LOCALE_ITIMEMARKPOSN:      return iTimePrefixW;
1102     }
1103     return NULL;
1104 }
1105
1106
1107 /******************************************************************************
1108  *              get_registry_locale_info
1109  *
1110  * Retrieve user-modified locale info from the registry.
1111  * Return length, 0 on error, -1 if not found.
1112  */
1113 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1114 {
1115     DWORD size;
1116     INT ret;
1117     HANDLE hkey;
1118     NTSTATUS status;
1119     UNICODE_STRING nameW;
1120     KEY_VALUE_PARTIAL_INFORMATION *info;
1121     static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1122
1123     if (!(hkey = create_registry_key())) return -1;
1124
1125     RtlInitUnicodeString( &nameW, value );
1126     size = info_size + len * sizeof(WCHAR);
1127
1128     if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1129     {
1130         NtClose( hkey );
1131         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1132         return 0;
1133     }
1134
1135     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1136
1137     if (!status)
1138     {
1139         ret = (size - info_size) / sizeof(WCHAR);
1140         /* append terminating null if needed */
1141         if (!ret || ((WCHAR *)info->Data)[ret-1])
1142         {
1143             if (ret < len || !buffer) ret++;
1144             else
1145             {
1146                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1147                 ret = 0;
1148             }
1149         }
1150         if (ret && buffer)
1151         {
1152             memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1153             buffer[ret-1] = 0;
1154         }
1155     }
1156     else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1157     {
1158         ret = (size - info_size) / sizeof(WCHAR) + 1;
1159     }
1160     else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1161     {
1162         ret = -1;
1163     }
1164     else
1165     {
1166         SetLastError( RtlNtStatusToDosError(status) );
1167         ret = 0;
1168     }
1169     NtClose( hkey );
1170     HeapFree( GetProcessHeap(), 0, info );
1171     return ret;
1172 }
1173
1174
1175 /******************************************************************************
1176  *              GetLocaleInfoA (KERNEL32.@)
1177  *
1178  * Get information about an aspect of a locale.
1179  *
1180  * PARAMS
1181  *  lcid   [I] LCID of the locale
1182  *  lctype [I] LCTYPE_ flags from "winnls.h"
1183  *  buffer [O] Destination for the information
1184  *  len    [I] Length of buffer in characters
1185  *
1186  * RETURNS
1187  *  Success: The size of the data requested. If buffer is non-NULL, it is filled
1188  *           with the information.
1189  *  Failure: 0. Use GetLastError() to determine the cause.
1190  *
1191  * NOTES
1192  *  - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1193  *  - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1194  *    which is a bit string.
1195  */
1196 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1197 {
1198     WCHAR *bufferW;
1199     INT lenW, ret;
1200
1201     TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1202
1203     if (len < 0 || (len && !buffer))
1204     {
1205         SetLastError( ERROR_INVALID_PARAMETER );
1206         return 0;
1207     }
1208     if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1209     {
1210         SetLastError( ERROR_INVALID_FLAGS );
1211         return 0;
1212     }
1213
1214     if (!len) buffer = NULL;
1215
1216     if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1217
1218     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1219     {
1220         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1221         return 0;
1222     }
1223     if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1224     {
1225         if ((lctype & LOCALE_RETURN_NUMBER) ||
1226             ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1227         {
1228             /* it's not an ASCII string, just bytes */
1229             ret *= sizeof(WCHAR);
1230             if (buffer)
1231             {
1232                 if (ret <= len) memcpy( buffer, bufferW, ret );
1233                 else
1234                 {
1235                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1236                     ret = 0;
1237                 }
1238             }
1239         }
1240         else
1241         {
1242             UINT codepage = CP_ACP;
1243             if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1244             ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1245         }
1246     }
1247     HeapFree( GetProcessHeap(), 0, bufferW );
1248     return ret;
1249 }
1250
1251
1252 /******************************************************************************
1253  *              GetLocaleInfoW (KERNEL32.@)
1254  *
1255  * See GetLocaleInfoA.
1256  */
1257 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1258 {
1259     LANGID lang_id;
1260     HRSRC hrsrc;
1261     HGLOBAL hmem;
1262     INT ret;
1263     UINT lcflags;
1264     const WCHAR *p;
1265     unsigned int i;
1266
1267     if (len < 0 || (len && !buffer))
1268     {
1269         SetLastError( ERROR_INVALID_PARAMETER );
1270         return 0;
1271     }
1272     if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1273        !is_genitive_name_supported( lctype ))
1274     {
1275         SetLastError( ERROR_INVALID_FLAGS );
1276         return 0;
1277     }
1278
1279     if (!len) buffer = NULL;
1280
1281     lcid = convert_default_lcid( lcid, lctype );
1282
1283     lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1284     lctype &= 0xffff;
1285
1286     TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1287
1288     /* first check for overrides in the registry */
1289
1290     if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1291         lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1292     {
1293         const WCHAR *value = get_locale_value_name(lctype);
1294
1295         if (value)
1296         {
1297             if (lcflags & LOCALE_RETURN_NUMBER)
1298             {
1299                 WCHAR tmp[16];
1300                 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1301                 if (ret > 0)
1302                 {
1303                     WCHAR *end;
1304                     UINT number = strtolW( tmp, &end, 10 );
1305                     if (*end)  /* invalid number */
1306                     {
1307                         SetLastError( ERROR_INVALID_FLAGS );
1308                         return 0;
1309                     }
1310                     ret = sizeof(UINT)/sizeof(WCHAR);
1311                     if (!buffer) return ret;
1312                     if (ret > len)
1313                     {
1314                         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1315                         return 0;
1316                     }
1317                     memcpy( buffer, &number, sizeof(number) );
1318                 }
1319             }
1320             else ret = get_registry_locale_info( value, buffer, len );
1321
1322             if (ret != -1) return ret;
1323         }
1324     }
1325
1326     /* now load it from kernel resources */
1327
1328     lang_id = LANGIDFROMLCID( lcid );
1329
1330     /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1331     if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1332         lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1333
1334     if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1335                                    ULongToPtr((lctype >> 4) + 1), lang_id )))
1336     {
1337         SetLastError( ERROR_INVALID_FLAGS );  /* no such lctype */
1338         return 0;
1339     }
1340     if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1341         return 0;
1342
1343     p = LockResource( hmem );
1344     for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1345
1346     if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1347     else if (is_genitive_name_supported( lctype ) && *p)
1348     {
1349         /* genitive form's stored after a null separator from a nominative */
1350         for (i = 1; i <= *p; i++) if (!p[i]) break;
1351
1352         if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1353         {
1354             ret = *p - i + 1;
1355             p += i;
1356         }
1357         else ret = i;
1358     }
1359     else
1360         ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1361
1362     if (!buffer) return ret;
1363
1364     if (ret > len)
1365     {
1366         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1367         return 0;
1368     }
1369
1370     if (lcflags & LOCALE_RETURN_NUMBER)
1371     {
1372         UINT number;
1373         WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1374         if (!tmp) return 0;
1375         memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1376         tmp[*p] = 0;
1377         number = strtolW( tmp, &end, 10 );
1378         if (!*end)
1379             memcpy( buffer, &number, sizeof(number) );
1380         else  /* invalid number */
1381         {
1382             SetLastError( ERROR_INVALID_FLAGS );
1383             ret = 0;
1384         }
1385         HeapFree( GetProcessHeap(), 0, tmp );
1386
1387         TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1388                lcid, lctype, buffer, len, number );
1389     }
1390     else
1391     {
1392         memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1393         if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1394
1395         TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1396                lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1397     }
1398     return ret;
1399 }
1400
1401
1402 /******************************************************************************
1403  *              SetLocaleInfoA  [KERNEL32.@]
1404  *
1405  * Set information about an aspect of a locale.
1406  *
1407  * PARAMS
1408  *  lcid   [I] LCID of the locale
1409  *  lctype [I] LCTYPE_ flags from "winnls.h"
1410  *  data   [I] Information to set
1411  *
1412  * RETURNS
1413  *  Success: TRUE. The information given will be returned by GetLocaleInfoA()
1414  *           whenever it is called without LOCALE_NOUSEROVERRIDE.
1415  *  Failure: FALSE. Use GetLastError() to determine the cause.
1416  *
1417  * NOTES
1418  *  - Values are only be set for the current user locale; the system locale
1419  *  settings cannot be changed.
1420  *  - Any settings changed by this call are lost when the locale is changed by
1421  *  the control panel (in Wine, this happens every time you change LANG).
1422  *  - The native implementation of this function does not check that lcid matches
1423  *  the current user locale, and simply sets the new values. Wine warns you in
1424  *  this case, but behaves the same.
1425  */
1426 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1427 {
1428     UINT codepage = CP_ACP;
1429     WCHAR *strW;
1430     DWORD len;
1431     BOOL ret;
1432
1433     if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1434
1435     if (!data)
1436     {
1437         SetLastError( ERROR_INVALID_PARAMETER );
1438         return FALSE;
1439     }
1440     len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1441     if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1442     {
1443         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1444         return FALSE;
1445     }
1446     MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1447     ret = SetLocaleInfoW( lcid, lctype, strW );
1448     HeapFree( GetProcessHeap(), 0, strW );
1449     return ret;
1450 }
1451
1452
1453 /******************************************************************************
1454  *              SetLocaleInfoW  (KERNEL32.@)
1455  *
1456  * See SetLocaleInfoA.
1457  */
1458 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1459 {
1460     const WCHAR *value;
1461     static const WCHAR intlW[] = {'i','n','t','l',0 };
1462     UNICODE_STRING valueW;
1463     NTSTATUS status;
1464     HANDLE hkey;
1465
1466     lctype &= 0xffff;
1467     value = get_locale_value_name( lctype );
1468
1469     if (!data || !value)
1470     {
1471         SetLastError( ERROR_INVALID_PARAMETER );
1472         return FALSE;
1473     }
1474
1475     if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1476     {
1477         SetLastError( ERROR_INVALID_FLAGS );
1478         return FALSE;
1479     }
1480
1481     TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1482
1483     /* FIXME: should check that data to set is sane */
1484
1485     /* FIXME: profile functions should map to registry */
1486     WriteProfileStringW( intlW, value, data );
1487
1488     if (!(hkey = create_registry_key())) return FALSE;
1489     RtlInitUnicodeString( &valueW, value );
1490     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1491
1492     if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1493     {
1494       /* Set I-value from S value */
1495       WCHAR *lpD, *lpM, *lpY;
1496       WCHAR szBuff[2];
1497
1498       lpD = strrchrW(data, 'd');
1499       lpM = strrchrW(data, 'M');
1500       lpY = strrchrW(data, 'y');
1501
1502       if (lpD <= lpM)
1503       {
1504         szBuff[0] = '1'; /* D-M-Y */
1505       }
1506       else
1507       {
1508         if (lpY <= lpM)
1509           szBuff[0] = '2'; /* Y-M-D */
1510         else
1511           szBuff[0] = '0'; /* M-D-Y */
1512       }
1513
1514       szBuff[1] = '\0';
1515
1516       if (lctype == LOCALE_SSHORTDATE)
1517         lctype = LOCALE_IDATE;
1518       else
1519         lctype = LOCALE_ILDATE;
1520
1521       value = get_locale_value_name( lctype );
1522
1523       WriteProfileStringW( intlW, value, szBuff );
1524
1525       RtlInitUnicodeString( &valueW, value );
1526       status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1527     }
1528
1529     NtClose( hkey );
1530
1531     if (status) SetLastError( RtlNtStatusToDosError(status) );
1532     return !status;
1533 }
1534
1535
1536 /******************************************************************************
1537  *              GetACP   (KERNEL32.@)
1538  *
1539  * Get the current Ansi code page Id for the system.
1540  *
1541  * PARAMS
1542  *  None.
1543  *
1544  * RETURNS
1545  *    The current Ansi code page identifier for the system.
1546  */
1547 UINT WINAPI GetACP(void)
1548 {
1549     assert( ansi_cptable );
1550     return ansi_cptable->info.codepage;
1551 }
1552
1553
1554 /******************************************************************************
1555  *              SetCPGlobal   (KERNEL32.@)
1556  *
1557  * Set the current Ansi code page Id for the system.
1558  *
1559  * PARAMS
1560  *    acp [I] code page ID to be the new ACP.
1561  *
1562  * RETURNS
1563  *    The previous ACP.
1564  */
1565 UINT WINAPI SetCPGlobal( UINT acp )
1566 {
1567     UINT ret = GetACP();
1568     const union cptable *new_cptable = wine_cp_get_table( acp );
1569
1570     if (new_cptable) ansi_cptable = new_cptable;
1571     return ret;
1572 }
1573
1574
1575 /***********************************************************************
1576  *              GetOEMCP   (KERNEL32.@)
1577  *
1578  * Get the current OEM code page Id for the system.
1579  *
1580  * PARAMS
1581  *  None.
1582  *
1583  * RETURNS
1584  *    The current OEM code page identifier for the system.
1585  */
1586 UINT WINAPI GetOEMCP(void)
1587 {
1588     assert( oem_cptable );
1589     return oem_cptable->info.codepage;
1590 }
1591
1592
1593 /***********************************************************************
1594  *           IsValidCodePage   (KERNEL32.@)
1595  *
1596  * Determine if a given code page identifier is valid.
1597  *
1598  * PARAMS
1599  *  codepage [I] Code page Id to verify.
1600  *
1601  * RETURNS
1602  *  TRUE, If codepage is valid and available on the system,
1603  *  FALSE otherwise.
1604  */
1605 BOOL WINAPI IsValidCodePage( UINT codepage )
1606 {
1607     switch(codepage) {
1608     case CP_UTF7:
1609     case CP_UTF8:
1610         return TRUE;
1611     default:
1612         return wine_cp_get_table( codepage ) != NULL;
1613     }
1614 }
1615
1616
1617 /***********************************************************************
1618  *           IsDBCSLeadByteEx   (KERNEL32.@)
1619  *
1620  * Determine if a character is a lead byte in a given code page.
1621  *
1622  * PARAMS
1623  *  codepage [I] Code page for the test.
1624  *  testchar [I] Character to test
1625  *
1626  * RETURNS
1627  *  TRUE, if testchar is a lead byte in codepage,
1628  *  FALSE otherwise.
1629  */
1630 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1631 {
1632     const union cptable *table = get_codepage_table( codepage );
1633     return table && wine_is_dbcs_leadbyte( table, testchar );
1634 }
1635
1636
1637 /***********************************************************************
1638  *           IsDBCSLeadByte   (KERNEL32.@)
1639  *           IsDBCSLeadByte   (KERNEL.207)
1640  *
1641  * Determine if a character is a lead byte.
1642  *
1643  * PARAMS
1644  *  testchar [I] Character to test
1645  *
1646  * RETURNS
1647  *  TRUE, if testchar is a lead byte in the ANSI code page,
1648  *  FALSE otherwise.
1649  */
1650 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1651 {
1652     if (!ansi_cptable) return FALSE;
1653     return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1654 }
1655
1656
1657 /***********************************************************************
1658  *           GetCPInfo   (KERNEL32.@)
1659  *
1660  * Get information about a code page.
1661  *
1662  * PARAMS
1663  *  codepage [I] Code page number
1664  *  cpinfo   [O] Destination for code page information
1665  *
1666  * RETURNS
1667  *  Success: TRUE. cpinfo is updated with the information about codepage.
1668  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1669  */
1670 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1671 {
1672     const union cptable *table;
1673
1674     if (!cpinfo)
1675     {
1676         SetLastError( ERROR_INVALID_PARAMETER );
1677         return FALSE;
1678     }
1679
1680     if (!(table = get_codepage_table( codepage )))
1681     {
1682         switch(codepage)
1683         {
1684             case CP_UTF7:
1685             case CP_UTF8:
1686                 cpinfo->DefaultChar[0] = 0x3f;
1687                 cpinfo->DefaultChar[1] = 0;
1688                 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1689                 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1690                 return TRUE;
1691         }
1692
1693         SetLastError( ERROR_INVALID_PARAMETER );
1694         return FALSE;
1695     }
1696     if (table->info.def_char & 0xff00)
1697     {
1698         cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1699         cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1700     }
1701     else
1702     {
1703         cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1704         cpinfo->DefaultChar[1] = 0;
1705     }
1706     if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1707         memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1708     else
1709         cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1710
1711     return TRUE;
1712 }
1713
1714 /***********************************************************************
1715  *           GetCPInfoExA   (KERNEL32.@)
1716  *
1717  * Get extended information about a code page.
1718  *
1719  * PARAMS
1720  *  codepage [I] Code page number
1721  *  dwFlags  [I] Reserved, must to 0.
1722  *  cpinfo   [O] Destination for code page information
1723  *
1724  * RETURNS
1725  *  Success: TRUE. cpinfo is updated with the information about codepage.
1726  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1727  */
1728 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1729 {
1730     CPINFOEXW cpinfoW;
1731
1732     if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1733       return FALSE;
1734
1735     /* the layout is the same except for CodePageName */
1736     memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1737     WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1738     return TRUE;
1739 }
1740
1741 /***********************************************************************
1742  *           GetCPInfoExW   (KERNEL32.@)
1743  *
1744  * Unicode version of GetCPInfoExA.
1745  */
1746 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1747 {
1748     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1749       return FALSE;
1750
1751     switch(codepage)
1752     {
1753         case CP_UTF7:
1754         {
1755             static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1756
1757             cpinfo->CodePage = CP_UTF7;
1758             cpinfo->UnicodeDefaultChar = 0x3f;
1759             strcpyW(cpinfo->CodePageName, utf7);
1760             break;
1761         }
1762
1763         case CP_UTF8:
1764         {
1765             static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1766
1767             cpinfo->CodePage = CP_UTF8;
1768             cpinfo->UnicodeDefaultChar = 0x3f;
1769             strcpyW(cpinfo->CodePageName, utf8);
1770             break;
1771         }
1772
1773         default:
1774         {
1775             const union cptable *table = get_codepage_table( codepage );
1776
1777             cpinfo->CodePage = table->info.codepage;
1778             cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1779             MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1780                                  sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1781             break;
1782         }
1783     }
1784     return TRUE;
1785 }
1786
1787 /***********************************************************************
1788  *              EnumSystemCodePagesA   (KERNEL32.@)
1789  *
1790  * Call a user defined function for every code page installed on the system.
1791  *
1792  * PARAMS
1793  *   lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1794  *   flags            [I] Reserved, set to 0.
1795  *
1796  * RETURNS
1797  *  TRUE, If all code pages have been enumerated, or
1798  *  FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1799  */
1800 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1801 {
1802     const union cptable *table;
1803     char buffer[10];
1804     int index = 0;
1805
1806     for (;;)
1807     {
1808         if (!(table = wine_cp_enum_table( index++ ))) break;
1809         sprintf( buffer, "%d", table->info.codepage );
1810         if (!lpfnCodePageEnum( buffer )) break;
1811     }
1812     return TRUE;
1813 }
1814
1815
1816 /***********************************************************************
1817  *              EnumSystemCodePagesW   (KERNEL32.@)
1818  *
1819  * See EnumSystemCodePagesA.
1820  */
1821 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1822 {
1823     const union cptable *table;
1824     WCHAR buffer[10], *p;
1825     int page, index = 0;
1826
1827     for (;;)
1828     {
1829         if (!(table = wine_cp_enum_table( index++ ))) break;
1830         p = buffer + sizeof(buffer)/sizeof(WCHAR);
1831         *--p = 0;
1832         page = table->info.codepage;
1833         do
1834         {
1835             *--p = '0' + (page % 10);
1836             page /= 10;
1837         } while( page );
1838         if (!lpfnCodePageEnum( p )) break;
1839     }
1840     return TRUE;
1841 }
1842
1843
1844 /***********************************************************************
1845  *              MultiByteToWideChar   (KERNEL32.@)
1846  *
1847  * Convert a multibyte character string into a Unicode string.
1848  *
1849  * PARAMS
1850  *   page   [I] Codepage character set to convert from
1851  *   flags  [I] Character mapping flags
1852  *   src    [I] Source string buffer
1853  *   srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1854  *   dst    [O] Destination buffer
1855  *   dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1856  *
1857  * RETURNS
1858  *   Success: If dstlen > 0, the number of characters written to dst.
1859  *            If dstlen == 0, the number of characters needed to perform the
1860  *            conversion. In both cases the count includes the terminating NUL.
1861  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1862  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1863  *            and dstlen != 0; ERROR_INVALID_PARAMETER,  if an invalid parameter
1864  *            is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1865  *            possible for src.
1866  */
1867 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1868                                 LPWSTR dst, INT dstlen )
1869 {
1870     const union cptable *table;
1871     int ret;
1872
1873     if (!src || (!dst && dstlen))
1874     {
1875         SetLastError( ERROR_INVALID_PARAMETER );
1876         return 0;
1877     }
1878
1879     if (srclen < 0) srclen = strlen(src) + 1;
1880
1881     switch(page)
1882     {
1883     case CP_SYMBOL:
1884         if( flags)
1885         {
1886             SetLastError( ERROR_INVALID_PARAMETER );
1887             return 0;
1888         }
1889         ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1890         break;
1891     case CP_UTF7:
1892         FIXME("UTF-7 not supported\n");
1893         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1894         return 0;
1895     case CP_UNIXCP:
1896         if (unix_cptable)
1897         {
1898             ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1899             break;
1900         }
1901 #ifdef __APPLE__
1902         flags |= MB_COMPOSITE;  /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1903 #endif
1904         /* fall through */
1905     case CP_UTF8:
1906         ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1907         break;
1908     default:
1909         if (!(table = get_codepage_table( page )))
1910         {
1911             SetLastError( ERROR_INVALID_PARAMETER );
1912             return 0;
1913         }
1914         ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1915         break;
1916     }
1917
1918     if (ret < 0)
1919     {
1920         switch(ret)
1921         {
1922         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1923         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1924         }
1925         ret = 0;
1926     }
1927     TRACE("cp %d %s -> %s, ret = %d\n",
1928           page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1929     return ret;
1930 }
1931
1932
1933 /***********************************************************************
1934  *              WideCharToMultiByte   (KERNEL32.@)
1935  *
1936  * Convert a Unicode character string into a multibyte string.
1937  *
1938  * PARAMS
1939  *   page    [I] Code page character set to convert to
1940  *   flags   [I] Mapping Flags (MB_ constants from "winnls.h").
1941  *   src     [I] Source string buffer
1942  *   srclen  [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1943  *   dst     [O] Destination buffer
1944  *   dstlen  [I] Length of dst (in bytes), or 0 to compute the required length
1945  *   defchar [I] Default character to use for conversion if no exact
1946  *                  conversion can be made
1947  *   used    [O] Set if default character was used in the conversion
1948  *
1949  * RETURNS
1950  *   Success: If dstlen > 0, the number of characters written to dst.
1951  *            If dstlen == 0, number of characters needed to perform the
1952  *            conversion. In both cases the count includes the terminating NUL.
1953  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1954  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1955  *            and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1956  *            parameter was given.
1957  */
1958 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1959                                 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1960 {
1961     const union cptable *table;
1962     int ret, used_tmp;
1963
1964     if (!src || (!dst && dstlen))
1965     {
1966         SetLastError( ERROR_INVALID_PARAMETER );
1967         return 0;
1968     }
1969
1970     if (srclen < 0) srclen = strlenW(src) + 1;
1971
1972     switch(page)
1973     {
1974     case CP_SYMBOL:
1975         if( flags || defchar || used)
1976         {
1977             SetLastError( ERROR_INVALID_PARAMETER );
1978             return 0;
1979         }
1980         ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
1981         break;
1982     case CP_UTF7:
1983         FIXME("UTF-7 not supported\n");
1984         SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1985         return 0;
1986     case CP_UNIXCP:
1987         if (unix_cptable)
1988         {
1989             ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
1990                                     defchar, used ? &used_tmp : NULL );
1991             break;
1992         }
1993         /* fall through */
1994     case CP_UTF8:
1995         if (used) *used = FALSE;  /* all chars are valid for UTF-8 */
1996         ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
1997         break;
1998     default:
1999         if (!(table = get_codepage_table( page )))
2000         {
2001             SetLastError( ERROR_INVALID_PARAMETER );
2002             return 0;
2003         }
2004         ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2005                                 defchar, used ? &used_tmp : NULL );
2006         if (used) *used = used_tmp;
2007         break;
2008     }
2009
2010     if (ret < 0)
2011     {
2012         switch(ret)
2013         {
2014         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2015         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2016         }
2017         ret = 0;
2018     }
2019     TRACE("cp %d %s -> %s, ret = %d\n",
2020           page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2021     return ret;
2022 }
2023
2024
2025 /***********************************************************************
2026  *           GetThreadLocale    (KERNEL32.@)
2027  *
2028  * Get the current threads locale.
2029  *
2030  * PARAMS
2031  *  None.
2032  *
2033  * RETURNS
2034  *  The LCID currently associated with the calling thread.
2035  */
2036 LCID WINAPI GetThreadLocale(void)
2037 {
2038     LCID ret = NtCurrentTeb()->CurrentLocale;
2039     if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2040     return ret;
2041 }
2042
2043 /**********************************************************************
2044  *           SetThreadLocale    (KERNEL32.@)
2045  *
2046  * Set the current threads locale.
2047  *
2048  * PARAMS
2049  *  lcid [I] LCID of the locale to set
2050  *
2051  * RETURNS
2052  *  Success: TRUE. The threads locale is set to lcid.
2053  *  Failure: FALSE. Use GetLastError() to determine the cause.
2054  */
2055 BOOL WINAPI SetThreadLocale( LCID lcid )
2056 {
2057     TRACE("(0x%04X)\n", lcid);
2058
2059     lcid = ConvertDefaultLocale(lcid);
2060
2061     if (lcid != GetThreadLocale())
2062     {
2063         if (!IsValidLocale(lcid, LCID_SUPPORTED))
2064         {
2065             SetLastError(ERROR_INVALID_PARAMETER);
2066             return FALSE;
2067         }
2068
2069         NtCurrentTeb()->CurrentLocale = lcid;
2070     }
2071     return TRUE;
2072 }
2073
2074 /**********************************************************************
2075  *           SetThreadUILanguage    (KERNEL32.@)
2076  *
2077  * Set the current threads UI language.
2078  *
2079  * PARAMS
2080  *  langid [I] LANGID of the language to set, or 0 to use
2081  *             the available language which is best supported
2082  *             for console applications
2083  *
2084  * RETURNS
2085  *  Success: The return value is the same as the input value.
2086  *  Failure: The return value differs from the input value.
2087  *           Use GetLastError() to determine the cause.
2088  */
2089 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2090 {
2091     TRACE("(0x%04x) stub - returning success\n", langid);
2092     return langid;
2093 }
2094
2095 /******************************************************************************
2096  *              ConvertDefaultLocale (KERNEL32.@)
2097  *
2098  * Convert a default locale identifier into a real identifier.
2099  *
2100  * PARAMS
2101  *  lcid [I] LCID identifier of the locale to convert
2102  *
2103  * RETURNS
2104  *  lcid unchanged, if not a default locale or its sublanguage is
2105  *   not SUBLANG_NEUTRAL.
2106  *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2107  *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2108  *  Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2109  */
2110 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2111 {
2112     LANGID langid;
2113
2114     switch (lcid)
2115     {
2116     case LOCALE_SYSTEM_DEFAULT:
2117         lcid = GetSystemDefaultLCID();
2118         break;
2119     case LOCALE_USER_DEFAULT:
2120     case LOCALE_NEUTRAL:
2121         lcid = GetUserDefaultLCID();
2122         break;
2123     default:
2124         /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2125         langid = LANGIDFROMLCID(lcid);
2126         if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2127         {
2128           langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2129           lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2130         }
2131     }
2132     return lcid;
2133 }
2134
2135
2136 /******************************************************************************
2137  *           IsValidLocale   (KERNEL32.@)
2138  *
2139  * Determine if a locale is valid.
2140  *
2141  * PARAMS
2142  *  lcid  [I] LCID of the locale to check
2143  *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2144  *
2145  * RETURNS
2146  *  TRUE,  if lcid is valid,
2147  *  FALSE, otherwise.
2148  *
2149  * NOTES
2150  *  Wine does not currently make the distinction between supported and installed. All
2151  *  languages supported are installed by default.
2152  */
2153 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2154 {
2155     /* check if language is registered in the kernel32 resources */
2156     return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2157                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2158 }
2159
2160
2161 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2162                                        LPCSTR name, WORD LangID, LONG_PTR lParam )
2163 {
2164     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2165     char buf[20];
2166
2167     sprintf(buf, "%08x", (UINT)LangID);
2168     return lpfnLocaleEnum( buf );
2169 }
2170
2171 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2172                                        LPCWSTR name, WORD LangID, LONG_PTR lParam )
2173 {
2174     static const WCHAR formatW[] = {'%','0','8','x',0};
2175     LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2176     WCHAR buf[20];
2177     sprintfW( buf, formatW, (UINT)LangID );
2178     return lpfnLocaleEnum( buf );
2179 }
2180
2181 /******************************************************************************
2182  *           EnumSystemLocalesA  (KERNEL32.@)
2183  *
2184  * Call a users function for each locale available on the system.
2185  *
2186  * PARAMS
2187  *  lpfnLocaleEnum [I] Callback function to call for each locale
2188  *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2189  *
2190  * RETURNS
2191  *  Success: TRUE.
2192  *  Failure: FALSE. Use GetLastError() to determine the cause.
2193  */
2194 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2195 {
2196     TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2197     EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2198                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2199                             (LONG_PTR)lpfnLocaleEnum);
2200     return TRUE;
2201 }
2202
2203
2204 /******************************************************************************
2205  *           EnumSystemLocalesW  (KERNEL32.@)
2206  *
2207  * See EnumSystemLocalesA.
2208  */
2209 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2210 {
2211     TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2212     EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2213                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2214                             (LONG_PTR)lpfnLocaleEnum);
2215     return TRUE;
2216 }
2217
2218
2219 struct enum_locale_ex_data
2220 {
2221     LOCALE_ENUMPROCEX proc;
2222     DWORD             flags;
2223     LPARAM            lparam;
2224 };
2225
2226 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2227                                           LPCWSTR name, WORD lang, LONG_PTR lparam )
2228 {
2229     struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2230     WCHAR buffer[256];
2231     DWORD neutral;
2232     unsigned int flags;
2233
2234     GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2235                     buffer, sizeof(buffer) / sizeof(WCHAR) );
2236     if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2237                          LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2238                          (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2239         neutral = 0;
2240     flags = LOCALE_WINDOWS;
2241     flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2242     if (data->flags && ~(data->flags & flags)) return TRUE;
2243     return data->proc( buffer, flags, data->lparam );
2244 }
2245
2246 /******************************************************************************
2247  *           EnumSystemLocalesEx  (KERNEL32.@)
2248  */
2249 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2250 {
2251     struct enum_locale_ex_data data;
2252
2253     if (reserved)
2254     {
2255         SetLastError( ERROR_INVALID_PARAMETER );
2256         return FALSE;
2257     }
2258     data.proc   = proc;
2259     data.flags  = flags;
2260     data.lparam = lparam;
2261     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2262                             (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2263                             enum_locale_ex_proc, (LONG_PTR)&data );
2264     return TRUE;
2265 }
2266
2267
2268 /***********************************************************************
2269  *           VerLanguageNameA  (KERNEL32.@)
2270  *
2271  * Get the name of a language.
2272  *
2273  * PARAMS
2274  *  wLang  [I] LANGID of the language
2275  *  szLang [O] Destination for the language name
2276  *
2277  * RETURNS
2278  *  Success: The size of the language name. If szLang is non-NULL, it is filled
2279  *           with the name.
2280  *  Failure: 0. Use GetLastError() to determine the cause.
2281  *
2282  */
2283 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2284 {
2285     return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2286 }
2287
2288
2289 /***********************************************************************
2290  *           VerLanguageNameW  (KERNEL32.@)
2291  *
2292  * See VerLanguageNameA.
2293  */
2294 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2295 {
2296     return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2297 }
2298
2299
2300 /******************************************************************************
2301  *           GetStringTypeW    (KERNEL32.@)
2302  *
2303  * See GetStringTypeA.
2304  */
2305 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2306 {
2307     static const unsigned char type2_map[16] =
2308     {
2309         C2_NOTAPPLICABLE,      /* unassigned */
2310         C2_LEFTTORIGHT,        /* L */
2311         C2_RIGHTTOLEFT,        /* R */
2312         C2_EUROPENUMBER,       /* EN */
2313         C2_EUROPESEPARATOR,    /* ES */
2314         C2_EUROPETERMINATOR,   /* ET */
2315         C2_ARABICNUMBER,       /* AN */
2316         C2_COMMONSEPARATOR,    /* CS */
2317         C2_BLOCKSEPARATOR,     /* B */
2318         C2_SEGMENTSEPARATOR,   /* S */
2319         C2_WHITESPACE,         /* WS */
2320         C2_OTHERNEUTRAL,       /* ON */
2321         C2_RIGHTTOLEFT,        /* AL */
2322         C2_NOTAPPLICABLE,      /* NSM */
2323         C2_NOTAPPLICABLE,      /* BN */
2324         C2_OTHERNEUTRAL        /* LRE, LRO, RLE, RLO, PDF */
2325     };
2326
2327     if (count == -1) count = strlenW(src) + 1;
2328     switch(type)
2329     {
2330     case CT_CTYPE1:
2331         while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2332         break;
2333     case CT_CTYPE2:
2334         while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2335         break;
2336     case CT_CTYPE3:
2337     {
2338         WARN("CT_CTYPE3: semi-stub.\n");
2339         while (count--)
2340         {
2341             int c = *src;
2342             WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2343
2344             type1 = get_char_typeW( *src++ ) & 0xfff;
2345             /* try to construct type3 from type1 */
2346             if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2347             if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2348             if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2349             if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2350             if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2351             if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2352             if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2353
2354             if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2355             if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2356             if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2357             if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2358             if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2359             if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2360             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2361             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2362
2363             if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2364             if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2365             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2366             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2367             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2368             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2369             *chartype++ = type3;
2370         }
2371         break;
2372     }
2373     default:
2374         SetLastError( ERROR_INVALID_PARAMETER );
2375         return FALSE;
2376     }
2377     return TRUE;
2378 }
2379
2380
2381 /******************************************************************************
2382  *           GetStringTypeExW    (KERNEL32.@)
2383  *
2384  * See GetStringTypeExA.
2385  */
2386 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2387 {
2388     /* locale is ignored for Unicode */
2389     return GetStringTypeW( type, src, count, chartype );
2390 }
2391
2392
2393 /******************************************************************************
2394  *           GetStringTypeA    (KERNEL32.@)
2395  *
2396  * Get characteristics of the characters making up a string.
2397  *
2398  * PARAMS
2399  *  locale   [I] Locale Id for the string
2400  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2401  *  src      [I] String to analyse
2402  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
2403  *  chartype [O] Destination for the calculated characteristics
2404  *
2405  * RETURNS
2406  *  Success: TRUE. chartype is filled with the requested characteristics of each char
2407  *           in src.
2408  *  Failure: FALSE. Use GetLastError() to determine the cause.
2409  */
2410 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2411 {
2412     UINT cp;
2413     INT countW;
2414     LPWSTR srcW;
2415     BOOL ret = FALSE;
2416
2417     if(count == -1) count = strlen(src) + 1;
2418
2419     if (!(cp = get_lcid_codepage( locale )))
2420     {
2421         FIXME("For locale %04x using current ANSI code page\n", locale);
2422         cp = GetACP();
2423     }
2424
2425     countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2426     if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2427     {
2428         MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2429     /*
2430      * NOTE: the target buffer has 1 word for each CHARACTER in the source
2431      * string, with multibyte characters there maybe be more bytes in count
2432      * than character space in the buffer!
2433      */
2434         ret = GetStringTypeW(type, srcW, countW, chartype);
2435         HeapFree(GetProcessHeap(), 0, srcW);
2436     }
2437     return ret;
2438 }
2439
2440 /******************************************************************************
2441  *           GetStringTypeExA    (KERNEL32.@)
2442  *
2443  * Get characteristics of the characters making up a string.
2444  *
2445  * PARAMS
2446  *  locale   [I] Locale Id for the string
2447  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2448  *  src      [I] String to analyse
2449  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
2450  *  chartype [O] Destination for the calculated characteristics
2451  *
2452  * RETURNS
2453  *  Success: TRUE. chartype is filled with the requested characteristics of each char
2454  *           in src.
2455  *  Failure: FALSE. Use GetLastError() to determine the cause.
2456  */
2457 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2458 {
2459     return GetStringTypeA(locale, type, src, count, chartype);
2460 }
2461
2462
2463 /*************************************************************************
2464  *           LCMapStringW    (KERNEL32.@)
2465  *
2466  * See LCMapStringA.
2467  */
2468 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2469                         LPWSTR dst, INT dstlen)
2470 {
2471     LPWSTR dst_ptr;
2472
2473     if (!src || !srclen || dstlen < 0)
2474     {
2475         SetLastError(ERROR_INVALID_PARAMETER);
2476         return 0;
2477     }
2478
2479     /* mutually exclusive flags */
2480     if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2481         (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2482         (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2483         (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2484     {
2485         SetLastError(ERROR_INVALID_FLAGS);
2486         return 0;
2487     }
2488
2489     if (!dstlen) dst = NULL;
2490
2491     lcid = ConvertDefaultLocale(lcid);
2492
2493     if (flags & LCMAP_SORTKEY)
2494     {
2495         INT ret;
2496         if (src == dst)
2497         {
2498             SetLastError(ERROR_INVALID_FLAGS);
2499             return 0;
2500         }
2501
2502         if (srclen < 0) srclen = strlenW(src);
2503
2504         TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2505               lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2506
2507         ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2508         if (ret == 0)
2509             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2510         else
2511             ret++;
2512         return ret;
2513     }
2514
2515     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2516     if (flags & SORT_STRINGSORT)
2517     {
2518         SetLastError(ERROR_INVALID_FLAGS);
2519         return 0;
2520     }
2521
2522     if (srclen < 0) srclen = strlenW(src) + 1;
2523
2524     TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2525           lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2526
2527     if (!dst) /* return required string length */
2528     {
2529         INT len;
2530
2531         for (len = 0; srclen; src++, srclen--)
2532         {
2533             WCHAR wch = *src;
2534             /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2535              * and skips white space and punctuation characters for
2536              * NORM_IGNORESYMBOLS.
2537              */
2538             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2539                 continue;
2540             len++;
2541         }
2542         return len;
2543     }
2544
2545     if (flags & LCMAP_UPPERCASE)
2546     {
2547         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2548         {
2549             WCHAR wch = *src;
2550             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2551                 continue;
2552             *dst_ptr++ = toupperW(wch);
2553             dstlen--;
2554         }
2555     }
2556     else if (flags & LCMAP_LOWERCASE)
2557     {
2558         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2559         {
2560             WCHAR wch = *src;
2561             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2562                 continue;
2563             *dst_ptr++ = tolowerW(wch);
2564             dstlen--;
2565         }
2566     }
2567     else
2568     {
2569         if (src == dst)
2570         {
2571             SetLastError(ERROR_INVALID_FLAGS);
2572             return 0;
2573         }
2574         for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2575         {
2576             WCHAR wch = *src;
2577             if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2578                 continue;
2579             *dst_ptr++ = wch;
2580             dstlen--;
2581         }
2582     }
2583
2584     if (srclen)
2585     {
2586         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2587         return 0;
2588     }
2589
2590     return dst_ptr - dst;
2591 }
2592
2593 /*************************************************************************
2594  *           LCMapStringA    (KERNEL32.@)
2595  *
2596  * Map characters in a locale sensitive string.
2597  *
2598  * PARAMS
2599  *  lcid   [I] LCID for the conversion.
2600  *  flags  [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2601  *  src    [I] String to map
2602  *  srclen [I] Length of src in chars, or -1 if src is NUL terminated
2603  *  dst    [O] Destination for mapped string
2604  *  dstlen [I] Length of dst in characters
2605  *
2606  * RETURNS
2607  *  Success: The length of the mapped string in dst, including the NUL terminator.
2608  *  Failure: 0. Use GetLastError() to determine the cause.
2609  */
2610 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2611                         LPSTR dst, INT dstlen)
2612 {
2613     WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2614     LPWSTR srcW, dstW;
2615     INT ret = 0, srclenW, dstlenW;
2616     UINT locale_cp = CP_ACP;
2617
2618     if (!src || !srclen || dstlen < 0)
2619     {
2620         SetLastError(ERROR_INVALID_PARAMETER);
2621         return 0;
2622     }
2623
2624     if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2625
2626     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2627     if (srclenW)
2628         srcW = bufW;
2629     else
2630     {
2631         srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2632         srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2633         if (!srcW)
2634         {
2635             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2636             return 0;
2637         }
2638         MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2639     }
2640
2641     if (flags & LCMAP_SORTKEY)
2642     {
2643         if (src == dst)
2644         {
2645             SetLastError(ERROR_INVALID_FLAGS);
2646             goto map_string_exit;
2647         }
2648         ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2649         if (ret == 0)
2650             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2651         else
2652             ret++;
2653         goto map_string_exit;
2654     }
2655
2656     if (flags & SORT_STRINGSORT)
2657     {
2658         SetLastError(ERROR_INVALID_FLAGS);
2659         goto map_string_exit;
2660     }
2661
2662     dstlenW = LCMapStringW(lcid, flags, srcW, srclenW, NULL, 0);
2663     if (!dstlenW)
2664         goto map_string_exit;
2665
2666     dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2667     if (!dstW)
2668     {
2669         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2670         goto map_string_exit;
2671     }
2672
2673     LCMapStringW(lcid, flags, srcW, srclenW, dstW, dstlenW);
2674     ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2675     HeapFree(GetProcessHeap(), 0, dstW);
2676
2677 map_string_exit:
2678     if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2679     return ret;
2680 }
2681
2682 /*************************************************************************
2683  *           FoldStringA    (KERNEL32.@)
2684  *
2685  * Map characters in a string.
2686  *
2687  * PARAMS
2688  *  dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2689  *  src     [I] String to map
2690  *  srclen  [I] Length of src, or -1 if src is NUL terminated
2691  *  dst     [O] Destination for mapped string
2692  *  dstlen  [I] Length of dst, or 0 to find the required length for the mapped string
2693  *
2694  * RETURNS
2695  *  Success: The length of the string written to dst, including the terminating NUL. If
2696  *           dstlen is 0, the value returned is the same, but nothing is written to dst,
2697  *           and dst may be NULL.
2698  *  Failure: 0. Use GetLastError() to determine the cause.
2699  */
2700 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2701                        LPSTR dst, INT dstlen)
2702 {
2703     INT ret = 0, srclenW = 0;
2704     WCHAR *srcW = NULL, *dstW = NULL;
2705
2706     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2707     {
2708         SetLastError(ERROR_INVALID_PARAMETER);
2709         return 0;
2710     }
2711
2712     srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2713                                   src, srclen, NULL, 0);
2714     srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2715
2716     if (!srcW)
2717     {
2718         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2719         goto FoldStringA_exit;
2720     }
2721
2722     MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2723                         src, srclen, srcW, srclenW);
2724
2725     dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2726
2727     ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2728     if (ret && dstlen)
2729     {
2730         dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2731
2732         if (!dstW)
2733         {
2734             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2735             goto FoldStringA_exit;
2736         }
2737
2738         ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2739         if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2740         {
2741             ret = 0;
2742             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2743         }
2744     }
2745
2746     HeapFree(GetProcessHeap(), 0, dstW);
2747
2748 FoldStringA_exit:
2749     HeapFree(GetProcessHeap(), 0, srcW);
2750     return ret;
2751 }
2752
2753 /*************************************************************************
2754  *           FoldStringW    (KERNEL32.@)
2755  *
2756  * See FoldStringA.
2757  */
2758 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2759                        LPWSTR dst, INT dstlen)
2760 {
2761     int ret;
2762
2763     switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2764     {
2765     case 0:
2766         if (dwFlags)
2767           break;
2768         /* Fall through for dwFlags == 0 */
2769     case MAP_PRECOMPOSED|MAP_COMPOSITE:
2770     case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2771     case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2772         SetLastError(ERROR_INVALID_FLAGS);
2773         return 0;
2774     }
2775
2776     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2777     {
2778         SetLastError(ERROR_INVALID_PARAMETER);
2779         return 0;
2780     }
2781
2782     ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2783     if (!ret)
2784         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2785     return ret;
2786 }
2787
2788 /******************************************************************************
2789  *           CompareStringW    (KERNEL32.@)
2790  *
2791  * See CompareStringA.
2792  */
2793 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2794                           LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2795 {
2796     INT ret;
2797
2798     if (!str1 || !str2)
2799     {
2800         SetLastError(ERROR_INVALID_PARAMETER);
2801         return 0;
2802     }
2803
2804     if( style & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2805         SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2806     {
2807         SetLastError(ERROR_INVALID_FLAGS);
2808         return 0;
2809     }
2810
2811     /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2812     if (style & 0x10000000)
2813         WARN("Ignoring unknown style 0x10000000\n");
2814
2815     if (len1 < 0) len1 = strlenW(str1);
2816     if (len2 < 0) len2 = strlenW(str2);
2817
2818     ret = wine_compare_string(style, str1, len1, str2, len2);
2819
2820     if (ret) /* need to translate result */
2821         return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2822     return CSTR_EQUAL;
2823 }
2824
2825 /******************************************************************************
2826  *           CompareStringA    (KERNEL32.@)
2827  *
2828  * Compare two locale sensitive strings.
2829  *
2830  * PARAMS
2831  *  lcid  [I] LCID for the comparison
2832  *  style [I] Flags for the comparison (NORM_ constants from "winnls.h").
2833  *  str1  [I] First string to compare
2834  *  len1  [I] Length of str1, or -1 if str1 is NUL terminated
2835  *  str2  [I] Second string to compare
2836  *  len2  [I] Length of str2, or -1 if str2 is NUL terminated
2837  *
2838  * RETURNS
2839  *  Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2840  *           str1 is less than, equal to or greater than str2 respectively.
2841  *  Failure: FALSE. Use GetLastError() to determine the cause.
2842  */
2843 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2844                           LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2845 {
2846     WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2847     WCHAR *buf2W = buf1W + 130;
2848     LPWSTR str1W, str2W;
2849     INT len1W, len2W, ret;
2850     UINT locale_cp = CP_ACP;
2851
2852     if (!str1 || !str2)
2853     {
2854         SetLastError(ERROR_INVALID_PARAMETER);
2855         return 0;
2856     }
2857     if (len1 < 0) len1 = strlen(str1);
2858     if (len2 < 0) len2 = strlen(str2);
2859
2860     if (!(style & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2861
2862     len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2863     if (len1W)
2864         str1W = buf1W;
2865     else
2866     {
2867         len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2868         str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2869         if (!str1W)
2870         {
2871             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2872             return 0;
2873         }
2874         MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2875     }
2876     len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2877     if (len2W)
2878         str2W = buf2W;
2879     else
2880     {
2881         len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2882         str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2883         if (!str2W)
2884         {
2885             if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2886             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2887             return 0;
2888         }
2889         MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2890     }
2891
2892     ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2893
2894     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2895     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2896     return ret;
2897 }
2898
2899 /*************************************************************************
2900  *           lstrcmp     (KERNEL32.@)
2901  *           lstrcmpA    (KERNEL32.@)
2902  *
2903  * Compare two strings using the current thread locale.
2904  *
2905  * PARAMS
2906  *  str1  [I] First string to compare
2907  *  str2  [I] Second string to compare
2908  *
2909  * RETURNS
2910  *  Success: A number less than, equal to or greater than 0 depending on whether
2911  *           str1 is less than, equal to or greater than str2 respectively.
2912  *  Failure: FALSE. Use GetLastError() to determine the cause.
2913  */
2914 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
2915 {
2916     int ret;
2917     
2918     if ((str1 == NULL) && (str2 == NULL)) return 0;
2919     if (str1 == NULL) return -1;
2920     if (str2 == NULL) return 1;
2921
2922     ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2923     if (ret) ret -= 2;
2924     
2925     return ret;
2926 }
2927
2928 /*************************************************************************
2929  *           lstrcmpi     (KERNEL32.@)
2930  *           lstrcmpiA    (KERNEL32.@)
2931  *
2932  * Compare two strings using the current thread locale, ignoring case.
2933  *
2934  * PARAMS
2935  *  str1  [I] First string to compare
2936  *  str2  [I] Second string to compare
2937  *
2938  * RETURNS
2939  *  Success: A number less than, equal to or greater than 0 depending on whether
2940  *           str2 is less than, equal to or greater than str1 respectively.
2941  *  Failure: FALSE. Use GetLastError() to determine the cause.
2942  */
2943 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
2944 {
2945     int ret;
2946     
2947     if ((str1 == NULL) && (str2 == NULL)) return 0;
2948     if (str1 == NULL) return -1;
2949     if (str2 == NULL) return 1;
2950
2951     ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
2952     if (ret) ret -= 2;
2953     
2954     return ret;
2955 }
2956
2957 /*************************************************************************
2958  *           lstrcmpW    (KERNEL32.@)
2959  *
2960  * See lstrcmpA.
2961  */
2962 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
2963 {
2964     int ret;
2965
2966     if ((str1 == NULL) && (str2 == NULL)) return 0;
2967     if (str1 == NULL) return -1;
2968     if (str2 == NULL) return 1;
2969
2970     ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
2971     if (ret) ret -= 2;
2972     
2973     return ret;
2974 }
2975
2976 /*************************************************************************
2977  *           lstrcmpiW    (KERNEL32.@)
2978  *
2979  * See lstrcmpiA.
2980  */
2981 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
2982 {
2983     int ret;
2984     
2985     if ((str1 == NULL) && (str2 == NULL)) return 0;
2986     if (str1 == NULL) return -1;
2987     if (str2 == NULL) return 1;
2988
2989     ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
2990     if (ret) ret -= 2;
2991     
2992     return ret;
2993 }
2994
2995 /******************************************************************************
2996  *              LOCALE_Init
2997  */
2998 void LOCALE_Init(void)
2999 {
3000     extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3001                                              const union cptable *unix_cp );
3002
3003     UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3004
3005 #ifdef __APPLE__
3006     /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3007     char user_locale[50];
3008
3009     CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3010     CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3011     CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3012     CFStringRef user_locale_string_ref;
3013
3014     if (user_locale_country_ref)
3015     {
3016         user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3017             user_locale_lang_ref, user_locale_country_ref);
3018     }
3019     else
3020     {
3021         user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3022             user_locale_lang_ref);
3023     }
3024
3025     CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3026
3027     unix_cp = CP_UTF8;  /* default to utf-8 even if we don't get a valid locale */
3028     setenv( "LANG", user_locale, 0 );
3029     TRACE( "setting locale to '%s'\n", user_locale );
3030 #endif /* __APPLE__ */
3031
3032     setlocale( LC_ALL, "" );
3033
3034     unix_cp = setup_unix_locales();
3035     if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3036
3037 #ifdef __APPLE__
3038     /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3039     if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3040     {
3041         /* Retrieve the preferred language as chosen in System Preferences. */
3042         /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3043            leave things be. */
3044         CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3045         CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3046         CFStringRef user_language_string_ref;
3047         if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3048             (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3049             !CFEqual(user_language_string_ref, user_locale_lang_ref))
3050         {
3051             struct locale_name locale_name;
3052             WCHAR buffer[128];
3053             CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3054             strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3055             parse_locale_name( buffer, &locale_name );
3056             lcid_LC_MESSAGES = locale_name.lcid;
3057             TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3058         }
3059         CFRelease( all_locales );
3060         if (preferred_locales)
3061             CFRelease( preferred_locales );
3062     }
3063
3064     CFRelease( user_locale_ref );
3065     CFRelease( user_locale_string_ref );
3066 #endif
3067
3068     NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3069     NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3070     NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3071
3072     ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3073     GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3074                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3075     GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3076                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3077     if (!unix_cp)
3078         GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3079                         (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3080
3081     if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3082         ansi_cptable = wine_cp_get_table( 1252 );
3083     if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3084         oem_cptable  = wine_cp_get_table( 437 );
3085     if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3086         mac_cptable  = wine_cp_get_table( 10000 );
3087     if (unix_cp != CP_UTF8)
3088     {
3089         if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3090             unix_cptable  = wine_cp_get_table( 28591 );
3091     }
3092
3093     __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3094
3095     TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3096            ansi_cptable->info.codepage, oem_cptable->info.codepage,
3097            mac_cptable->info.codepage, unix_cp );
3098
3099     setlocale(LC_NUMERIC, "C");  /* FIXME: oleaut32 depends on this */
3100 }
3101
3102 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3103 {
3104     UNICODE_STRING keyName;
3105     OBJECT_ATTRIBUTES attr;
3106     HANDLE hkey;
3107
3108     RtlInitUnicodeString( &keyName, szKeyName );
3109     InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3110
3111     if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3112         hkey = 0;
3113
3114     return hkey;
3115 }
3116
3117 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3118                               ULONG keyNameSize)
3119 {
3120     BYTE buffer[80];
3121     KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3122     DWORD dwLen;
3123
3124     if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3125                         sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3126         info->NameLength > keyNameSize)
3127     {
3128         return FALSE;
3129     }
3130
3131     TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3132
3133     memcpy( szKeyName, info->Name, info->NameLength);
3134     szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3135
3136     TRACE("returning %s\n", debugstr_w(szKeyName));
3137     return TRUE;
3138 }
3139
3140 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3141                              LPWSTR szValueName, ULONG valueNameSize,
3142                              LPWSTR szValueData, ULONG valueDataSize)
3143 {
3144     BYTE buffer[80];
3145     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3146     DWORD dwLen;
3147
3148     if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3149         buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3150         info->NameLength > valueNameSize ||
3151         info->DataLength > valueDataSize)
3152     {
3153         return FALSE;
3154     }
3155
3156     TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3157
3158     memcpy( szValueName, info->Name, info->NameLength);
3159     szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3160     memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3161     szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3162
3163     TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3164     return TRUE;
3165 }
3166
3167 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3168 {
3169     BYTE buffer[128];
3170     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3171     DWORD dwSize = sizeof(buffer);
3172     UNICODE_STRING valueName;
3173
3174     RtlInitUnicodeString( &valueName, szValueName );
3175
3176     TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3177     if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3178                          buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3179         info->DataLength == sizeof(DWORD))
3180     {
3181         memcpy(lpVal, info->Data, sizeof(DWORD));
3182         return TRUE;
3183     }
3184
3185     return FALSE;
3186 }
3187
3188 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3189 {
3190     LANGID  langId;
3191     LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3192     HRSRC   hResource;
3193     BOOL    bRet = FALSE;
3194
3195     /* FIXME: Is it correct to use the system default langid? */
3196     langId = GetSystemDefaultLangID();
3197
3198     if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3199         langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3200
3201     hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3202
3203     if (hResource)
3204     {
3205         HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3206
3207         if (hResDir)
3208         {
3209             ULONG   iResourceIndex = lgrpid & 0xf;
3210             LPCWSTR lpResEntry = LockResource( hResDir );
3211             ULONG   i;
3212
3213             for (i = 0; i < iResourceIndex; i++)
3214                 lpResEntry += *lpResEntry + 1;
3215
3216             if (*lpResEntry < nameSize)
3217             {
3218                 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3219                 szName[*lpResEntry] = '\0';
3220                 bRet = TRUE;
3221             }
3222
3223         }
3224         FreeResource( hResource );
3225     }
3226     return bRet;
3227 }
3228
3229 /* Registry keys for NLS related information */
3230
3231 static const WCHAR szCountryListName[] = {
3232     'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3233     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3234     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3235     'T','e','l','e','p','h','o','n','y','\\',
3236     'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3237 };
3238
3239
3240 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3241 typedef struct
3242 {
3243   LANGUAGEGROUP_ENUMPROCA procA;
3244   LANGUAGEGROUP_ENUMPROCW procW;
3245   DWORD    dwFlags;
3246   LONG_PTR lParam;
3247 } ENUMLANGUAGEGROUP_CALLBACKS;
3248
3249 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3250 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3251 {
3252     WCHAR szNumber[10], szValue[4];
3253     HANDLE hKey;
3254     BOOL bContinue = TRUE;
3255     ULONG ulIndex = 0;
3256
3257     if (!lpProcs)
3258     {
3259         SetLastError(ERROR_INVALID_PARAMETER);
3260         return FALSE;
3261     }
3262
3263     switch (lpProcs->dwFlags)
3264     {
3265     case 0:
3266         /* Default to LGRPID_INSTALLED */
3267         lpProcs->dwFlags = LGRPID_INSTALLED;
3268         /* Fall through... */
3269     case LGRPID_INSTALLED:
3270     case LGRPID_SUPPORTED:
3271         break;
3272     default:
3273         SetLastError(ERROR_INVALID_FLAGS);
3274         return FALSE;
3275     }
3276
3277     hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3278
3279     if (!hKey)
3280         FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3281
3282     while (bContinue)
3283     {
3284         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3285                               szValue, sizeof(szValue) ))
3286         {
3287             BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
3288             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3289
3290             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3291                    bInstalled ? "" : "not ");
3292
3293             if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3294             {
3295                 WCHAR szGrpName[48];
3296
3297                 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3298                     szGrpName[0] = '\0';
3299
3300                 if (lpProcs->procW)
3301                     bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3302                                                 lpProcs->lParam );
3303                 else
3304                 {
3305                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3306                     char szGrpNameA[48];
3307
3308                     /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3309                      *        or whether the language names are ever localised. Assume CP_ACP.
3310                      */
3311
3312                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3313                     WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3314
3315                     bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3316                                                 lpProcs->lParam );
3317                 }
3318             }
3319
3320             ulIndex++;
3321         }
3322         else
3323             bContinue = FALSE;
3324
3325         if (!bContinue)
3326             break;
3327     }
3328
3329     if (hKey)
3330         NtClose( hKey );
3331
3332     return TRUE;
3333 }
3334
3335 /******************************************************************************
3336  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
3337  *
3338  * Call a users function for each language group available on the system.
3339  *
3340  * PARAMS
3341  *  pLangGrpEnumProc [I] Callback function to call for each language group
3342  *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3343  *  lParam           [I] User parameter to pass to pLangGrpEnumProc
3344  *
3345  * RETURNS
3346  *  Success: TRUE.
3347  *  Failure: FALSE. Use GetLastError() to determine the cause.
3348  */
3349 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3350                                       DWORD dwFlags, LONG_PTR lParam)
3351 {
3352     ENUMLANGUAGEGROUP_CALLBACKS procs;
3353
3354     TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3355
3356     procs.procA = pLangGrpEnumProc;
3357     procs.procW = NULL;
3358     procs.dwFlags = dwFlags;
3359     procs.lParam = lParam;
3360
3361     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3362 }
3363
3364 /******************************************************************************
3365  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
3366  *
3367  * See EnumSystemLanguageGroupsA.
3368  */
3369 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3370                                       DWORD dwFlags, LONG_PTR lParam)
3371 {
3372     ENUMLANGUAGEGROUP_CALLBACKS procs;
3373
3374     TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3375
3376     procs.procA = NULL;
3377     procs.procW = pLangGrpEnumProc;
3378     procs.dwFlags = dwFlags;
3379     procs.lParam = lParam;
3380
3381     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3382 }
3383
3384 /******************************************************************************
3385  *           IsValidLanguageGroup    (KERNEL32.@)
3386  *
3387  * Determine if a language group is supported and/or installed.
3388  *
3389  * PARAMS
3390  *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
3391  *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3392  *
3393  * RETURNS
3394  *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3395  *  FALSE otherwise.
3396  */
3397 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3398 {
3399     static const WCHAR szFormat[] = { '%','x','\0' };
3400     WCHAR szValueName[16], szValue[2];
3401     BOOL bSupported = FALSE, bInstalled = FALSE;
3402     HANDLE hKey;
3403
3404
3405     switch (dwFlags)
3406     {
3407     case LGRPID_INSTALLED:
3408     case LGRPID_SUPPORTED:
3409
3410         hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3411
3412         sprintfW( szValueName, szFormat, lgrpid );
3413
3414         if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3415         {
3416             bSupported = TRUE;
3417
3418             if (szValue[0] == '1')
3419                 bInstalled = TRUE;
3420         }
3421
3422         if (hKey)
3423             NtClose( hKey );
3424
3425         break;
3426     }
3427
3428     if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3429         (dwFlags == LGRPID_INSTALLED && bInstalled))
3430         return TRUE;
3431
3432     return FALSE;
3433 }
3434
3435 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3436 typedef struct
3437 {
3438   LANGGROUPLOCALE_ENUMPROCA procA;
3439   LANGGROUPLOCALE_ENUMPROCW procW;
3440   DWORD    dwFlags;
3441   LGRPID   lgrpid;
3442   LONG_PTR lParam;
3443 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3444
3445 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3446 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3447 {
3448     static const WCHAR szAlternateSortsKeyName[] = {
3449       'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3450     };
3451     WCHAR szNumber[10], szValue[4];
3452     HANDLE hKey;
3453     BOOL bContinue = TRUE, bAlternate = FALSE;
3454     LGRPID lgrpid;
3455     ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
3456
3457     if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3458     {
3459         SetLastError(ERROR_INVALID_PARAMETER);
3460         return FALSE;
3461     }
3462
3463     if (lpProcs->dwFlags)
3464     {
3465         SetLastError(ERROR_INVALID_FLAGS);
3466         return FALSE;
3467     }
3468
3469     hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3470
3471     if (!hKey)
3472         WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3473
3474     while (bContinue)
3475     {
3476         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3477                               szValue, sizeof(szValue) ))
3478         {
3479             lgrpid = strtoulW( szValue, NULL, 16 );
3480
3481             TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3482                    lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3483
3484             if (lgrpid == lpProcs->lgrpid)
3485             {
3486                 LCID lcid;
3487
3488                 lcid = strtoulW( szNumber, NULL, 16 );
3489
3490                 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3491                  * '00000437          ;Georgian'
3492                  * At present we only pass the LCID string.
3493                  */
3494
3495                 if (lpProcs->procW)
3496                     bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3497                 else
3498                 {
3499                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3500
3501                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3502
3503                     bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3504                 }
3505             }
3506
3507             ulIndex++;
3508         }
3509         else
3510         {
3511             /* Finished enumerating this key */
3512             if (!bAlternate)
3513             {
3514                 /* Enumerate alternate sorts also */
3515                 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3516                 bAlternate = TRUE;
3517                 ulIndex = 0;
3518             }
3519             else
3520                 bContinue = FALSE; /* Finished both keys */
3521         }
3522
3523         if (!bContinue)
3524             break;
3525     }
3526
3527     if (hKey)
3528         NtClose( hKey );
3529
3530     return TRUE;
3531 }
3532
3533 /******************************************************************************
3534  *           EnumLanguageGroupLocalesA    (KERNEL32.@)
3535  *
3536  * Call a users function for every locale in a language group available on the system.
3537  *
3538  * PARAMS
3539  *  pLangGrpLcEnumProc [I] Callback function to call for each locale
3540  *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
3541  *  dwFlags            [I] Reserved, set to 0
3542  *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
3543  *
3544  * RETURNS
3545  *  Success: TRUE.
3546  *  Failure: FALSE. Use GetLastError() to determine the cause.
3547  */
3548 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3549                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3550 {
3551     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3552
3553     TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3554
3555     callbacks.procA   = pLangGrpLcEnumProc;
3556     callbacks.procW   = NULL;
3557     callbacks.dwFlags = dwFlags;
3558     callbacks.lgrpid  = lgrpid;
3559     callbacks.lParam  = lParam;
3560
3561     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3562 }
3563
3564 /******************************************************************************
3565  *           EnumLanguageGroupLocalesW    (KERNEL32.@)
3566  *
3567  * See EnumLanguageGroupLocalesA.
3568  */
3569 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3570                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3571 {
3572     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3573
3574     TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3575
3576     callbacks.procA   = NULL;
3577     callbacks.procW   = pLangGrpLcEnumProc;
3578     callbacks.dwFlags = dwFlags;
3579     callbacks.lgrpid  = lgrpid;
3580     callbacks.lParam  = lParam;
3581
3582     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3583 }
3584
3585 /******************************************************************************
3586  *           EnumSystemGeoID    (KERNEL32.@)
3587  *
3588  * Call a users function for every location available on the system.
3589  *
3590  * PARAMS
3591  *  geoclass     [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3592  *  reserved     [I] Reserved, set to 0
3593  *  pGeoEnumProc [I] Callback function to call for each location
3594  *
3595  * RETURNS
3596  *  Success: TRUE.
3597  *  Failure: FALSE. Use GetLastError() to determine the cause.
3598  */
3599 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3600 {
3601     static const WCHAR szCountryCodeValueName[] = {
3602       'C','o','u','n','t','r','y','C','o','d','e','\0'
3603     };
3604     WCHAR szNumber[10];
3605     HANDLE hKey;
3606     ULONG ulIndex = 0;
3607
3608     TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3609
3610     if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3611     {
3612         SetLastError(ERROR_INVALID_PARAMETER);
3613         return FALSE;
3614     }
3615
3616     hKey = NLS_RegOpenKey( 0, szCountryListName );
3617
3618     while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3619     {
3620         BOOL bContinue = TRUE;
3621         DWORD dwGeoId;
3622         HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3623
3624         if (hSubKey)
3625         {
3626             if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3627             {
3628                 TRACE("Got geoid %d\n", dwGeoId);
3629
3630                 if (!pGeoEnumProc( dwGeoId ))
3631                     bContinue = FALSE;
3632             }
3633
3634             NtClose( hSubKey );
3635         }
3636
3637         if (!bContinue)
3638             break;
3639
3640         ulIndex++;
3641     }
3642
3643     if (hKey)
3644         NtClose( hKey );
3645
3646     return TRUE;
3647 }
3648
3649 /******************************************************************************
3650  *           InvalidateNLSCache           (KERNEL32.@)
3651  *
3652  * Invalidate the cache of NLS values.
3653  *
3654  * PARAMS
3655  *  None.
3656  *
3657  * RETURNS
3658  *  Success: TRUE.
3659  *  Failure: FALSE.
3660  */
3661 BOOL WINAPI InvalidateNLSCache(void)
3662 {
3663   FIXME("() stub\n");
3664   return FALSE;
3665 }
3666
3667 /******************************************************************************
3668  *           GetUserGeoID (KERNEL32.@)
3669  */
3670 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3671 {
3672     GEOID ret = GEOID_NOT_AVAILABLE;
3673     static const WCHAR geoW[] = {'G','e','o',0};
3674     static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3675     WCHAR bufferW[40], *end;
3676     DWORD count;
3677     HANDLE hkey, hSubkey = 0;
3678     UNICODE_STRING keyW;
3679     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3680     RtlInitUnicodeString( &keyW, nationW );
3681     count = sizeof(bufferW);
3682
3683     if(!(hkey = create_registry_key())) return ret;
3684
3685     switch( GeoClass ){
3686     case GEOCLASS_NATION:
3687         if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3688         {
3689             if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3690                                 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3691                 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3692         }
3693         break;
3694     case GEOCLASS_REGION:
3695         FIXME("GEOCLASS_REGION not handled yet\n");
3696         break;
3697     }
3698
3699     NtClose(hkey);
3700     if (hSubkey) NtClose(hSubkey);
3701     return ret;
3702 }
3703
3704 /******************************************************************************
3705  *           SetUserGeoID (KERNEL32.@)
3706  */
3707 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3708 {
3709     static const WCHAR geoW[] = {'G','e','o',0};
3710     static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3711     static const WCHAR formatW[] = {'%','i',0};
3712     UNICODE_STRING nameW,keyW;
3713     WCHAR bufferW[10];
3714     OBJECT_ATTRIBUTES attr;
3715     HANDLE hkey;
3716
3717     if(!(hkey = create_registry_key())) return FALSE;
3718
3719     attr.Length = sizeof(attr);
3720     attr.RootDirectory = hkey;
3721     attr.ObjectName = &nameW;
3722     attr.Attributes = 0;
3723     attr.SecurityDescriptor = NULL;
3724     attr.SecurityQualityOfService = NULL;
3725     RtlInitUnicodeString( &nameW, geoW );
3726     RtlInitUnicodeString( &keyW, nationW );
3727
3728     if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3729
3730     {
3731         NtClose(attr.RootDirectory);
3732         return FALSE;
3733     }
3734
3735     sprintfW(bufferW, formatW, GeoID);
3736     NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3737     NtClose(attr.RootDirectory);
3738     NtClose(hkey);
3739     return TRUE;
3740 }
3741
3742 typedef struct
3743 {
3744     union
3745     {
3746         UILANGUAGE_ENUMPROCA procA;
3747         UILANGUAGE_ENUMPROCW procW;
3748     } u;
3749     DWORD flags;
3750     LONG_PTR param;
3751 } ENUM_UILANG_CALLBACK;
3752
3753 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3754                                          LPCSTR name, WORD LangID, LONG_PTR lParam )
3755 {
3756     ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3757     char buf[20];
3758
3759     sprintf(buf, "%08x", (UINT)LangID);
3760     return enum_uilang->u.procA( buf, enum_uilang->param );
3761 }
3762
3763 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3764                                          LPCWSTR name, WORD LangID, LONG_PTR lParam )
3765 {
3766     static const WCHAR formatW[] = {'%','0','8','x',0};
3767     ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3768     WCHAR buf[20];
3769
3770     sprintfW( buf, formatW, (UINT)LangID );
3771     return enum_uilang->u.procW( buf, enum_uilang->param );
3772 }
3773
3774 /******************************************************************************
3775  *           EnumUILanguagesA (KERNEL32.@)
3776  */
3777 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3778 {
3779     ENUM_UILANG_CALLBACK enum_uilang;
3780
3781     TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3782
3783     if(!pUILangEnumProc) {
3784         SetLastError(ERROR_INVALID_PARAMETER);
3785         return FALSE;
3786     }
3787     if(dwFlags) {
3788         SetLastError(ERROR_INVALID_FLAGS);
3789         return FALSE;
3790     }
3791
3792     enum_uilang.u.procA = pUILangEnumProc;
3793     enum_uilang.flags = dwFlags;
3794     enum_uilang.param = lParam;
3795
3796     EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3797                             (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3798                             (LONG_PTR)&enum_uilang);
3799     return TRUE;
3800 }
3801
3802 /******************************************************************************
3803  *           EnumUILanguagesW (KERNEL32.@)
3804  */
3805 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3806 {
3807     ENUM_UILANG_CALLBACK enum_uilang;
3808
3809     TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3810
3811
3812     if(!pUILangEnumProc) {
3813         SetLastError(ERROR_INVALID_PARAMETER);
3814         return FALSE;
3815     }
3816     if(dwFlags) {
3817         SetLastError(ERROR_INVALID_FLAGS);
3818         return FALSE;
3819     }
3820
3821     enum_uilang.u.procW = pUILangEnumProc;
3822     enum_uilang.flags = dwFlags;
3823     enum_uilang.param = lParam;
3824
3825     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3826                             (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3827                             (LONG_PTR)&enum_uilang);
3828     return TRUE;
3829 }
3830
3831 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData, 
3832                 int cchData, LANGID language)
3833 {
3834     FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3835     return 0;
3836 }
3837
3838 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData, 
3839                 int cchData, LANGID language)
3840 {
3841     FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3842     return 0;
3843 }
3844
3845 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
3846 {
3847     LCID userlcid;
3848
3849     TRACE("%p, %d\n", localename,  buffersize);
3850     
3851     userlcid = GetUserDefaultLCID();
3852     return LCIDToLocaleName(userlcid, localename, buffersize, 0);
3853 }
3854
3855 /******************************************************************************
3856  *           NormalizeString (KERNEL32.@)
3857  */
3858 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
3859                            LPWSTR lpDstString, INT cwDstLength)
3860 {
3861     FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
3862     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3863     return 0;
3864 }
3865
3866 /******************************************************************************
3867  *           IsNormalizedString (KERNEL32.@)
3868  */
3869 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
3870 {
3871     FIXME("%x %p %d\n", NormForm, lpString, cwLength);
3872     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3873     return FALSE;
3874 }
3875
3876 /******************************************************************************
3877  *           IdnToAscii (KERNEL32.@)
3878  */
3879 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
3880                       LPWSTR lpASCIICharStr, INT cchASCIIChar)
3881 {
3882     FIXME("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
3883         lpASCIICharStr, cchASCIIChar);
3884     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3885     return 0;
3886 }
3887
3888 /******************************************************************************
3889  *           IdnToNameprepUnicode (KERNEL32.@)
3890  */
3891 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
3892                                 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
3893 {
3894     FIXME("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
3895         lpNameprepCharStr, cchNameprepChar);
3896     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3897     return 0;
3898 }
3899
3900 /******************************************************************************
3901  *           IdnToUnicode (KERNEL32.@)
3902  */
3903 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
3904                         LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
3905 {
3906     FIXME("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
3907         lpUnicodeCharStr, cchUnicodeChar);
3908     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3909     return 0;
3910 }