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