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 ®istry_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