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 <k32.h>
25 
26 #define NDEBUG
27 #include <debug.h>
28 DEBUG_CHANNEL(nls);
29 
30 #include "lcformat_private.h"
31 #ifdef __REACTOS__
32     #include "japanese.h"
33     #define strcasecmp _stricmp
34 #endif
35 
36 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
37                            LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam);
38 
39 #undef WINVER
40 #define WINVER DLL_EXPORT_VERSION
41 
42 /* From winnls.h */
43 #define LOCALE_NAME_USER_DEFAULT    NULL
44 
45 #define REG_SZ 1
46 extern int wine_fold_string(int flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen);
47 extern int wine_get_sortkey(int flags, const WCHAR *src, int srclen, char *dst, int dstlen);
48 extern int wine_compare_string(int flags, const WCHAR *str1, int len1, const WCHAR *str2, int len2);
49 #ifdef __REACTOS__
50 extern UINT GetLocalisedText(IN UINT uID, IN LPWSTR lpszDest, IN UINT cchDest, IN LANGID lang);
51 #else
52 extern UINT GetLocalisedText(IN UINT uID, IN LPWSTR lpszDest, IN UINT cchDest);
53 #endif
54 #define NLSRC_OFFSET 5000 /* FIXME */
55 
56 extern HMODULE kernel32_handle;
57 
58 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
59                                     LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
60 #define MB_FLAGSMASK (MB_PRECOMPOSED|MB_COMPOSITE|MB_USEGLYPHCHARS|MB_ERR_INVALID_CHARS)
61 #define WC_FLAGSMASK (WC_DISCARDNS|WC_SEPCHARS|WC_DEFAULTCHAR|WC_ERR_INVALID_CHARS|\
62                       WC_COMPOSITECHECK|WC_NO_BEST_FIT_CHARS)
63 
64 /* current code pages */
65 static const union cptable *ansi_cptable;
66 static const union cptable *oem_cptable;
67 static const union cptable *mac_cptable;
68 static const union cptable *unix_cptable;  /* NULL if UTF8 */
69 
70 static const WCHAR szLocaleKeyName[] = {
71     '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
72     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
73     'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
74 };
75 
76 static const WCHAR szLangGroupsKeyName[] = {
77     '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
78     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
79     'C','o','n','t','r','o','l','\\','N','l','s','\\',
80     'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
81 };
82 
83 #if (WINVER >= 0x0600)
84 /* Charset to codepage map, sorted by name. */
85 static const struct charset_entry
86 {
87     const char *charset_name;
88     UINT        codepage;
89 } charset_names[] =
90 {
91     { "BIG5", 950 },
92     { "CP1250", 1250 },
93     { "CP1251", 1251 },
94     { "CP1252", 1252 },
95     { "CP1253", 1253 },
96     { "CP1254", 1254 },
97     { "CP1255", 1255 },
98     { "CP1256", 1256 },
99     { "CP1257", 1257 },
100     { "CP1258", 1258 },
101     { "CP932", 932 },
102     { "CP936", 936 },
103     { "CP949", 949 },
104     { "CP950", 950 },
105     { "EUCJP", 20932 },
106     { "GB2312", 936 },
107     { "IBM037", 37 },
108     { "IBM1026", 1026 },
109     { "IBM424", 424 },
110     { "IBM437", 437 },
111     { "IBM500", 500 },
112     { "IBM850", 850 },
113     { "IBM852", 852 },
114     { "IBM855", 855 },
115     { "IBM857", 857 },
116     { "IBM860", 860 },
117     { "IBM861", 861 },
118     { "IBM862", 862 },
119     { "IBM863", 863 },
120     { "IBM864", 864 },
121     { "IBM865", 865 },
122     { "IBM866", 866 },
123     { "IBM869", 869 },
124     { "IBM874", 874 },
125     { "IBM875", 875 },
126     { "ISO88591", 28591 },
127     { "ISO885910", 28600 },
128     { "ISO885913", 28603 },
129     { "ISO885914", 28604 },
130     { "ISO885915", 28605 },
131     { "ISO885916", 28606 },
132     { "ISO88592", 28592 },
133     { "ISO88593", 28593 },
134     { "ISO88594", 28594 },
135     { "ISO88595", 28595 },
136     { "ISO88596", 28596 },
137     { "ISO88597", 28597 },
138     { "ISO88598", 28598 },
139     { "ISO88599", 28599 },
140     { "KOI8R", 20866 },
141     { "KOI8U", 21866 },
142     { "UTF8", CP_UTF8 }
143 };
144 #endif
145 
146 
147 struct locale_name
148 {
149     WCHAR  win_name[128];   /* Windows name ("en-US") */
150     WCHAR  lang[128];       /* language ("en") (note: buffer contains the other strings too) */
151     WCHAR *country;         /* country ("US") */
152     WCHAR *charset;         /* charset ("UTF-8") for Unix format only */
153     WCHAR *script;          /* script ("Latn") for Windows format only */
154     WCHAR *modifier;        /* modifier or sort order */
155     LCID   lcid;            /* corresponding LCID */
156     int    matches;         /* number of elements matching LCID (0..4) */
157     UINT   codepage;        /* codepage corresponding to charset */
158 };
159 
160 /* locale ids corresponding to the various Unix locale parameters */
161 static LCID lcid_LC_COLLATE;
162 static LCID lcid_LC_CTYPE;
163 static LCID lcid_LC_MESSAGES;
164 static LCID lcid_LC_MONETARY;
165 static LCID lcid_LC_NUMERIC;
166 static LCID lcid_LC_TIME;
167 static LCID lcid_LC_PAPER;
168 static LCID lcid_LC_MEASUREMENT;
169 static LCID lcid_LC_TELEPHONE;
170 
171 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
172 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
173 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
174 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
175 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
176 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
177 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
178 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
179 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
180 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
181 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
182 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
183 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
184 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
185 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
186 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
187 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
188 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
189 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
190 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
191 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
192 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
193 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
194 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
195 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
196 static const WCHAR sListW[] = {'s','L','i','s','t',0};
197 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
198 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
199 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
200 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
201 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
202 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
203 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
204 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
205 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
206 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
207 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
208 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
209 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
210 
211 static struct registry_value
212 {
213     DWORD           lctype;
214     const WCHAR    *name;
215     WCHAR          *cached_value;
216 } registry_values[] =
217 {
218     { LOCALE_ICALENDARTYPE, iCalendarTypeW },
219     { LOCALE_ICURRDIGITS, iCurrDigitsW },
220     { LOCALE_ICURRENCY, iCurrencyW },
221     { LOCALE_IDIGITS, iDigitsW },
222     { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
223     { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
224     { LOCALE_ILZERO, iLZeroW },
225     { LOCALE_IMEASURE, iMeasureW },
226     { LOCALE_INEGCURR, iNegCurrW },
227     { LOCALE_INEGNUMBER, iNegNumberW },
228     { LOCALE_IPAPERSIZE, iPaperSizeW },
229     { LOCALE_ITIME, iTimeW },
230     { LOCALE_S1159, s1159W },
231     { LOCALE_S2359, s2359W },
232     { LOCALE_SCURRENCY, sCurrencyW },
233     { LOCALE_SDATE, sDateW },
234     { LOCALE_SDECIMAL, sDecimalW },
235     { LOCALE_SGROUPING, sGroupingW },
236     { LOCALE_SLIST, sListW },
237     { LOCALE_SLONGDATE, sLongDateW },
238     { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
239     { LOCALE_SMONGROUPING, sMonGroupingW },
240     { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
241     { LOCALE_SNEGATIVESIGN, sNegativeSignW },
242     { LOCALE_SPOSITIVESIGN, sPositiveSignW },
243     { LOCALE_SSHORTDATE, sShortDateW },
244     { LOCALE_STHOUSAND, sThousandW },
245     { LOCALE_STIME, sTimeW },
246     { LOCALE_STIMEFORMAT, sTimeFormatW },
247     { LOCALE_SYEARMONTH, sYearMonthW },
248     /* The following are not listed under MSDN as supported,
249      * but seem to be used and also stored in the registry.
250      */
251     { LOCALE_ICOUNTRY, iCountryW },
252     { LOCALE_IDATE, iDateW },
253     { LOCALE_ILDATE, iLDateW },
254     { LOCALE_ITLZERO, iTLZeroW },
255     { LOCALE_SCOUNTRY, sCountryW },
256     { LOCALE_SABBREVLANGNAME, sLanguageW },
257     /* The following are used in XP and later */
258     { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
259     { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
260     { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
261 };
262 
263 static RTL_CRITICAL_SECTION cache_section = { NULL, -1, 0, 0, 0, 0 };
264 
265 #ifndef __REACTOS__
266 /* Copy Ascii string to Unicode without using codepages */
267 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
268 {
269     while (n > 1 && *src)
270     {
271         *dst++ = (unsigned char)*src++;
272         n--;
273     }
274     if (n) *dst = 0;
275 }
276 
277 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
278 {
279     return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
280 }
281 #endif // !__REACTOS__
282 
283 /***********************************************************************
284  *		get_lcid_codepage
285  *
286  * Retrieve the ANSI codepage for a given locale.
287  */
288 static inline UINT get_lcid_codepage( LCID lcid )
289 {
290     UINT ret;
291     if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
292                          sizeof(ret)/sizeof(WCHAR) )) ret = 0;
293     return ret;
294 }
295 
296 #ifndef __REACTOS__
297 /***********************************************************************
298  *		get_codepage_table
299  *
300  * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
301  */
302 static const union cptable *get_codepage_table( unsigned int codepage )
303 {
304     const union cptable *ret = NULL;
305 
306     assert( ansi_cptable );  /* init must have been done already */
307 
308     switch(codepage)
309     {
310     case CP_ACP:
311         return ansi_cptable;
312     case CP_OEMCP:
313         return oem_cptable;
314     case CP_MACCP:
315         return mac_cptable;
316     case CP_UTF7:
317     case CP_UTF8:
318         break;
319     case CP_THREAD_ACP:
320         if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
321         codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
322         if (!codepage) return ansi_cptable;
323         /* fall through */
324     default:
325         if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
326         if (codepage == oem_cptable->info.codepage) return oem_cptable;
327         if (codepage == mac_cptable->info.codepage) return mac_cptable;
328         ret = wine_cp_get_table( codepage );
329         break;
330     }
331     return ret;
332 }
333 #endif // !__REACTOS__
334 
335 #if (WINVER >= 0x0600)
336 /***********************************************************************
337  *              charset_cmp (internal)
338  */
339 static int charset_cmp( const void *name, const void *entry )
340 {
341     const struct charset_entry *charset = entry;
342     return strcasecmp( name, charset->charset_name );
343 }
344 
345 /***********************************************************************
346  *		find_charset
347  */
348 static UINT find_charset( const WCHAR *name )
349 {
350     const struct charset_entry *entry;
351     char charset_name[16];
352     size_t i, j;
353 
354     /* remove punctuation characters from charset name */
355     for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
356         if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
357     charset_name[j] = 0;
358 
359     entry = bsearch( charset_name, charset_names,
360                      sizeof(charset_names)/sizeof(charset_names[0]),
361                      sizeof(charset_names[0]), charset_cmp );
362     if (entry) return entry->codepage;
363     return 0;
364 }
365 #endif // (WINVER >= 0x0600)
366 
367 static LANGID get_default_sublang( LANGID lang )
368 {
369     switch (lang)
370     {
371     case MAKELANGID( LANG_SPANISH, SUBLANG_NEUTRAL ):
372         return MAKELANGID( LANG_SPANISH, SUBLANG_SPANISH_MODERN );
373     case MAKELANGID( LANG_CHINESE, SUBLANG_NEUTRAL ):
374         return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
375     case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE ):
376         return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
377     case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ):
378     case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_MACAU ):
379         return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG );
380     }
381     if (SUBLANGID( lang ) == SUBLANG_NEUTRAL) lang = MAKELANGID( PRIMARYLANGID(lang), SUBLANG_DEFAULT );
382     return lang;
383 }
384 
385 #if (WINVER >= 0x0600)
386 /***********************************************************************
387  *           find_locale_id_callback
388  */
389 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
390                                               LPCWSTR name, LANGID lang, LPARAM lParam )
391 {
392     struct locale_name *data = (struct locale_name *)lParam;
393     WCHAR buffer[128];
394     int matches = 0;
395     LCID lcid = MAKELCID( lang, SORT_DEFAULT );  /* FIXME: handle sort order */
396 
397     if (PRIMARYLANGID(lang) == LANG_NEUTRAL) return TRUE; /* continue search */
398 
399     /* first check exact name */
400     if (data->win_name[0] &&
401         GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
402                         buffer, sizeof(buffer)/sizeof(WCHAR) ))
403     {
404         if (!strcmpiW( data->win_name, buffer ))
405         {
406             matches = 4;  /* everything matches */
407             goto done;
408         }
409     }
410 
411     if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
412                          buffer, sizeof(buffer)/sizeof(WCHAR) ))
413         return TRUE;
414     if (strcmpiW( buffer, data->lang )) return TRUE;
415     matches++;  /* language name matched */
416 
417     if (data->script)
418     {
419         if (GetLocaleInfoW( lcid, LOCALE_SSCRIPTS | LOCALE_NOUSEROVERRIDE,
420                             buffer, sizeof(buffer)/sizeof(WCHAR) ))
421         {
422             const WCHAR *p = buffer;
423             unsigned int len = strlenW( data->script );
424             while (*p)
425             {
426                 if (!strncmpiW( p, data->script, len ) && (!p[len] || p[len] == ';')) break;
427                 if (!(p = strchrW( p, ';'))) goto done;
428                 p++;
429             }
430             if (!*p) goto done;
431             matches++;  /* script matched */
432         }
433     }
434 
435     if (data->country)
436     {
437         if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
438                             buffer, sizeof(buffer)/sizeof(WCHAR) ))
439         {
440             if (strcmpiW( buffer, data->country )) goto done;
441             matches++;  /* country name matched */
442         }
443     }
444     else  /* match default language */
445     {
446         LANGID def_lang = data->script ? lang : MAKELANGID( PRIMARYLANGID(lang), LANG_NEUTRAL );
447         if (lang == get_default_sublang( def_lang )) matches++;
448     }
449 
450     if (data->codepage)
451     {
452         UINT unix_cp;
453         if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
454                             (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
455         {
456             if (unix_cp == data->codepage) matches++;
457         }
458     }
459 
460     /* FIXME: check sort order */
461 
462 done:
463     if (matches > data->matches)
464     {
465         data->lcid = lcid;
466         data->matches = matches;
467     }
468     return (data->matches < 4);  /* no need to continue for perfect match */
469 }
470 
471 
472 /***********************************************************************
473  *		parse_locale_name
474  *
475  * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
476  * Unix format is: lang[_country][.charset][@modifier]
477  * Windows format is: lang[-script][-country][_modifier]
478  */
479 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
480 {
481     static const WCHAR sepW[] = {'-','_','.','@',0};
482     static const WCHAR winsepW[] = {'-','_',0};
483     static const WCHAR posixW[] = {'P','O','S','I','X',0};
484     static const WCHAR cW[] = {'C',0};
485     static const WCHAR latinW[] = {'l','a','t','i','n',0};
486     static const WCHAR latnW[] = {'-','L','a','t','n',0};
487     WCHAR *p;
488 
489     TRACE("%s\n", debugstr_w(str));
490 
491     name->country = name->charset = name->script = name->modifier = NULL;
492     name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
493     name->matches = 0;
494     name->codepage = 0;
495     name->win_name[0] = 0;
496     lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
497 
498     if (!*name->lang)
499     {
500         name->lcid = LOCALE_INVARIANT;
501         name->matches = 4;
502         return;
503     }
504 
505     if (!(p = strpbrkW( name->lang, sepW )))
506     {
507         if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
508         {
509             name->matches = 4;  /* perfect match for default English lcid */
510             return;
511         }
512         strcpyW( name->win_name, name->lang );
513     }
514     else if (*p == '-')  /* Windows format */
515     {
516         strcpyW( name->win_name, name->lang );
517         *p++ = 0;
518         name->country = p;
519         if ((p = strpbrkW( p, winsepW )) && *p == '-')
520         {
521             *p++ = 0;
522             name->script = name->country;
523             name->country = p;
524             p = strpbrkW( p, winsepW );
525         }
526         if (p)
527         {
528             *p++ = 0;
529             name->modifier = p;
530         }
531         /* second value can be script or country, check length to resolve the ambiguity */
532         if (!name->script && strlenW( name->country ) == 4)
533         {
534             name->script = name->country;
535             name->country = NULL;
536         }
537     }
538     else  /* Unix format */
539     {
540         if (*p == '_')
541         {
542             *p++ = 0;
543             name->country = p;
544             p = strpbrkW( p, sepW + 2 );
545         }
546         if (p && *p == '.')
547         {
548             *p++ = 0;
549             name->charset = p;
550             p = strchrW( p, '@' );
551         }
552         if (p)
553         {
554             *p++ = 0;
555             name->modifier = p;
556         }
557 
558         if (name->charset)
559             name->codepage = find_charset( name->charset );
560 
561         /* rebuild a Windows name if possible */
562 
563         if (name->charset) goto done;  /* can't specify charset in Windows format */
564         if (name->modifier && strcmpW( name->modifier, latinW ))
565             goto done;  /* only Latn script supported for now */
566         strcpyW( name->win_name, name->lang );
567         if (name->modifier) strcatW( name->win_name, latnW );
568         if (name->country)
569         {
570             p = name->win_name + strlenW(name->win_name);
571             *p++ = '-';
572             strcpyW( p, name->country );
573         }
574     }
575 done:
576     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
577                             find_locale_id_callback, (LPARAM)name );
578 }
579 #endif
580 
581 
582 /***********************************************************************
583  *           convert_default_lcid
584  *
585  * Get the default LCID to use for a given lctype in GetLocaleInfo.
586  */
587 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
588 {
589     if (lcid == LOCALE_SYSTEM_DEFAULT ||
590         lcid == LOCALE_USER_DEFAULT ||
591         lcid == LOCALE_NEUTRAL)
592     {
593         LCID default_id = 0;
594 
595         switch(lctype & 0xffff)
596         {
597         case LOCALE_SSORTNAME:
598             default_id = lcid_LC_COLLATE;
599             break;
600 
601         case LOCALE_FONTSIGNATURE:
602         case LOCALE_IDEFAULTANSICODEPAGE:
603         case LOCALE_IDEFAULTCODEPAGE:
604         case LOCALE_IDEFAULTEBCDICCODEPAGE:
605         case LOCALE_IDEFAULTMACCODEPAGE:
606         case LOCALE_IDEFAULTUNIXCODEPAGE:
607             default_id = lcid_LC_CTYPE;
608             break;
609 
610         case LOCALE_ICURRDIGITS:
611         case LOCALE_ICURRENCY:
612         case LOCALE_IINTLCURRDIGITS:
613         case LOCALE_INEGCURR:
614         case LOCALE_INEGSEPBYSPACE:
615         case LOCALE_INEGSIGNPOSN:
616         case LOCALE_INEGSYMPRECEDES:
617         case LOCALE_IPOSSEPBYSPACE:
618         case LOCALE_IPOSSIGNPOSN:
619         case LOCALE_IPOSSYMPRECEDES:
620         case LOCALE_SCURRENCY:
621         case LOCALE_SINTLSYMBOL:
622         case LOCALE_SMONDECIMALSEP:
623         case LOCALE_SMONGROUPING:
624         case LOCALE_SMONTHOUSANDSEP:
625         case LOCALE_SNATIVECURRNAME:
626             default_id = lcid_LC_MONETARY;
627             break;
628 
629         case LOCALE_IDIGITS:
630         case LOCALE_IDIGITSUBSTITUTION:
631         case LOCALE_ILZERO:
632         case LOCALE_INEGNUMBER:
633         case LOCALE_SDECIMAL:
634         case LOCALE_SGROUPING:
635         //case LOCALE_SNAN:
636         case LOCALE_SNATIVEDIGITS:
637         case LOCALE_SNEGATIVESIGN:
638         //case LOCALE_SNEGINFINITY:
639         //case LOCALE_SPOSINFINITY:
640         case LOCALE_SPOSITIVESIGN:
641         case LOCALE_STHOUSAND:
642             default_id = lcid_LC_NUMERIC;
643             break;
644 
645         case LOCALE_ICALENDARTYPE:
646         case LOCALE_ICENTURY:
647         case LOCALE_IDATE:
648         case LOCALE_IDAYLZERO:
649         case LOCALE_IFIRSTDAYOFWEEK:
650         case LOCALE_IFIRSTWEEKOFYEAR:
651         case LOCALE_ILDATE:
652         case LOCALE_IMONLZERO:
653         case LOCALE_IOPTIONALCALENDAR:
654         case LOCALE_ITIME:
655         case LOCALE_ITIMEMARKPOSN:
656         case LOCALE_ITLZERO:
657         case LOCALE_S1159:
658         case LOCALE_S2359:
659         case LOCALE_SABBREVDAYNAME1:
660         case LOCALE_SABBREVDAYNAME2:
661         case LOCALE_SABBREVDAYNAME3:
662         case LOCALE_SABBREVDAYNAME4:
663         case LOCALE_SABBREVDAYNAME5:
664         case LOCALE_SABBREVDAYNAME6:
665         case LOCALE_SABBREVDAYNAME7:
666         case LOCALE_SABBREVMONTHNAME1:
667         case LOCALE_SABBREVMONTHNAME2:
668         case LOCALE_SABBREVMONTHNAME3:
669         case LOCALE_SABBREVMONTHNAME4:
670         case LOCALE_SABBREVMONTHNAME5:
671         case LOCALE_SABBREVMONTHNAME6:
672         case LOCALE_SABBREVMONTHNAME7:
673         case LOCALE_SABBREVMONTHNAME8:
674         case LOCALE_SABBREVMONTHNAME9:
675         case LOCALE_SABBREVMONTHNAME10:
676         case LOCALE_SABBREVMONTHNAME11:
677         case LOCALE_SABBREVMONTHNAME12:
678         case LOCALE_SABBREVMONTHNAME13:
679         case LOCALE_SDATE:
680         case LOCALE_SDAYNAME1:
681         case LOCALE_SDAYNAME2:
682         case LOCALE_SDAYNAME3:
683         case LOCALE_SDAYNAME4:
684         case LOCALE_SDAYNAME5:
685         case LOCALE_SDAYNAME6:
686         case LOCALE_SDAYNAME7:
687         //case LOCALE_SDURATION:
688         case LOCALE_SLONGDATE:
689         case LOCALE_SMONTHNAME1:
690         case LOCALE_SMONTHNAME2:
691         case LOCALE_SMONTHNAME3:
692         case LOCALE_SMONTHNAME4:
693         case LOCALE_SMONTHNAME5:
694         case LOCALE_SMONTHNAME6:
695         case LOCALE_SMONTHNAME7:
696         case LOCALE_SMONTHNAME8:
697         case LOCALE_SMONTHNAME9:
698         case LOCALE_SMONTHNAME10:
699         case LOCALE_SMONTHNAME11:
700         case LOCALE_SMONTHNAME12:
701         case LOCALE_SMONTHNAME13:
702         case LOCALE_SSHORTDATE:
703         //case LOCALE_SSHORTESTDAYNAME1:
704         //case LOCALE_SSHORTESTDAYNAME2:
705         //case LOCALE_SSHORTESTDAYNAME3:
706         //case LOCALE_SSHORTESTDAYNAME4:
707         //case LOCALE_SSHORTESTDAYNAME5:
708         //case LOCALE_SSHORTESTDAYNAME6:
709         //case LOCALE_SSHORTESTDAYNAME7:
710         case LOCALE_STIME:
711         case LOCALE_STIMEFORMAT:
712         case LOCALE_SYEARMONTH:
713             default_id = lcid_LC_TIME;
714             break;
715 
716         case LOCALE_IPAPERSIZE:
717             default_id = lcid_LC_PAPER;
718             break;
719 
720         case LOCALE_IMEASURE:
721             default_id = lcid_LC_MEASUREMENT;
722             break;
723 
724         case LOCALE_ICOUNTRY:
725             default_id = lcid_LC_TELEPHONE;
726             break;
727         }
728         if (default_id) lcid = default_id;
729     }
730     return ConvertDefaultLocale( lcid );
731 }
732 
733 /***********************************************************************
734  *           is_genitive_name_supported
735  *
736  * Determine could LCTYPE basically support genitive name form or not.
737  */
738 static BOOL is_genitive_name_supported( LCTYPE lctype )
739 {
740     switch(lctype & 0xffff)
741     {
742     case LOCALE_SMONTHNAME1:
743     case LOCALE_SMONTHNAME2:
744     case LOCALE_SMONTHNAME3:
745     case LOCALE_SMONTHNAME4:
746     case LOCALE_SMONTHNAME5:
747     case LOCALE_SMONTHNAME6:
748     case LOCALE_SMONTHNAME7:
749     case LOCALE_SMONTHNAME8:
750     case LOCALE_SMONTHNAME9:
751     case LOCALE_SMONTHNAME10:
752     case LOCALE_SMONTHNAME11:
753     case LOCALE_SMONTHNAME12:
754     case LOCALE_SMONTHNAME13:
755          return TRUE;
756     default:
757          return FALSE;
758     }
759 }
760 
761 /***********************************************************************
762  *		create_registry_key
763  *
764  * Create the Control Panel\\International registry key.
765  */
766 static inline HANDLE create_registry_key(void)
767 {
768     static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
769     static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
770     OBJECT_ATTRIBUTES attr;
771     UNICODE_STRING nameW;
772     HANDLE cpl_key, hkey = 0;
773 
774     if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
775 
776     attr.Length = sizeof(attr);
777     attr.RootDirectory = hkey;
778     attr.ObjectName = &nameW;
779     attr.Attributes = 0;
780     attr.SecurityDescriptor = NULL;
781     attr.SecurityQualityOfService = NULL;
782     RtlInitUnicodeString( &nameW, cplW );
783 
784     if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
785     {
786         NtClose( attr.RootDirectory );
787         attr.RootDirectory = cpl_key;
788         RtlInitUnicodeString( &nameW, intlW );
789         if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
790     }
791     NtClose( attr.RootDirectory );
792     return hkey;
793 }
794 
795 
796 #ifndef __REACTOS__
797 /* update the registry settings for a given locale parameter */
798 /* return TRUE if an update was needed */
799 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
800                                     const LCTYPE *values, UINT nb_values )
801 {
802     static const WCHAR formatW[] = { '%','0','8','x',0 };
803     WCHAR bufferW[40];
804     UNICODE_STRING nameW;
805     DWORD count, i;
806 
807     RtlInitUnicodeString( &nameW, name );
808     count = sizeof(bufferW);
809     if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
810     {
811         const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
812         LPCWSTR text = (LPCWSTR)info->Data;
813 
814         if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
815         TRACE( "updating registry, locale %s changed %s -> %08x\n",
816                debugstr_w(name), debugstr_w(text), lcid );
817     }
818     else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
819     sprintfW( bufferW, formatW, lcid );
820     NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
821 
822     for (i = 0; i < nb_values; i++)
823     {
824         GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
825                         sizeof(bufferW)/sizeof(WCHAR) );
826         SetLocaleInfoW( lcid, values[i], bufferW );
827     }
828     return TRUE;
829 }
830 
831 
832 /***********************************************************************
833  *		LOCALE_InitRegistry
834  *
835  * Update registry contents on startup if the user locale has changed.
836  * This simulates the action of the Windows control panel.
837  */
838 void LOCALE_InitRegistry(void)
839 {
840     static const WCHAR acpW[] = {'A','C','P',0};
841     static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
842     static const WCHAR maccpW[] = {'M','A','C','C','P',0};
843     static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
844     static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
845     static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
846     static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
847     static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
848     static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
849     static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
850     static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
851     static const struct
852     {
853         LPCWSTR name;
854         USHORT value;
855     } update_cp_values[] = {
856         { acpW, LOCALE_IDEFAULTANSICODEPAGE },
857         { oemcpW, LOCALE_IDEFAULTCODEPAGE },
858         { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
859     };
860     static const LCTYPE lc_messages_values[] = {
861       LOCALE_SABBREVLANGNAME,
862       LOCALE_SCOUNTRY,
863       LOCALE_SLIST };
864     static const LCTYPE lc_monetary_values[] = {
865       LOCALE_SCURRENCY,
866       LOCALE_ICURRENCY,
867       LOCALE_INEGCURR,
868       LOCALE_ICURRDIGITS,
869       LOCALE_ILZERO,
870       LOCALE_SMONDECIMALSEP,
871       LOCALE_SMONGROUPING,
872       LOCALE_SMONTHOUSANDSEP };
873     static const LCTYPE lc_numeric_values[] = {
874       LOCALE_SDECIMAL,
875       LOCALE_STHOUSAND,
876       LOCALE_IDIGITS,
877       LOCALE_IDIGITSUBSTITUTION,
878       LOCALE_SNATIVEDIGITS,
879       LOCALE_INEGNUMBER,
880       LOCALE_SNEGATIVESIGN,
881       LOCALE_SPOSITIVESIGN,
882       LOCALE_SGROUPING };
883     static const LCTYPE lc_time_values[] = {
884       LOCALE_S1159,
885       LOCALE_S2359,
886       LOCALE_STIME,
887       LOCALE_ITIME,
888       LOCALE_ITLZERO,
889       LOCALE_SSHORTDATE,
890       LOCALE_SLONGDATE,
891       LOCALE_SDATE,
892       LOCALE_ITIMEMARKPOSN,
893       LOCALE_ICALENDARTYPE,
894       LOCALE_IFIRSTDAYOFWEEK,
895       LOCALE_IFIRSTWEEKOFYEAR,
896       LOCALE_STIMEFORMAT,
897       LOCALE_SYEARMONTH,
898       LOCALE_IDATE };
899     static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
900     static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
901     static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
902 
903     UNICODE_STRING nameW;
904     WCHAR bufferW[80];
905     DWORD count, i;
906     HANDLE hkey;
907     LCID lcid = GetUserDefaultLCID();
908 
909     if (!(hkey = create_registry_key()))
910         return;  /* don't do anything if we can't create the registry key */
911 
912     locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
913                             sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
914     locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
915                             sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
916     locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
917                             sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
918     locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
919                             sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
920     locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
921                             sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
922     locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
923                             sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
924     locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
925                             sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
926 
927     if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
928     {
929         static const WCHAR codepageW[] =
930             {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
931              'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
932              'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
933 
934         OBJECT_ATTRIBUTES attr;
935         HANDLE nls_key;
936         DWORD len = 14;
937 
938         RtlInitUnicodeString( &nameW, codepageW );
939         InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
940         while (codepageW[len])
941         {
942             nameW.Length = len * sizeof(WCHAR);
943             if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
944             NtClose( nls_key );
945             len++;
946             while (codepageW[len] && codepageW[len] != '\\') len++;
947         }
948         nameW.Length = len * sizeof(WCHAR);
949         if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
950         {
951             for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
952             {
953                 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
954                                         bufferW, sizeof(bufferW)/sizeof(WCHAR) );
955                 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
956                 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
957             }
958             NtClose( nls_key );
959         }
960     }
961 
962     NtClose( hkey );
963 }
964 
965 
966 #ifdef __APPLE__
967 /***********************************************************************
968  *           get_mac_locale
969  *
970  * Return a locale identifier string reflecting the Mac locale, in a form
971  * that parse_locale_name() will understand.  So, strip out unusual
972  * things like script, variant, etc.  Or, rather, just construct it as
973  * <lang>[_<country>].UTF-8.
974  */
975 static const char* get_mac_locale(void)
976 {
977     static char mac_locale[50];
978 
979     if (!mac_locale[0])
980     {
981         CFLocaleRef locale = CFLocaleCopyCurrent();
982         CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
983         CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
984         CFStringRef locale_string;
985 
986         if (country)
987             locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
988         else
989             locale_string = CFStringCreateCopy(NULL, lang);
990 
991         CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
992         strcat(mac_locale, ".UTF-8");
993 
994         CFRelease(locale);
995         CFRelease(locale_string);
996     }
997 
998     return mac_locale;
999 }
1000 
1001 
1002 /***********************************************************************
1003  *           has_env
1004  */
1005 static BOOL has_env(const char* name)
1006 {
1007     const char* value = getenv( name );
1008     return value && value[0];
1009 }
1010 #endif
1011 
1012 
1013 /***********************************************************************
1014  *           get_locale
1015  *
1016  * Get the locale identifier for a given category.  On most platforms,
1017  * this is just a thin wrapper around setlocale().  On OS X, though, it
1018  * is common for the Mac locale settings to not be supported by the C
1019  * library.  So, we sometimes override the result with the Mac locale.
1020  */
1021 static const char* get_locale(int category, const char* category_name)
1022 {
1023     const char* ret = setlocale(category, NULL);
1024 
1025 #ifdef __ANDROID__
1026     if (!strcmp(ret, "C"))
1027     {
1028         ret = getenv( category_name );
1029         if (!ret || !ret[0]) ret = getenv( "LC_ALL" );
1030         if (!ret || !ret[0]) ret = "C";
1031     }
1032 #endif
1033 
1034 #ifdef __APPLE__
1035     /* If LC_ALL is set, respect it as a user override.
1036        If LC_* is set, respect it as a user override, except if it's LC_CTYPE
1037        and equal to UTF-8.  That's because, when the Mac locale isn't supported
1038        by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
1039        parse_locale_name() doesn't handle that properly, so we override that
1040        with the Mac locale (which uses UTF-8 for the charset, anyway).
1041        Otherwise:
1042        For LC_MESSAGES, we override the C library because the user language
1043        setting is separate from the locale setting on which LANG was based.
1044        If the C library didn't get anything better from LANG than C or POSIX,
1045        override that.  That probably means the Mac locale isn't supported by
1046        the C library. */
1047     if (!has_env( "LC_ALL" ) &&
1048         ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
1049          (!has_env( category_name ) &&
1050           (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
1051     {
1052         const char* override = get_mac_locale();
1053 
1054         if (category == LC_MESSAGES)
1055         {
1056             /* Retrieve the preferred language as chosen in System Preferences. */
1057             static char messages_locale[50];
1058 
1059             if (!messages_locale[0])
1060             {
1061                 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1062                 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1063                 {
1064                     CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1065                     CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1066                     if (components)
1067                     {
1068                         CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1069                         CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1070                         CFLocaleRef locale = NULL;
1071                         CFStringRef locale_string;
1072 
1073                         if (!country)
1074                         {
1075                             locale = CFLocaleCopyCurrent();
1076                             country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1077                         }
1078 
1079                         if (country)
1080                             locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1081                         else
1082                             locale_string = CFStringCreateCopy( NULL, lang );
1083                         CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1084                         strcat( messages_locale, ".UTF-8" );
1085 
1086                         CFRelease( locale_string );
1087                         if (locale) CFRelease( locale );
1088                         CFRelease( components );
1089                     }
1090                 }
1091                 if (preferred_langs)
1092                     CFRelease( preferred_langs );
1093             }
1094 
1095             if (messages_locale[0])
1096                 override = messages_locale;
1097         }
1098 
1099         TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1100         ret = override;
1101     }
1102 #endif
1103 
1104     return ret;
1105 }
1106 
1107 
1108 /***********************************************************************
1109  *           setup_unix_locales
1110  */
1111 static UINT setup_unix_locales(void)
1112 {
1113     struct locale_name locale_name;
1114     WCHAR buffer[128], ctype_buff[128];
1115     const char *locale;
1116     UINT unix_cp = 0;
1117 
1118     if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1119     {
1120         strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
1121         parse_locale_name( ctype_buff, &locale_name );
1122         lcid_LC_CTYPE = locale_name.lcid;
1123         unix_cp = locale_name.codepage;
1124     }
1125     if (!lcid_LC_CTYPE)  /* this one needs a default value */
1126         lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
1127 
1128     TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1129            locale_name.lcid, locale_name.matches, debugstr_a(locale) );
1130 
1131 #define GET_UNIX_LOCALE(cat) do \
1132     if ((locale = get_locale( cat, #cat ))) \
1133     { \
1134         strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
1135         if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1136         else { \
1137             parse_locale_name( buffer, &locale_name );  \
1138             lcid_##cat = locale_name.lcid; \
1139             TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n",        \
1140                    locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1141         } \
1142     } while (0)
1143 
1144     GET_UNIX_LOCALE( LC_COLLATE );
1145     GET_UNIX_LOCALE( LC_MESSAGES );
1146     GET_UNIX_LOCALE( LC_MONETARY );
1147     GET_UNIX_LOCALE( LC_NUMERIC );
1148     GET_UNIX_LOCALE( LC_TIME );
1149 #ifdef LC_PAPER
1150     GET_UNIX_LOCALE( LC_PAPER );
1151 #endif
1152 #ifdef LC_MEASUREMENT
1153     GET_UNIX_LOCALE( LC_MEASUREMENT );
1154 #endif
1155 #ifdef LC_TELEPHONE
1156     GET_UNIX_LOCALE( LC_TELEPHONE );
1157 #endif
1158 
1159 #undef GET_UNIX_LOCALE
1160 
1161     return unix_cp;
1162 }
1163 #endif // !__REACTOS__
1164 
1165 
1166 /***********************************************************************
1167  *		GetUserDefaultLangID (KERNEL32.@)
1168  *
1169  * Get the default language Id for the current user.
1170  *
1171  * PARAMS
1172  *  None.
1173  *
1174  * RETURNS
1175  *  The current LANGID of the default language for the current user.
1176  */
1177 LANGID WINAPI GetUserDefaultLangID(void)
1178 {
1179     return LANGIDFROMLCID(GetUserDefaultLCID());
1180 }
1181 
1182 
1183 /***********************************************************************
1184  *		GetSystemDefaultLangID (KERNEL32.@)
1185  *
1186  * Get the default language Id for the system.
1187  *
1188  * PARAMS
1189  *  None.
1190  *
1191  * RETURNS
1192  *  The current LANGID of the default language for the system.
1193  */
1194 LANGID WINAPI GetSystemDefaultLangID(void)
1195 {
1196     return LANGIDFROMLCID(GetSystemDefaultLCID());
1197 }
1198 
1199 
1200 /***********************************************************************
1201  *		GetUserDefaultLCID (KERNEL32.@)
1202  *
1203  * Get the default locale Id for the current user.
1204  *
1205  * PARAMS
1206  *  None.
1207  *
1208  * RETURNS
1209  *  The current LCID of the default locale for the current user.
1210  */
1211 LCID WINAPI GetUserDefaultLCID(void)
1212 {
1213     LCID lcid;
1214     NtQueryDefaultLocale( TRUE, &lcid );
1215     return lcid;
1216 }
1217 
1218 
1219 /***********************************************************************
1220  *		GetSystemDefaultLCID (KERNEL32.@)
1221  *
1222  * Get the default locale Id for the system.
1223  *
1224  * PARAMS
1225  *  None.
1226  *
1227  * RETURNS
1228  *  The current LCID of the default locale for the system.
1229  */
1230 LCID WINAPI GetSystemDefaultLCID(void)
1231 {
1232     LCID lcid;
1233     NtQueryDefaultLocale( FALSE, &lcid );
1234     return lcid;
1235 }
1236 
1237 #ifndef __REACTOS__
1238 /***********************************************************************
1239  *		GetSystemDefaultLocaleName (KERNEL32.@)
1240  */
1241 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1242 {
1243     LCID lcid = GetSystemDefaultLCID();
1244     return LCIDToLocaleName(lcid, localename, len, 0);
1245 }
1246 
1247 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1248 {
1249     LCTYPE type;
1250     int lsize;
1251 
1252     FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1253 
1254     if (flags & MUI_LANGUAGE_ID)
1255         type = LOCALE_ILANGUAGE;
1256     else
1257         type = LOCALE_SNAME;
1258 
1259     lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1260     if (!lsize)
1261     {
1262         /* keep last error from callee */
1263         return FALSE;
1264     }
1265     lsize++;
1266     if (!*size)
1267     {
1268         *size = lsize;
1269         *count = 1;
1270         return TRUE;
1271     }
1272 
1273     if (lsize > *size)
1274     {
1275         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1276         return FALSE;
1277     }
1278 
1279     if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1280     {
1281         /* keep last error from callee */
1282         return FALSE;
1283     }
1284 
1285     buffer[lsize-1] = 0;
1286     *size = lsize;
1287     *count = 1;
1288     TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1289     return TRUE;
1290 
1291 }
1292 
1293 /***********************************************************************
1294  *             GetSystemPreferredUILanguages (KERNEL32.@)
1295  */
1296 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1297 {
1298     if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1299     {
1300         SetLastError(ERROR_INVALID_PARAMETER);
1301         return FALSE;
1302     }
1303     if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1304     {
1305         SetLastError(ERROR_INVALID_PARAMETER);
1306         return FALSE;
1307     }
1308     if (*size && !buffer)
1309     {
1310         SetLastError(ERROR_INVALID_PARAMETER);
1311         return FALSE;
1312     }
1313 
1314     return get_dummy_preferred_ui_language( flags, count, buffer, size );
1315 }
1316 
1317 /***********************************************************************
1318  *              SetThreadPreferredUILanguages (KERNEL32.@)
1319  */
1320 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1321 {
1322     FIXME( "%u, %p, %p\n", flags, buffer, count );
1323     return TRUE;
1324 }
1325 
1326 /***********************************************************************
1327  *              GetThreadPreferredUILanguages (KERNEL32.@)
1328  */
1329 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1330 {
1331     FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1332     return get_dummy_preferred_ui_language( flags, count, buf, size );
1333 }
1334 
1335 #if (WINVER >= 0x0600)
1336 /******************************************************************************
1337  *             GetUserPreferredUILanguages (KERNEL32.@)
1338  */
1339 BOOL WINAPI GetUserPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1340 {
1341     TRACE( "%u %p %p %p\n", flags, count, buffer, size );
1342 
1343     if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID))
1344     {
1345         SetLastError(ERROR_INVALID_PARAMETER);
1346         return FALSE;
1347     }
1348     if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1349     {
1350         SetLastError(ERROR_INVALID_PARAMETER);
1351         return FALSE;
1352     }
1353     if (*size && !buffer)
1354     {
1355         SetLastError(ERROR_INVALID_PARAMETER);
1356         return FALSE;
1357     }
1358 
1359     return get_dummy_preferred_ui_language( flags, count, buffer, size );
1360 }
1361 #endif // (WINVER >= 0x0600)
1362 #endif // !__REACTOS__
1363 
1364 /***********************************************************************
1365  *		GetUserDefaultUILanguage (KERNEL32.@)
1366  *
1367  * Get the default user interface language Id for the current user.
1368  *
1369  * PARAMS
1370  *  None.
1371  *
1372  * RETURNS
1373  *  The current LANGID of the default UI language for the current user.
1374  */
1375 LANGID WINAPI GetUserDefaultUILanguage(void)
1376 {
1377     LANGID lang;
1378     NtQueryDefaultUILanguage( &lang );
1379     return lang;
1380 }
1381 
1382 
1383 /***********************************************************************
1384  *		GetSystemDefaultUILanguage (KERNEL32.@)
1385  *
1386  * Get the default user interface language Id for the system.
1387  *
1388  * PARAMS
1389  *  None.
1390  *
1391  * RETURNS
1392  *  The current LANGID of the default UI language for the system. This is
1393  *  typically the same language used during the installation process.
1394  */
1395 LANGID WINAPI GetSystemDefaultUILanguage(void)
1396 {
1397     LANGID lang;
1398     NtQueryInstallUILanguage( &lang );
1399     return lang;
1400 }
1401 
1402 #if (WINVER >= 0x0600)
1403 /***********************************************************************
1404  *           LocaleNameToLCID  (KERNEL32.@)
1405  */
1406 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1407 {
1408     struct locale_name locale_name;
1409 
1410     if (flags) FIXME( "unsupported flags %x\n", flags );
1411 
1412     if (name == LOCALE_NAME_USER_DEFAULT)
1413         return GetUserDefaultLCID();
1414 
1415     /* string parsing */
1416     parse_locale_name( name, &locale_name );
1417 
1418     TRACE( "found lcid %x for %s, matches %d\n",
1419            locale_name.lcid, debugstr_w(name), locale_name.matches );
1420 
1421     if (!locale_name.matches)
1422     {
1423         SetLastError(ERROR_INVALID_PARAMETER);
1424         return 0;
1425     }
1426 
1427     if (locale_name.matches == 1)
1428         WARN( "locale %s not recognized, defaulting to %s\n",
1429               debugstr_w(name), debugstr_w(locale_name.lang) );
1430 
1431     return locale_name.lcid;
1432 }
1433 
1434 
1435 /***********************************************************************
1436  *           LCIDToLocaleName  (KERNEL32.@)
1437  */
1438 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1439 {
1440     if (flags) FIXME( "unsupported flags %x\n", flags );
1441 
1442     return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1443 }
1444 #endif
1445 
1446 
1447 /******************************************************************************
1448  *		get_locale_registry_value
1449  *
1450  * Gets the registry value name and cache for a given lctype.
1451  */
1452 static struct registry_value *get_locale_registry_value( DWORD lctype )
1453 {
1454     int i;
1455     for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1456         if (registry_values[i].lctype == lctype)
1457             return &registry_values[i];
1458     return NULL;
1459 }
1460 
1461 
1462 /******************************************************************************
1463  *		get_registry_locale_info
1464  *
1465  * Retrieve user-modified locale info from the registry.
1466  * Return length, 0 on error, -1 if not found.
1467  */
1468 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1469 {
1470     DWORD size;
1471     INT ret;
1472     HANDLE hkey;
1473     NTSTATUS status;
1474     UNICODE_STRING nameW;
1475     KEY_VALUE_PARTIAL_INFORMATION *info;
1476     static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1477 
1478     RtlEnterCriticalSection( &cache_section );
1479 
1480     if (!registry_value->cached_value)
1481     {
1482         if (!(hkey = create_registry_key()))
1483         {
1484             RtlLeaveCriticalSection( &cache_section );
1485             return -1;
1486         }
1487 
1488         RtlInitUnicodeString( &nameW, registry_value->name );
1489         size = info_size + len * sizeof(WCHAR);
1490 
1491         if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1492         {
1493             NtClose( hkey );
1494             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1495             RtlLeaveCriticalSection( &cache_section );
1496             return 0;
1497         }
1498 
1499         status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1500 
1501         /* try again with a bigger buffer when we have to return the correct size */
1502         if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1503         {
1504             KEY_VALUE_PARTIAL_INFORMATION *new_info;
1505             if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1506             {
1507                 info = new_info;
1508                 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1509             }
1510         }
1511 
1512         NtClose( hkey );
1513 
1514         if (!status)
1515         {
1516             INT length = (size - info_size) / sizeof(WCHAR);
1517             LPWSTR cached_value;
1518 
1519             if (!length || ((WCHAR *)&info->Data)[length-1])
1520                 length++;
1521 
1522             cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1523 
1524             if (!cached_value)
1525             {
1526                 HeapFree( GetProcessHeap(), 0, info );
1527                 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1528                 RtlLeaveCriticalSection( &cache_section );
1529                 return 0;
1530             }
1531 
1532             memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1533             cached_value[length-1] = 0;
1534             HeapFree( GetProcessHeap(), 0, info );
1535             registry_value->cached_value = cached_value;
1536         }
1537         else
1538         {
1539             if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1540             {
1541                 ret = (size - info_size) / sizeof(WCHAR);
1542             }
1543             else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1544             {
1545                 ret = -1;
1546             }
1547             else
1548             {
1549                 SetLastError( RtlNtStatusToDosError(status) );
1550                 ret = 0;
1551             }
1552             HeapFree( GetProcessHeap(), 0, info );
1553             RtlLeaveCriticalSection( &cache_section );
1554             return ret;
1555         }
1556     }
1557 
1558     ret = lstrlenW( registry_value->cached_value ) + 1;
1559 
1560     if (buffer)
1561     {
1562         if (ret > len)
1563         {
1564             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1565             ret = 0;
1566         }
1567         else
1568         {
1569             lstrcpyW( buffer, registry_value->cached_value );
1570         }
1571     }
1572 
1573     RtlLeaveCriticalSection( &cache_section );
1574 
1575     return ret;
1576 }
1577 
1578 
1579 /******************************************************************************
1580  *		GetLocaleInfoA (KERNEL32.@)
1581  *
1582  * Get information about an aspect of a locale.
1583  *
1584  * PARAMS
1585  *  lcid   [I] LCID of the locale
1586  *  lctype [I] LCTYPE_ flags from "winnls.h"
1587  *  buffer [O] Destination for the information
1588  *  len    [I] Length of buffer in characters
1589  *
1590  * RETURNS
1591  *  Success: The size of the data requested. If buffer is non-NULL, it is filled
1592  *           with the information.
1593  *  Failure: 0. Use GetLastError() to determine the cause.
1594  *
1595  * NOTES
1596  *  - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1597  *  - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1598  *    which is a bit string.
1599  */
1600 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1601 {
1602     WCHAR *bufferW;
1603     INT lenW, ret;
1604 
1605     TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1606 
1607     if (len < 0 || (len && !buffer))
1608     {
1609         SetLastError( ERROR_INVALID_PARAMETER );
1610         return 0;
1611     }
1612     if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1613          (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1614     {
1615         SetLastError( ERROR_INVALID_FLAGS );
1616         return 0;
1617     }
1618 
1619     if (!len) buffer = NULL;
1620 
1621     if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1622 
1623     if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1624     {
1625         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1626         return 0;
1627     }
1628     if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1629     {
1630         if ((lctype & LOCALE_RETURN_NUMBER) ||
1631             ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1632         {
1633             /* it's not an ASCII string, just bytes */
1634             ret *= sizeof(WCHAR);
1635             if (buffer)
1636             {
1637                 if (ret <= len) memcpy( buffer, bufferW, ret );
1638                 else
1639                 {
1640                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1641                     ret = 0;
1642                 }
1643             }
1644         }
1645         else
1646         {
1647             UINT codepage = CP_ACP;
1648             if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1649             ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1650         }
1651     }
1652     HeapFree( GetProcessHeap(), 0, bufferW );
1653     return ret;
1654 }
1655 
1656 static int get_value_base_by_lctype( LCTYPE lctype )
1657 {
1658     return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1659 }
1660 
1661 /******************************************************************************
1662  *		GetLocaleInfoW (KERNEL32.@)
1663  *
1664  * See GetLocaleInfoA.
1665  */
1666 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1667 {
1668     LANGID lang_id;
1669     HRSRC hrsrc;
1670     HGLOBAL hmem;
1671     INT ret;
1672     UINT lcflags;
1673     const WCHAR *p;
1674     unsigned int i;
1675 
1676     if (len < 0 || (len && !buffer))
1677     {
1678         SetLastError( ERROR_INVALID_PARAMETER );
1679         return 0;
1680     }
1681     if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1682        !is_genitive_name_supported( lctype ))
1683     {
1684         SetLastError( ERROR_INVALID_FLAGS );
1685         return 0;
1686     }
1687 
1688     if (!len) buffer = NULL;
1689 
1690     lcid = convert_default_lcid( lcid, lctype );
1691 
1692     lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1693     lctype &= 0xffff;
1694 
1695     TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1696 
1697     /* first check for overrides in the registry */
1698 
1699     if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1700         lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1701     {
1702         struct registry_value *value = get_locale_registry_value(lctype);
1703 
1704         if (value)
1705         {
1706             if (lcflags & LOCALE_RETURN_NUMBER)
1707             {
1708                 WCHAR tmp[16];
1709                 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1710                 if (ret > 0)
1711                 {
1712                     WCHAR *end;
1713                     UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1714                     if (*end)  /* invalid number */
1715                     {
1716                         SetLastError( ERROR_INVALID_FLAGS );
1717                         return 0;
1718                     }
1719                     ret = sizeof(UINT)/sizeof(WCHAR);
1720                     if (!buffer) return ret;
1721                     if (ret > len)
1722                     {
1723                         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1724                         return 0;
1725                     }
1726                     memcpy( buffer, &number, sizeof(number) );
1727                 }
1728             }
1729             else ret = get_registry_locale_info( value, buffer, len );
1730 
1731             if (ret != -1) return ret;
1732         }
1733     }
1734 
1735     /* now load it from kernel resources */
1736 
1737     lang_id = LANGIDFROMLCID( lcid );
1738 
1739     /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1740     if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL) lang_id = get_default_sublang( lang_id );
1741 
1742     if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1743                                    ULongToPtr((lctype >> 4) + 1), lang_id )))
1744     {
1745         SetLastError( ERROR_INVALID_FLAGS );  /* no such lctype */
1746         return 0;
1747     }
1748     if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1749         return 0;
1750 
1751     p = LockResource( hmem );
1752     for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1753 
1754     if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1755     else if (is_genitive_name_supported( lctype ) && *p)
1756     {
1757         /* genitive form's stored after a null separator from a nominative */
1758         for (i = 1; i <= *p; i++) if (!p[i]) break;
1759 
1760         if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1761         {
1762             ret = *p - i + 1;
1763             p += i;
1764         }
1765         else ret = i;
1766     }
1767     else
1768         ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1769 
1770     if (!buffer) return ret;
1771 
1772     if (ret > len)
1773     {
1774         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1775         return 0;
1776     }
1777 
1778     if (lcflags & LOCALE_RETURN_NUMBER)
1779     {
1780         UINT number;
1781         WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1782         if (!tmp) return 0;
1783         memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1784         tmp[*p] = 0;
1785         number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1786         if (!*end)
1787             memcpy( buffer, &number, sizeof(number) );
1788         else  /* invalid number */
1789         {
1790             SetLastError( ERROR_INVALID_FLAGS );
1791             ret = 0;
1792         }
1793         HeapFree( GetProcessHeap(), 0, tmp );
1794 
1795         TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1796                lcid, lctype, buffer, len, number );
1797     }
1798     else
1799     {
1800         memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1801         if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1802 
1803         TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1804                lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1805     }
1806     return ret;
1807 }
1808 
1809 #if (WINVER >= 0x0600)
1810 /******************************************************************************
1811  *           GetLocaleInfoEx (KERNEL32.@)
1812  */
1813 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1814 {
1815     LCID lcid = LocaleNameToLCID(locale, 0);
1816 
1817     TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1818 
1819     if (!lcid) return 0;
1820 
1821     /* special handling for neutral locale names */
1822     if (locale && strlenW(locale) == 2)
1823     {
1824         switch (info)
1825         {
1826         case LOCALE_SNAME:
1827             if (len && len < 3)
1828             {
1829                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1830                 return 0;
1831             }
1832             if (len) strcpyW(buffer, locale);
1833             return 3;
1834         case LOCALE_SPARENT:
1835             if (len) buffer[0] = 0;
1836             return 1;
1837         }
1838     }
1839 
1840     return GetLocaleInfoW(lcid, info, buffer, len);
1841 }
1842 
1843 BOOL
1844 WINAPI
1845 IsValidLocaleName(
1846   LPCWSTR lpLocaleName
1847 )
1848 {
1849     TRACE( "IsValidLocaleName not implemented (lpLocaleName=%s)\n", debugstr_w(lpLocaleName));
1850     return TRUE;
1851 }
1852 
1853 INT
1854 WINAPI
1855 GetUserDefaultLocaleName(
1856   LPWSTR lpLocaleName,
1857   INT    cchLocaleName
1858 )
1859 {
1860     TRACE( "GetUserDefaultLocaleName not implemented (lpLocaleName=%s, cchLocaleName=%d)\n", debugstr_w(lpLocaleName), cchLocaleName);
1861     return 0;
1862 }
1863 #endif
1864 
1865 /******************************************************************************
1866  *		SetLocaleInfoA	[KERNEL32.@]
1867  *
1868  * Set information about an aspect of a locale.
1869  *
1870  * PARAMS
1871  *  lcid   [I] LCID of the locale
1872  *  lctype [I] LCTYPE_ flags from "winnls.h"
1873  *  data   [I] Information to set
1874  *
1875  * RETURNS
1876  *  Success: TRUE. The information given will be returned by GetLocaleInfoA()
1877  *           whenever it is called without LOCALE_NOUSEROVERRIDE.
1878  *  Failure: FALSE. Use GetLastError() to determine the cause.
1879  *
1880  * NOTES
1881  *  - Values are only be set for the current user locale; the system locale
1882  *  settings cannot be changed.
1883  *  - Any settings changed by this call are lost when the locale is changed by
1884  *  the control panel (in Wine, this happens every time you change LANG).
1885  *  - The native implementation of this function does not check that lcid matches
1886  *  the current user locale, and simply sets the new values. Wine warns you in
1887  *  this case, but behaves the same.
1888  */
1889 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1890 {
1891     UINT codepage = CP_ACP;
1892     WCHAR *strW;
1893     DWORD len;
1894     BOOL ret;
1895 
1896     if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1897 
1898     if (!data)
1899     {
1900         SetLastError( ERROR_INVALID_PARAMETER );
1901         return FALSE;
1902     }
1903     len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1904     if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1905     {
1906         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1907         return FALSE;
1908     }
1909     MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1910     ret = SetLocaleInfoW( lcid, lctype, strW );
1911     HeapFree( GetProcessHeap(), 0, strW );
1912     return ret;
1913 }
1914 
1915 
1916 /******************************************************************************
1917  *		SetLocaleInfoW	(KERNEL32.@)
1918  *
1919  * See SetLocaleInfoA.
1920  */
1921 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1922 {
1923     struct registry_value *value;
1924     static const WCHAR intlW[] = {'i','n','t','l',0 };
1925     UNICODE_STRING valueW;
1926     NTSTATUS status;
1927     HANDLE hkey;
1928 
1929     lctype &= 0xffff;
1930     value = get_locale_registry_value( lctype );
1931 
1932     if (!data || !value)
1933     {
1934         SetLastError( ERROR_INVALID_PARAMETER );
1935         return FALSE;
1936     }
1937 
1938     if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1939     {
1940         SetLastError( ERROR_INVALID_FLAGS );
1941         return FALSE;
1942     }
1943 
1944     TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1945 
1946     /* FIXME: should check that data to set is sane */
1947 
1948     /* FIXME: profile functions should map to registry */
1949     WriteProfileStringW( intlW, value->name, data );
1950 
1951     if (!(hkey = create_registry_key())) return FALSE;
1952     RtlInitUnicodeString( &valueW, value->name );
1953     status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, (PVOID)data, (strlenW(data)+1)*sizeof(WCHAR) );
1954 
1955     RtlEnterCriticalSection( &cache_section );
1956     HeapFree( GetProcessHeap(), 0, value->cached_value );
1957     value->cached_value = NULL;
1958     RtlLeaveCriticalSection( &cache_section );
1959 
1960     if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1961     {
1962       /* Set I-value from S value */
1963       WCHAR *lpD, *lpM, *lpY;
1964       WCHAR szBuff[2];
1965 
1966       lpD = strrchrW(data, 'd');
1967       lpM = strrchrW(data, 'M');
1968       lpY = strrchrW(data, 'y');
1969 
1970       if (lpD <= lpM)
1971       {
1972         szBuff[0] = '1'; /* D-M-Y */
1973       }
1974       else
1975       {
1976         if (lpY <= lpM)
1977           szBuff[0] = '2'; /* Y-M-D */
1978         else
1979           szBuff[0] = '0'; /* M-D-Y */
1980       }
1981 
1982       szBuff[1] = '\0';
1983 
1984       if (lctype == LOCALE_SSHORTDATE)
1985         lctype = LOCALE_IDATE;
1986       else
1987         lctype = LOCALE_ILDATE;
1988 
1989       value = get_locale_registry_value( lctype );
1990 
1991       WriteProfileStringW( intlW, value->name, szBuff );
1992 
1993       RtlInitUnicodeString( &valueW, value->name );
1994       status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1995 
1996       RtlEnterCriticalSection( &cache_section );
1997       HeapFree( GetProcessHeap(), 0, value->cached_value );
1998       value->cached_value = NULL;
1999       RtlLeaveCriticalSection( &cache_section );
2000     }
2001 
2002     NtClose( hkey );
2003 
2004     if (status) SetLastError( RtlNtStatusToDosError(status) );
2005     return !status;
2006 }
2007 
2008 
2009 #ifndef __REACTOS__
2010 /******************************************************************************
2011  *              GetACP   (KERNEL32.@)
2012  *
2013  * Get the current Ansi code page Id for the system.
2014  *
2015  * PARAMS
2016  *  None.
2017  *
2018  * RETURNS
2019  *    The current Ansi code page identifier for the system.
2020  */
2021 UINT WINAPI GetACP(void)
2022 {
2023     assert( ansi_cptable );
2024     return ansi_cptable->info.codepage;
2025 }
2026 
2027 
2028 /******************************************************************************
2029  *              SetCPGlobal   (KERNEL32.@)
2030  *
2031  * Set the current Ansi code page Id for the system.
2032  *
2033  * PARAMS
2034  *    acp [I] code page ID to be the new ACP.
2035  *
2036  * RETURNS
2037  *    The previous ACP.
2038  */
2039 UINT WINAPI SetCPGlobal( UINT acp )
2040 {
2041     UINT ret = GetACP();
2042     const union cptable *new_cptable = wine_cp_get_table( acp );
2043 
2044     if (new_cptable) ansi_cptable = new_cptable;
2045     return ret;
2046 }
2047 
2048 
2049 /***********************************************************************
2050  *              GetOEMCP   (KERNEL32.@)
2051  *
2052  * Get the current OEM code page Id for the system.
2053  *
2054  * PARAMS
2055  *  None.
2056  *
2057  * RETURNS
2058  *    The current OEM code page identifier for the system.
2059  */
2060 UINT WINAPI GetOEMCP(void)
2061 {
2062     assert( oem_cptable );
2063     return oem_cptable->info.codepage;
2064 }
2065 
2066 
2067 /***********************************************************************
2068  *           IsValidCodePage   (KERNEL32.@)
2069  *
2070  * Determine if a given code page identifier is valid.
2071  *
2072  * PARAMS
2073  *  codepage [I] Code page Id to verify.
2074  *
2075  * RETURNS
2076  *  TRUE, If codepage is valid and available on the system,
2077  *  FALSE otherwise.
2078  */
2079 BOOL WINAPI IsValidCodePage( UINT codepage )
2080 {
2081     switch(codepage) {
2082     case CP_UTF7:
2083     case CP_UTF8:
2084         return TRUE;
2085     default:
2086         return wine_cp_get_table( codepage ) != NULL;
2087     }
2088 }
2089 
2090 
2091 /***********************************************************************
2092  *           IsDBCSLeadByteEx   (KERNEL32.@)
2093  *
2094  * Determine if a character is a lead byte in a given code page.
2095  *
2096  * PARAMS
2097  *  codepage [I] Code page for the test.
2098  *  testchar [I] Character to test
2099  *
2100  * RETURNS
2101  *  TRUE, if testchar is a lead byte in codepage,
2102  *  FALSE otherwise.
2103  */
2104 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
2105 {
2106     const union cptable *table = get_codepage_table( codepage );
2107     return table && wine_is_dbcs_leadbyte( table, testchar );
2108 }
2109 
2110 
2111 /***********************************************************************
2112  *           IsDBCSLeadByte   (KERNEL32.@)
2113  *           IsDBCSLeadByte   (KERNEL.207)
2114  *
2115  * Determine if a character is a lead byte.
2116  *
2117  * PARAMS
2118  *  testchar [I] Character to test
2119  *
2120  * RETURNS
2121  *  TRUE, if testchar is a lead byte in the ANSI code page,
2122  *  FALSE otherwise.
2123  */
2124 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2125 {
2126     if (!ansi_cptable) return FALSE;
2127     return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2128 }
2129 
2130 
2131 /***********************************************************************
2132  *           GetCPInfo   (KERNEL32.@)
2133  *
2134  * Get information about a code page.
2135  *
2136  * PARAMS
2137  *  codepage [I] Code page number
2138  *  cpinfo   [O] Destination for code page information
2139  *
2140  * RETURNS
2141  *  Success: TRUE. cpinfo is updated with the information about codepage.
2142  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2143  */
2144 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2145 {
2146     const union cptable *table;
2147 
2148     if (!cpinfo)
2149     {
2150         SetLastError( ERROR_INVALID_PARAMETER );
2151         return FALSE;
2152     }
2153 
2154     if (!(table = get_codepage_table( codepage )))
2155     {
2156         switch(codepage)
2157         {
2158             case CP_UTF7:
2159             case CP_UTF8:
2160                 cpinfo->DefaultChar[0] = 0x3f;
2161                 cpinfo->DefaultChar[1] = 0;
2162                 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2163                 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2164                 return TRUE;
2165         }
2166 
2167         SetLastError( ERROR_INVALID_PARAMETER );
2168         return FALSE;
2169     }
2170     if (table->info.def_char & 0xff00)
2171     {
2172         cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2173         cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2174     }
2175     else
2176     {
2177         cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2178         cpinfo->DefaultChar[1] = 0;
2179     }
2180     if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2181         memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2182     else
2183         cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2184 
2185     return TRUE;
2186 }
2187 
2188 /***********************************************************************
2189  *           GetCPInfoExA   (KERNEL32.@)
2190  *
2191  * Get extended information about a code page.
2192  *
2193  * PARAMS
2194  *  codepage [I] Code page number
2195  *  dwFlags  [I] Reserved, must to 0.
2196  *  cpinfo   [O] Destination for code page information
2197  *
2198  * RETURNS
2199  *  Success: TRUE. cpinfo is updated with the information about codepage.
2200  *  Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2201  */
2202 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2203 {
2204     CPINFOEXW cpinfoW;
2205 
2206     if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2207       return FALSE;
2208 
2209     /* the layout is the same except for CodePageName */
2210     memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2211     WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2212     return TRUE;
2213 }
2214 
2215 /***********************************************************************
2216  *           GetCPInfoExW   (KERNEL32.@)
2217  *
2218  * Unicode version of GetCPInfoExA.
2219  */
2220 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2221 {
2222     if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2223       return FALSE;
2224 
2225     switch(codepage)
2226     {
2227         case CP_UTF7:
2228         {
2229             static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2230 
2231             cpinfo->CodePage = CP_UTF7;
2232             cpinfo->UnicodeDefaultChar = 0x3f;
2233             strcpyW(cpinfo->CodePageName, utf7);
2234             break;
2235         }
2236 
2237         case CP_UTF8:
2238         {
2239             static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2240 
2241             cpinfo->CodePage = CP_UTF8;
2242             cpinfo->UnicodeDefaultChar = 0x3f;
2243             strcpyW(cpinfo->CodePageName, utf8);
2244             break;
2245         }
2246 
2247         default:
2248         {
2249             const union cptable *table = get_codepage_table( codepage );
2250 
2251             cpinfo->CodePage = table->info.codepage;
2252             cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2253             MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2254                                  sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2255             break;
2256         }
2257     }
2258     return TRUE;
2259 }
2260 
2261 /***********************************************************************
2262  *              EnumSystemCodePagesA   (KERNEL32.@)
2263  *
2264  * Call a user defined function for every code page installed on the system.
2265  *
2266  * PARAMS
2267  *   lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2268  *   flags            [I] Reserved, set to 0.
2269  *
2270  * RETURNS
2271  *  TRUE, If all code pages have been enumerated, or
2272  *  FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2273  */
2274 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2275 {
2276     const union cptable *table;
2277     char buffer[10];
2278     int index = 0;
2279 
2280     for (;;)
2281     {
2282         if (!(table = wine_cp_enum_table( index++ ))) break;
2283         sprintf( buffer, "%d", table->info.codepage );
2284         if (!lpfnCodePageEnum( buffer )) break;
2285     }
2286     return TRUE;
2287 }
2288 
2289 
2290 /***********************************************************************
2291  *              EnumSystemCodePagesW   (KERNEL32.@)
2292  *
2293  * See EnumSystemCodePagesA.
2294  */
2295 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2296 {
2297     const union cptable *table;
2298     WCHAR buffer[10], *p;
2299     int page, index = 0;
2300 
2301     for (;;)
2302     {
2303         if (!(table = wine_cp_enum_table( index++ ))) break;
2304         p = buffer + sizeof(buffer)/sizeof(WCHAR);
2305         *--p = 0;
2306         page = table->info.codepage;
2307         do
2308         {
2309             *--p = '0' + (page % 10);
2310             page /= 10;
2311         } while( page );
2312         if (!lpfnCodePageEnum( p )) break;
2313     }
2314     return TRUE;
2315 }
2316 
2317 
2318 /***********************************************************************
2319  *              utf7_write_w
2320  *
2321  * Helper for utf7_mbstowcs
2322  *
2323  * RETURNS
2324  *   TRUE on success, FALSE on error
2325  */
2326 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2327 {
2328     if (dstlen > 0)
2329     {
2330         if (*index >= dstlen)
2331             return FALSE;
2332 
2333         dst[*index] = character;
2334     }
2335 
2336     (*index)++;
2337 
2338     return TRUE;
2339 }
2340 
2341 /***********************************************************************
2342  *              utf7_mbstowcs
2343  *
2344  * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2345  *
2346  * RETURNS
2347  *   On success, the number of characters written
2348  *   On dst buffer overflow, -1
2349  */
2350 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2351 {
2352     static const signed char base64_decoding_table[] =
2353     {
2354         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2355         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2356         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2357         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2358         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2359         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2360         -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2361         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1  /* 0x70-0x7F */
2362     };
2363 
2364     const char *source_end = src + srclen;
2365     int dest_index = 0;
2366 
2367     DWORD byte_pair = 0;
2368     short offset = 0;
2369 
2370     while (src < source_end)
2371     {
2372         if (*src == '+')
2373         {
2374             src++;
2375             if (src >= source_end)
2376                 break;
2377 
2378             if (*src == '-')
2379             {
2380                 /* just a plus sign escaped as +- */
2381                 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2382                     return -1;
2383                 src++;
2384                 continue;
2385             }
2386 
2387             do
2388             {
2389                 signed char sextet = *src;
2390                 if (sextet == '-')
2391                 {
2392                     /* skip over the dash and end base64 decoding
2393                      * the current, unfinished byte pair is discarded */
2394                     src++;
2395                     offset = 0;
2396                     break;
2397                 }
2398                 if (sextet < 0)
2399                 {
2400                     /* the next character of src is < 0 and therefore not part of a base64 sequence
2401                      * the current, unfinished byte pair is NOT discarded in this case
2402                      * this is probably a bug in Windows */
2403                     break;
2404                 }
2405 
2406                 sextet = base64_decoding_table[sextet];
2407                 if (sextet == -1)
2408                 {
2409                     /* -1 means that the next character of src is not part of a base64 sequence
2410                      * in other words, all sextets in this base64 sequence have been processed
2411                      * the current, unfinished byte pair is discarded */
2412                     offset = 0;
2413                     break;
2414                 }
2415 
2416                 byte_pair = (byte_pair << 6) | sextet;
2417                 offset += 6;
2418 
2419                 if (offset >= 16)
2420                 {
2421                     /* this byte pair is done */
2422                     if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2423                         return -1;
2424                     offset -= 16;
2425                 }
2426 
2427                 src++;
2428             }
2429             while (src < source_end);
2430         }
2431         else
2432         {
2433             /* we have to convert to unsigned char in case *src < 0 */
2434             if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2435                 return -1;
2436             src++;
2437         }
2438     }
2439 
2440     return dest_index;
2441 }
2442 
2443 /***********************************************************************
2444  *              MultiByteToWideChar   (KERNEL32.@)
2445  *
2446  * Convert a multibyte character string into a Unicode string.
2447  *
2448  * PARAMS
2449  *   page   [I] Codepage character set to convert from
2450  *   flags  [I] Character mapping flags
2451  *   src    [I] Source string buffer
2452  *   srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2453  *   dst    [O] Destination buffer
2454  *   dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2455  *
2456  * RETURNS
2457  *   Success: If dstlen > 0, the number of characters written to dst.
2458  *            If dstlen == 0, the number of characters needed to perform the
2459  *            conversion. In both cases the count includes the terminating NUL.
2460  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2461  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2462  *            and dstlen != 0; ERROR_INVALID_PARAMETER,  if an invalid parameter
2463  *            is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2464  *            possible for src.
2465  */
2466 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2467                                 LPWSTR dst, INT dstlen )
2468 {
2469     const union cptable *table;
2470     int ret;
2471 
2472     if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2473     {
2474         SetLastError( ERROR_INVALID_PARAMETER );
2475         return 0;
2476     }
2477 
2478     if (srclen < 0) srclen = strlen(src) + 1;
2479 
2480     switch(page)
2481     {
2482     case CP_SYMBOL:
2483         if (flags)
2484         {
2485             SetLastError( ERROR_INVALID_FLAGS );
2486             return 0;
2487         }
2488         ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2489         break;
2490     case CP_UTF7:
2491         if (flags)
2492         {
2493             SetLastError( ERROR_INVALID_FLAGS );
2494             return 0;
2495         }
2496         ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2497         break;
2498     case CP_UNIXCP:
2499         if (unix_cptable)
2500         {
2501             ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2502             break;
2503         }
2504 #ifdef __APPLE__
2505         flags |= MB_COMPOSITE;  /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2506 #endif
2507         /* fall through */
2508     case CP_UTF8:
2509         if (flags & ~MB_FLAGSMASK)
2510         {
2511             SetLastError( ERROR_INVALID_FLAGS );
2512             return 0;
2513         }
2514         ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2515         break;
2516     default:
2517         if (!(table = get_codepage_table( page )))
2518         {
2519             SetLastError( ERROR_INVALID_PARAMETER );
2520             return 0;
2521         }
2522         if (flags & ~MB_FLAGSMASK)
2523         {
2524             SetLastError( ERROR_INVALID_FLAGS );
2525             return 0;
2526         }
2527         ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2528         break;
2529     }
2530 
2531     if (ret < 0)
2532     {
2533         switch(ret)
2534         {
2535         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2536         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2537         }
2538         ret = 0;
2539     }
2540     TRACE("cp %d %s -> %s, ret = %d\n",
2541           page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2542     return ret;
2543 }
2544 
2545 
2546 /***********************************************************************
2547  *              utf7_can_directly_encode
2548  *
2549  * Helper for utf7_wcstombs
2550  */
2551 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2552 {
2553     static const BOOL directly_encodable_table[] =
2554     {
2555         1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2556         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2557         1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2558         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2559         0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2560         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2561         0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2562         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1                 /* 0x70 - 0x7A */
2563     };
2564 
2565     return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2566 }
2567 
2568 /***********************************************************************
2569  *              utf7_write_c
2570  *
2571  * Helper for utf7_wcstombs
2572  *
2573  * RETURNS
2574  *   TRUE on success, FALSE on error
2575  */
2576 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2577 {
2578     if (dstlen > 0)
2579     {
2580         if (*index >= dstlen)
2581             return FALSE;
2582 
2583         dst[*index] = character;
2584     }
2585 
2586     (*index)++;
2587 
2588     return TRUE;
2589 }
2590 
2591 /***********************************************************************
2592  *              utf7_wcstombs
2593  *
2594  * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2595  *
2596  * RETURNS
2597  *   On success, the number of characters written
2598  *   On dst buffer overflow, -1
2599  */
2600 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2601 {
2602     static const char base64_encoding_table[] =
2603         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2604 
2605     const WCHAR *source_end = src + srclen;
2606     int dest_index = 0;
2607 
2608     while (src < source_end)
2609     {
2610         if (*src == '+')
2611         {
2612             if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2613                 return -1;
2614             if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2615                 return -1;
2616             src++;
2617         }
2618         else if (utf7_can_directly_encode(*src))
2619         {
2620             if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2621                 return -1;
2622             src++;
2623         }
2624         else
2625         {
2626             unsigned int offset = 0;
2627             DWORD byte_pair = 0;
2628 
2629             if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2630                 return -1;
2631 
2632             while (src < source_end && !utf7_can_directly_encode(*src))
2633             {
2634                 byte_pair = (byte_pair << 16) | *src;
2635                 offset += 16;
2636                 while (offset >= 6)
2637                 {
2638                     if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2639                         return -1;
2640                     offset -= 6;
2641                 }
2642                 src++;
2643             }
2644 
2645             if (offset)
2646             {
2647                 /* Windows won't create a padded base64 character if there's no room for the - sign
2648                  * as well ; this is probably a bug in Windows */
2649                 if (dstlen > 0 && dest_index + 1 >= dstlen)
2650                     return -1;
2651 
2652                 byte_pair <<= (6 - offset);
2653                 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2654                     return -1;
2655             }
2656 
2657             /* Windows always explicitly terminates the base64 sequence
2658                even though RFC 2152 (page 3, rule 2) does not require this */
2659             if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2660                 return -1;
2661         }
2662     }
2663 
2664     return dest_index;
2665 }
2666 
2667 /***********************************************************************
2668  *              WideCharToMultiByte   (KERNEL32.@)
2669  *
2670  * Convert a Unicode character string into a multibyte string.
2671  *
2672  * PARAMS
2673  *   page    [I] Code page character set to convert to
2674  *   flags   [I] Mapping Flags (MB_ constants from "winnls.h").
2675  *   src     [I] Source string buffer
2676  *   srclen  [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2677  *   dst     [O] Destination buffer
2678  *   dstlen  [I] Length of dst (in bytes), or 0 to compute the required length
2679  *   defchar [I] Default character to use for conversion if no exact
2680  *		    conversion can be made
2681  *   used    [O] Set if default character was used in the conversion
2682  *
2683  * RETURNS
2684  *   Success: If dstlen > 0, the number of characters written to dst.
2685  *            If dstlen == 0, number of characters needed to perform the
2686  *            conversion. In both cases the count includes the terminating NUL.
2687  *   Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2688  *            ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2689  *            and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2690  *            parameter was given.
2691  */
2692 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2693                                 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2694 {
2695     const union cptable *table;
2696     int ret, used_tmp;
2697 
2698     if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2699     {
2700         SetLastError( ERROR_INVALID_PARAMETER );
2701         return 0;
2702     }
2703 
2704     if (srclen < 0) srclen = strlenW(src) + 1;
2705 
2706     switch(page)
2707     {
2708     case CP_SYMBOL:
2709         /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2710         if (flags)
2711         {
2712             SetLastError( ERROR_INVALID_FLAGS );
2713             return 0;
2714         }
2715         if (defchar || used)
2716         {
2717             SetLastError( ERROR_INVALID_PARAMETER );
2718             return 0;
2719         }
2720         ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2721         break;
2722     case CP_UTF7:
2723         /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2724         if (defchar || used)
2725         {
2726             SetLastError( ERROR_INVALID_PARAMETER );
2727             return 0;
2728         }
2729         if (flags)
2730         {
2731             SetLastError( ERROR_INVALID_FLAGS );
2732             return 0;
2733         }
2734         ret = utf7_wcstombs( src, srclen, dst, dstlen );
2735         break;
2736     case CP_UNIXCP:
2737         if (unix_cptable)
2738         {
2739             ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2740                                     defchar, used ? &used_tmp : NULL );
2741             break;
2742         }
2743         /* fall through */
2744     case CP_UTF8:
2745         if (defchar || used)
2746         {
2747             SetLastError( ERROR_INVALID_PARAMETER );
2748             return 0;
2749         }
2750         if (flags & ~WC_FLAGSMASK)
2751         {
2752             SetLastError( ERROR_INVALID_FLAGS );
2753             return 0;
2754         }
2755         ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2756         break;
2757     default:
2758         if (!(table = get_codepage_table( page )))
2759         {
2760             SetLastError( ERROR_INVALID_PARAMETER );
2761             return 0;
2762         }
2763         if (flags & ~WC_FLAGSMASK)
2764         {
2765             SetLastError( ERROR_INVALID_FLAGS );
2766             return 0;
2767         }
2768         ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2769                                 defchar, used ? &used_tmp : NULL );
2770         if (used) *used = used_tmp;
2771         break;
2772     }
2773 
2774     if (ret < 0)
2775     {
2776         switch(ret)
2777         {
2778         case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2779         case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2780         }
2781         ret = 0;
2782     }
2783     TRACE("cp %d %s -> %s, ret = %d\n",
2784           page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2785     return ret;
2786 }
2787 #endif // !__REACTOS__
2788 
2789 
2790 /***********************************************************************
2791  *           GetThreadLocale    (KERNEL32.@)
2792  *
2793  * Get the current threads locale.
2794  *
2795  * PARAMS
2796  *  None.
2797  *
2798  * RETURNS
2799  *  The LCID currently associated with the calling thread.
2800  */
2801 LCID WINAPI GetThreadLocale(void)
2802 {
2803     LCID ret = NtCurrentTeb()->CurrentLocale;
2804     if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2805     return ret;
2806 }
2807 
2808 /**********************************************************************
2809  *           SetThreadLocale    (KERNEL32.@)
2810  *
2811  * Set the current threads locale.
2812  *
2813  * PARAMS
2814  *  lcid [I] LCID of the locale to set
2815  *
2816  * RETURNS
2817  *  Success: TRUE. The threads locale is set to lcid.
2818  *  Failure: FALSE. Use GetLastError() to determine the cause.
2819  */
2820 BOOL WINAPI SetThreadLocale( LCID lcid )
2821 {
2822     TRACE("(0x%04X)\n", lcid);
2823 
2824     lcid = ConvertDefaultLocale(lcid);
2825 
2826     if (lcid != GetThreadLocale())
2827     {
2828         if (!IsValidLocale(lcid, LCID_SUPPORTED))
2829         {
2830             SetLastError(ERROR_INVALID_PARAMETER);
2831             return FALSE;
2832         }
2833 
2834         NtCurrentTeb()->CurrentLocale = lcid;
2835     }
2836     return TRUE;
2837 }
2838 
2839 #ifndef __REACTOS__
2840 /**********************************************************************
2841  *           SetThreadUILanguage    (KERNEL32.@)
2842  *
2843  * Set the current threads UI language.
2844  *
2845  * PARAMS
2846  *  langid [I] LANGID of the language to set, or 0 to use
2847  *             the available language which is best supported
2848  *             for console applications
2849  *
2850  * RETURNS
2851  *  Success: The return value is the same as the input value.
2852  *  Failure: The return value differs from the input value.
2853  *           Use GetLastError() to determine the cause.
2854  */
2855 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2856 {
2857     TRACE("(0x%04x) stub - returning success\n", langid);
2858     return langid;
2859 }
2860 #endif // !__REACTOS__
2861 
2862 /******************************************************************************
2863  *		ConvertDefaultLocale (KERNEL32.@)
2864  *
2865  * Convert a default locale identifier into a real identifier.
2866  *
2867  * PARAMS
2868  *  lcid [I] LCID identifier of the locale to convert
2869  *
2870  * RETURNS
2871  *  lcid unchanged, if not a default locale or its sublanguage is
2872  *   not SUBLANG_NEUTRAL.
2873  *  GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2874  *  GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2875  *  Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2876  */
2877 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2878 {
2879     LANGID langid;
2880 
2881     switch (lcid)
2882     {
2883     case LOCALE_INVARIANT:
2884         /* keep as-is */
2885         break;
2886     case LOCALE_SYSTEM_DEFAULT:
2887         lcid = GetSystemDefaultLCID();
2888         break;
2889     case LOCALE_USER_DEFAULT:
2890     case LOCALE_NEUTRAL:
2891         lcid = GetUserDefaultLCID();
2892         break;
2893     default:
2894         /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2895         langid = LANGIDFROMLCID(lcid);
2896         if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2897         {
2898           langid = get_default_sublang( langid );
2899           lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2900         }
2901     }
2902     return lcid;
2903 }
2904 
2905 
2906 /******************************************************************************
2907  *           IsValidLocale   (KERNEL32.@)
2908  *
2909  * Determine if a locale is valid.
2910  *
2911  * PARAMS
2912  *  lcid  [I] LCID of the locale to check
2913  *  flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2914  *
2915  * RETURNS
2916  *  TRUE,  if lcid is valid,
2917  *  FALSE, otherwise.
2918  *
2919  * NOTES
2920  *  Wine does not currently make the distinction between supported and installed. All
2921  *  languages supported are installed by default.
2922  */
2923 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2924 {
2925     /* check if language is registered in the kernel32 resources */
2926     return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2927                             (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2928 }
2929 
2930 #ifndef __REACTOS__
2931 /******************************************************************************
2932  *           IsValidLocaleName   (KERNEL32.@)
2933  */
2934 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2935 {
2936     struct locale_name locale_name;
2937 
2938     if (!locale)
2939         return FALSE;
2940 
2941     /* string parsing */
2942     parse_locale_name( locale, &locale_name );
2943 
2944     TRACE( "found lcid %x for %s, matches %d\n",
2945            locale_name.lcid, debugstr_w(locale), locale_name.matches );
2946 
2947     return locale_name.matches > 0;
2948 }
2949 #endif // !__REACTOS__
2950 
2951 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2952                                        LPCSTR name, WORD LangID, LONG_PTR lParam )
2953 {
2954     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2955     char buf[20];
2956 
2957     sprintf(buf, "%08x", (UINT)LangID);
2958     return lpfnLocaleEnum( buf );
2959 }
2960 
2961 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2962                                        LPCWSTR name, WORD LangID, LONG_PTR lParam )
2963 {
2964     static const WCHAR formatW[] = {'%','0','8','x',0};
2965     LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2966     WCHAR buf[20];
2967     sprintfW( buf, formatW, (UINT)LangID );
2968     return lpfnLocaleEnum( buf );
2969 }
2970 
2971 /******************************************************************************
2972  *           EnumSystemLocalesA  (KERNEL32.@)
2973  *
2974  * Call a users function for each locale available on the system.
2975  *
2976  * PARAMS
2977  *  lpfnLocaleEnum [I] Callback function to call for each locale
2978  *  dwFlags        [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2979  *
2980  * RETURNS
2981  *  Success: TRUE.
2982  *  Failure: FALSE. Use GetLastError() to determine the cause.
2983  */
2984 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2985 {
2986     TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2987     EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2988                             (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2989                             (LONG_PTR)lpfnLocaleEnum);
2990     return TRUE;
2991 }
2992 
2993 
2994 /******************************************************************************
2995  *           EnumSystemLocalesW  (KERNEL32.@)
2996  *
2997  * See EnumSystemLocalesA.
2998  */
2999 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
3000 {
3001     TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
3002     EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
3003                             (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
3004                             (LONG_PTR)lpfnLocaleEnum);
3005     return TRUE;
3006 }
3007 
3008 
3009 struct enum_locale_ex_data
3010 {
3011     LOCALE_ENUMPROCEX proc;
3012     DWORD             flags;
3013     LPARAM            lparam;
3014 };
3015 
3016 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
3017                                           LPCWSTR name, WORD lang, LONG_PTR lparam )
3018 {
3019     struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
3020     WCHAR buffer[256];
3021     DWORD neutral;
3022     unsigned int flags;
3023 
3024     GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
3025                     buffer, sizeof(buffer) / sizeof(WCHAR) );
3026     if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
3027                          LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
3028                          (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
3029         neutral = 0;
3030     flags = LOCALE_WINDOWS;
3031     flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
3032     if (data->flags && !(data->flags & flags)) return TRUE;
3033     return data->proc( buffer, flags, data->lparam );
3034 }
3035 
3036 /******************************************************************************
3037  *           EnumSystemLocalesEx  (KERNEL32.@)
3038  */
3039 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
3040 {
3041     struct enum_locale_ex_data data;
3042 
3043     if (reserved)
3044     {
3045         SetLastError( ERROR_INVALID_PARAMETER );
3046         return FALSE;
3047     }
3048     data.proc   = proc;
3049     data.flags  = flags;
3050     data.lparam = lparam;
3051     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3052                             (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
3053                             enum_locale_ex_proc, (LONG_PTR)&data );
3054     return TRUE;
3055 }
3056 
3057 
3058 /***********************************************************************
3059  *           VerLanguageNameA  (KERNEL32.@)
3060  *
3061  * Get the name of a language.
3062  *
3063  * PARAMS
3064  *  wLang  [I] LANGID of the language
3065  *  szLang [O] Destination for the language name
3066  *
3067  * RETURNS
3068  *  Success: The size of the language name. If szLang is non-NULL, it is filled
3069  *           with the name.
3070  *  Failure: 0. Use GetLastError() to determine the cause.
3071  *
3072  */
3073 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
3074 {
3075     return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3076 }
3077 
3078 
3079 /***********************************************************************
3080  *           VerLanguageNameW  (KERNEL32.@)
3081  *
3082  * See VerLanguageNameA.
3083  */
3084 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
3085 {
3086     return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3087 }
3088 
3089 
3090 /******************************************************************************
3091  *           GetStringTypeW    (KERNEL32.@)
3092  *
3093  * See GetStringTypeA.
3094  */
3095 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3096 {
3097     static const unsigned char type2_map[16] =
3098     {
3099         C2_NOTAPPLICABLE,      /* unassigned */
3100         C2_LEFTTORIGHT,        /* L */
3101         C2_RIGHTTOLEFT,        /* R */
3102         C2_EUROPENUMBER,       /* EN */
3103         C2_EUROPESEPARATOR,    /* ES */
3104         C2_EUROPETERMINATOR,   /* ET */
3105         C2_ARABICNUMBER,       /* AN */
3106         C2_COMMONSEPARATOR,    /* CS */
3107         C2_BLOCKSEPARATOR,     /* B */
3108         C2_SEGMENTSEPARATOR,   /* S */
3109         C2_WHITESPACE,         /* WS */
3110         C2_OTHERNEUTRAL,       /* ON */
3111         C2_RIGHTTOLEFT,        /* AL */
3112         C2_NOTAPPLICABLE,      /* NSM */
3113         C2_NOTAPPLICABLE,      /* BN */
3114         C2_OTHERNEUTRAL        /* LRE, LRO, RLE, RLO, PDF */
3115     };
3116 
3117     if (!src)
3118     {
3119         SetLastError( ERROR_INVALID_PARAMETER );
3120         return FALSE;
3121     }
3122 
3123     if (count == -1) count = strlenW(src) + 1;
3124     switch(type)
3125     {
3126     case CT_CTYPE1:
3127         while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
3128         break;
3129     case CT_CTYPE2:
3130         while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
3131         break;
3132     case CT_CTYPE3:
3133     {
3134         WARN("CT_CTYPE3: semi-stub.\n");
3135         while (count--)
3136         {
3137             int c = *src;
3138             WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
3139 
3140             type1 = get_char_typeW( *src++ ) & 0xfff;
3141             /* try to construct type3 from type1 */
3142             if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
3143             if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
3144             if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
3145             if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
3146             if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3147             if (c == 0x0640) type3 |= C3_KASHIDA;
3148             if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3149 
3150             if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3151             if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3152 
3153             if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3154             if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3155             if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3156             if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3157             if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3158             if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3159             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3160             if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3161 
3162             if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3163             if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3164             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3165             if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3166             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3167             if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3168             *chartype++ = type3;
3169         }
3170         break;
3171     }
3172     default:
3173         SetLastError( ERROR_INVALID_PARAMETER );
3174         return FALSE;
3175     }
3176     return TRUE;
3177 }
3178 
3179 
3180 /******************************************************************************
3181  *           GetStringTypeExW    (KERNEL32.@)
3182  *
3183  * See GetStringTypeExA.
3184  */
3185 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3186 {
3187     /* locale is ignored for Unicode */
3188     return GetStringTypeW( type, src, count, chartype );
3189 }
3190 
3191 
3192 /******************************************************************************
3193  *           GetStringTypeA    (KERNEL32.@)
3194  *
3195  * Get characteristics of the characters making up a string.
3196  *
3197  * PARAMS
3198  *  locale   [I] Locale Id for the string
3199  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3200  *  src      [I] String to analyse
3201  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
3202  *  chartype [O] Destination for the calculated characteristics
3203  *
3204  * RETURNS
3205  *  Success: TRUE. chartype is filled with the requested characteristics of each char
3206  *           in src.
3207  *  Failure: FALSE. Use GetLastError() to determine the cause.
3208  */
3209 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3210 {
3211     UINT cp;
3212     INT countW;
3213     LPWSTR srcW;
3214     BOOL ret = FALSE;
3215 
3216     if(count == -1) count = strlen(src) + 1;
3217 
3218     if (!(cp = get_lcid_codepage( locale )))
3219     {
3220         FIXME("For locale %04x using current ANSI code page\n", locale);
3221         cp = GetACP();
3222     }
3223 
3224     countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3225     if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3226     {
3227         MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3228     /*
3229      * NOTE: the target buffer has 1 word for each CHARACTER in the source
3230      * string, with multibyte characters there maybe be more bytes in count
3231      * than character space in the buffer!
3232      */
3233         ret = GetStringTypeW(type, srcW, countW, chartype);
3234         HeapFree(GetProcessHeap(), 0, srcW);
3235     }
3236     return ret;
3237 }
3238 
3239 /******************************************************************************
3240  *           GetStringTypeExA    (KERNEL32.@)
3241  *
3242  * Get characteristics of the characters making up a string.
3243  *
3244  * PARAMS
3245  *  locale   [I] Locale Id for the string
3246  *  type     [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3247  *  src      [I] String to analyse
3248  *  count    [I] Length of src in chars, or -1 if src is NUL terminated
3249  *  chartype [O] Destination for the calculated characteristics
3250  *
3251  * RETURNS
3252  *  Success: TRUE. chartype is filled with the requested characteristics of each char
3253  *           in src.
3254  *  Failure: FALSE. Use GetLastError() to determine the cause.
3255  */
3256 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3257 {
3258     return GetStringTypeA(locale, type, src, count, chartype);
3259 }
3260 
3261 #ifdef __REACTOS__
3262 static inline void map_byterev(const WCHAR *src, int len, WCHAR *dst)
3263 {
3264     while (len--)
3265         *dst++ = RtlUshortByteSwap(*src++);
3266 }
3267 
3268 static int map_to_hiragana(const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3269 {
3270     int pos;
3271     for (pos = 0; srclen; src++, srclen--, pos++)
3272     {
3273         /*
3274          * U+30A1 ... U+30F3: Katakana
3275          * U+30F4: Katakana Letter VU
3276          * U+30F5: Katakana Letter Small KA
3277          * U+30FD: Katakana Iteration Mark
3278          * U+30FE: Katakana Voiced Iteration Mark
3279          */
3280         WCHAR wch = *src;
3281         if ((0x30A1 <= wch && wch <= 0x30F3) ||
3282             wch == 0x30F4 || wch == 0x30F5 || wch == 0x30FD || wch == 0x30FE)
3283         {
3284             wch -= 0x60; /* Katakana to Hiragana */
3285         }
3286         if (pos < dstlen)
3287             dst[pos] = wch;
3288     }
3289     return pos;
3290 }
3291 
3292 static int map_to_katakana(const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3293 {
3294     int pos;
3295     for (pos = 0; srclen; src++, srclen--, pos++)
3296     {
3297         /*
3298          * U+3041 ... U+3093: Hiragana
3299          * U+3094: Hiragana Letter VU
3300          * U+3095: Hiragana Letter Small KA
3301          * U+309D: Hiragana Iteration Mark
3302          * U+309E: Hiragana Voiced Iteration Mark
3303          */
3304         WCHAR wch = *src;
3305         if ((0x3041 <= wch && wch <= 0x3093) ||
3306             wch == 3094 || wch == 0x3095 || wch == 0x309D || wch == 0x309E)
3307         {
3308             wch += 0x60; /* Hiragana to Katakana */
3309         }
3310         if (pos < dstlen)
3311             dst[pos] = wch;
3312     }
3313     return pos;
3314 }
3315 
3316 /* The table that contains fullwidth characters and halfwidth characters */
3317 typedef WCHAR FULL2HALF_ENTRY[3];
3318 static const FULL2HALF_ENTRY full2half_table[] =
3319 {
3320 #define DEFINE_FULL2HALF(full, half1, half2) { full, half1, half2 },
3321 #include "full2half.h"
3322 #undef DEFINE_FULL2HALF
3323 };
3324 #define GET_FULL(table, index)  ((table)[index][0])
3325 #define GET_HALF1(table, index) ((table)[index][1])
3326 #define GET_HALF2(table, index) ((table)[index][2])
3327 
3328 /* The table that contains dakuten entries */
3329 typedef WCHAR DAKUTEN_ENTRY[3];
3330 static const DAKUTEN_ENTRY dakuten_table[] =
3331 {
3332 #define DEFINE_DAKUTEN(voiced, single1, single2, half1, half2) { voiced, single1, single2 },
3333 #include "dakuten.h"
3334 #undef DEFINE_DAKUTEN
3335 };
3336 #define GET_VOICED(table, index) ((table)[index][0])
3337 #define GET_SINGLE1(table, index) ((table)[index][1])
3338 #define GET_SINGLE2(table, index) ((table)[index][2])
3339 
3340 static int map_to_halfwidth(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3341 {
3342     int pos, i;
3343     const int count1 = (int)ARRAY_SIZE(full2half_table);
3344     const FULL2HALF_ENTRY *table1 = full2half_table;
3345 
3346     for (pos = 0; srclen; src++, srclen--, pos++)
3347     {
3348         WCHAR ch = *src;
3349 
3350         if (flags & LCMAP_KATAKANA)
3351             map_to_katakana(&ch, 1, &ch, 1);
3352         else if (flags & LCMAP_HIRAGANA)
3353             map_to_hiragana(&ch, 1, &ch, 1);
3354 
3355         if (ch < 0x3000) /* Quick judgment */
3356         {
3357             if (pos < dstlen)
3358                 dst[pos] = ch;
3359             continue;
3360         }
3361 
3362         if (0xFF01 <= ch && ch <= 0xFF5E) /* U+FF01 ... U+FF5E */
3363         {
3364             if (pos < dstlen)
3365                 dst[pos] = ch - 0xFEE0; /* Fullwidth ASCII to halfwidth ASCII */
3366             continue;
3367         }
3368 
3369         /* Search in table1 (full/half) */
3370         for (i = count1 - 1; i >= 0; --i) /* In reverse order */
3371         {
3372             if (GET_FULL(table1, i) != ch)
3373                 continue;
3374 
3375             if (GET_HALF2(table1, i) == 0)
3376             {
3377                 if (pos < dstlen)
3378                     dst[pos] = GET_HALF1(table1, i);
3379             }
3380             else if (!dstlen)
3381             {
3382                 pos++;
3383             }
3384             else if (pos + 1 < dstlen)
3385             {
3386                 dst[pos++] = GET_HALF1(table1, i);
3387                 dst[pos  ] = GET_HALF2(table1, i);
3388             }
3389             else
3390             {
3391                 dst[pos] = ch;
3392             }
3393             break;
3394         }
3395 
3396         if (i >= 0)
3397             continue;
3398 
3399         if (pos < dstlen)
3400             dst[pos] = ch;
3401     }
3402 
3403     return pos;
3404 }
3405 
3406 static int map_to_fullwidth(const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3407 {
3408     int pos, i;
3409     const FULL2HALF_ENTRY *table1 = full2half_table;
3410     const DAKUTEN_ENTRY *table2 = dakuten_table;
3411     const int count1 = (int)ARRAY_SIZE(full2half_table);
3412     const int count2 = (int)ARRAY_SIZE(dakuten_table);
3413 
3414     for (pos = 0; srclen; src++, srclen--, pos++)
3415     {
3416         WCHAR ch = *src;
3417 
3418         if (ch == 0x20) /* U+0020: Space */
3419         {
3420             if (pos < dstlen)
3421                 dst[pos] = 0x3000; /* U+3000: Ideographic Space */
3422             continue;
3423         }
3424 
3425         if (0x21 <= ch && ch <= 0x7E) /* Mappable halfwidth ASCII */
3426         {
3427             if (pos < dstlen)
3428                 dst[pos] = ch + 0xFEE0; /* U+FF01 ... U+FF5E */
3429             continue;
3430         }
3431 
3432         if (ch < 0xFF00) /* Quick judgment */
3433         {
3434             if (pos < dstlen)
3435                 dst[pos] = ch;
3436             continue;
3437         }
3438 
3439         /* Search in table1 (full/half) */
3440         for (i = count1 - 1; i >= 0; --i) /* In reverse order */
3441         {
3442             if (GET_HALF1(table1, i) != ch)
3443                 continue; /* Mismatched */
3444 
3445             if (GET_HALF2(table1, i) == 0)
3446             {
3447                 if (pos < dstlen)
3448                     dst[pos] = GET_FULL(table1, i);
3449                 break;
3450             }
3451 
3452             if (srclen <= 1 || GET_HALF2(table1, i) != src[1])
3453                 continue; /* Mismatched */
3454 
3455             --srclen;
3456             ++src;
3457 
3458             if (pos < dstlen)
3459                 dst[pos] = GET_FULL(table1, i);
3460             break;
3461         }
3462 
3463         if (i >= 0)
3464             continue;
3465 
3466         /* Search in table2 (dakuten) */
3467         for (i = count2 - 1; i >= 0; --i) /* In reverse order */
3468         {
3469             if (GET_SINGLE1(table2, i) != ch)
3470                 continue; /* Mismatched */
3471 
3472             if (srclen <= 1 || GET_SINGLE2(table2, i) != src[1])
3473                 continue; /* Mismatched */
3474 
3475             --srclen;
3476             ++src;
3477 
3478             if (pos < dstlen)
3479                 dst[pos] = GET_VOICED(table2, i);
3480             break;
3481         }
3482 
3483         if (i >= 0)
3484             continue;
3485 
3486         if (pos < dstlen)
3487             dst[pos] = ch;
3488     }
3489 
3490     return pos;
3491 }
3492 
3493 static int map_to_lowercase(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3494 {
3495     int pos;
3496     for (pos = 0; srclen; src++, srclen--)
3497     {
3498         WCHAR wch = *src;
3499         if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3500             continue;
3501         if (pos < dstlen)
3502             dst[pos] = tolowerW(wch);
3503         pos++;
3504     }
3505     return pos;
3506 }
3507 
3508 static int map_to_uppercase(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3509 {
3510     int pos;
3511     for (pos = 0; srclen; src++, srclen--)
3512     {
3513         WCHAR wch = *src;
3514         if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3515             continue;
3516         if (pos < dstlen)
3517             dst[pos] = toupperW(wch);
3518         pos++;
3519     }
3520     return pos;
3521 }
3522 
3523 typedef struct tagWCHAR_PAIR
3524 {
3525     WCHAR from, to;
3526 } WCHAR_PAIR, *PWCHAR_PAIR;
3527 
3528 /* The table to convert Simplified Chinese to Traditional Chinese */
3529 static const WCHAR_PAIR s_sim2tra[] =
3530 {
3531 #define DEFINE_SIM2TRA(from, to) { from, to },
3532 #include "sim2tra.h"
3533 #undef DEFINE_SIM2TRA
3534 };
3535 
3536 /* The table to convert Traditional Chinese to Simplified Chinese */
3537 static const WCHAR_PAIR s_tra2sim[] =
3538 {
3539 #define DEFINE_TRA2SIM(from, to) { from, to },
3540 #include "tra2sim.h"
3541 #undef DEFINE_TRA2SIM
3542 };
3543 
3544 /* The comparison function to do bsearch */
3545 static int compare_wchar_pair(const void *x, const void *y)
3546 {
3547     const WCHAR_PAIR *a = x;
3548     const WCHAR_PAIR *b = y;
3549     if (a->from < b->from)
3550         return -1;
3551     if (a->from > b->from)
3552         return +1;
3553     return 0;
3554 }
3555 
3556 static WCHAR find_wchar_pair(const WCHAR_PAIR *pairs, size_t count, WCHAR ch)
3557 {
3558     PWCHAR_PAIR found = bsearch(&ch, pairs, count, sizeof(WCHAR_PAIR), compare_wchar_pair);
3559     if (found)
3560         return found->to;
3561     return ch;
3562 }
3563 
3564 static int map_to_simplified_chinese(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3565 {
3566     int pos;
3567     for (pos = 0; srclen; src++, srclen--)
3568     {
3569         WCHAR wch = *src;
3570         if (pos < dstlen)
3571             dst[pos] = find_wchar_pair(s_tra2sim, ARRAY_SIZE(s_tra2sim), wch);
3572         pos++;
3573     }
3574     return pos;
3575 }
3576 
3577 static int map_to_traditional_chinese(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3578 {
3579     int pos;
3580     for (pos = 0; srclen; src++, srclen--)
3581     {
3582         WCHAR wch = *src;
3583         if (pos < dstlen)
3584             dst[pos] = find_wchar_pair(s_sim2tra, ARRAY_SIZE(s_sim2tra), wch);
3585         pos++;
3586     }
3587     return pos;
3588 }
3589 
3590 static int map_remove_ignored(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3591 {
3592     int pos;
3593     WORD wC1, wC2, wC3;
3594     for (pos = 0; srclen; src++, srclen--)
3595     {
3596         WCHAR wch = *src;
3597         GetStringTypeW(CT_CTYPE1, &wch, 1, &wC1);
3598         GetStringTypeW(CT_CTYPE2, &wch, 1, &wC2);
3599         GetStringTypeW(CT_CTYPE3, &wch, 1, &wC3);
3600         if (flags & NORM_IGNORESYMBOLS)
3601         {
3602             if ((wC1 & C1_PUNCT) || (wC3 & C3_SYMBOL))
3603                 continue;
3604         }
3605         if (flags & NORM_IGNORENONSPACE)
3606         {
3607             if ((wC2 & C2_OTHERNEUTRAL) && (wC3 & (C3_NONSPACING | C3_DIACRITIC)))
3608                 continue;
3609         }
3610         if (pos < dstlen)
3611             dst[pos] = wch;
3612         pos++;
3613     }
3614     return pos;
3615 }
3616 
3617 static int lcmap_string(DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen)
3618 {
3619     int ret = 0;
3620 
3621     if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE))
3622     {
3623         SetLastError(ERROR_INVALID_FLAGS);
3624         return 0;
3625     }
3626 
3627     switch (flags & ~(LCMAP_BYTEREV | LCMAP_LOWERCASE | LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING))
3628     {
3629     case LCMAP_HIRAGANA:
3630         ret = map_to_hiragana(src, srclen, dst, dstlen);
3631         break;
3632     case LCMAP_KATAKANA:
3633         ret = map_to_katakana(src, srclen, dst, dstlen);
3634         break;
3635     case LCMAP_HALFWIDTH:
3636         ret = map_to_halfwidth(flags, src, srclen, dst, dstlen);
3637         break;
3638     case LCMAP_HIRAGANA | LCMAP_HALFWIDTH:
3639         ret = map_to_halfwidth(flags, src, srclen, dst, dstlen);
3640         break;
3641     case LCMAP_KATAKANA | LCMAP_HALFWIDTH:
3642         ret = map_to_halfwidth(flags, src, srclen, dst, dstlen);
3643         break;
3644     case LCMAP_FULLWIDTH:
3645         ret = map_to_fullwidth(src, srclen, dst, dstlen);
3646         break;
3647     case LCMAP_HIRAGANA | LCMAP_FULLWIDTH:
3648         ret = map_to_fullwidth(src, srclen, dst, dstlen);
3649         if (dstlen && ret)
3650             map_to_hiragana(dst, ret, dst, dstlen);
3651         break;
3652     case LCMAP_KATAKANA | LCMAP_FULLWIDTH:
3653         ret = map_to_fullwidth(src, srclen, dst, dstlen);
3654         if (dstlen && ret)
3655             map_to_katakana(dst, ret, dst, dstlen);
3656         break;
3657     case LCMAP_SIMPLIFIED_CHINESE:
3658         ret = map_to_simplified_chinese(flags, src, srclen, dst, dstlen);
3659         break;
3660     case LCMAP_TRADITIONAL_CHINESE:
3661         ret = map_to_traditional_chinese(flags, src, srclen, dst, dstlen);
3662         break;
3663     case NORM_IGNORENONSPACE:
3664     case NORM_IGNORESYMBOLS:
3665     case NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS:
3666         if (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | LCMAP_BYTEREV))
3667         {
3668             SetLastError(ERROR_INVALID_FLAGS);
3669             return 0;
3670         }
3671         ret = map_remove_ignored(flags, src, srclen, dst, dstlen);
3672         break;
3673     case 0:
3674         if (flags & LCMAP_LOWERCASE)
3675         {
3676             ret = map_to_lowercase(flags, src, srclen, dst, dstlen);
3677             flags &= ~LCMAP_LOWERCASE;
3678             break;
3679         }
3680         if (flags & LCMAP_UPPERCASE)
3681         {
3682             ret = map_to_uppercase(flags, src, srclen, dst, dstlen);
3683             flags &= ~LCMAP_UPPERCASE;
3684             break;
3685         }
3686         if (flags & LCMAP_BYTEREV)
3687         {
3688             if (dstlen == 0)
3689             {
3690                 ret = srclen;
3691                 break;
3692             }
3693             ret = min(srclen, dstlen);
3694             RtlCopyMemory(dst, src, ret * sizeof(WCHAR));
3695             break;
3696         }
3697         /* fall through */
3698     default:
3699         SetLastError(ERROR_INVALID_FLAGS);
3700         return 0;
3701     }
3702 
3703     if (dstlen)
3704     {
3705         if (flags & LCMAP_LOWERCASE)
3706             map_to_lowercase(flags, dst, ret, dst, dstlen);
3707         if (flags & LCMAP_UPPERCASE)
3708             map_to_uppercase(flags, dst, ret, dst, dstlen);
3709         if (flags & LCMAP_BYTEREV)
3710             map_byterev(dst, min(ret, dstlen), dst);
3711 
3712         if (dstlen < ret)
3713         {
3714             SetLastError(ERROR_INSUFFICIENT_BUFFER);
3715             return 0;
3716         }
3717     }
3718 
3719     return ret;
3720 }
3721 #endif // __REACTOS__
3722 
3723 /*************************************************************************
3724  *           LCMapStringEx   (KERNEL32.@)
3725  *
3726  * Map characters in a locale sensitive string.
3727  *
3728  * PARAMS
3729  *  locale   [I] Locale name for the conversion.
3730  *  flags    [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3731  *  src      [I] String to map
3732  *  srclen   [I] Length of src in chars, or -1 if src is NUL terminated
3733  *  dst      [O] Destination for mapped string
3734  *  dstlen   [I] Length of dst in characters
3735  *  version  [I] reserved, must be NULL
3736  *  reserved [I] reserved, must be NULL
3737  *  lparam   [I] reserved, must be 0
3738  *
3739  * RETURNS
3740  *  Success: The length of the mapped string in dst, including the NUL terminator.
3741  *  Failure: 0. Use GetLastError() to determine the cause.
3742  */
3743 INT WINAPI LCMapStringEx(LPCWSTR locale, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3744                          LPNLSVERSIONINFO version, LPVOID reserved, LPARAM handle)
3745 {
3746     if (version) FIXME("unsupported version structure %p\n", version);
3747     if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3748     if (handle)
3749     {
3750         static int once;
3751         if (!once++) FIXME("unsupported lparam %Ix\n", handle);
3752     }
3753 
3754     if (!src || !srclen || dstlen < 0)
3755     {
3756         SetLastError(ERROR_INVALID_PARAMETER);
3757         return 0;
3758     }
3759 
3760     if (srclen < 0) srclen = lstrlenW(src) + 1;
3761 
3762     TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n",
3763            debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
3764 
3765     flags &= ~LOCALE_USE_CP_ACP;
3766 
3767     if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3768     {
3769         SetLastError(ERROR_INVALID_FLAGS);
3770         return 0;
3771     }
3772 
3773     if (!dstlen) dst = NULL;
3774 
3775     if (flags & LCMAP_SORTKEY)
3776     {
3777         INT ret;
3778 
3779         if (srclen < 0)
3780             srclen = strlenW(src);
3781 
3782         ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3783         if (ret == 0)
3784             SetLastError(ERROR_INSUFFICIENT_BUFFER);
3785         else
3786             ret++;
3787         return ret;
3788     }
3789 
3790     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3791     if (flags & SORT_STRINGSORT)
3792     {
3793         SetLastError(ERROR_INVALID_FLAGS);
3794         return 0;
3795     }
3796 
3797     return lcmap_string(flags, src, srclen, dst, dstlen);
3798 }
3799 
3800 /*************************************************************************
3801  *           LCMapStringW    (KERNEL32.@)
3802  *
3803  * See LCMapStringA.
3804  */
3805 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3806                         LPWSTR dst, INT dstlen)
3807 {
3808     TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3809           lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3810 
3811     return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3812 }
3813 
3814 /*************************************************************************
3815  *           LCMapStringA    (KERNEL32.@)
3816  *
3817  * Map characters in a locale sensitive string.
3818  *
3819  * PARAMS
3820  *  lcid   [I] LCID for the conversion.
3821  *  flags  [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3822  *  src    [I] String to map
3823  *  srclen [I] Length of src in chars, or -1 if src is NUL terminated
3824  *  dst    [O] Destination for mapped string
3825  *  dstlen [I] Length of dst in characters
3826  *
3827  * RETURNS
3828  *  Success: The length of the mapped string in dst, including the NUL terminator.
3829  *  Failure: 0. Use GetLastError() to determine the cause.
3830  */
3831 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3832                         LPSTR dst, INT dstlen)
3833 {
3834     WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3835     LPWSTR srcW, dstW;
3836     INT ret = 0, srclenW, dstlenW;
3837     UINT locale_cp = CP_ACP;
3838 
3839     if (!src || !srclen || dstlen < 0)
3840     {
3841         SetLastError(ERROR_INVALID_PARAMETER);
3842         return 0;
3843     }
3844 
3845     if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3846 
3847     srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3848     if (srclenW)
3849         srcW = bufW;
3850     else
3851     {
3852         srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3853         srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3854         if (!srcW)
3855         {
3856             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3857             return 0;
3858         }
3859         MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3860     }
3861 
3862     if (flags & LCMAP_SORTKEY)
3863     {
3864         if (src == dst)
3865         {
3866             SetLastError(ERROR_INVALID_FLAGS);
3867             goto map_string_exit;
3868         }
3869         ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3870         if (ret == 0)
3871             SetLastError(ERROR_INSUFFICIENT_BUFFER);
3872         else
3873             ret++;
3874         goto map_string_exit;
3875     }
3876 
3877     if (flags & SORT_STRINGSORT)
3878     {
3879         SetLastError(ERROR_INVALID_FLAGS);
3880         goto map_string_exit;
3881     }
3882 
3883     dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3884     if (!dstlenW)
3885         goto map_string_exit;
3886 
3887     dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3888     if (!dstW)
3889     {
3890         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3891         goto map_string_exit;
3892     }
3893 
3894     LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3895     ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3896     HeapFree(GetProcessHeap(), 0, dstW);
3897 
3898 map_string_exit:
3899     if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3900     return ret;
3901 }
3902 
3903 /*************************************************************************
3904  *           FoldStringA    (KERNEL32.@)
3905  *
3906  * Map characters in a string.
3907  *
3908  * PARAMS
3909  *  dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3910  *  src     [I] String to map
3911  *  srclen  [I] Length of src, or -1 if src is NUL terminated
3912  *  dst     [O] Destination for mapped string
3913  *  dstlen  [I] Length of dst, or 0 to find the required length for the mapped string
3914  *
3915  * RETURNS
3916  *  Success: The length of the string written to dst, including the terminating NUL. If
3917  *           dstlen is 0, the value returned is the same, but nothing is written to dst,
3918  *           and dst may be NULL.
3919  *  Failure: 0. Use GetLastError() to determine the cause.
3920  */
3921 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3922                        LPSTR dst, INT dstlen)
3923 {
3924     INT ret = 0, srclenW = 0;
3925     WCHAR *srcW = NULL, *dstW = NULL;
3926 
3927     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3928     {
3929         SetLastError(ERROR_INVALID_PARAMETER);
3930         return 0;
3931     }
3932 
3933     srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3934                                   src, srclen, NULL, 0);
3935     srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3936 
3937     if (!srcW)
3938     {
3939         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3940         goto FoldStringA_exit;
3941     }
3942 
3943     MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3944                         src, srclen, srcW, srclenW);
3945 
3946     dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3947 
3948     ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3949     if (ret && dstlen)
3950     {
3951         dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3952 
3953         if (!dstW)
3954         {
3955             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3956             goto FoldStringA_exit;
3957         }
3958 
3959         ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3960         if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3961         {
3962             ret = 0;
3963             SetLastError(ERROR_INSUFFICIENT_BUFFER);
3964         }
3965     }
3966 
3967     HeapFree(GetProcessHeap(), 0, dstW);
3968 
3969 FoldStringA_exit:
3970     HeapFree(GetProcessHeap(), 0, srcW);
3971     return ret;
3972 }
3973 
3974 /*************************************************************************
3975  *           FoldStringW    (KERNEL32.@)
3976  *
3977  * See FoldStringA.
3978  */
3979 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3980                        LPWSTR dst, INT dstlen)
3981 {
3982     int ret;
3983 
3984     switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3985     {
3986     case 0:
3987         if (dwFlags)
3988           break;
3989         /* Fall through for dwFlags == 0 */
3990     case MAP_PRECOMPOSED|MAP_COMPOSITE:
3991     case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3992     case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3993         SetLastError(ERROR_INVALID_FLAGS);
3994         return 0;
3995     }
3996 
3997     if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3998     {
3999         SetLastError(ERROR_INVALID_PARAMETER);
4000         return 0;
4001     }
4002 
4003     ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
4004     if (!ret)
4005         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4006     return ret;
4007 }
4008 
4009 /******************************************************************************
4010  *           CompareStringW    (KERNEL32.@)
4011  *
4012  * See CompareStringA.
4013  */
4014 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
4015                           LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
4016 {
4017     return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
4018 }
4019 
4020 /******************************************************************************
4021  *           CompareStringEx    (KERNEL32.@)
4022  */
4023 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
4024                            LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
4025 {
4026     DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
4027                            |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
4028     DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
4029     /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
4030     INT ret;
4031     static int once;
4032 
4033     if (version) FIXME("unexpected version parameter\n");
4034     if (reserved) FIXME("unexpected reserved value\n");
4035     if (lParam) FIXME("unexpected lParam\n");
4036 
4037     if (!str1 || !str2)
4038     {
4039         SetLastError(ERROR_INVALID_PARAMETER);
4040         return 0;
4041     }
4042 
4043     if (flags & ~(supported_flags|semistub_flags))
4044     {
4045         SetLastError(ERROR_INVALID_FLAGS);
4046         return 0;
4047     }
4048 
4049     if (flags & semistub_flags)
4050     {
4051         if (!once++)
4052             FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
4053     }
4054 
4055     if (len1 < 0) len1 = strlenW(str1);
4056     if (len2 < 0) len2 = strlenW(str2);
4057 
4058     ret = wine_compare_string(flags, str1, len1, str2, len2);
4059 
4060     if (ret) /* need to translate result */
4061         return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
4062     return CSTR_EQUAL;
4063 }
4064 
4065 /******************************************************************************
4066  *           CompareStringA    (KERNEL32.@)
4067  *
4068  * Compare two locale sensitive strings.
4069  *
4070  * PARAMS
4071  *  lcid  [I] LCID for the comparison
4072  *  flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
4073  *  str1  [I] First string to compare
4074  *  len1  [I] Length of str1, or -1 if str1 is NUL terminated
4075  *  str2  [I] Second string to compare
4076  *  len2  [I] Length of str2, or -1 if str2 is NUL terminated
4077  *
4078  * RETURNS
4079  *  Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
4080  *           str1 is less than, equal to or greater than str2 respectively.
4081  *  Failure: FALSE. Use GetLastError() to determine the cause.
4082  */
4083 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
4084                           LPCSTR str1, INT len1, LPCSTR str2, INT len2)
4085 {
4086     WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
4087     WCHAR *buf2W = buf1W + 130;
4088     LPWSTR str1W, str2W;
4089     INT len1W = 0, len2W = 0, ret;
4090     UINT locale_cp = CP_ACP;
4091 
4092     if (!str1 || !str2)
4093     {
4094         SetLastError(ERROR_INVALID_PARAMETER);
4095         return 0;
4096     }
4097     if (len1 < 0) len1 = strlen(str1);
4098     if (len2 < 0) len2 = strlen(str2);
4099 
4100     if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
4101 
4102     if (len1)
4103     {
4104         if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
4105         if (len1W)
4106             str1W = buf1W;
4107         else
4108         {
4109             len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
4110             str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
4111             if (!str1W)
4112             {
4113                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4114                 return 0;
4115             }
4116             MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
4117         }
4118     }
4119     else
4120     {
4121         len1W = 0;
4122         str1W = buf1W;
4123     }
4124 
4125     if (len2)
4126     {
4127         if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
4128         if (len2W)
4129             str2W = buf2W;
4130         else
4131         {
4132             len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
4133             str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
4134             if (!str2W)
4135             {
4136                 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4137                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4138                 return 0;
4139             }
4140             MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
4141         }
4142     }
4143     else
4144     {
4145         len2W = 0;
4146         str2W = buf2W;
4147     }
4148 
4149     ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
4150 
4151     if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4152     if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
4153     return ret;
4154 }
4155 
4156 #if (WINVER >= 0x0600)
4157 /******************************************************************************
4158  *           CompareStringOrdinal    (KERNEL32.@)
4159  */
4160 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
4161 {
4162     int ret;
4163 
4164     if (!str1 || !str2)
4165     {
4166         SetLastError(ERROR_INVALID_PARAMETER);
4167         return 0;
4168     }
4169     if (len1 < 0) len1 = strlenW(str1);
4170     if (len2 < 0) len2 = strlenW(str2);
4171 
4172     ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4173     if (ret < 0) return CSTR_LESS_THAN;
4174     if (ret > 0) return CSTR_GREATER_THAN;
4175     return CSTR_EQUAL;
4176 }
4177 #endif // (WINVER >= 0x0600)
4178 
4179 #ifndef __REACTOS__
4180 /*************************************************************************
4181  *           lstrcmp     (KERNEL32.@)
4182  *           lstrcmpA    (KERNEL32.@)
4183  *
4184  * Compare two strings using the current thread locale.
4185  *
4186  * PARAMS
4187  *  str1  [I] First string to compare
4188  *  str2  [I] Second string to compare
4189  *
4190  * RETURNS
4191  *  Success: A number less than, equal to or greater than 0 depending on whether
4192  *           str1 is less than, equal to or greater than str2 respectively.
4193  *  Failure: FALSE. Use GetLastError() to determine the cause.
4194  */
4195 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
4196 {
4197     int ret;
4198 
4199     if ((str1 == NULL) && (str2 == NULL)) return 0;
4200     if (str1 == NULL) return -1;
4201     if (str2 == NULL) return 1;
4202 
4203     ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4204     if (ret) ret -= 2;
4205 
4206     return ret;
4207 }
4208 
4209 /*************************************************************************
4210  *           lstrcmpi     (KERNEL32.@)
4211  *           lstrcmpiA    (KERNEL32.@)
4212  *
4213  * Compare two strings using the current thread locale, ignoring case.
4214  *
4215  * PARAMS
4216  *  str1  [I] First string to compare
4217  *  str2  [I] Second string to compare
4218  *
4219  * RETURNS
4220  *  Success: A number less than, equal to or greater than 0 depending on whether
4221  *           str2 is less than, equal to or greater than str1 respectively.
4222  *  Failure: FALSE. Use GetLastError() to determine the cause.
4223  */
4224 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
4225 {
4226     int ret;
4227 
4228     if ((str1 == NULL) && (str2 == NULL)) return 0;
4229     if (str1 == NULL) return -1;
4230     if (str2 == NULL) return 1;
4231 
4232     ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4233     if (ret) ret -= 2;
4234 
4235     return ret;
4236 }
4237 
4238 /*************************************************************************
4239  *           lstrcmpW    (KERNEL32.@)
4240  *
4241  * See lstrcmpA.
4242  */
4243 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
4244 {
4245     int ret;
4246 
4247     if ((str1 == NULL) && (str2 == NULL)) return 0;
4248     if (str1 == NULL) return -1;
4249     if (str2 == NULL) return 1;
4250 
4251     ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
4252     if (ret) ret -= 2;
4253 
4254     return ret;
4255 }
4256 
4257 /*************************************************************************
4258  *           lstrcmpiW    (KERNEL32.@)
4259  *
4260  * See lstrcmpiA.
4261  */
4262 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
4263 {
4264     int ret;
4265 
4266     if ((str1 == NULL) && (str2 == NULL)) return 0;
4267     if (str1 == NULL) return -1;
4268     if (str2 == NULL) return 1;
4269 
4270     ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
4271     if (ret) ret -= 2;
4272 
4273     return ret;
4274 }
4275 
4276 /******************************************************************************
4277  *		LOCALE_Init
4278  */
4279 void LOCALE_Init(void)
4280 {
4281     extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
4282                                              const union cptable *unix_cp );
4283 
4284     UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
4285 
4286     setlocale( LC_ALL, "" );
4287 
4288 #ifdef __APPLE__
4289     /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
4290     if (!has_env("LANG"))
4291     {
4292         const char* mac_locale = get_mac_locale();
4293 
4294         setenv( "LANG", mac_locale, 1 );
4295         if (setlocale( LC_ALL, "" ))
4296             TRACE( "setting LANG to '%s'\n", mac_locale );
4297         else
4298         {
4299             /* no C library locale matching Mac locale; don't pass garbage to children */
4300             unsetenv("LANG");
4301             TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
4302         }
4303     }
4304 #endif /* __APPLE__ */
4305 
4306     unix_cp = setup_unix_locales();
4307     if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
4308 
4309 #ifdef __APPLE__
4310     if (!unix_cp)
4311         unix_cp = CP_UTF8;  /* default to utf-8 even if we don't get a valid locale */
4312 #endif
4313 
4314     NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
4315     NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
4316     NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
4317 
4318     ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
4319     GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
4320                     (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
4321     GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
4322                     (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
4323     if (!unix_cp)
4324         GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
4325                         (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
4326 
4327     if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
4328         ansi_cptable = wine_cp_get_table( 1252 );
4329     if (!(oem_cptable = wine_cp_get_table( oem_cp )))
4330         oem_cptable  = wine_cp_get_table( 437 );
4331     if (!(mac_cptable = wine_cp_get_table( mac_cp )))
4332         mac_cptable  = wine_cp_get_table( 10000 );
4333     if (unix_cp != CP_UTF8)
4334     {
4335         if (!(unix_cptable = wine_cp_get_table( unix_cp )))
4336             unix_cptable  = wine_cp_get_table( 28591 );
4337     }
4338 
4339     __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
4340 
4341     TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
4342            ansi_cptable->info.codepage, oem_cptable->info.codepage,
4343            mac_cptable->info.codepage, unix_cp );
4344 
4345     setlocale(LC_NUMERIC, "C");  /* FIXME: oleaut32 depends on this */
4346 }
4347 
4348 #endif // !__REACTOS__
4349 
4350 #ifdef __REACTOS__
4351 HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4352 #else
4353 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4354 #endif
4355 {
4356     UNICODE_STRING keyName;
4357     OBJECT_ATTRIBUTES attr;
4358     HANDLE hkey;
4359 
4360     RtlInitUnicodeString( &keyName, szKeyName );
4361 #ifdef __REACTOS__
4362     InitializeObjectAttributes(&attr, &keyName, OBJ_CASE_INSENSITIVE, hRootKey, NULL);
4363 #else
4364     InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
4365 #endif
4366 
4367     if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
4368         hkey = 0;
4369 
4370     return hkey;
4371 }
4372 
4373 #ifdef __REACTOS__
4374 BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4375                       LPWSTR szValueName, ULONG valueNameSize,
4376                       LPWSTR szValueData, ULONG valueDataSize)
4377 #else
4378 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4379                              LPWSTR szValueName, ULONG valueNameSize,
4380                              LPWSTR szValueData, ULONG valueDataSize)
4381 #endif
4382 {
4383     BYTE buffer[80];
4384     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
4385     DWORD dwLen;
4386 
4387     if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
4388         buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
4389         info->NameLength > valueNameSize ||
4390         info->DataLength > valueDataSize)
4391     {
4392         return FALSE;
4393     }
4394 
4395     TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
4396 
4397     memcpy( szValueName, info->Name, info->NameLength);
4398     szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
4399     memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
4400     szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
4401 
4402     TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
4403     return TRUE;
4404 }
4405 
4406 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
4407 {
4408     BYTE buffer[128];
4409     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
4410     DWORD dwSize = sizeof(buffer);
4411     UNICODE_STRING valueName;
4412 
4413     RtlInitUnicodeString( &valueName, szValueName );
4414 
4415     TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
4416     if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
4417                          buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
4418         info->DataLength == sizeof(DWORD))
4419     {
4420         memcpy(lpVal, info->Data, sizeof(DWORD));
4421         return TRUE;
4422     }
4423 
4424     return FALSE;
4425 }
4426 
4427 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
4428 {
4429     LANGID  langId;
4430     LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
4431     HRSRC   hResource;
4432     BOOL    bRet = FALSE;
4433 
4434     /* FIXME: Is it correct to use the system default langid? */
4435     langId = GetSystemDefaultLangID();
4436 
4437     if (SUBLANGID(langId) == SUBLANG_NEUTRAL) langId = get_default_sublang( langId );
4438 
4439     hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
4440 
4441     if (hResource)
4442     {
4443         HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
4444 
4445         if (hResDir)
4446         {
4447             ULONG   iResourceIndex = lgrpid & 0xf;
4448             LPCWSTR lpResEntry = LockResource( hResDir );
4449             ULONG   i;
4450 
4451             for (i = 0; i < iResourceIndex; i++)
4452                 lpResEntry += *lpResEntry + 1;
4453 
4454             if (*lpResEntry < nameSize)
4455             {
4456                 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
4457                 szName[*lpResEntry] = '\0';
4458                 bRet = TRUE;
4459             }
4460 
4461         }
4462         FreeResource( hResource );
4463     }
4464     return bRet;
4465 }
4466 
4467 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
4468 typedef struct
4469 {
4470   LANGUAGEGROUP_ENUMPROCA procA;
4471   LANGUAGEGROUP_ENUMPROCW procW;
4472   DWORD    dwFlags;
4473   LONG_PTR lParam;
4474 } ENUMLANGUAGEGROUP_CALLBACKS;
4475 
4476 /* Internal implementation of EnumSystemLanguageGroupsA/W */
4477 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
4478 {
4479     WCHAR szNumber[10], szValue[4];
4480     HANDLE hKey;
4481     BOOL bContinue = TRUE;
4482     ULONG ulIndex = 0;
4483 
4484     if (!lpProcs)
4485     {
4486         SetLastError(ERROR_INVALID_PARAMETER);
4487         return FALSE;
4488     }
4489 
4490     switch (lpProcs->dwFlags)
4491     {
4492     case 0:
4493         /* Default to LGRPID_INSTALLED */
4494         lpProcs->dwFlags = LGRPID_INSTALLED;
4495         /* Fall through... */
4496     case LGRPID_INSTALLED:
4497     case LGRPID_SUPPORTED:
4498         break;
4499     default:
4500         SetLastError(ERROR_INVALID_FLAGS);
4501         return FALSE;
4502     }
4503 
4504     hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4505 
4506     if (!hKey)
4507         FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4508 
4509     while (bContinue)
4510     {
4511         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4512                               szValue, sizeof(szValue) ))
4513         {
4514             BOOL bInstalled = szValue[0] == '1';
4515             LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4516 
4517             TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4518                    bInstalled ? "" : "not ");
4519 
4520             if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4521             {
4522                 WCHAR szGrpName[48];
4523 
4524                 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
4525                     szGrpName[0] = '\0';
4526 
4527                 if (lpProcs->procW)
4528                     bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4529                                                 lpProcs->lParam );
4530                 else
4531                 {
4532                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4533                     char szGrpNameA[48];
4534 
4535                     /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4536                      *        or whether the language names are ever localised. Assume CP_ACP.
4537                      */
4538 
4539                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4540                     WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4541 
4542                     bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4543                                                 lpProcs->lParam );
4544                 }
4545             }
4546 
4547             ulIndex++;
4548         }
4549         else
4550             bContinue = FALSE;
4551 
4552         if (!bContinue)
4553             break;
4554     }
4555 
4556     if (hKey)
4557         NtClose( hKey );
4558 
4559     return TRUE;
4560 }
4561 
4562 /******************************************************************************
4563  *           EnumSystemLanguageGroupsA    (KERNEL32.@)
4564  *
4565  * Call a users function for each language group available on the system.
4566  *
4567  * PARAMS
4568  *  pLangGrpEnumProc [I] Callback function to call for each language group
4569  *  dwFlags          [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4570  *  lParam           [I] User parameter to pass to pLangGrpEnumProc
4571  *
4572  * RETURNS
4573  *  Success: TRUE.
4574  *  Failure: FALSE. Use GetLastError() to determine the cause.
4575  */
4576 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4577                                       DWORD dwFlags, LONG_PTR lParam)
4578 {
4579     ENUMLANGUAGEGROUP_CALLBACKS procs;
4580 
4581     TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4582 
4583     procs.procA = pLangGrpEnumProc;
4584     procs.procW = NULL;
4585     procs.dwFlags = dwFlags;
4586     procs.lParam = lParam;
4587 
4588     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4589 }
4590 
4591 /******************************************************************************
4592  *           EnumSystemLanguageGroupsW    (KERNEL32.@)
4593  *
4594  * See EnumSystemLanguageGroupsA.
4595  */
4596 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4597                                       DWORD dwFlags, LONG_PTR lParam)
4598 {
4599     ENUMLANGUAGEGROUP_CALLBACKS procs;
4600 
4601     TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4602 
4603     procs.procA = NULL;
4604     procs.procW = pLangGrpEnumProc;
4605     procs.dwFlags = dwFlags;
4606     procs.lParam = lParam;
4607 
4608     return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4609 }
4610 
4611 /******************************************************************************
4612  *           IsValidLanguageGroup    (KERNEL32.@)
4613  *
4614  * Determine if a language group is supported and/or installed.
4615  *
4616  * PARAMS
4617  *  lgrpid  [I] Language Group Id (LGRPID_ values from "winnls.h")
4618  *  dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4619  *
4620  * RETURNS
4621  *  TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4622  *  FALSE otherwise.
4623  */
4624 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4625 {
4626     static const WCHAR szFormat[] = { '%','x','\0' };
4627     WCHAR szValueName[16], szValue[2];
4628     BOOL bSupported = FALSE, bInstalled = FALSE;
4629     HANDLE hKey;
4630 
4631 
4632     switch (dwFlags)
4633     {
4634     case LGRPID_INSTALLED:
4635     case LGRPID_SUPPORTED:
4636 
4637         hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4638 
4639         sprintfW( szValueName, szFormat, lgrpid );
4640 
4641         if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4642         {
4643             bSupported = TRUE;
4644 
4645             if (szValue[0] == '1')
4646                 bInstalled = TRUE;
4647         }
4648 
4649         if (hKey)
4650             NtClose( hKey );
4651 
4652         break;
4653     }
4654 
4655     if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4656         (dwFlags == LGRPID_INSTALLED && bInstalled))
4657         return TRUE;
4658 
4659     return FALSE;
4660 }
4661 
4662 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4663 typedef struct
4664 {
4665   LANGGROUPLOCALE_ENUMPROCA procA;
4666   LANGGROUPLOCALE_ENUMPROCW procW;
4667   DWORD    dwFlags;
4668   LGRPID   lgrpid;
4669   LONG_PTR lParam;
4670 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4671 
4672 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4673 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4674 {
4675     static const WCHAR szAlternateSortsKeyName[] = {
4676       'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4677     };
4678     WCHAR szNumber[10], szValue[4];
4679     HANDLE hKey;
4680     BOOL bContinue = TRUE, bAlternate = FALSE;
4681     LGRPID lgrpid;
4682     ULONG ulIndex = 1;  /* Ignore default entry of 1st key */
4683 
4684     if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4685     {
4686         SetLastError(ERROR_INVALID_PARAMETER);
4687         return FALSE;
4688     }
4689 
4690     if (lpProcs->dwFlags)
4691     {
4692         SetLastError(ERROR_INVALID_FLAGS);
4693         return FALSE;
4694     }
4695 
4696     hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4697 
4698     if (!hKey)
4699         WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4700 
4701     while (bContinue)
4702     {
4703         if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4704                               szValue, sizeof(szValue) ))
4705         {
4706             lgrpid = strtoulW( szValue, NULL, 16 );
4707 
4708             TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4709                    lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4710 
4711             if (lgrpid == lpProcs->lgrpid)
4712             {
4713                 LCID lcid;
4714 
4715                 lcid = strtoulW( szNumber, NULL, 16 );
4716 
4717                 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4718                  * '00000437          ;Georgian'
4719                  * At present we only pass the LCID string.
4720                  */
4721 
4722                 if (lpProcs->procW)
4723                     bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4724                 else
4725                 {
4726                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4727 
4728                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4729 
4730                     bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4731                 }
4732             }
4733 
4734             ulIndex++;
4735         }
4736         else
4737         {
4738             /* Finished enumerating this key */
4739             if (!bAlternate)
4740             {
4741                 /* Enumerate alternate sorts also */
4742                 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4743                 bAlternate = TRUE;
4744                 ulIndex = 0;
4745             }
4746             else
4747                 bContinue = FALSE; /* Finished both keys */
4748         }
4749 
4750         if (!bContinue)
4751             break;
4752     }
4753 
4754     if (hKey)
4755         NtClose( hKey );
4756 
4757     return TRUE;
4758 }
4759 
4760 /******************************************************************************
4761  *           EnumLanguageGroupLocalesA    (KERNEL32.@)
4762  *
4763  * Call a users function for every locale in a language group available on the system.
4764  *
4765  * PARAMS
4766  *  pLangGrpLcEnumProc [I] Callback function to call for each locale
4767  *  lgrpid             [I] Language group (LGRPID_ values from "winnls.h")
4768  *  dwFlags            [I] Reserved, set to 0
4769  *  lParam             [I] User parameter to pass to pLangGrpLcEnumProc
4770  *
4771  * RETURNS
4772  *  Success: TRUE.
4773  *  Failure: FALSE. Use GetLastError() to determine the cause.
4774  */
4775 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4776                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4777 {
4778     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4779 
4780     TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4781 
4782     callbacks.procA   = pLangGrpLcEnumProc;
4783     callbacks.procW   = NULL;
4784     callbacks.dwFlags = dwFlags;
4785     callbacks.lgrpid  = lgrpid;
4786     callbacks.lParam  = lParam;
4787 
4788     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4789 }
4790 
4791 /******************************************************************************
4792  *           EnumLanguageGroupLocalesW    (KERNEL32.@)
4793  *
4794  * See EnumLanguageGroupLocalesA.
4795  */
4796 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4797                                       LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4798 {
4799     ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4800 
4801     TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4802 
4803     callbacks.procA   = NULL;
4804     callbacks.procW   = pLangGrpLcEnumProc;
4805     callbacks.dwFlags = dwFlags;
4806     callbacks.lgrpid  = lgrpid;
4807     callbacks.lParam  = lParam;
4808 
4809     return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4810 }
4811 
4812 /******************************************************************************
4813  *           InvalidateNLSCache           (KERNEL32.@)
4814  *
4815  * Invalidate the cache of NLS values.
4816  *
4817  * PARAMS
4818  *  None.
4819  *
4820  * RETURNS
4821  *  Success: TRUE.
4822  *  Failure: FALSE.
4823  */
4824 BOOL WINAPI InvalidateNLSCache(void)
4825 {
4826 #ifdef __REACTOS__
4827     JapaneseEra_ClearCache();
4828     return TRUE;
4829 #else
4830   FIXME("() stub\n");
4831   return FALSE;
4832 #endif
4833 }
4834 
4835 /******************************************************************************
4836  *           GetUserGeoID (KERNEL32.@)
4837  */
4838 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4839 {
4840     GEOID ret = GEOID_NOT_AVAILABLE;
4841     static const WCHAR geoW[] = {'G','e','o',0};
4842     static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4843     WCHAR bufferW[40], *end;
4844     DWORD count;
4845     HANDLE hkey, hSubkey = 0;
4846     UNICODE_STRING keyW;
4847     const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4848     RtlInitUnicodeString( &keyW, nationW );
4849     count = sizeof(bufferW);
4850 
4851     if(!(hkey = create_registry_key())) return ret;
4852 
4853     switch( GeoClass ){
4854     case GEOCLASS_NATION:
4855         if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4856         {
4857             if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4858                                 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4859                 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4860         }
4861         break;
4862     case GEOCLASS_REGION:
4863         FIXME("GEOCLASS_REGION not handled yet\n");
4864         break;
4865     }
4866 
4867     NtClose(hkey);
4868     if (hSubkey) NtClose(hSubkey);
4869     return ret;
4870 }
4871 
4872 /******************************************************************************
4873  *           SetUserGeoID (KERNEL32.@)
4874  */
4875 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4876 {
4877     static const WCHAR geoW[] = {'G','e','o',0};
4878     static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4879     static const WCHAR formatW[] = {'%','i',0};
4880     UNICODE_STRING nameW,keyW;
4881     WCHAR bufferW[10];
4882     OBJECT_ATTRIBUTES attr;
4883     HANDLE hkey;
4884 
4885     if(!(hkey = create_registry_key())) return FALSE;
4886 
4887     attr.Length = sizeof(attr);
4888     attr.RootDirectory = hkey;
4889     attr.ObjectName = &nameW;
4890     attr.Attributes = 0;
4891     attr.SecurityDescriptor = NULL;
4892     attr.SecurityQualityOfService = NULL;
4893     RtlInitUnicodeString( &nameW, geoW );
4894     RtlInitUnicodeString( &keyW, nationW );
4895 
4896     if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4897 
4898     {
4899         NtClose(attr.RootDirectory);
4900         return FALSE;
4901     }
4902 
4903     sprintfW(bufferW, formatW, GeoID);
4904     NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4905     NtClose(attr.RootDirectory);
4906     NtClose(hkey);
4907     return TRUE;
4908 }
4909 
4910 typedef struct
4911 {
4912     union
4913     {
4914         UILANGUAGE_ENUMPROCA procA;
4915         UILANGUAGE_ENUMPROCW procW;
4916     } u;
4917     DWORD flags;
4918     LONG_PTR param;
4919 } ENUM_UILANG_CALLBACK;
4920 
4921 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4922                                          LPCSTR name, WORD LangID, LONG_PTR lParam )
4923 {
4924     ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4925     char buf[20];
4926 
4927     sprintf(buf, "%08x", (UINT)LangID);
4928     return enum_uilang->u.procA( buf, enum_uilang->param );
4929 }
4930 
4931 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4932                                          LPCWSTR name, WORD LangID, LONG_PTR lParam )
4933 {
4934     static const WCHAR formatW[] = {'%','0','8','x',0};
4935     ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4936     WCHAR buf[20];
4937 
4938     sprintfW( buf, formatW, (UINT)LangID );
4939     return enum_uilang->u.procW( buf, enum_uilang->param );
4940 }
4941 
4942 /******************************************************************************
4943  *           EnumUILanguagesA (KERNEL32.@)
4944  */
4945 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4946 {
4947     ENUM_UILANG_CALLBACK enum_uilang;
4948 
4949     TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4950 
4951     if(!pUILangEnumProc) {
4952 	SetLastError(ERROR_INVALID_PARAMETER);
4953 	return FALSE;
4954     }
4955     if(dwFlags) {
4956 	SetLastError(ERROR_INVALID_FLAGS);
4957 	return FALSE;
4958     }
4959 
4960     enum_uilang.u.procA = pUILangEnumProc;
4961     enum_uilang.flags = dwFlags;
4962     enum_uilang.param = lParam;
4963 
4964     EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4965                             (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4966                             (LONG_PTR)&enum_uilang);
4967     return TRUE;
4968 }
4969 
4970 /******************************************************************************
4971  *           EnumUILanguagesW (KERNEL32.@)
4972  */
4973 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4974 {
4975     ENUM_UILANG_CALLBACK enum_uilang;
4976 
4977     TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4978 
4979 
4980     if(!pUILangEnumProc) {
4981 	SetLastError(ERROR_INVALID_PARAMETER);
4982 	return FALSE;
4983     }
4984     if(dwFlags) {
4985 	SetLastError(ERROR_INVALID_FLAGS);
4986 	return FALSE;
4987     }
4988 
4989     enum_uilang.u.procW = pUILangEnumProc;
4990     enum_uilang.flags = dwFlags;
4991     enum_uilang.param = lParam;
4992 
4993     EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4994                             (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4995                             (LONG_PTR)&enum_uilang);
4996     return TRUE;
4997 }
4998 
4999 enum locationkind {
5000     LOCATION_NATION = 0,
5001     LOCATION_REGION,
5002     LOCATION_BOTH
5003 };
5004 
5005 struct geoinfo_t {
5006     GEOID id;
5007     WCHAR iso2W[3];
5008     WCHAR iso3W[4];
5009     GEOID parent;
5010     INT   uncode;
5011     enum locationkind kind;
5012 };
5013 
5014 static const struct geoinfo_t geoinfodata[] = {
5015     { 2, {'A','G',0}, {'A','T','G',0}, 10039880,  28 }, /* Antigua and Barbuda */
5016     { 3, {'A','F',0}, {'A','F','G',0}, 47614,   4 }, /* Afghanistan */
5017     { 4, {'D','Z',0}, {'D','Z','A',0}, 42487,  12 }, /* Algeria */
5018     { 5, {'A','Z',0}, {'A','Z','E',0}, 47611,  31 }, /* Azerbaijan */
5019     { 6, {'A','L',0}, {'A','L','B',0}, 47610,   8 }, /* Albania */
5020     { 7, {'A','M',0}, {'A','R','M',0}, 47611,  51 }, /* Armenia */
5021     { 8, {'A','D',0}, {'A','N','D',0}, 47610,  20 }, /* Andorra */
5022     { 9, {'A','O',0}, {'A','G','O',0}, 42484,  24 }, /* Angola */
5023     { 10, {'A','S',0}, {'A','S','M',0}, 26286,  16 }, /* American Samoa */
5024     { 11, {'A','R',0}, {'A','R','G',0}, 31396,  32 }, /* Argentina */
5025     { 12, {'A','U',0}, {'A','U','S',0}, 10210825,  36 }, /* Australia */
5026     { 14, {'A','T',0}, {'A','U','T',0}, 10210824,  40 }, /* Austria */
5027     { 17, {'B','H',0}, {'B','H','R',0}, 47611,  48 }, /* Bahrain */
5028     { 18, {'B','B',0}, {'B','R','B',0}, 10039880,  52 }, /* Barbados */
5029     { 19, {'B','W',0}, {'B','W','A',0}, 10039883,  72 }, /* Botswana */
5030     { 20, {'B','M',0}, {'B','M','U',0}, 23581,  60 }, /* Bermuda */
5031     { 21, {'B','E',0}, {'B','E','L',0}, 10210824,  56 }, /* Belgium */
5032     { 22, {'B','S',0}, {'B','H','S',0}, 10039880,  44 }, /* Bahamas, The */
5033     { 23, {'B','D',0}, {'B','G','D',0}, 47614,  50 }, /* Bangladesh */
5034     { 24, {'B','Z',0}, {'B','L','Z',0}, 27082,  84 }, /* Belize */
5035     { 25, {'B','A',0}, {'B','I','H',0}, 47610,  70 }, /* Bosnia and Herzegovina */
5036     { 26, {'B','O',0}, {'B','O','L',0}, 31396,  68 }, /* Bolivia */
5037     { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
5038     { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
5039     { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
5040     { 30, {'S','B',0}, {'S','L','B',0}, 20900,  90 }, /* Solomon Islands */
5041     { 32, {'B','R',0}, {'B','R','A',0}, 31396,  76 }, /* Brazil */
5042     { 34, {'B','T',0}, {'B','T','N',0}, 47614,  64 }, /* Bhutan */
5043     { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
5044     { 37, {'B','N',0}, {'B','R','N',0}, 47599,  96 }, /* Brunei */
5045     { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
5046     { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
5047     { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
5048     { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
5049     { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
5050     { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
5051     { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
5052     { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
5053     { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
5054     { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
5055     { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
5056     { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
5057     { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
5058     { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
5059     { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
5060     { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
5061     { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
5062     { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
5063     { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
5064     { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
5065     { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
5066     { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
5067     { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
5068     { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
5069     { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
5070     { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
5071     { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
5072     { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
5073     { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
5074     { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
5075     { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
5076     { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
5077     { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
5078     { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
5079     { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
5080     { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
5081     { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
5082     { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
5083     { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
5084     { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
5085     { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
5086     { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
5087     { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
5088     { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
5089     { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
5090     { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
5091     { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
5092     { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
5093     { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
5094     { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
5095     { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
5096     { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
5097     { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
5098     { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
5099     { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
5100     { 114, {'I','O',0}, {'I','O','T',0}, 39070,  86 }, /* British Indian Ocean Territory */
5101     { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
5102     { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
5103     { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
5104     { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
5105     { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
5106     { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
5107     { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
5108     { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
5109     { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
5110     { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
5111     { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
5112     { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
5113     { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
5114     { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
5115     { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
5116     { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
5117     { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
5118     { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
5119     { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
5120     { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
5121     { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
5122     { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
5123     { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
5124     { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
5125     { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
5126     { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
5127     { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
5128     { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
5129     { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
5130     { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
5131     { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
5132     { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
5133     { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
5134     { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
5135     { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
5136     { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
5137     { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
5138     { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
5139     { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
5140     { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
5141     { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
5142     { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
5143     { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
5144     { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
5145     { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
5146     { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
5147     { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
5148     { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
5149     { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
5150     { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
5151     { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
5152     { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
5153     { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
5154     { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
5155     { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
5156     { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
5157     { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
5158     { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
5159     { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
5160     { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
5161     { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
5162     { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
5163     { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
5164     { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
5165     { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
5166     { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
5167     { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
5168     { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
5169     { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
5170     { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
5171     { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
5172     { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
5173     { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
5174     { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
5175     { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
5176     { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
5177     { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
5178     { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
5179     { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
5180     { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
5181     { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
5182     { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
5183     { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
5184     { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
5185     { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
5186     { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
5187     { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
5188     { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
5189     { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
5190     { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
5191     { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
5192     { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
5193     { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
5194     { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
5195     { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
5196     { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
5197     { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
5198     { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
5199     { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
5200     { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
5201     { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
5202     { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
5203     { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
5204     { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
5205     { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
5206     { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
5207     { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
5208     { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
5209     { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
5210     { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
5211     { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
5212     { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
5213     { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
5214     { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
5215     { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
5216     { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
5217     { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
5218     { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
5219     { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
5220     { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
5221     { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
5222     { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
5223     { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
5224     { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
5225     { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
5226     { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
5227     { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
5228     { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
5229     { 301, {'A','Q',0}, {'A','T','A',0}, 39070,  10 }, /* Antarctica */
5230     { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
5231     { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
5232     { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
5233     { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
5234     { 306, {'B','V',0}, {'B','V','T',0}, 39070,  74 }, /* Bouvet Island */
5235     { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
5236     { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
5237     { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
5238     { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
5239     { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
5240     { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
5241     { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
5242     { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
5243     { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
5244     { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
5245     { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
5246     { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
5247     { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
5248     { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
5249     { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
5250     { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
5251     { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
5252     { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
5253     { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
5254     { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
5255     { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
5256     { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
5257     { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
5258     { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
5259     { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
5260     { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
5261     { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
5262     { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
5263     { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
5264     { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
5265     { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
5266     { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
5267     { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
5268     { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
5269     { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
5270     { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
5271     { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
5272     { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
5273     { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
5274     { 351, {'V','G',0}, {'V','G','B',0}, 10039880,  92 }, /* Virgin Islands, British */
5275     { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
5276     { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
5277     { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
5278     { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
5279     { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
5280     { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
5281     { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
5282     { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
5283     { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
5284     { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
5285     { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
5286     { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
5287     { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
5288     { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
5289     { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
5290     { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
5291     { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
5292     { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
5293     { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
5294     { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
5295     { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
5296     { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
5297     { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
5298     { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
5299     { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
5300     { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
5301     { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
5302     { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
5303     { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
5304     { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
5305     { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
5306     { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
5307     { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
5308     { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
5309     { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
5310     { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
5311     { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
5312     { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
5313     { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
5314 };
5315 
5316 #ifdef __REACTOS__
5317 /* Callback function ptrs for EnumSystemCodePagesA/W */
5318 typedef struct
5319 {
5320   CODEPAGE_ENUMPROCA procA;
5321   CODEPAGE_ENUMPROCW procW;
5322   DWORD    dwFlags;
5323 } ENUMSYSTEMCODEPAGES_CALLBACKS;
5324 
5325 /* Internal implementation of EnumSystemCodePagesA/W */
5326 static BOOL NLS_EnumSystemCodePages(ENUMSYSTEMCODEPAGES_CALLBACKS *lpProcs)
5327 {
5328     WCHAR szNumber[5 + 1], szValue[MAX_PATH];
5329     HANDLE hKey;
5330     BOOL bContinue = TRUE;
5331     ULONG ulIndex = 0;
5332 
5333     if (!lpProcs)
5334     {
5335         SetLastError(ERROR_INVALID_PARAMETER);
5336         return FALSE;
5337     }
5338 
5339     switch (lpProcs->dwFlags)
5340     {
5341         case CP_INSTALLED:
5342         case CP_SUPPORTED:
5343             break;
5344         default:
5345             SetLastError(ERROR_INVALID_FLAGS);
5346             return FALSE;
5347     }
5348 
5349     hKey = NLS_RegOpenKey(0, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\NLS\\CodePage");
5350     if (!hKey)
5351     {
5352         WARN("NLS_RegOpenKey() failed\n");
5353         return FALSE;
5354     }
5355 
5356     while (bContinue)
5357     {
5358         if (NLS_RegEnumValue(hKey, ulIndex, szNumber, sizeof(szNumber),
5359                              szValue, sizeof(szValue)))
5360         {
5361             if ((lpProcs->dwFlags == CP_SUPPORTED)||
5362                 ((lpProcs->dwFlags == CP_INSTALLED)&&(wcslen(szValue) > 2)))
5363             {
5364                 if (lpProcs->procW)
5365                 {
5366                     bContinue = lpProcs->procW(szNumber);
5367                 }
5368                 else
5369                 {
5370                     char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
5371 
5372                     WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
5373                     bContinue = lpProcs->procA(szNumberA);
5374                 }
5375             }
5376 
5377             ulIndex++;
5378 
5379         } else bContinue = FALSE;
5380 
5381         if (!bContinue)
5382             break;
5383     }
5384 
5385     if (hKey)
5386         NtClose(hKey);
5387 
5388     return TRUE;
5389 }
5390 
5391 /*
5392  * @implemented
5393  */
5394 BOOL
5395 WINAPI
5396 EnumSystemCodePagesW (
5397     CODEPAGE_ENUMPROCW  lpCodePageEnumProc,
5398     DWORD               dwFlags
5399     )
5400 {
5401     ENUMSYSTEMCODEPAGES_CALLBACKS procs;
5402 
5403     TRACE("(%p,0x%08X)\n", lpCodePageEnumProc, dwFlags);
5404 
5405     procs.procA = NULL;
5406     procs.procW = lpCodePageEnumProc;
5407     procs.dwFlags = dwFlags;
5408 
5409     return NLS_EnumSystemCodePages(lpCodePageEnumProc ? &procs : NULL);
5410 }
5411 
5412 
5413 /*
5414  * @implemented
5415  */
5416 BOOL
5417 WINAPI
5418 EnumSystemCodePagesA (
5419     CODEPAGE_ENUMPROCA lpCodePageEnumProc,
5420     DWORD              dwFlags
5421     )
5422 {
5423     ENUMSYSTEMCODEPAGES_CALLBACKS procs;
5424 
5425     TRACE("(%p,0x%08X)\n", lpCodePageEnumProc, dwFlags);
5426 
5427     procs.procA = lpCodePageEnumProc;
5428     procs.procW = NULL;
5429     procs.dwFlags = dwFlags;
5430 
5431     return NLS_EnumSystemCodePages(lpCodePageEnumProc ? &procs : NULL);
5432 }
5433 
5434 
5435 static int
5436 #ifdef __REACTOS__
5437 NLS_GetGeoFriendlyName(GEOID Location, LPWSTR szFriendlyName, int cchData, LANGID lang)
5438 #else
5439 NLS_GetGeoFriendlyName(GEOID Location, LPWSTR szFriendlyName, int cchData)
5440 #endif
5441 {
5442     /* FIXME: move *.nls resources out of kernel32 into locale.nls */
5443     Location += NLSRC_OFFSET;
5444     Location &= 0xFFFF;
5445 
5446     if (cchData == 0)
5447 #ifdef __REACTOS__
5448         return GetLocalisedText(Location, NULL, 0, lang);
5449 #else
5450         return GetLocalisedText(Location, NULL, 0);
5451 #endif
5452 
5453 #ifdef __REACTOS__
5454     if (GetLocalisedText(Location, szFriendlyName, (UINT)cchData, lang))
5455 #else
5456     if (GetLocalisedText(Location, szFriendlyName, (UINT)cchData))
5457 #endif
5458         return strlenW(szFriendlyName) + 1;
5459 
5460     return 0;
5461 }
5462 #endif // __REACTOS__
5463 
5464 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
5465 {
5466     int min, max;
5467 
5468     min = 0;
5469     max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
5470 
5471     while (min <= max) {
5472         const struct geoinfo_t *ptr;
5473         int n = (min+max)/2;
5474 
5475         ptr = &geoinfodata[n];
5476         if (geoid == ptr->id)
5477             /* we don't need empty entries */
5478             return *ptr->iso2W ? ptr : NULL;
5479 
5480         if (ptr->id > geoid)
5481             max = n-1;
5482         else
5483             min = n+1;
5484     }
5485 
5486     return NULL;
5487 }
5488 
5489 /******************************************************************************
5490  *           GetGeoInfoW (KERNEL32.@)
5491  */
5492 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
5493 {
5494     const struct geoinfo_t *ptr;
5495     const WCHAR *str = NULL;
5496     WCHAR buffW[12];
5497     LONG val = 0;
5498     INT len;
5499 
5500     TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5501 
5502     if (!(ptr = get_geoinfo_dataptr(geoid))) {
5503         SetLastError(ERROR_INVALID_PARAMETER);
5504         return 0;
5505     }
5506 
5507     switch (geotype) {
5508     case GEO_FRIENDLYNAME:
5509     {
5510 #ifdef __REACTOS__
5511         return NLS_GetGeoFriendlyName(geoid, data, data_len, lang);
5512 #else
5513         return NLS_GetGeoFriendlyName(geoid, data, data_len);
5514 #endif
5515     }
5516     case GEO_NATION:
5517         val = geoid;
5518         break;
5519     case GEO_ISO_UN_NUMBER:
5520         val = ptr->uncode;
5521         break;
5522     case GEO_PARENT:
5523         val = ptr->parent;
5524         break;
5525     case GEO_ISO2:
5526     case GEO_ISO3:
5527     {
5528         str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
5529         break;
5530     }
5531     case GEO_RFC1766:
5532     case GEO_LCID:
5533     case GEO_OFFICIALNAME:
5534     case GEO_TIMEZONES:
5535     case GEO_OFFICIALLANGUAGES:
5536     case GEO_LATITUDE:
5537     case GEO_LONGITUDE:
5538         FIXME("type %d is not supported\n", geotype);
5539         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5540         return 0;
5541     default:
5542         WARN("unrecognized type %d\n", geotype);
5543         SetLastError(ERROR_INVALID_FLAGS);
5544         return 0;
5545     }
5546 
5547     if (val) {
5548         static const WCHAR fmtW[] = {'%','d',0};
5549         sprintfW(buffW, fmtW, val);
5550         str = buffW;
5551     }
5552 
5553     len = strlenW(str) + 1;
5554     if (!data || !data_len)
5555         return len;
5556 
5557     memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
5558     if (data_len < len)
5559         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5560     return data_len < len ? 0 : len;
5561 }
5562 
5563 /******************************************************************************
5564  *           GetGeoInfoA (KERNEL32.@)
5565  */
5566 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
5567 {
5568     WCHAR *buffW;
5569     INT len;
5570 
5571     TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5572 
5573     len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
5574     if (!len)
5575         return 0;
5576 
5577     buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
5578     if (!buffW)
5579         return 0;
5580 
5581     GetGeoInfoW(geoid, geotype, buffW, len, lang);
5582     len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
5583     if (!data || !data_len) {
5584         HeapFree(GetProcessHeap(), 0, buffW);
5585         return len;
5586     }
5587 
5588     len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
5589     HeapFree(GetProcessHeap(), 0, buffW);
5590 
5591     if (data_len < len)
5592         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5593     return data_len < len ? 0 : len;
5594 }
5595 
5596 /******************************************************************************
5597  *           EnumSystemGeoID    (KERNEL32.@)
5598  *
5599  * Call a users function for every location available on the system.
5600  *
5601  * PARAMS
5602  *  geoclass   [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
5603  *  parent     [I] GEOID for the parent
5604  *  enumproc   [I] Callback function to call for each location
5605  *
5606  * RETURNS
5607  *  Success: TRUE.
5608  *  Failure: FALSE. Use GetLastError() to determine the cause.
5609  */
5610 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
5611 {
5612     INT i;
5613 
5614     TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
5615 
5616     if (!enumproc) {
5617         SetLastError(ERROR_INVALID_PARAMETER);
5618         return FALSE;
5619     }
5620 
5621     if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
5622         SetLastError(ERROR_INVALID_FLAGS);
5623         return FALSE;
5624     }
5625 
5626     for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
5627         const struct geoinfo_t *ptr = &geoinfodata[i];
5628 
5629         if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
5630             continue;
5631 
5632         if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
5633             continue;
5634 
5635         if (parent && ptr->parent != parent)
5636             continue;
5637 
5638         if (!enumproc(ptr->id))
5639             return TRUE;
5640     }
5641 
5642     return TRUE;
5643 }
5644 
5645 #ifndef __REACTOS__
5646 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
5647 {
5648     LCID userlcid;
5649 
5650     TRACE("%p, %d\n", localename,  buffersize);
5651 
5652     userlcid = GetUserDefaultLCID();
5653     return LCIDToLocaleName(userlcid, localename, buffersize, 0);
5654 }
5655 
5656 /******************************************************************************
5657  *           NormalizeString (KERNEL32.@)
5658  */
5659 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
5660                            LPWSTR lpDstString, INT cwDstLength)
5661 {
5662     FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
5663     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5664     return 0;
5665 }
5666 
5667 /******************************************************************************
5668  *           IsNormalizedString (KERNEL32.@)
5669  */
5670 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
5671 {
5672     FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5673     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5674     return FALSE;
5675 }
5676 
5677 enum {
5678     BASE = 36,
5679     TMIN = 1,
5680     TMAX = 26,
5681     SKEW = 38,
5682     DAMP = 700,
5683     INIT_BIAS = 72,
5684     INIT_N = 128
5685 };
5686 
5687 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5688 {
5689     INT k;
5690 
5691     delta /= (firsttime ? DAMP : 2);
5692     delta += delta/numpoints;
5693 
5694     for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5695         delta /= BASE-TMIN;
5696     return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5697 }
5698 
5699 /******************************************************************************
5700  *           IdnToAscii (KERNEL32.@)
5701  * Implementation of Punycode based on RFC 3492.
5702  */
5703 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5704                       LPWSTR lpASCIICharStr, INT cchASCIIChar)
5705 {
5706     static const WCHAR prefixW[] = {'x','n','-','-'};
5707 
5708     WCHAR *norm_str;
5709     INT i, label_start, label_end, norm_len, out_label, out = 0;
5710 
5711     TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5712         lpASCIICharStr, cchASCIIChar);
5713 
5714     norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5715     if(!norm_len)
5716         return 0;
5717     norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5718     if(!norm_str) {
5719         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5720         return 0;
5721     }
5722     norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5723             cchUnicodeChar, norm_str, norm_len);
5724     if(!norm_len) {
5725         HeapFree(GetProcessHeap(), 0, norm_str);
5726         return 0;
5727     }
5728 
5729     for(label_start=0; label_start<norm_len;) {
5730         INT n = INIT_N, bias = INIT_BIAS;
5731         INT delta = 0, b = 0, h;
5732 
5733         out_label = out;
5734         for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5735                 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5736             if(norm_str[i] < 0x80)
5737                 b++;
5738         label_end = i;
5739 
5740         if(b == label_end-label_start) {
5741             if(label_end < norm_len)
5742                 b++;
5743             if(!lpASCIICharStr) {
5744                 out += b;
5745             }else if(out+b <= cchASCIIChar) {
5746                 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5747                 out += b;
5748             }else {
5749                 HeapFree(GetProcessHeap(), 0, norm_str);
5750                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5751                 return 0;
5752             }
5753             label_start = label_end+1;
5754             continue;
5755         }
5756 
5757         if(!lpASCIICharStr) {
5758             out += 5+b; /* strlen(xn--...-) */
5759         }else if(out+5+b <= cchASCIIChar) {
5760             memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5761             out += 4;
5762             for(i=label_start; i<label_end; i++)
5763                 if(norm_str[i] < 0x80)
5764                     lpASCIICharStr[out++] = norm_str[i];
5765             lpASCIICharStr[out++] = '-';
5766         }else {
5767             HeapFree(GetProcessHeap(), 0, norm_str);
5768             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5769             return 0;
5770         }
5771         if(!b)
5772             out--;
5773 
5774         for(h=b; h<label_end-label_start;) {
5775             INT m = 0xffff, q, k;
5776 
5777             for(i=label_start; i<label_end; i++) {
5778                 if(norm_str[i]>=n && m>norm_str[i])
5779                     m = norm_str[i];
5780             }
5781             delta += (m-n)*(h+1);
5782             n = m;
5783 
5784             for(i=label_start; i<label_end; i++) {
5785                 if(norm_str[i] < n) {
5786                     delta++;
5787                 }else if(norm_str[i] == n) {
5788                     for(q=delta, k=BASE; ; k+=BASE) {
5789                         INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5790                         INT disp = q<t ? q : t+(q-t)%(BASE-t);
5791                         if(!lpASCIICharStr) {
5792                             out++;
5793                         }else if(out+1 <= cchASCIIChar) {
5794                             lpASCIICharStr[out++] = disp<='z'-'a' ?
5795                                 'a'+disp : '0'+disp-'z'+'a'-1;
5796                         }else {
5797                             HeapFree(GetProcessHeap(), 0, norm_str);
5798                             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5799                             return 0;
5800                         }
5801                         if(q < t)
5802                             break;
5803                         q = (q-t)/(BASE-t);
5804                     }
5805                     bias = adapt(delta, h+1, h==b);
5806                     delta = 0;
5807                     h++;
5808                 }
5809             }
5810             delta++;
5811             n++;
5812         }
5813 
5814         if(out-out_label > 63) {
5815             HeapFree(GetProcessHeap(), 0, norm_str);
5816             SetLastError(ERROR_INVALID_NAME);
5817             return 0;
5818         }
5819 
5820         if(label_end < norm_len) {
5821             if(!lpASCIICharStr) {
5822                 out++;
5823             }else if(out+1 <= cchASCIIChar) {
5824                 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5825             }else {
5826                 HeapFree(GetProcessHeap(), 0, norm_str);
5827                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5828                 return 0;
5829             }
5830         }
5831         label_start = label_end+1;
5832     }
5833 
5834     HeapFree(GetProcessHeap(), 0, norm_str);
5835     return out;
5836 }
5837 
5838 /******************************************************************************
5839  *           IdnToNameprepUnicode (KERNEL32.@)
5840  */
5841 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5842                                 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5843 {
5844     enum {
5845         UNASSIGNED = 0x1,
5846         PROHIBITED = 0x2,
5847         BIDI_RAL   = 0x4,
5848         BIDI_L     = 0x8
5849     };
5850 
5851     extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5852     extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5853     const WCHAR *ptr;
5854     WORD flags;
5855     WCHAR buf[64], *map_str, norm_str[64], ch;
5856     DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5857     BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5858 
5859     TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5860         lpNameprepCharStr, cchNameprepChar);
5861 
5862     if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5863         SetLastError(ERROR_INVALID_FLAGS);
5864         return 0;
5865     }
5866 
5867     if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5868         SetLastError(ERROR_INVALID_PARAMETER);
5869         return 0;
5870     }
5871 
5872     if(cchUnicodeChar == -1)
5873         cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5874     if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5875         SetLastError(ERROR_INVALID_NAME);
5876         return 0;
5877     }
5878 
5879     for(label_start=0; label_start<cchUnicodeChar;) {
5880         ascii_only = TRUE;
5881         for(i=label_start; i<cchUnicodeChar; i++) {
5882             ch = lpUnicodeCharStr[i];
5883 
5884             if(i!=cchUnicodeChar-1 && !ch) {
5885                 SetLastError(ERROR_INVALID_NAME);
5886                 return 0;
5887             }
5888             /* check if ch is one of label separators defined in RFC3490 */
5889             if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5890                 break;
5891 
5892             if(ch > 0x7f) {
5893                 ascii_only = FALSE;
5894                 continue;
5895             }
5896 
5897             if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5898                 continue;
5899             if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5900                     || (ch>='0' && ch<='9') || ch=='-')
5901                 continue;
5902 
5903             SetLastError(ERROR_INVALID_NAME);
5904             return 0;
5905         }
5906         label_end = i;
5907         /* last label may be empty */
5908         if(label_start==label_end && ch) {
5909             SetLastError(ERROR_INVALID_NAME);
5910             return 0;
5911         }
5912 
5913         if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5914                     lpUnicodeCharStr[label_end-1]=='-')) {
5915             SetLastError(ERROR_INVALID_NAME);
5916             return 0;
5917         }
5918 
5919         if(ascii_only) {
5920             /* maximal label length is 63 characters */
5921             if(label_end-label_start > 63) {
5922                 SetLastError(ERROR_INVALID_NAME);
5923                 return 0;
5924             }
5925             if(label_end < cchUnicodeChar)
5926                 label_end++;
5927 
5928             if(!lpNameprepCharStr) {
5929                 out += label_end-label_start;
5930             }else if(out+label_end-label_start <= cchNameprepChar) {
5931                 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5932                         (label_end-label_start)*sizeof(WCHAR));
5933                 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5934                     lpNameprepCharStr[out+label_end-label_start-1] = '.';
5935                 out += label_end-label_start;
5936             }else {
5937                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5938                 return 0;
5939             }
5940 
5941             label_start = label_end;
5942             continue;
5943         }
5944 
5945         map_len = 0;
5946         for(i=label_start; i<label_end; i++) {
5947             ch = lpUnicodeCharStr[i];
5948             ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5949             ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5950 
5951             if(!ptr[0]) map_len++;
5952             else if(!ptr[1]) map_len++;
5953             else if(!ptr[2]) map_len += 2;
5954             else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5955         }
5956         if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5957             map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5958             if(!map_str) {
5959                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5960                 return 0;
5961             }
5962         }else {
5963             map_str = buf;
5964         }
5965         map_len = 0;
5966         for(i=label_start; i<label_end; i++) {
5967             ch = lpUnicodeCharStr[i];
5968             ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5969             ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5970 
5971             if(!ptr[0]) {
5972                 map_str[map_len++] = ch;
5973             }else if(!ptr[1]) {
5974                 map_str[map_len++] = ptr[0];
5975             }else if(!ptr[2]) {
5976                 map_str[map_len++] = ptr[0];
5977                 map_str[map_len++] = ptr[1];
5978             }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5979                 map_str[map_len++] = ptr[0];
5980                 map_str[map_len++] = ptr[1];
5981                 map_str[map_len++] = ptr[2];
5982             }
5983         }
5984 
5985         norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5986                 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5987         if(map_str != buf)
5988             HeapFree(GetProcessHeap(), 0, map_str);
5989         if(!norm_len) {
5990             if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5991                 SetLastError(ERROR_INVALID_NAME);
5992             return 0;
5993         }
5994 
5995         if(label_end < cchUnicodeChar) {
5996             norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5997             label_end++;
5998         }
5999 
6000         if(!lpNameprepCharStr) {
6001             out += norm_len;
6002         }else if(out+norm_len <= cchNameprepChar) {
6003             memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
6004             out += norm_len;
6005         }else {
6006             SetLastError(ERROR_INSUFFICIENT_BUFFER);
6007             return 0;
6008         }
6009 
6010         have_bidi_ral = prohibit_bidi_ral = FALSE;
6011         mask = PROHIBITED;
6012         if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
6013             mask |= UNASSIGNED;
6014         for(i=0; i<norm_len; i++) {
6015             ch = norm_str[i];
6016             flags = get_table_entry( nameprep_char_type, ch );
6017 
6018             if(flags & mask) {
6019                 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
6020                         : ERROR_NO_UNICODE_TRANSLATION);
6021                 return 0;
6022             }
6023 
6024             if(flags & BIDI_RAL)
6025                 have_bidi_ral = TRUE;
6026             if(flags & BIDI_L)
6027                 prohibit_bidi_ral = TRUE;
6028         }
6029 
6030         if(have_bidi_ral) {
6031             ch = norm_str[0];
6032             flags = get_table_entry( nameprep_char_type, ch );
6033             if((flags & BIDI_RAL) == 0)
6034                 prohibit_bidi_ral = TRUE;
6035 
6036             ch = norm_str[norm_len-1];
6037             flags = get_table_entry( nameprep_char_type, ch );
6038             if((flags & BIDI_RAL) == 0)
6039                 prohibit_bidi_ral = TRUE;
6040         }
6041 
6042         if(have_bidi_ral && prohibit_bidi_ral) {
6043             SetLastError(ERROR_INVALID_NAME);
6044             return 0;
6045         }
6046 
6047         label_start = label_end;
6048     }
6049 
6050     return out;
6051 }
6052 
6053 /******************************************************************************
6054  *           IdnToUnicode (KERNEL32.@)
6055  */
6056 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
6057                         LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
6058 {
6059     extern const unsigned short nameprep_char_type[];
6060 
6061     INT i, label_start, label_end, out_label, out = 0;
6062     WCHAR ch;
6063 
6064     TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
6065         lpUnicodeCharStr, cchUnicodeChar);
6066 
6067     for(label_start=0; label_start<cchASCIIChar;) {
6068         INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
6069 
6070         out_label = out;
6071         for(i=label_start; i<cchASCIIChar; i++) {
6072             ch = lpASCIICharStr[i];
6073 
6074             if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
6075                 SetLastError(ERROR_INVALID_NAME);
6076                 return 0;
6077             }
6078 
6079             if(!ch || ch=='.')
6080                 break;
6081             if(ch == '-')
6082                 delim = i;
6083 
6084             if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
6085                 continue;
6086             if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
6087                     || (ch>='0' && ch<='9') || ch=='-')
6088                 continue;
6089 
6090             SetLastError(ERROR_INVALID_NAME);
6091             return 0;
6092         }
6093         label_end = i;
6094         /* last label may be empty */
6095         if(label_start==label_end && ch) {
6096             SetLastError(ERROR_INVALID_NAME);
6097             return 0;
6098         }
6099 
6100         if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
6101                     lpASCIICharStr[label_end-1]=='-')) {
6102             SetLastError(ERROR_INVALID_NAME);
6103             return 0;
6104         }
6105         if(label_end-label_start > 63) {
6106             SetLastError(ERROR_INVALID_NAME);
6107             return 0;
6108         }
6109 
6110         if(label_end-label_start<4 ||
6111                 tolowerW(lpASCIICharStr[label_start])!='x' ||
6112                 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
6113                 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
6114             if(label_end < cchASCIIChar)
6115                 label_end++;
6116 
6117             if(!lpUnicodeCharStr) {
6118                 out += label_end-label_start;
6119             }else if(out+label_end-label_start <= cchUnicodeChar) {
6120                 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
6121                         (label_end-label_start)*sizeof(WCHAR));
6122                 out += label_end-label_start;
6123             }else {
6124                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
6125                 return 0;
6126             }
6127 
6128             label_start = label_end;
6129             continue;
6130         }
6131 
6132         if(delim == label_start+3)
6133             delim++;
6134         if(!lpUnicodeCharStr) {
6135             out += delim-label_start-4;
6136         }else if(out+delim-label_start-4 <= cchUnicodeChar) {
6137             memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
6138                     (delim-label_start-4)*sizeof(WCHAR));
6139             out += delim-label_start-4;
6140         }else {
6141             SetLastError(ERROR_INSUFFICIENT_BUFFER);
6142             return 0;
6143         }
6144         if(out != out_label)
6145             delim++;
6146 
6147         for(i=delim; i<label_end;) {
6148             old_pos = pos;
6149             w = 1;
6150             for(k=BASE; ; k+=BASE) {
6151                 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
6152                 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
6153                     SetLastError(ERROR_INVALID_NAME);
6154                     return 0;
6155                 }
6156                 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
6157                 pos += digit*w;
6158                 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
6159                 if(digit < t)
6160                     break;
6161                 w *= BASE-t;
6162             }
6163             bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
6164             n += pos/(out-out_label+1);
6165             pos %= out-out_label+1;
6166 
6167             if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
6168                     get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
6169                 SetLastError(ERROR_INVALID_NAME);
6170                 return 0;
6171             }
6172             if(!lpUnicodeCharStr) {
6173                 out++;
6174             }else if(out+1 <= cchASCIIChar) {
6175                 memmove(lpUnicodeCharStr+out_label+pos+1,
6176                         lpUnicodeCharStr+out_label+pos,
6177                         (out-out_label-pos)*sizeof(WCHAR));
6178                 lpUnicodeCharStr[out_label+pos] = n;
6179                 out++;
6180             }else {
6181                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
6182                 return 0;
6183             }
6184             pos++;
6185         }
6186 
6187         if(out-out_label > 63) {
6188             SetLastError(ERROR_INVALID_NAME);
6189             return 0;
6190         }
6191 
6192         if(label_end < cchASCIIChar) {
6193             if(!lpUnicodeCharStr) {
6194                 out++;
6195             }else if(out+1 <= cchUnicodeChar) {
6196                 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
6197             }else {
6198                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
6199                 return 0;
6200             }
6201         }
6202         label_start = label_end+1;
6203     }
6204 
6205     return out;
6206 }
6207 
6208 
6209 /******************************************************************************
6210  *           GetFileMUIPath (KERNEL32.@)
6211  */
6212 
6213 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
6214                            PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
6215 {
6216     FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
6217            debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
6218 
6219     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6220 
6221     return FALSE;
6222 }
6223 
6224 /******************************************************************************
6225  *           GetFileMUIInfo (KERNEL32.@)
6226  */
6227 
6228 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
6229 {
6230     FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
6231 
6232     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6233     return FALSE;
6234 }
6235 
6236 /******************************************************************************
6237  *           ResolveLocaleName (KERNEL32.@)
6238  */
6239 
6240 INT WINAPI ResolveLocaleName(LPCWSTR name, LPWSTR localename, INT len)
6241 {
6242     FIXME("stub: %s, %p, %d\n", wine_dbgstr_w(name), localename, len);
6243 
6244     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6245     return 0;
6246 }
6247 #endif // !__REACTOS__
6248