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