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