wined3d: Implement SM5 deriv_rtx_fine instruction.
[wine.git] / dlls / kernel32 / locale.c
blobfefa373917c0feb3f88ef4452ca2e2990df060ad
1 /*
2 * Locale support
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
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.
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.
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
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFLocale.h>
37 # include <CoreFoundation/CFString.h>
38 #endif
40 #include "ntstatus.h"
41 #define WIN32_NO_STATUS
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h" /* for RT_STRINGW */
45 #include "winternl.h"
46 #include "wine/unicode.h"
47 #include "winnls.h"
48 #include "winerror.h"
49 #include "winver.h"
50 #include "kernel_private.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(nls);
55 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
56 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
58 /* current code pages */
59 static const union cptable *ansi_cptable;
60 static const union cptable *oem_cptable;
61 static const union cptable *mac_cptable;
62 static const union cptable *unix_cptable; /* NULL if UTF8 */
64 static const WCHAR szLocaleKeyName[] = {
65 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
66 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
67 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
70 static const WCHAR szLangGroupsKeyName[] = {
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','\\',
74 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
77 /* Charset to codepage map, sorted by name. */
78 static const struct charset_entry
80 const char *charset_name;
81 UINT codepage;
82 } charset_names[] =
84 { "BIG5", 950 },
85 { "CP1250", 1250 },
86 { "CP1251", 1251 },
87 { "CP1252", 1252 },
88 { "CP1253", 1253 },
89 { "CP1254", 1254 },
90 { "CP1255", 1255 },
91 { "CP1256", 1256 },
92 { "CP1257", 1257 },
93 { "CP1258", 1258 },
94 { "CP932", 932 },
95 { "CP936", 936 },
96 { "CP949", 949 },
97 { "CP950", 950 },
98 { "EUCJP", 20932 },
99 { "GB2312", 936 },
100 { "IBM037", 37 },
101 { "IBM1026", 1026 },
102 { "IBM424", 424 },
103 { "IBM437", 437 },
104 { "IBM500", 500 },
105 { "IBM850", 850 },
106 { "IBM852", 852 },
107 { "IBM855", 855 },
108 { "IBM857", 857 },
109 { "IBM860", 860 },
110 { "IBM861", 861 },
111 { "IBM862", 862 },
112 { "IBM863", 863 },
113 { "IBM864", 864 },
114 { "IBM865", 865 },
115 { "IBM866", 866 },
116 { "IBM869", 869 },
117 { "IBM874", 874 },
118 { "IBM875", 875 },
119 { "ISO88591", 28591 },
120 { "ISO885910", 28600 },
121 { "ISO885913", 28603 },
122 { "ISO885914", 28604 },
123 { "ISO885915", 28605 },
124 { "ISO885916", 28606 },
125 { "ISO88592", 28592 },
126 { "ISO88593", 28593 },
127 { "ISO88594", 28594 },
128 { "ISO88595", 28595 },
129 { "ISO88596", 28596 },
130 { "ISO88597", 28597 },
131 { "ISO88598", 28598 },
132 { "ISO88599", 28599 },
133 { "KOI8R", 20866 },
134 { "KOI8U", 21866 },
135 { "UTF8", CP_UTF8 }
139 struct locale_name
141 WCHAR win_name[128]; /* Windows name ("en-US") */
142 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
143 WCHAR *country; /* country ("US") */
144 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
145 WCHAR *script; /* script ("Latn") for Windows format only */
146 WCHAR *modifier; /* modifier or sort order */
147 LCID lcid; /* corresponding LCID */
148 int matches; /* number of elements matching LCID (0..4) */
149 UINT codepage; /* codepage corresponding to charset */
152 /* locale ids corresponding to the various Unix locale parameters */
153 static LCID lcid_LC_COLLATE;
154 static LCID lcid_LC_CTYPE;
155 static LCID lcid_LC_MESSAGES;
156 static LCID lcid_LC_MONETARY;
157 static LCID lcid_LC_NUMERIC;
158 static LCID lcid_LC_TIME;
159 static LCID lcid_LC_PAPER;
160 static LCID lcid_LC_MEASUREMENT;
161 static LCID lcid_LC_TELEPHONE;
163 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
164 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
165 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
166 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
167 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
168 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
169 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
170 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
171 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
172 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
173 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
174 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
175 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
176 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
177 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
178 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
179 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
180 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
181 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
182 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
183 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
184 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
185 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
186 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
187 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
188 static const WCHAR sListW[] = {'s','L','i','s','t',0};
189 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
190 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
191 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
192 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
193 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
194 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
195 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
196 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
197 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
198 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
199 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
200 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
201 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
203 static struct registry_value
205 DWORD lctype;
206 const WCHAR *name;
207 WCHAR *cached_value;
208 } registry_values[] =
210 { LOCALE_ICALENDARTYPE, iCalendarTypeW },
211 { LOCALE_ICURRDIGITS, iCurrDigitsW },
212 { LOCALE_ICURRENCY, iCurrencyW },
213 { LOCALE_IDIGITS, iDigitsW },
214 { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
215 { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
216 { LOCALE_ILZERO, iLZeroW },
217 { LOCALE_IMEASURE, iMeasureW },
218 { LOCALE_INEGCURR, iNegCurrW },
219 { LOCALE_INEGNUMBER, iNegNumberW },
220 { LOCALE_IPAPERSIZE, iPaperSizeW },
221 { LOCALE_ITIME, iTimeW },
222 { LOCALE_S1159, s1159W },
223 { LOCALE_S2359, s2359W },
224 { LOCALE_SCURRENCY, sCurrencyW },
225 { LOCALE_SDATE, sDateW },
226 { LOCALE_SDECIMAL, sDecimalW },
227 { LOCALE_SGROUPING, sGroupingW },
228 { LOCALE_SLIST, sListW },
229 { LOCALE_SLONGDATE, sLongDateW },
230 { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
231 { LOCALE_SMONGROUPING, sMonGroupingW },
232 { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
233 { LOCALE_SNEGATIVESIGN, sNegativeSignW },
234 { LOCALE_SPOSITIVESIGN, sPositiveSignW },
235 { LOCALE_SSHORTDATE, sShortDateW },
236 { LOCALE_STHOUSAND, sThousandW },
237 { LOCALE_STIME, sTimeW },
238 { LOCALE_STIMEFORMAT, sTimeFormatW },
239 { LOCALE_SYEARMONTH, sYearMonthW },
240 /* The following are not listed under MSDN as supported,
241 * but seem to be used and also stored in the registry.
243 { LOCALE_ICOUNTRY, iCountryW },
244 { LOCALE_IDATE, iDateW },
245 { LOCALE_ILDATE, iLDateW },
246 { LOCALE_ITLZERO, iTLZeroW },
247 { LOCALE_SCOUNTRY, sCountryW },
248 { LOCALE_SABBREVLANGNAME, sLanguageW },
249 /* The following are used in XP and later */
250 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
251 { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
252 { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
255 static CRITICAL_SECTION cache_section;
256 static CRITICAL_SECTION_DEBUG critsect_debug =
258 0, 0, &cache_section,
259 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
260 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
262 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
264 /* Copy Ascii string to Unicode without using codepages */
265 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
267 while (n > 1 && *src)
269 *dst++ = (unsigned char)*src++;
270 n--;
272 if (n) *dst = 0;
275 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
277 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
280 /***********************************************************************
281 * get_lcid_codepage
283 * Retrieve the ANSI codepage for a given locale.
285 static inline UINT get_lcid_codepage( LCID lcid )
287 UINT ret;
288 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
289 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
290 return ret;
294 /***********************************************************************
295 * get_codepage_table
297 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
299 static const union cptable *get_codepage_table( unsigned int codepage )
301 const union cptable *ret = NULL;
303 assert( ansi_cptable ); /* init must have been done already */
305 switch(codepage)
307 case CP_ACP:
308 return ansi_cptable;
309 case CP_OEMCP:
310 return oem_cptable;
311 case CP_MACCP:
312 return mac_cptable;
313 case CP_UTF7:
314 case CP_UTF8:
315 break;
316 case CP_THREAD_ACP:
317 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
318 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
319 if (!codepage) return ansi_cptable;
320 /* fall through */
321 default:
322 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
323 if (codepage == oem_cptable->info.codepage) return oem_cptable;
324 if (codepage == mac_cptable->info.codepage) return mac_cptable;
325 ret = wine_cp_get_table( codepage );
326 break;
328 return ret;
332 /***********************************************************************
333 * charset_cmp (internal)
335 static int charset_cmp( const void *name, const void *entry )
337 const struct charset_entry *charset = entry;
338 return strcasecmp( name, charset->charset_name );
341 /***********************************************************************
342 * find_charset
344 static UINT find_charset( const WCHAR *name )
346 const struct charset_entry *entry;
347 char charset_name[16];
348 size_t i, j;
350 /* remove punctuation characters from charset name */
351 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
352 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
353 charset_name[j] = 0;
355 entry = bsearch( charset_name, charset_names,
356 sizeof(charset_names)/sizeof(charset_names[0]),
357 sizeof(charset_names[0]), charset_cmp );
358 if (entry) return entry->codepage;
359 return 0;
363 /***********************************************************************
364 * find_locale_id_callback
366 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
367 LPCWSTR name, WORD LangID, LPARAM lParam )
369 struct locale_name *data = (struct locale_name *)lParam;
370 WCHAR buffer[128];
371 int matches = 0;
372 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
374 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
376 /* first check exact name */
377 if (data->win_name[0] &&
378 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
379 buffer, sizeof(buffer)/sizeof(WCHAR) ))
381 if (!strcmpW( data->win_name, buffer ))
383 matches = 4; /* everything matches */
384 goto done;
388 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
389 buffer, sizeof(buffer)/sizeof(WCHAR) ))
390 return TRUE;
391 if (strcmpW( buffer, data->lang )) return TRUE;
392 matches++; /* language name matched */
394 if (data->country)
396 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
397 buffer, sizeof(buffer)/sizeof(WCHAR) ))
399 if (strcmpW( buffer, data->country )) goto done;
400 matches++; /* country name matched */
403 else /* match default language */
405 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
408 if (data->codepage)
410 UINT unix_cp;
411 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
412 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
414 if (unix_cp == data->codepage) matches++;
418 /* FIXME: check sort order */
420 done:
421 if (matches > data->matches)
423 data->lcid = lcid;
424 data->matches = matches;
426 return (data->matches < 4); /* no need to continue for perfect match */
430 /***********************************************************************
431 * parse_locale_name
433 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
434 * Unix format is: lang[_country][.charset][@modifier]
435 * Windows format is: lang[-script][-country][_modifier]
437 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
439 static const WCHAR sepW[] = {'-','_','.','@',0};
440 static const WCHAR winsepW[] = {'-','_',0};
441 static const WCHAR posixW[] = {'P','O','S','I','X',0};
442 static const WCHAR cW[] = {'C',0};
443 static const WCHAR latinW[] = {'l','a','t','i','n',0};
444 static const WCHAR latnW[] = {'-','L','a','t','n',0};
445 WCHAR *p;
447 TRACE("%s\n", debugstr_w(str));
449 name->country = name->charset = name->script = name->modifier = NULL;
450 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
451 name->matches = 0;
452 name->codepage = 0;
453 name->win_name[0] = 0;
454 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
456 if (!*name->lang)
458 name->lcid = LOCALE_INVARIANT;
459 name->matches = 4;
460 return;
463 if (!(p = strpbrkW( name->lang, sepW )))
465 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
467 name->matches = 4; /* perfect match for default English lcid */
468 return;
470 strcpyW( name->win_name, name->lang );
472 else if (*p == '-') /* Windows format */
474 strcpyW( name->win_name, name->lang );
475 *p++ = 0;
476 name->country = p;
477 if (!(p = strpbrkW( p, winsepW ))) goto done;
478 if (*p == '-')
480 *p++ = 0;
481 name->script = name->country;
482 name->country = p;
483 if (!(p = strpbrkW( p, winsepW ))) goto done;
485 *p++ = 0;
486 name->modifier = p;
488 else /* Unix format */
490 if (*p == '_')
492 *p++ = 0;
493 name->country = p;
494 p = strpbrkW( p, sepW + 2 );
496 if (p && *p == '.')
498 *p++ = 0;
499 name->charset = p;
500 p = strchrW( p, '@' );
502 if (p)
504 *p++ = 0;
505 name->modifier = p;
508 if (name->charset)
509 name->codepage = find_charset( name->charset );
511 /* rebuild a Windows name if possible */
513 if (name->charset) goto done; /* can't specify charset in Windows format */
514 if (name->modifier && strcmpW( name->modifier, latinW ))
515 goto done; /* only Latn script supported for now */
516 strcpyW( name->win_name, name->lang );
517 if (name->modifier) strcatW( name->win_name, latnW );
518 if (name->country)
520 p = name->win_name + strlenW(name->win_name);
521 *p++ = '-';
522 strcpyW( p, name->country );
525 done:
526 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
527 find_locale_id_callback, (LPARAM)name );
531 /***********************************************************************
532 * convert_default_lcid
534 * Get the default LCID to use for a given lctype in GetLocaleInfo.
536 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
538 if (lcid == LOCALE_SYSTEM_DEFAULT ||
539 lcid == LOCALE_USER_DEFAULT ||
540 lcid == LOCALE_NEUTRAL)
542 LCID default_id = 0;
544 switch(lctype & 0xffff)
546 case LOCALE_SSORTNAME:
547 default_id = lcid_LC_COLLATE;
548 break;
550 case LOCALE_FONTSIGNATURE:
551 case LOCALE_IDEFAULTANSICODEPAGE:
552 case LOCALE_IDEFAULTCODEPAGE:
553 case LOCALE_IDEFAULTEBCDICCODEPAGE:
554 case LOCALE_IDEFAULTMACCODEPAGE:
555 case LOCALE_IDEFAULTUNIXCODEPAGE:
556 default_id = lcid_LC_CTYPE;
557 break;
559 case LOCALE_ICURRDIGITS:
560 case LOCALE_ICURRENCY:
561 case LOCALE_IINTLCURRDIGITS:
562 case LOCALE_INEGCURR:
563 case LOCALE_INEGSEPBYSPACE:
564 case LOCALE_INEGSIGNPOSN:
565 case LOCALE_INEGSYMPRECEDES:
566 case LOCALE_IPOSSEPBYSPACE:
567 case LOCALE_IPOSSIGNPOSN:
568 case LOCALE_IPOSSYMPRECEDES:
569 case LOCALE_SCURRENCY:
570 case LOCALE_SINTLSYMBOL:
571 case LOCALE_SMONDECIMALSEP:
572 case LOCALE_SMONGROUPING:
573 case LOCALE_SMONTHOUSANDSEP:
574 case LOCALE_SNATIVECURRNAME:
575 default_id = lcid_LC_MONETARY;
576 break;
578 case LOCALE_IDIGITS:
579 case LOCALE_IDIGITSUBSTITUTION:
580 case LOCALE_ILZERO:
581 case LOCALE_INEGNUMBER:
582 case LOCALE_SDECIMAL:
583 case LOCALE_SGROUPING:
584 case LOCALE_SNAN:
585 case LOCALE_SNATIVEDIGITS:
586 case LOCALE_SNEGATIVESIGN:
587 case LOCALE_SNEGINFINITY:
588 case LOCALE_SPOSINFINITY:
589 case LOCALE_SPOSITIVESIGN:
590 case LOCALE_STHOUSAND:
591 default_id = lcid_LC_NUMERIC;
592 break;
594 case LOCALE_ICALENDARTYPE:
595 case LOCALE_ICENTURY:
596 case LOCALE_IDATE:
597 case LOCALE_IDAYLZERO:
598 case LOCALE_IFIRSTDAYOFWEEK:
599 case LOCALE_IFIRSTWEEKOFYEAR:
600 case LOCALE_ILDATE:
601 case LOCALE_IMONLZERO:
602 case LOCALE_IOPTIONALCALENDAR:
603 case LOCALE_ITIME:
604 case LOCALE_ITIMEMARKPOSN:
605 case LOCALE_ITLZERO:
606 case LOCALE_S1159:
607 case LOCALE_S2359:
608 case LOCALE_SABBREVDAYNAME1:
609 case LOCALE_SABBREVDAYNAME2:
610 case LOCALE_SABBREVDAYNAME3:
611 case LOCALE_SABBREVDAYNAME4:
612 case LOCALE_SABBREVDAYNAME5:
613 case LOCALE_SABBREVDAYNAME6:
614 case LOCALE_SABBREVDAYNAME7:
615 case LOCALE_SABBREVMONTHNAME1:
616 case LOCALE_SABBREVMONTHNAME2:
617 case LOCALE_SABBREVMONTHNAME3:
618 case LOCALE_SABBREVMONTHNAME4:
619 case LOCALE_SABBREVMONTHNAME5:
620 case LOCALE_SABBREVMONTHNAME6:
621 case LOCALE_SABBREVMONTHNAME7:
622 case LOCALE_SABBREVMONTHNAME8:
623 case LOCALE_SABBREVMONTHNAME9:
624 case LOCALE_SABBREVMONTHNAME10:
625 case LOCALE_SABBREVMONTHNAME11:
626 case LOCALE_SABBREVMONTHNAME12:
627 case LOCALE_SABBREVMONTHNAME13:
628 case LOCALE_SDATE:
629 case LOCALE_SDAYNAME1:
630 case LOCALE_SDAYNAME2:
631 case LOCALE_SDAYNAME3:
632 case LOCALE_SDAYNAME4:
633 case LOCALE_SDAYNAME5:
634 case LOCALE_SDAYNAME6:
635 case LOCALE_SDAYNAME7:
636 case LOCALE_SDURATION:
637 case LOCALE_SLONGDATE:
638 case LOCALE_SMONTHNAME1:
639 case LOCALE_SMONTHNAME2:
640 case LOCALE_SMONTHNAME3:
641 case LOCALE_SMONTHNAME4:
642 case LOCALE_SMONTHNAME5:
643 case LOCALE_SMONTHNAME6:
644 case LOCALE_SMONTHNAME7:
645 case LOCALE_SMONTHNAME8:
646 case LOCALE_SMONTHNAME9:
647 case LOCALE_SMONTHNAME10:
648 case LOCALE_SMONTHNAME11:
649 case LOCALE_SMONTHNAME12:
650 case LOCALE_SMONTHNAME13:
651 case LOCALE_SSHORTDATE:
652 case LOCALE_SSHORTESTDAYNAME1:
653 case LOCALE_SSHORTESTDAYNAME2:
654 case LOCALE_SSHORTESTDAYNAME3:
655 case LOCALE_SSHORTESTDAYNAME4:
656 case LOCALE_SSHORTESTDAYNAME5:
657 case LOCALE_SSHORTESTDAYNAME6:
658 case LOCALE_SSHORTESTDAYNAME7:
659 case LOCALE_STIME:
660 case LOCALE_STIMEFORMAT:
661 case LOCALE_SYEARMONTH:
662 default_id = lcid_LC_TIME;
663 break;
665 case LOCALE_IPAPERSIZE:
666 default_id = lcid_LC_PAPER;
667 break;
669 case LOCALE_IMEASURE:
670 default_id = lcid_LC_MEASUREMENT;
671 break;
673 case LOCALE_ICOUNTRY:
674 default_id = lcid_LC_TELEPHONE;
675 break;
677 if (default_id) lcid = default_id;
679 return ConvertDefaultLocale( lcid );
682 /***********************************************************************
683 * is_genitive_name_supported
685 * Determine could LCTYPE basically support genitive name form or not.
687 static BOOL is_genitive_name_supported( LCTYPE lctype )
689 switch(lctype & 0xffff)
691 case LOCALE_SMONTHNAME1:
692 case LOCALE_SMONTHNAME2:
693 case LOCALE_SMONTHNAME3:
694 case LOCALE_SMONTHNAME4:
695 case LOCALE_SMONTHNAME5:
696 case LOCALE_SMONTHNAME6:
697 case LOCALE_SMONTHNAME7:
698 case LOCALE_SMONTHNAME8:
699 case LOCALE_SMONTHNAME9:
700 case LOCALE_SMONTHNAME10:
701 case LOCALE_SMONTHNAME11:
702 case LOCALE_SMONTHNAME12:
703 case LOCALE_SMONTHNAME13:
704 return TRUE;
705 default:
706 return FALSE;
710 /***********************************************************************
711 * create_registry_key
713 * Create the Control Panel\\International registry key.
715 static inline HANDLE create_registry_key(void)
717 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
718 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
719 OBJECT_ATTRIBUTES attr;
720 UNICODE_STRING nameW;
721 HANDLE cpl_key, hkey = 0;
723 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
725 attr.Length = sizeof(attr);
726 attr.RootDirectory = hkey;
727 attr.ObjectName = &nameW;
728 attr.Attributes = 0;
729 attr.SecurityDescriptor = NULL;
730 attr.SecurityQualityOfService = NULL;
731 RtlInitUnicodeString( &nameW, cplW );
733 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
735 NtClose( attr.RootDirectory );
736 attr.RootDirectory = cpl_key;
737 RtlInitUnicodeString( &nameW, intlW );
738 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
740 NtClose( attr.RootDirectory );
741 return hkey;
745 /* update the registry settings for a given locale parameter */
746 /* return TRUE if an update was needed */
747 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
748 const LCTYPE *values, UINT nb_values )
750 static const WCHAR formatW[] = { '%','0','8','x',0 };
751 WCHAR bufferW[40];
752 UNICODE_STRING nameW;
753 DWORD count, i;
755 RtlInitUnicodeString( &nameW, name );
756 count = sizeof(bufferW);
757 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
759 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
760 LPCWSTR text = (LPCWSTR)info->Data;
762 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
763 TRACE( "updating registry, locale %s changed %s -> %08x\n",
764 debugstr_w(name), debugstr_w(text), lcid );
766 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
767 sprintfW( bufferW, formatW, lcid );
768 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
770 for (i = 0; i < nb_values; i++)
772 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
773 sizeof(bufferW)/sizeof(WCHAR) );
774 SetLocaleInfoW( lcid, values[i], bufferW );
776 return TRUE;
780 /***********************************************************************
781 * LOCALE_InitRegistry
783 * Update registry contents on startup if the user locale has changed.
784 * This simulates the action of the Windows control panel.
786 void LOCALE_InitRegistry(void)
788 static const WCHAR acpW[] = {'A','C','P',0};
789 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
790 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
791 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
792 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
793 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
794 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
795 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
796 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
797 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
798 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
799 static const struct
801 LPCWSTR name;
802 USHORT value;
803 } update_cp_values[] = {
804 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
805 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
806 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
808 static const LCTYPE lc_messages_values[] = {
809 LOCALE_SABBREVLANGNAME,
810 LOCALE_SCOUNTRY,
811 LOCALE_SLIST };
812 static const LCTYPE lc_monetary_values[] = {
813 LOCALE_SCURRENCY,
814 LOCALE_ICURRENCY,
815 LOCALE_INEGCURR,
816 LOCALE_ICURRDIGITS,
817 LOCALE_ILZERO,
818 LOCALE_SMONDECIMALSEP,
819 LOCALE_SMONGROUPING,
820 LOCALE_SMONTHOUSANDSEP };
821 static const LCTYPE lc_numeric_values[] = {
822 LOCALE_SDECIMAL,
823 LOCALE_STHOUSAND,
824 LOCALE_IDIGITS,
825 LOCALE_IDIGITSUBSTITUTION,
826 LOCALE_SNATIVEDIGITS,
827 LOCALE_INEGNUMBER,
828 LOCALE_SNEGATIVESIGN,
829 LOCALE_SPOSITIVESIGN,
830 LOCALE_SGROUPING };
831 static const LCTYPE lc_time_values[] = {
832 LOCALE_S1159,
833 LOCALE_S2359,
834 LOCALE_STIME,
835 LOCALE_ITIME,
836 LOCALE_ITLZERO,
837 LOCALE_SSHORTDATE,
838 LOCALE_SLONGDATE,
839 LOCALE_SDATE,
840 LOCALE_ITIMEMARKPOSN,
841 LOCALE_ICALENDARTYPE,
842 LOCALE_IFIRSTDAYOFWEEK,
843 LOCALE_IFIRSTWEEKOFYEAR,
844 LOCALE_STIMEFORMAT,
845 LOCALE_SYEARMONTH,
846 LOCALE_IDATE };
847 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
848 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
849 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
851 UNICODE_STRING nameW;
852 WCHAR bufferW[80];
853 DWORD count, i;
854 HANDLE hkey;
855 LCID lcid = GetUserDefaultLCID();
857 if (!(hkey = create_registry_key()))
858 return; /* don't do anything if we can't create the registry key */
860 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
861 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
862 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
863 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
864 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
865 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
866 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
867 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
868 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
869 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
870 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
871 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
872 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
873 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
875 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
877 static const WCHAR codepageW[] =
878 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
879 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
880 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
882 OBJECT_ATTRIBUTES attr;
883 HANDLE nls_key;
884 DWORD len = 14;
886 RtlInitUnicodeString( &nameW, codepageW );
887 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
888 while (codepageW[len])
890 nameW.Length = len * sizeof(WCHAR);
891 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
892 NtClose( nls_key );
893 len++;
894 while (codepageW[len] && codepageW[len] != '\\') len++;
896 nameW.Length = len * sizeof(WCHAR);
897 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
899 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
901 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
902 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
903 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
904 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
906 NtClose( nls_key );
910 NtClose( hkey );
914 #ifdef __APPLE__
915 /***********************************************************************
916 * get_mac_locale
918 * Return a locale identifier string reflecting the Mac locale, in a form
919 * that parse_locale_name() will understand. So, strip out unusual
920 * things like script, variant, etc. Or, rather, just construct it as
921 * <lang>[_<country>].UTF-8.
923 static const char* get_mac_locale(void)
925 static char mac_locale[50];
927 if (!mac_locale[0])
929 CFLocaleRef locale = CFLocaleCopyCurrent();
930 CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
931 CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
932 CFStringRef locale_string;
934 if (country)
935 locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
936 else
937 locale_string = CFStringCreateCopy(NULL, lang);
939 CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
940 strcat(mac_locale, ".UTF-8");
942 CFRelease(locale);
943 CFRelease(locale_string);
946 return mac_locale;
950 /***********************************************************************
951 * has_env
953 static BOOL has_env(const char* name)
955 const char* value = getenv( name );
956 return value && value[0];
958 #endif
961 /***********************************************************************
962 * get_locale
964 * Get the locale identifier for a given category. On most platforms,
965 * this is just a thin wrapper around setlocale(). On OS X, though, it
966 * is common for the Mac locale settings to not be supported by the C
967 * library. So, we sometimes override the result with the Mac locale.
969 static const char* get_locale(int category, const char* category_name)
971 const char* ret = setlocale(category, NULL);
973 #ifdef __APPLE__
974 /* If LC_ALL is set, respect it as a user override.
975 If LC_* is set, respect it as a user override, except if it's LC_CTYPE
976 and equal to UTF-8. That's because, when the Mac locale isn't supported
977 by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
978 parse_locale_name() doesn't handle that properly, so we override that
979 with the Mac locale (which uses UTF-8 for the charset, anyway).
980 Otherwise:
981 For LC_MESSAGES, we override the C library because the user language
982 setting is separate from the locale setting on which LANG was based.
983 If the C library didn't get anything better from LANG than C or POSIX,
984 override that. That probably means the Mac locale isn't supported by
985 the C library. */
986 if (!has_env( "LC_ALL" ) &&
987 ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
988 (!has_env( category_name ) &&
989 (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
991 const char* override = get_mac_locale();
993 if (category == LC_MESSAGES)
995 /* Retrieve the preferred language as chosen in System Preferences. */
996 static char messages_locale[50];
998 if (!messages_locale[0])
1000 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1001 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1003 CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1004 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1005 if (components)
1007 CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1008 CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1009 CFLocaleRef locale = NULL;
1010 CFStringRef locale_string;
1012 if (!country)
1014 locale = CFLocaleCopyCurrent();
1015 country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1018 if (country)
1019 locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1020 else
1021 locale_string = CFStringCreateCopy( NULL, lang );
1022 CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1023 strcat( messages_locale, ".UTF-8" );
1025 CFRelease( locale_string );
1026 if (locale) CFRelease( locale );
1027 CFRelease( components );
1030 if (preferred_langs)
1031 CFRelease( preferred_langs );
1034 if (messages_locale[0])
1035 override = messages_locale;
1038 TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1039 ret = override;
1041 #endif
1043 return ret;
1047 /***********************************************************************
1048 * setup_unix_locales
1050 static UINT setup_unix_locales(void)
1052 struct locale_name locale_name;
1053 WCHAR buffer[128], ctype_buff[128];
1054 const char *locale;
1055 UINT unix_cp = 0;
1057 if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1059 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
1060 parse_locale_name( ctype_buff, &locale_name );
1061 lcid_LC_CTYPE = locale_name.lcid;
1062 unix_cp = locale_name.codepage;
1064 if (!lcid_LC_CTYPE) /* this one needs a default value */
1065 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
1067 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1068 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
1070 #define GET_UNIX_LOCALE(cat) do \
1071 if ((locale = get_locale( cat, #cat ))) \
1073 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
1074 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1075 else { \
1076 parse_locale_name( buffer, &locale_name ); \
1077 lcid_##cat = locale_name.lcid; \
1078 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
1079 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1081 } while (0)
1083 GET_UNIX_LOCALE( LC_COLLATE );
1084 GET_UNIX_LOCALE( LC_MESSAGES );
1085 GET_UNIX_LOCALE( LC_MONETARY );
1086 GET_UNIX_LOCALE( LC_NUMERIC );
1087 GET_UNIX_LOCALE( LC_TIME );
1088 #ifdef LC_PAPER
1089 GET_UNIX_LOCALE( LC_PAPER );
1090 #endif
1091 #ifdef LC_MEASUREMENT
1092 GET_UNIX_LOCALE( LC_MEASUREMENT );
1093 #endif
1094 #ifdef LC_TELEPHONE
1095 GET_UNIX_LOCALE( LC_TELEPHONE );
1096 #endif
1098 #undef GET_UNIX_LOCALE
1100 return unix_cp;
1104 /***********************************************************************
1105 * GetUserDefaultLangID (KERNEL32.@)
1107 * Get the default language Id for the current user.
1109 * PARAMS
1110 * None.
1112 * RETURNS
1113 * The current LANGID of the default language for the current user.
1115 LANGID WINAPI GetUserDefaultLangID(void)
1117 return LANGIDFROMLCID(GetUserDefaultLCID());
1121 /***********************************************************************
1122 * GetSystemDefaultLangID (KERNEL32.@)
1124 * Get the default language Id for the system.
1126 * PARAMS
1127 * None.
1129 * RETURNS
1130 * The current LANGID of the default language for the system.
1132 LANGID WINAPI GetSystemDefaultLangID(void)
1134 return LANGIDFROMLCID(GetSystemDefaultLCID());
1138 /***********************************************************************
1139 * GetUserDefaultLCID (KERNEL32.@)
1141 * Get the default locale Id for the current user.
1143 * PARAMS
1144 * None.
1146 * RETURNS
1147 * The current LCID of the default locale for the current user.
1149 LCID WINAPI GetUserDefaultLCID(void)
1151 LCID lcid;
1152 NtQueryDefaultLocale( TRUE, &lcid );
1153 return lcid;
1157 /***********************************************************************
1158 * GetSystemDefaultLCID (KERNEL32.@)
1160 * Get the default locale Id for the system.
1162 * PARAMS
1163 * None.
1165 * RETURNS
1166 * The current LCID of the default locale for the system.
1168 LCID WINAPI GetSystemDefaultLCID(void)
1170 LCID lcid;
1171 NtQueryDefaultLocale( FALSE, &lcid );
1172 return lcid;
1175 /***********************************************************************
1176 * GetSystemDefaultLocaleName (KERNEL32.@)
1178 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1180 LCID lcid = GetSystemDefaultLCID();
1181 return LCIDToLocaleName(lcid, localename, len, 0);
1184 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1186 LCTYPE type;
1187 int lsize;
1189 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1191 if (flags & MUI_LANGUAGE_ID)
1192 type = LOCALE_ILANGUAGE;
1193 else
1194 type = LOCALE_SNAME;
1196 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1197 if (!lsize)
1199 /* keep last error from callee */
1200 return FALSE;
1202 lsize++;
1203 if (!*size)
1205 *size = lsize;
1206 *count = 1;
1207 return TRUE;
1210 if (lsize > *size)
1212 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1213 return FALSE;
1216 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1218 /* keep last error from callee */
1219 return FALSE;
1222 buffer[lsize-1] = 0;
1223 *size = lsize;
1224 *count = 1;
1225 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1226 return TRUE;
1230 /***********************************************************************
1231 * GetSystemPreferredUILanguages (KERNEL32.@)
1233 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1235 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1237 SetLastError(ERROR_INVALID_PARAMETER);
1238 return FALSE;
1240 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1242 SetLastError(ERROR_INVALID_PARAMETER);
1243 return FALSE;
1245 if (*size && !buffer)
1247 SetLastError(ERROR_INVALID_PARAMETER);
1248 return FALSE;
1251 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1254 /***********************************************************************
1255 * SetThreadPreferredUILanguages (KERNEL32.@)
1257 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1259 FIXME( "%u, %p, %p\n", flags, buffer, count );
1260 return TRUE;
1263 /***********************************************************************
1264 * GetThreadPreferredUILanguages (KERNEL32.@)
1266 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1268 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1269 return get_dummy_preferred_ui_language( flags, count, buf, size );
1272 /***********************************************************************
1273 * GetUserDefaultUILanguage (KERNEL32.@)
1275 * Get the default user interface language Id for the current user.
1277 * PARAMS
1278 * None.
1280 * RETURNS
1281 * The current LANGID of the default UI language for the current user.
1283 LANGID WINAPI GetUserDefaultUILanguage(void)
1285 LANGID lang;
1286 NtQueryDefaultUILanguage( &lang );
1287 return lang;
1291 /***********************************************************************
1292 * GetSystemDefaultUILanguage (KERNEL32.@)
1294 * Get the default user interface language Id for the system.
1296 * PARAMS
1297 * None.
1299 * RETURNS
1300 * The current LANGID of the default UI language for the system. This is
1301 * typically the same language used during the installation process.
1303 LANGID WINAPI GetSystemDefaultUILanguage(void)
1305 LANGID lang;
1306 NtQueryInstallUILanguage( &lang );
1307 return lang;
1311 /***********************************************************************
1312 * LocaleNameToLCID (KERNEL32.@)
1314 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1316 struct locale_name locale_name;
1318 if (flags) FIXME( "unsupported flags %x\n", flags );
1320 if (name == LOCALE_NAME_USER_DEFAULT)
1321 return GetUserDefaultLCID();
1323 /* string parsing */
1324 parse_locale_name( name, &locale_name );
1326 TRACE( "found lcid %x for %s, matches %d\n",
1327 locale_name.lcid, debugstr_w(name), locale_name.matches );
1329 if (!locale_name.matches)
1331 SetLastError(ERROR_INVALID_PARAMETER);
1332 return 0;
1335 if (locale_name.matches == 1)
1336 WARN( "locale %s not recognized, defaulting to %s\n",
1337 debugstr_w(name), debugstr_w(locale_name.lang) );
1339 return locale_name.lcid;
1343 /***********************************************************************
1344 * LCIDToLocaleName (KERNEL32.@)
1346 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1348 if (flags) FIXME( "unsupported flags %x\n", flags );
1350 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1354 /******************************************************************************
1355 * get_locale_registry_value
1357 * Gets the registry value name and cache for a given lctype.
1359 static struct registry_value *get_locale_registry_value( DWORD lctype )
1361 int i;
1362 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1363 if (registry_values[i].lctype == lctype)
1364 return &registry_values[i];
1365 return NULL;
1369 /******************************************************************************
1370 * get_registry_locale_info
1372 * Retrieve user-modified locale info from the registry.
1373 * Return length, 0 on error, -1 if not found.
1375 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1377 DWORD size;
1378 INT ret;
1379 HANDLE hkey;
1380 NTSTATUS status;
1381 UNICODE_STRING nameW;
1382 KEY_VALUE_PARTIAL_INFORMATION *info;
1383 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1385 RtlEnterCriticalSection( &cache_section );
1387 if (!registry_value->cached_value)
1389 if (!(hkey = create_registry_key()))
1391 RtlLeaveCriticalSection( &cache_section );
1392 return -1;
1395 RtlInitUnicodeString( &nameW, registry_value->name );
1396 size = info_size + len * sizeof(WCHAR);
1398 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1400 NtClose( hkey );
1401 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1402 RtlLeaveCriticalSection( &cache_section );
1403 return 0;
1406 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1408 /* try again with a bigger buffer when we have to return the correct size */
1409 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1411 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1412 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1414 info = new_info;
1415 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1419 NtClose( hkey );
1421 if (!status)
1423 INT length = (size - info_size) / sizeof(WCHAR);
1424 LPWSTR cached_value;
1426 if (!length || ((WCHAR *)&info->Data)[length-1])
1427 length++;
1429 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1431 if (!cached_value)
1433 HeapFree( GetProcessHeap(), 0, info );
1434 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1435 RtlLeaveCriticalSection( &cache_section );
1436 return 0;
1439 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1440 cached_value[length-1] = 0;
1441 HeapFree( GetProcessHeap(), 0, info );
1442 registry_value->cached_value = cached_value;
1444 else
1446 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1448 ret = (size - info_size) / sizeof(WCHAR);
1450 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1452 ret = -1;
1454 else
1456 SetLastError( RtlNtStatusToDosError(status) );
1457 ret = 0;
1459 HeapFree( GetProcessHeap(), 0, info );
1460 RtlLeaveCriticalSection( &cache_section );
1461 return ret;
1465 ret = lstrlenW( registry_value->cached_value ) + 1;
1467 if (buffer)
1469 if (ret > len)
1471 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1472 ret = 0;
1474 else
1476 lstrcpyW( buffer, registry_value->cached_value );
1480 RtlLeaveCriticalSection( &cache_section );
1482 return ret;
1486 /******************************************************************************
1487 * GetLocaleInfoA (KERNEL32.@)
1489 * Get information about an aspect of a locale.
1491 * PARAMS
1492 * lcid [I] LCID of the locale
1493 * lctype [I] LCTYPE_ flags from "winnls.h"
1494 * buffer [O] Destination for the information
1495 * len [I] Length of buffer in characters
1497 * RETURNS
1498 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1499 * with the information.
1500 * Failure: 0. Use GetLastError() to determine the cause.
1502 * NOTES
1503 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1504 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1505 * which is a bit string.
1507 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1509 WCHAR *bufferW;
1510 INT lenW, ret;
1512 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1514 if (len < 0 || (len && !buffer))
1516 SetLastError( ERROR_INVALID_PARAMETER );
1517 return 0;
1519 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1520 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1522 SetLastError( ERROR_INVALID_FLAGS );
1523 return 0;
1526 if (!len) buffer = NULL;
1528 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1530 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1532 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1533 return 0;
1535 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1537 if ((lctype & LOCALE_RETURN_NUMBER) ||
1538 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1540 /* it's not an ASCII string, just bytes */
1541 ret *= sizeof(WCHAR);
1542 if (buffer)
1544 if (ret <= len) memcpy( buffer, bufferW, ret );
1545 else
1547 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1548 ret = 0;
1552 else
1554 UINT codepage = CP_ACP;
1555 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1556 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1559 HeapFree( GetProcessHeap(), 0, bufferW );
1560 return ret;
1563 static int get_value_base_by_lctype( LCTYPE lctype )
1565 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1568 /******************************************************************************
1569 * GetLocaleInfoW (KERNEL32.@)
1571 * See GetLocaleInfoA.
1573 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1575 LANGID lang_id;
1576 HRSRC hrsrc;
1577 HGLOBAL hmem;
1578 INT ret;
1579 UINT lcflags;
1580 const WCHAR *p;
1581 unsigned int i;
1583 if (len < 0 || (len && !buffer))
1585 SetLastError( ERROR_INVALID_PARAMETER );
1586 return 0;
1588 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1589 !is_genitive_name_supported( lctype ))
1591 SetLastError( ERROR_INVALID_FLAGS );
1592 return 0;
1595 if (!len) buffer = NULL;
1597 lcid = convert_default_lcid( lcid, lctype );
1599 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1600 lctype &= 0xffff;
1602 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1604 /* first check for overrides in the registry */
1606 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1607 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1609 struct registry_value *value = get_locale_registry_value(lctype);
1611 if (value)
1613 if (lcflags & LOCALE_RETURN_NUMBER)
1615 WCHAR tmp[16];
1616 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1617 if (ret > 0)
1619 WCHAR *end;
1620 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1621 if (*end) /* invalid number */
1623 SetLastError( ERROR_INVALID_FLAGS );
1624 return 0;
1626 ret = sizeof(UINT)/sizeof(WCHAR);
1627 if (!buffer) return ret;
1628 if (ret > len)
1630 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1631 return 0;
1633 memcpy( buffer, &number, sizeof(number) );
1636 else ret = get_registry_locale_info( value, buffer, len );
1638 if (ret != -1) return ret;
1642 /* now load it from kernel resources */
1644 lang_id = LANGIDFROMLCID( lcid );
1646 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1647 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1648 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1650 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1651 ULongToPtr((lctype >> 4) + 1), lang_id )))
1653 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1654 return 0;
1656 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1657 return 0;
1659 p = LockResource( hmem );
1660 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1662 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1663 else if (is_genitive_name_supported( lctype ) && *p)
1665 /* genitive form's stored after a null separator from a nominative */
1666 for (i = 1; i <= *p; i++) if (!p[i]) break;
1668 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1670 ret = *p - i + 1;
1671 p += i;
1673 else ret = i;
1675 else
1676 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1678 if (!buffer) return ret;
1680 if (ret > len)
1682 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1683 return 0;
1686 if (lcflags & LOCALE_RETURN_NUMBER)
1688 UINT number;
1689 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1690 if (!tmp) return 0;
1691 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1692 tmp[*p] = 0;
1693 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1694 if (!*end)
1695 memcpy( buffer, &number, sizeof(number) );
1696 else /* invalid number */
1698 SetLastError( ERROR_INVALID_FLAGS );
1699 ret = 0;
1701 HeapFree( GetProcessHeap(), 0, tmp );
1703 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1704 lcid, lctype, buffer, len, number );
1706 else
1708 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1709 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1711 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1712 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1714 return ret;
1717 /******************************************************************************
1718 * GetLocaleInfoEx (KERNEL32.@)
1720 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1722 LCID lcid = LocaleNameToLCID(locale, 0);
1724 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1726 if (!lcid) return 0;
1728 /* special handling for neutral locale names */
1729 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1731 if (len && len < 3)
1733 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1734 return 0;
1737 if (len) strcpyW(buffer, locale);
1738 return 3;
1741 return GetLocaleInfoW(lcid, info, buffer, len);
1744 /******************************************************************************
1745 * SetLocaleInfoA [KERNEL32.@]
1747 * Set information about an aspect of a locale.
1749 * PARAMS
1750 * lcid [I] LCID of the locale
1751 * lctype [I] LCTYPE_ flags from "winnls.h"
1752 * data [I] Information to set
1754 * RETURNS
1755 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1756 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1757 * Failure: FALSE. Use GetLastError() to determine the cause.
1759 * NOTES
1760 * - Values are only be set for the current user locale; the system locale
1761 * settings cannot be changed.
1762 * - Any settings changed by this call are lost when the locale is changed by
1763 * the control panel (in Wine, this happens every time you change LANG).
1764 * - The native implementation of this function does not check that lcid matches
1765 * the current user locale, and simply sets the new values. Wine warns you in
1766 * this case, but behaves the same.
1768 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1770 UINT codepage = CP_ACP;
1771 WCHAR *strW;
1772 DWORD len;
1773 BOOL ret;
1775 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1777 if (!data)
1779 SetLastError( ERROR_INVALID_PARAMETER );
1780 return FALSE;
1782 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1783 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1785 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1786 return FALSE;
1788 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1789 ret = SetLocaleInfoW( lcid, lctype, strW );
1790 HeapFree( GetProcessHeap(), 0, strW );
1791 return ret;
1795 /******************************************************************************
1796 * SetLocaleInfoW (KERNEL32.@)
1798 * See SetLocaleInfoA.
1800 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1802 struct registry_value *value;
1803 static const WCHAR intlW[] = {'i','n','t','l',0 };
1804 UNICODE_STRING valueW;
1805 NTSTATUS status;
1806 HANDLE hkey;
1808 lctype &= 0xffff;
1809 value = get_locale_registry_value( lctype );
1811 if (!data || !value)
1813 SetLastError( ERROR_INVALID_PARAMETER );
1814 return FALSE;
1817 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1819 SetLastError( ERROR_INVALID_FLAGS );
1820 return FALSE;
1823 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1825 /* FIXME: should check that data to set is sane */
1827 /* FIXME: profile functions should map to registry */
1828 WriteProfileStringW( intlW, value->name, data );
1830 if (!(hkey = create_registry_key())) return FALSE;
1831 RtlInitUnicodeString( &valueW, value->name );
1832 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1834 RtlEnterCriticalSection( &cache_section );
1835 HeapFree( GetProcessHeap(), 0, value->cached_value );
1836 value->cached_value = NULL;
1837 RtlLeaveCriticalSection( &cache_section );
1839 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1841 /* Set I-value from S value */
1842 WCHAR *lpD, *lpM, *lpY;
1843 WCHAR szBuff[2];
1845 lpD = strrchrW(data, 'd');
1846 lpM = strrchrW(data, 'M');
1847 lpY = strrchrW(data, 'y');
1849 if (lpD <= lpM)
1851 szBuff[0] = '1'; /* D-M-Y */
1853 else
1855 if (lpY <= lpM)
1856 szBuff[0] = '2'; /* Y-M-D */
1857 else
1858 szBuff[0] = '0'; /* M-D-Y */
1861 szBuff[1] = '\0';
1863 if (lctype == LOCALE_SSHORTDATE)
1864 lctype = LOCALE_IDATE;
1865 else
1866 lctype = LOCALE_ILDATE;
1868 value = get_locale_registry_value( lctype );
1870 WriteProfileStringW( intlW, value->name, szBuff );
1872 RtlInitUnicodeString( &valueW, value->name );
1873 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1875 RtlEnterCriticalSection( &cache_section );
1876 HeapFree( GetProcessHeap(), 0, value->cached_value );
1877 value->cached_value = NULL;
1878 RtlLeaveCriticalSection( &cache_section );
1881 NtClose( hkey );
1883 if (status) SetLastError( RtlNtStatusToDosError(status) );
1884 return !status;
1888 /******************************************************************************
1889 * GetACP (KERNEL32.@)
1891 * Get the current Ansi code page Id for the system.
1893 * PARAMS
1894 * None.
1896 * RETURNS
1897 * The current Ansi code page identifier for the system.
1899 UINT WINAPI GetACP(void)
1901 assert( ansi_cptable );
1902 return ansi_cptable->info.codepage;
1906 /******************************************************************************
1907 * SetCPGlobal (KERNEL32.@)
1909 * Set the current Ansi code page Id for the system.
1911 * PARAMS
1912 * acp [I] code page ID to be the new ACP.
1914 * RETURNS
1915 * The previous ACP.
1917 UINT WINAPI SetCPGlobal( UINT acp )
1919 UINT ret = GetACP();
1920 const union cptable *new_cptable = wine_cp_get_table( acp );
1922 if (new_cptable) ansi_cptable = new_cptable;
1923 return ret;
1927 /***********************************************************************
1928 * GetOEMCP (KERNEL32.@)
1930 * Get the current OEM code page Id for the system.
1932 * PARAMS
1933 * None.
1935 * RETURNS
1936 * The current OEM code page identifier for the system.
1938 UINT WINAPI GetOEMCP(void)
1940 assert( oem_cptable );
1941 return oem_cptable->info.codepage;
1945 /***********************************************************************
1946 * IsValidCodePage (KERNEL32.@)
1948 * Determine if a given code page identifier is valid.
1950 * PARAMS
1951 * codepage [I] Code page Id to verify.
1953 * RETURNS
1954 * TRUE, If codepage is valid and available on the system,
1955 * FALSE otherwise.
1957 BOOL WINAPI IsValidCodePage( UINT codepage )
1959 switch(codepage) {
1960 case CP_UTF7:
1961 case CP_UTF8:
1962 return TRUE;
1963 default:
1964 return wine_cp_get_table( codepage ) != NULL;
1969 /***********************************************************************
1970 * IsDBCSLeadByteEx (KERNEL32.@)
1972 * Determine if a character is a lead byte in a given code page.
1974 * PARAMS
1975 * codepage [I] Code page for the test.
1976 * testchar [I] Character to test
1978 * RETURNS
1979 * TRUE, if testchar is a lead byte in codepage,
1980 * FALSE otherwise.
1982 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1984 const union cptable *table = get_codepage_table( codepage );
1985 return table && wine_is_dbcs_leadbyte( table, testchar );
1989 /***********************************************************************
1990 * IsDBCSLeadByte (KERNEL32.@)
1991 * IsDBCSLeadByte (KERNEL.207)
1993 * Determine if a character is a lead byte.
1995 * PARAMS
1996 * testchar [I] Character to test
1998 * RETURNS
1999 * TRUE, if testchar is a lead byte in the ANSI code page,
2000 * FALSE otherwise.
2002 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2004 if (!ansi_cptable) return FALSE;
2005 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2009 /***********************************************************************
2010 * GetCPInfo (KERNEL32.@)
2012 * Get information about a code page.
2014 * PARAMS
2015 * codepage [I] Code page number
2016 * cpinfo [O] Destination for code page information
2018 * RETURNS
2019 * Success: TRUE. cpinfo is updated with the information about codepage.
2020 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2022 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2024 const union cptable *table;
2026 if (!cpinfo)
2028 SetLastError( ERROR_INVALID_PARAMETER );
2029 return FALSE;
2032 if (!(table = get_codepage_table( codepage )))
2034 switch(codepage)
2036 case CP_UTF7:
2037 case CP_UTF8:
2038 cpinfo->DefaultChar[0] = 0x3f;
2039 cpinfo->DefaultChar[1] = 0;
2040 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2041 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2042 return TRUE;
2045 SetLastError( ERROR_INVALID_PARAMETER );
2046 return FALSE;
2048 if (table->info.def_char & 0xff00)
2050 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2051 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2053 else
2055 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2056 cpinfo->DefaultChar[1] = 0;
2058 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2059 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2060 else
2061 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2063 return TRUE;
2066 /***********************************************************************
2067 * GetCPInfoExA (KERNEL32.@)
2069 * Get extended information about a code page.
2071 * PARAMS
2072 * codepage [I] Code page number
2073 * dwFlags [I] Reserved, must to 0.
2074 * cpinfo [O] Destination for code page information
2076 * RETURNS
2077 * Success: TRUE. cpinfo is updated with the information about codepage.
2078 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2080 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2082 CPINFOEXW cpinfoW;
2084 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2085 return FALSE;
2087 /* the layout is the same except for CodePageName */
2088 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2089 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2090 return TRUE;
2093 /***********************************************************************
2094 * GetCPInfoExW (KERNEL32.@)
2096 * Unicode version of GetCPInfoExA.
2098 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2100 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2101 return FALSE;
2103 switch(codepage)
2105 case CP_UTF7:
2107 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2109 cpinfo->CodePage = CP_UTF7;
2110 cpinfo->UnicodeDefaultChar = 0x3f;
2111 strcpyW(cpinfo->CodePageName, utf7);
2112 break;
2115 case CP_UTF8:
2117 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2119 cpinfo->CodePage = CP_UTF8;
2120 cpinfo->UnicodeDefaultChar = 0x3f;
2121 strcpyW(cpinfo->CodePageName, utf8);
2122 break;
2125 default:
2127 const union cptable *table = get_codepage_table( codepage );
2129 cpinfo->CodePage = table->info.codepage;
2130 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2131 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2132 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2133 break;
2136 return TRUE;
2139 /***********************************************************************
2140 * EnumSystemCodePagesA (KERNEL32.@)
2142 * Call a user defined function for every code page installed on the system.
2144 * PARAMS
2145 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2146 * flags [I] Reserved, set to 0.
2148 * RETURNS
2149 * TRUE, If all code pages have been enumerated, or
2150 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2152 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2154 const union cptable *table;
2155 char buffer[10];
2156 int index = 0;
2158 for (;;)
2160 if (!(table = wine_cp_enum_table( index++ ))) break;
2161 sprintf( buffer, "%d", table->info.codepage );
2162 if (!lpfnCodePageEnum( buffer )) break;
2164 return TRUE;
2168 /***********************************************************************
2169 * EnumSystemCodePagesW (KERNEL32.@)
2171 * See EnumSystemCodePagesA.
2173 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2175 const union cptable *table;
2176 WCHAR buffer[10], *p;
2177 int page, index = 0;
2179 for (;;)
2181 if (!(table = wine_cp_enum_table( index++ ))) break;
2182 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2183 *--p = 0;
2184 page = table->info.codepage;
2187 *--p = '0' + (page % 10);
2188 page /= 10;
2189 } while( page );
2190 if (!lpfnCodePageEnum( p )) break;
2192 return TRUE;
2196 /***********************************************************************
2197 * utf7_write_w
2199 * Helper for utf7_mbstowcs
2201 * RETURNS
2202 * TRUE on success, FALSE on error
2204 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2206 if (dstlen > 0)
2208 if (*index >= dstlen)
2209 return FALSE;
2211 dst[*index] = character;
2214 (*index)++;
2216 return TRUE;
2219 /***********************************************************************
2220 * utf7_mbstowcs
2222 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2224 * RETURNS
2225 * On success, the number of characters written
2226 * On dst buffer overflow, -1
2228 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2230 static const signed char base64_decoding_table[] =
2232 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2233 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2234 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2235 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2236 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2237 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2238 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2239 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2242 const char *source_end = src + srclen;
2243 int dest_index = 0;
2245 DWORD byte_pair = 0;
2246 short offset = 0;
2248 while (src < source_end)
2250 if (*src == '+')
2252 src++;
2253 if (src >= source_end)
2254 break;
2256 if (*src == '-')
2258 /* just a plus sign escaped as +- */
2259 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2260 return -1;
2261 src++;
2262 continue;
2267 signed char sextet = *src;
2268 if (sextet == '-')
2270 /* skip over the dash and end base64 decoding
2271 * the current, unfinished byte pair is discarded */
2272 src++;
2273 offset = 0;
2274 break;
2276 if (sextet < 0)
2278 /* the next character of src is < 0 and therefore not part of a base64 sequence
2279 * the current, unfinished byte pair is NOT discarded in this case
2280 * this is probably a bug in Windows */
2281 break;
2284 sextet = base64_decoding_table[sextet];
2285 if (sextet == -1)
2287 /* -1 means that the next character of src is not part of a base64 sequence
2288 * in other words, all sextets in this base64 sequence have been processed
2289 * the current, unfinished byte pair is discarded */
2290 offset = 0;
2291 break;
2294 byte_pair = (byte_pair << 6) | sextet;
2295 offset += 6;
2297 if (offset >= 16)
2299 /* this byte pair is done */
2300 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2301 return -1;
2302 offset -= 16;
2305 src++;
2307 while (src < source_end);
2309 else
2311 /* we have to convert to unsigned char in case *src < 0 */
2312 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2313 return -1;
2314 src++;
2318 return dest_index;
2321 /***********************************************************************
2322 * MultiByteToWideChar (KERNEL32.@)
2324 * Convert a multibyte character string into a Unicode string.
2326 * PARAMS
2327 * page [I] Codepage character set to convert from
2328 * flags [I] Character mapping flags
2329 * src [I] Source string buffer
2330 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2331 * dst [O] Destination buffer
2332 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2334 * RETURNS
2335 * Success: If dstlen > 0, the number of characters written to dst.
2336 * If dstlen == 0, the number of characters needed to perform the
2337 * conversion. In both cases the count includes the terminating NUL.
2338 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2339 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2340 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2341 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2342 * possible for src.
2344 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2345 LPWSTR dst, INT dstlen )
2347 const union cptable *table;
2348 int ret;
2350 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2352 SetLastError( ERROR_INVALID_PARAMETER );
2353 return 0;
2356 if (srclen < 0) srclen = strlen(src) + 1;
2358 switch(page)
2360 case CP_SYMBOL:
2361 if (flags)
2363 SetLastError( ERROR_INVALID_FLAGS );
2364 return 0;
2366 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2367 break;
2368 case CP_UTF7:
2369 if (flags)
2371 SetLastError( ERROR_INVALID_FLAGS );
2372 return 0;
2374 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2375 break;
2376 case CP_UNIXCP:
2377 if (unix_cptable)
2379 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2380 break;
2382 #ifdef __APPLE__
2383 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2384 #endif
2385 /* fall through */
2386 case CP_UTF8:
2387 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2388 break;
2389 default:
2390 if (!(table = get_codepage_table( page )))
2392 SetLastError( ERROR_INVALID_PARAMETER );
2393 return 0;
2395 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2396 break;
2399 if (ret < 0)
2401 switch(ret)
2403 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2404 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2406 ret = 0;
2408 TRACE("cp %d %s -> %s, ret = %d\n",
2409 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2410 return ret;
2414 /***********************************************************************
2415 * utf7_can_directly_encode
2417 * Helper for utf7_wcstombs
2419 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2421 static const BOOL directly_encodable_table[] =
2423 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2425 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2426 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2427 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2428 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2429 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2430 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2433 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2436 /***********************************************************************
2437 * utf7_write_c
2439 * Helper for utf7_wcstombs
2441 * RETURNS
2442 * TRUE on success, FALSE on error
2444 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2446 if (dstlen > 0)
2448 if (*index >= dstlen)
2449 return FALSE;
2451 dst[*index] = character;
2454 (*index)++;
2456 return TRUE;
2459 /***********************************************************************
2460 * utf7_wcstombs
2462 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2464 * RETURNS
2465 * On success, the number of characters written
2466 * On dst buffer overflow, -1
2468 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2470 static const char base64_encoding_table[] =
2471 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2473 const WCHAR *source_end = src + srclen;
2474 int dest_index = 0;
2476 while (src < source_end)
2478 if (*src == '+')
2480 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2481 return -1;
2482 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2483 return -1;
2484 src++;
2486 else if (utf7_can_directly_encode(*src))
2488 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2489 return -1;
2490 src++;
2492 else
2494 unsigned int offset = 0;
2495 DWORD byte_pair = 0;
2497 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2498 return -1;
2500 while (src < source_end && !utf7_can_directly_encode(*src))
2502 byte_pair = (byte_pair << 16) | *src;
2503 offset += 16;
2504 while (offset >= 6)
2506 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2507 return -1;
2508 offset -= 6;
2510 src++;
2513 if (offset)
2515 /* Windows won't create a padded base64 character if there's no room for the - sign
2516 * as well ; this is probably a bug in Windows */
2517 if (dstlen > 0 && dest_index + 1 >= dstlen)
2518 return -1;
2520 byte_pair <<= (6 - offset);
2521 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2522 return -1;
2525 /* Windows always explicitly terminates the base64 sequence
2526 even though RFC 2152 (page 3, rule 2) does not require this */
2527 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2528 return -1;
2532 return dest_index;
2535 /***********************************************************************
2536 * WideCharToMultiByte (KERNEL32.@)
2538 * Convert a Unicode character string into a multibyte string.
2540 * PARAMS
2541 * page [I] Code page character set to convert to
2542 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2543 * src [I] Source string buffer
2544 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2545 * dst [O] Destination buffer
2546 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2547 * defchar [I] Default character to use for conversion if no exact
2548 * conversion can be made
2549 * used [O] Set if default character was used in the conversion
2551 * RETURNS
2552 * Success: If dstlen > 0, the number of characters written to dst.
2553 * If dstlen == 0, number of characters needed to perform the
2554 * conversion. In both cases the count includes the terminating NUL.
2555 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2556 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2557 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2558 * parameter was given.
2560 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2561 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2563 const union cptable *table;
2564 int ret, used_tmp;
2566 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2568 SetLastError( ERROR_INVALID_PARAMETER );
2569 return 0;
2572 if (srclen < 0) srclen = strlenW(src) + 1;
2574 switch(page)
2576 case CP_SYMBOL:
2577 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2578 if (flags)
2580 SetLastError( ERROR_INVALID_FLAGS );
2581 return 0;
2583 if (defchar || used)
2585 SetLastError( ERROR_INVALID_PARAMETER );
2586 return 0;
2588 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2589 break;
2590 case CP_UTF7:
2591 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2592 if (defchar || used)
2594 SetLastError( ERROR_INVALID_PARAMETER );
2595 return 0;
2597 if (flags)
2599 SetLastError( ERROR_INVALID_FLAGS );
2600 return 0;
2602 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2603 break;
2604 case CP_UNIXCP:
2605 if (unix_cptable)
2607 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2608 defchar, used ? &used_tmp : NULL );
2609 break;
2611 /* fall through */
2612 case CP_UTF8:
2613 if (defchar || used)
2615 SetLastError( ERROR_INVALID_PARAMETER );
2616 return 0;
2618 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2619 break;
2620 default:
2621 if (!(table = get_codepage_table( page )))
2623 SetLastError( ERROR_INVALID_PARAMETER );
2624 return 0;
2626 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2627 defchar, used ? &used_tmp : NULL );
2628 if (used) *used = used_tmp;
2629 break;
2632 if (ret < 0)
2634 switch(ret)
2636 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2637 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2639 ret = 0;
2641 TRACE("cp %d %s -> %s, ret = %d\n",
2642 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2643 return ret;
2647 /***********************************************************************
2648 * GetThreadLocale (KERNEL32.@)
2650 * Get the current threads locale.
2652 * PARAMS
2653 * None.
2655 * RETURNS
2656 * The LCID currently associated with the calling thread.
2658 LCID WINAPI GetThreadLocale(void)
2660 LCID ret = NtCurrentTeb()->CurrentLocale;
2661 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2662 return ret;
2665 /**********************************************************************
2666 * SetThreadLocale (KERNEL32.@)
2668 * Set the current threads locale.
2670 * PARAMS
2671 * lcid [I] LCID of the locale to set
2673 * RETURNS
2674 * Success: TRUE. The threads locale is set to lcid.
2675 * Failure: FALSE. Use GetLastError() to determine the cause.
2677 BOOL WINAPI SetThreadLocale( LCID lcid )
2679 TRACE("(0x%04X)\n", lcid);
2681 lcid = ConvertDefaultLocale(lcid);
2683 if (lcid != GetThreadLocale())
2685 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2687 SetLastError(ERROR_INVALID_PARAMETER);
2688 return FALSE;
2691 NtCurrentTeb()->CurrentLocale = lcid;
2693 return TRUE;
2696 /**********************************************************************
2697 * SetThreadUILanguage (KERNEL32.@)
2699 * Set the current threads UI language.
2701 * PARAMS
2702 * langid [I] LANGID of the language to set, or 0 to use
2703 * the available language which is best supported
2704 * for console applications
2706 * RETURNS
2707 * Success: The return value is the same as the input value.
2708 * Failure: The return value differs from the input value.
2709 * Use GetLastError() to determine the cause.
2711 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2713 TRACE("(0x%04x) stub - returning success\n", langid);
2714 return langid;
2717 /******************************************************************************
2718 * ConvertDefaultLocale (KERNEL32.@)
2720 * Convert a default locale identifier into a real identifier.
2722 * PARAMS
2723 * lcid [I] LCID identifier of the locale to convert
2725 * RETURNS
2726 * lcid unchanged, if not a default locale or its sublanguage is
2727 * not SUBLANG_NEUTRAL.
2728 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2729 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2730 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2732 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2734 LANGID langid;
2736 switch (lcid)
2738 case LOCALE_INVARIANT:
2739 /* keep as-is */
2740 break;
2741 case LOCALE_SYSTEM_DEFAULT:
2742 lcid = GetSystemDefaultLCID();
2743 break;
2744 case LOCALE_USER_DEFAULT:
2745 case LOCALE_NEUTRAL:
2746 lcid = GetUserDefaultLCID();
2747 break;
2748 default:
2749 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2750 langid = LANGIDFROMLCID(lcid);
2751 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2753 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2754 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2757 return lcid;
2761 /******************************************************************************
2762 * IsValidLocale (KERNEL32.@)
2764 * Determine if a locale is valid.
2766 * PARAMS
2767 * lcid [I] LCID of the locale to check
2768 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2770 * RETURNS
2771 * TRUE, if lcid is valid,
2772 * FALSE, otherwise.
2774 * NOTES
2775 * Wine does not currently make the distinction between supported and installed. All
2776 * languages supported are installed by default.
2778 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2780 /* check if language is registered in the kernel32 resources */
2781 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2782 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2785 /******************************************************************************
2786 * IsValidLocaleName (KERNEL32.@)
2788 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2790 struct locale_name locale_name;
2792 if (!locale)
2793 return FALSE;
2795 /* string parsing */
2796 parse_locale_name( locale, &locale_name );
2798 TRACE( "found lcid %x for %s, matches %d\n",
2799 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2801 return locale_name.matches > 0;
2804 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2805 LPCSTR name, WORD LangID, LONG_PTR lParam )
2807 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2808 char buf[20];
2810 sprintf(buf, "%08x", (UINT)LangID);
2811 return lpfnLocaleEnum( buf );
2814 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2815 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2817 static const WCHAR formatW[] = {'%','0','8','x',0};
2818 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2819 WCHAR buf[20];
2820 sprintfW( buf, formatW, (UINT)LangID );
2821 return lpfnLocaleEnum( buf );
2824 /******************************************************************************
2825 * EnumSystemLocalesA (KERNEL32.@)
2827 * Call a users function for each locale available on the system.
2829 * PARAMS
2830 * lpfnLocaleEnum [I] Callback function to call for each locale
2831 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2833 * RETURNS
2834 * Success: TRUE.
2835 * Failure: FALSE. Use GetLastError() to determine the cause.
2837 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2839 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2840 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2841 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2842 (LONG_PTR)lpfnLocaleEnum);
2843 return TRUE;
2847 /******************************************************************************
2848 * EnumSystemLocalesW (KERNEL32.@)
2850 * See EnumSystemLocalesA.
2852 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2854 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2855 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2856 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2857 (LONG_PTR)lpfnLocaleEnum);
2858 return TRUE;
2862 struct enum_locale_ex_data
2864 LOCALE_ENUMPROCEX proc;
2865 DWORD flags;
2866 LPARAM lparam;
2869 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2870 LPCWSTR name, WORD lang, LONG_PTR lparam )
2872 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2873 WCHAR buffer[256];
2874 DWORD neutral;
2875 unsigned int flags;
2877 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2878 buffer, sizeof(buffer) / sizeof(WCHAR) );
2879 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2880 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2881 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2882 neutral = 0;
2883 flags = LOCALE_WINDOWS;
2884 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2885 if (data->flags && !(data->flags & flags)) return TRUE;
2886 return data->proc( buffer, flags, data->lparam );
2889 /******************************************************************************
2890 * EnumSystemLocalesEx (KERNEL32.@)
2892 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2894 struct enum_locale_ex_data data;
2896 if (reserved)
2898 SetLastError( ERROR_INVALID_PARAMETER );
2899 return FALSE;
2901 data.proc = proc;
2902 data.flags = flags;
2903 data.lparam = lparam;
2904 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2905 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2906 enum_locale_ex_proc, (LONG_PTR)&data );
2907 return TRUE;
2911 /***********************************************************************
2912 * VerLanguageNameA (KERNEL32.@)
2914 * Get the name of a language.
2916 * PARAMS
2917 * wLang [I] LANGID of the language
2918 * szLang [O] Destination for the language name
2920 * RETURNS
2921 * Success: The size of the language name. If szLang is non-NULL, it is filled
2922 * with the name.
2923 * Failure: 0. Use GetLastError() to determine the cause.
2926 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2928 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2932 /***********************************************************************
2933 * VerLanguageNameW (KERNEL32.@)
2935 * See VerLanguageNameA.
2937 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2939 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2943 /******************************************************************************
2944 * GetStringTypeW (KERNEL32.@)
2946 * See GetStringTypeA.
2948 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2950 static const unsigned char type2_map[16] =
2952 C2_NOTAPPLICABLE, /* unassigned */
2953 C2_LEFTTORIGHT, /* L */
2954 C2_RIGHTTOLEFT, /* R */
2955 C2_EUROPENUMBER, /* EN */
2956 C2_EUROPESEPARATOR, /* ES */
2957 C2_EUROPETERMINATOR, /* ET */
2958 C2_ARABICNUMBER, /* AN */
2959 C2_COMMONSEPARATOR, /* CS */
2960 C2_BLOCKSEPARATOR, /* B */
2961 C2_SEGMENTSEPARATOR, /* S */
2962 C2_WHITESPACE, /* WS */
2963 C2_OTHERNEUTRAL, /* ON */
2964 C2_RIGHTTOLEFT, /* AL */
2965 C2_NOTAPPLICABLE, /* NSM */
2966 C2_NOTAPPLICABLE, /* BN */
2967 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2970 if (!src)
2972 SetLastError( ERROR_INVALID_PARAMETER );
2973 return FALSE;
2976 if (count == -1) count = strlenW(src) + 1;
2977 switch(type)
2979 case CT_CTYPE1:
2980 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2981 break;
2982 case CT_CTYPE2:
2983 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2984 break;
2985 case CT_CTYPE3:
2987 WARN("CT_CTYPE3: semi-stub.\n");
2988 while (count--)
2990 int c = *src;
2991 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2993 type1 = get_char_typeW( *src++ ) & 0xfff;
2994 /* try to construct type3 from type1 */
2995 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2996 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2997 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2998 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2999 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3000 if (c == 0x0640) type3 |= C3_KASHIDA;
3001 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3003 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3004 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3006 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3007 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3008 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3009 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3010 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3011 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3012 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3013 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3015 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3016 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3017 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3018 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3019 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3020 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3021 *chartype++ = type3;
3023 break;
3025 default:
3026 SetLastError( ERROR_INVALID_PARAMETER );
3027 return FALSE;
3029 return TRUE;
3033 /******************************************************************************
3034 * GetStringTypeExW (KERNEL32.@)
3036 * See GetStringTypeExA.
3038 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3040 /* locale is ignored for Unicode */
3041 return GetStringTypeW( type, src, count, chartype );
3045 /******************************************************************************
3046 * GetStringTypeA (KERNEL32.@)
3048 * Get characteristics of the characters making up a string.
3050 * PARAMS
3051 * locale [I] Locale Id for the string
3052 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3053 * src [I] String to analyse
3054 * count [I] Length of src in chars, or -1 if src is NUL terminated
3055 * chartype [O] Destination for the calculated characteristics
3057 * RETURNS
3058 * Success: TRUE. chartype is filled with the requested characteristics of each char
3059 * in src.
3060 * Failure: FALSE. Use GetLastError() to determine the cause.
3062 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3064 UINT cp;
3065 INT countW;
3066 LPWSTR srcW;
3067 BOOL ret = FALSE;
3069 if(count == -1) count = strlen(src) + 1;
3071 if (!(cp = get_lcid_codepage( locale )))
3073 FIXME("For locale %04x using current ANSI code page\n", locale);
3074 cp = GetACP();
3077 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3078 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3080 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3082 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3083 * string, with multibyte characters there maybe be more bytes in count
3084 * than character space in the buffer!
3086 ret = GetStringTypeW(type, srcW, countW, chartype);
3087 HeapFree(GetProcessHeap(), 0, srcW);
3089 return ret;
3092 /******************************************************************************
3093 * GetStringTypeExA (KERNEL32.@)
3095 * Get characteristics of the characters making up a string.
3097 * PARAMS
3098 * locale [I] Locale Id for the string
3099 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3100 * src [I] String to analyse
3101 * count [I] Length of src in chars, or -1 if src is NUL terminated
3102 * chartype [O] Destination for the calculated characteristics
3104 * RETURNS
3105 * Success: TRUE. chartype is filled with the requested characteristics of each char
3106 * in src.
3107 * Failure: FALSE. Use GetLastError() to determine the cause.
3109 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3111 return GetStringTypeA(locale, type, src, count, chartype);
3114 /*************************************************************************
3115 * LCMapStringEx (KERNEL32.@)
3117 * Map characters in a locale sensitive string.
3119 * PARAMS
3120 * name [I] Locale name for the conversion.
3121 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3122 * src [I] String to map
3123 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3124 * dst [O] Destination for mapped string
3125 * dstlen [I] Length of dst in characters
3126 * version [I] reserved, must be NULL
3127 * reserved [I] reserved, must be NULL
3128 * lparam [I] reserved, must be 0
3130 * RETURNS
3131 * Success: The length of the mapped string in dst, including the NUL terminator.
3132 * Failure: 0. Use GetLastError() to determine the cause.
3134 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3135 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3137 LPWSTR dst_ptr;
3139 if (version) FIXME("unsupported version structure %p\n", version);
3140 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3141 if (lparam)
3143 static int once;
3144 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3147 if (!src || !srclen || dstlen < 0)
3149 SetLastError(ERROR_INVALID_PARAMETER);
3150 return 0;
3153 /* mutually exclusive flags */
3154 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3155 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3156 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3157 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
3159 SetLastError(ERROR_INVALID_FLAGS);
3160 return 0;
3163 if (!dstlen) dst = NULL;
3165 if (flags & LCMAP_SORTKEY)
3167 INT ret;
3168 if (src == dst)
3170 SetLastError(ERROR_INVALID_FLAGS);
3171 return 0;
3174 if (srclen < 0) srclen = strlenW(src);
3176 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3177 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3179 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3180 if (ret == 0)
3181 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3182 else
3183 ret++;
3184 return ret;
3187 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3188 if (flags & SORT_STRINGSORT)
3190 SetLastError(ERROR_INVALID_FLAGS);
3191 return 0;
3194 if (srclen < 0) srclen = strlenW(src) + 1;
3196 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3197 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3199 if (!dst) /* return required string length */
3201 INT len;
3203 for (len = 0; srclen; src++, srclen--)
3205 WCHAR wch = *src;
3206 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3207 * and skips white space and punctuation characters for
3208 * NORM_IGNORESYMBOLS.
3210 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3211 continue;
3212 len++;
3214 return len;
3217 if (flags & LCMAP_UPPERCASE)
3219 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3221 WCHAR wch = *src;
3222 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3223 continue;
3224 *dst_ptr++ = toupperW(wch);
3225 dstlen--;
3228 else if (flags & LCMAP_LOWERCASE)
3230 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3232 WCHAR wch = *src;
3233 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3234 continue;
3235 *dst_ptr++ = tolowerW(wch);
3236 dstlen--;
3239 else
3241 if (src == dst)
3243 SetLastError(ERROR_INVALID_FLAGS);
3244 return 0;
3246 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3248 WCHAR wch = *src;
3249 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3250 continue;
3251 *dst_ptr++ = wch;
3252 dstlen--;
3256 if (srclen)
3258 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3259 return 0;
3262 return dst_ptr - dst;
3265 /*************************************************************************
3266 * LCMapStringW (KERNEL32.@)
3268 * See LCMapStringA.
3270 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3271 LPWSTR dst, INT dstlen)
3273 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3274 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3276 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3279 /*************************************************************************
3280 * LCMapStringA (KERNEL32.@)
3282 * Map characters in a locale sensitive string.
3284 * PARAMS
3285 * lcid [I] LCID for the conversion.
3286 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3287 * src [I] String to map
3288 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3289 * dst [O] Destination for mapped string
3290 * dstlen [I] Length of dst in characters
3292 * RETURNS
3293 * Success: The length of the mapped string in dst, including the NUL terminator.
3294 * Failure: 0. Use GetLastError() to determine the cause.
3296 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3297 LPSTR dst, INT dstlen)
3299 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3300 LPWSTR srcW, dstW;
3301 INT ret = 0, srclenW, dstlenW;
3302 UINT locale_cp = CP_ACP;
3304 if (!src || !srclen || dstlen < 0)
3306 SetLastError(ERROR_INVALID_PARAMETER);
3307 return 0;
3310 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3312 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3313 if (srclenW)
3314 srcW = bufW;
3315 else
3317 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3318 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3319 if (!srcW)
3321 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3322 return 0;
3324 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3327 if (flags & LCMAP_SORTKEY)
3329 if (src == dst)
3331 SetLastError(ERROR_INVALID_FLAGS);
3332 goto map_string_exit;
3334 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3335 if (ret == 0)
3336 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3337 else
3338 ret++;
3339 goto map_string_exit;
3342 if (flags & SORT_STRINGSORT)
3344 SetLastError(ERROR_INVALID_FLAGS);
3345 goto map_string_exit;
3348 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3349 if (!dstlenW)
3350 goto map_string_exit;
3352 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3353 if (!dstW)
3355 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3356 goto map_string_exit;
3359 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3360 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3361 HeapFree(GetProcessHeap(), 0, dstW);
3363 map_string_exit:
3364 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3365 return ret;
3368 /*************************************************************************
3369 * FoldStringA (KERNEL32.@)
3371 * Map characters in a string.
3373 * PARAMS
3374 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3375 * src [I] String to map
3376 * srclen [I] Length of src, or -1 if src is NUL terminated
3377 * dst [O] Destination for mapped string
3378 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3380 * RETURNS
3381 * Success: The length of the string written to dst, including the terminating NUL. If
3382 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3383 * and dst may be NULL.
3384 * Failure: 0. Use GetLastError() to determine the cause.
3386 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3387 LPSTR dst, INT dstlen)
3389 INT ret = 0, srclenW = 0;
3390 WCHAR *srcW = NULL, *dstW = NULL;
3392 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3394 SetLastError(ERROR_INVALID_PARAMETER);
3395 return 0;
3398 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3399 src, srclen, NULL, 0);
3400 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3402 if (!srcW)
3404 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3405 goto FoldStringA_exit;
3408 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3409 src, srclen, srcW, srclenW);
3411 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3413 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3414 if (ret && dstlen)
3416 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3418 if (!dstW)
3420 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3421 goto FoldStringA_exit;
3424 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3425 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3427 ret = 0;
3428 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3432 HeapFree(GetProcessHeap(), 0, dstW);
3434 FoldStringA_exit:
3435 HeapFree(GetProcessHeap(), 0, srcW);
3436 return ret;
3439 /*************************************************************************
3440 * FoldStringW (KERNEL32.@)
3442 * See FoldStringA.
3444 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3445 LPWSTR dst, INT dstlen)
3447 int ret;
3449 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3451 case 0:
3452 if (dwFlags)
3453 break;
3454 /* Fall through for dwFlags == 0 */
3455 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3456 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3457 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3458 SetLastError(ERROR_INVALID_FLAGS);
3459 return 0;
3462 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3464 SetLastError(ERROR_INVALID_PARAMETER);
3465 return 0;
3468 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3469 if (!ret)
3470 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3471 return ret;
3474 /******************************************************************************
3475 * CompareStringW (KERNEL32.@)
3477 * See CompareStringA.
3479 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3480 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3482 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3485 /******************************************************************************
3486 * CompareStringEx (KERNEL32.@)
3488 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3489 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3491 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3492 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3493 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3494 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3495 INT ret;
3496 static int once;
3498 if (version) FIXME("unexpected version parameter\n");
3499 if (reserved) FIXME("unexpected reserved value\n");
3500 if (lParam) FIXME("unexpected lParam\n");
3502 if (!str1 || !str2)
3504 SetLastError(ERROR_INVALID_PARAMETER);
3505 return 0;
3508 if (flags & ~(supported_flags|semistub_flags))
3510 SetLastError(ERROR_INVALID_FLAGS);
3511 return 0;
3514 if (flags & semistub_flags)
3516 if (!once++)
3517 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3520 if (len1 < 0) len1 = strlenW(str1);
3521 if (len2 < 0) len2 = strlenW(str2);
3523 ret = wine_compare_string(flags, str1, len1, str2, len2);
3525 if (ret) /* need to translate result */
3526 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3527 return CSTR_EQUAL;
3530 /******************************************************************************
3531 * CompareStringA (KERNEL32.@)
3533 * Compare two locale sensitive strings.
3535 * PARAMS
3536 * lcid [I] LCID for the comparison
3537 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3538 * str1 [I] First string to compare
3539 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3540 * str2 [I] Second string to compare
3541 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3543 * RETURNS
3544 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3545 * str1 is less than, equal to or greater than str2 respectively.
3546 * Failure: FALSE. Use GetLastError() to determine the cause.
3548 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3549 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3551 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3552 WCHAR *buf2W = buf1W + 130;
3553 LPWSTR str1W, str2W;
3554 INT len1W = 0, len2W = 0, ret;
3555 UINT locale_cp = CP_ACP;
3557 if (!str1 || !str2)
3559 SetLastError(ERROR_INVALID_PARAMETER);
3560 return 0;
3562 if (len1 < 0) len1 = strlen(str1);
3563 if (len2 < 0) len2 = strlen(str2);
3565 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3567 if (len1)
3569 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3570 if (len1W)
3571 str1W = buf1W;
3572 else
3574 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3575 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3576 if (!str1W)
3578 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3579 return 0;
3581 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3584 else
3586 len1W = 0;
3587 str1W = buf1W;
3590 if (len2)
3592 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3593 if (len2W)
3594 str2W = buf2W;
3595 else
3597 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3598 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3599 if (!str2W)
3601 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3602 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3603 return 0;
3605 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3608 else
3610 len2W = 0;
3611 str2W = buf2W;
3614 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3616 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3617 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3618 return ret;
3621 /******************************************************************************
3622 * CompareStringOrdinal (KERNEL32.@)
3624 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3626 int ret;
3628 if (!str1 || !str2)
3630 SetLastError(ERROR_INVALID_PARAMETER);
3631 return 0;
3633 if (len1 < 0) len1 = strlenW(str1);
3634 if (len2 < 0) len2 = strlenW(str2);
3636 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
3637 if (ret < 0) return CSTR_LESS_THAN;
3638 if (ret > 0) return CSTR_GREATER_THAN;
3639 return CSTR_EQUAL;
3642 /*************************************************************************
3643 * lstrcmp (KERNEL32.@)
3644 * lstrcmpA (KERNEL32.@)
3646 * Compare two strings using the current thread locale.
3648 * PARAMS
3649 * str1 [I] First string to compare
3650 * str2 [I] Second string to compare
3652 * RETURNS
3653 * Success: A number less than, equal to or greater than 0 depending on whether
3654 * str1 is less than, equal to or greater than str2 respectively.
3655 * Failure: FALSE. Use GetLastError() to determine the cause.
3657 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3659 int ret;
3661 if ((str1 == NULL) && (str2 == NULL)) return 0;
3662 if (str1 == NULL) return -1;
3663 if (str2 == NULL) return 1;
3665 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3666 if (ret) ret -= 2;
3668 return ret;
3671 /*************************************************************************
3672 * lstrcmpi (KERNEL32.@)
3673 * lstrcmpiA (KERNEL32.@)
3675 * Compare two strings using the current thread locale, ignoring case.
3677 * PARAMS
3678 * str1 [I] First string to compare
3679 * str2 [I] Second string to compare
3681 * RETURNS
3682 * Success: A number less than, equal to or greater than 0 depending on whether
3683 * str2 is less than, equal to or greater than str1 respectively.
3684 * Failure: FALSE. Use GetLastError() to determine the cause.
3686 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3688 int ret;
3690 if ((str1 == NULL) && (str2 == NULL)) return 0;
3691 if (str1 == NULL) return -1;
3692 if (str2 == NULL) return 1;
3694 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3695 if (ret) ret -= 2;
3697 return ret;
3700 /*************************************************************************
3701 * lstrcmpW (KERNEL32.@)
3703 * See lstrcmpA.
3705 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3707 int ret;
3709 if ((str1 == NULL) && (str2 == NULL)) return 0;
3710 if (str1 == NULL) return -1;
3711 if (str2 == NULL) return 1;
3713 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3714 if (ret) ret -= 2;
3716 return ret;
3719 /*************************************************************************
3720 * lstrcmpiW (KERNEL32.@)
3722 * See lstrcmpiA.
3724 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3726 int ret;
3728 if ((str1 == NULL) && (str2 == NULL)) return 0;
3729 if (str1 == NULL) return -1;
3730 if (str2 == NULL) return 1;
3732 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3733 if (ret) ret -= 2;
3735 return ret;
3738 /******************************************************************************
3739 * LOCALE_Init
3741 void LOCALE_Init(void)
3743 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3744 const union cptable *unix_cp );
3746 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3748 setlocale( LC_ALL, "" );
3750 #ifdef __APPLE__
3751 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3752 if (!has_env("LANG"))
3754 const char* mac_locale = get_mac_locale();
3756 setenv( "LANG", mac_locale, 1 );
3757 if (setlocale( LC_ALL, "" ))
3758 TRACE( "setting LANG to '%s'\n", mac_locale );
3759 else
3761 /* no C library locale matching Mac locale; don't pass garbage to children */
3762 unsetenv("LANG");
3763 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
3766 #endif /* __APPLE__ */
3768 unix_cp = setup_unix_locales();
3769 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3771 #ifdef __APPLE__
3772 if (!unix_cp)
3773 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3774 #endif
3776 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3777 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3778 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3780 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3781 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3782 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3783 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3784 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3785 if (!unix_cp)
3786 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3787 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3789 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3790 ansi_cptable = wine_cp_get_table( 1252 );
3791 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3792 oem_cptable = wine_cp_get_table( 437 );
3793 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3794 mac_cptable = wine_cp_get_table( 10000 );
3795 if (unix_cp != CP_UTF8)
3797 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3798 unix_cptable = wine_cp_get_table( 28591 );
3801 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3803 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3804 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3805 mac_cptable->info.codepage, unix_cp );
3807 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3810 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3812 UNICODE_STRING keyName;
3813 OBJECT_ATTRIBUTES attr;
3814 HANDLE hkey;
3816 RtlInitUnicodeString( &keyName, szKeyName );
3817 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3819 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3820 hkey = 0;
3822 return hkey;
3825 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3826 LPWSTR szValueName, ULONG valueNameSize,
3827 LPWSTR szValueData, ULONG valueDataSize)
3829 BYTE buffer[80];
3830 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3831 DWORD dwLen;
3833 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3834 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3835 info->NameLength > valueNameSize ||
3836 info->DataLength > valueDataSize)
3838 return FALSE;
3841 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3843 memcpy( szValueName, info->Name, info->NameLength);
3844 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3845 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3846 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3848 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3849 return TRUE;
3852 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3854 BYTE buffer[128];
3855 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3856 DWORD dwSize = sizeof(buffer);
3857 UNICODE_STRING valueName;
3859 RtlInitUnicodeString( &valueName, szValueName );
3861 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3862 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3863 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3864 info->DataLength == sizeof(DWORD))
3866 memcpy(lpVal, info->Data, sizeof(DWORD));
3867 return TRUE;
3870 return FALSE;
3873 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3875 LANGID langId;
3876 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3877 HRSRC hResource;
3878 BOOL bRet = FALSE;
3880 /* FIXME: Is it correct to use the system default langid? */
3881 langId = GetSystemDefaultLangID();
3883 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3884 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3886 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3888 if (hResource)
3890 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3892 if (hResDir)
3894 ULONG iResourceIndex = lgrpid & 0xf;
3895 LPCWSTR lpResEntry = LockResource( hResDir );
3896 ULONG i;
3898 for (i = 0; i < iResourceIndex; i++)
3899 lpResEntry += *lpResEntry + 1;
3901 if (*lpResEntry < nameSize)
3903 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3904 szName[*lpResEntry] = '\0';
3905 bRet = TRUE;
3909 FreeResource( hResource );
3911 return bRet;
3914 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3915 typedef struct
3917 LANGUAGEGROUP_ENUMPROCA procA;
3918 LANGUAGEGROUP_ENUMPROCW procW;
3919 DWORD dwFlags;
3920 LONG_PTR lParam;
3921 } ENUMLANGUAGEGROUP_CALLBACKS;
3923 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3924 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3926 WCHAR szNumber[10], szValue[4];
3927 HANDLE hKey;
3928 BOOL bContinue = TRUE;
3929 ULONG ulIndex = 0;
3931 if (!lpProcs)
3933 SetLastError(ERROR_INVALID_PARAMETER);
3934 return FALSE;
3937 switch (lpProcs->dwFlags)
3939 case 0:
3940 /* Default to LGRPID_INSTALLED */
3941 lpProcs->dwFlags = LGRPID_INSTALLED;
3942 /* Fall through... */
3943 case LGRPID_INSTALLED:
3944 case LGRPID_SUPPORTED:
3945 break;
3946 default:
3947 SetLastError(ERROR_INVALID_FLAGS);
3948 return FALSE;
3951 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3953 if (!hKey)
3954 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3956 while (bContinue)
3958 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3959 szValue, sizeof(szValue) ))
3961 BOOL bInstalled = szValue[0] == '1';
3962 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3964 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3965 bInstalled ? "" : "not ");
3967 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3969 WCHAR szGrpName[48];
3971 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3972 szGrpName[0] = '\0';
3974 if (lpProcs->procW)
3975 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3976 lpProcs->lParam );
3977 else
3979 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3980 char szGrpNameA[48];
3982 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3983 * or whether the language names are ever localised. Assume CP_ACP.
3986 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3987 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3989 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3990 lpProcs->lParam );
3994 ulIndex++;
3996 else
3997 bContinue = FALSE;
3999 if (!bContinue)
4000 break;
4003 if (hKey)
4004 NtClose( hKey );
4006 return TRUE;
4009 /******************************************************************************
4010 * EnumSystemLanguageGroupsA (KERNEL32.@)
4012 * Call a users function for each language group available on the system.
4014 * PARAMS
4015 * pLangGrpEnumProc [I] Callback function to call for each language group
4016 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4017 * lParam [I] User parameter to pass to pLangGrpEnumProc
4019 * RETURNS
4020 * Success: TRUE.
4021 * Failure: FALSE. Use GetLastError() to determine the cause.
4023 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4024 DWORD dwFlags, LONG_PTR lParam)
4026 ENUMLANGUAGEGROUP_CALLBACKS procs;
4028 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4030 procs.procA = pLangGrpEnumProc;
4031 procs.procW = NULL;
4032 procs.dwFlags = dwFlags;
4033 procs.lParam = lParam;
4035 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4038 /******************************************************************************
4039 * EnumSystemLanguageGroupsW (KERNEL32.@)
4041 * See EnumSystemLanguageGroupsA.
4043 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4044 DWORD dwFlags, LONG_PTR lParam)
4046 ENUMLANGUAGEGROUP_CALLBACKS procs;
4048 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4050 procs.procA = NULL;
4051 procs.procW = pLangGrpEnumProc;
4052 procs.dwFlags = dwFlags;
4053 procs.lParam = lParam;
4055 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4058 /******************************************************************************
4059 * IsValidLanguageGroup (KERNEL32.@)
4061 * Determine if a language group is supported and/or installed.
4063 * PARAMS
4064 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4065 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4067 * RETURNS
4068 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4069 * FALSE otherwise.
4071 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4073 static const WCHAR szFormat[] = { '%','x','\0' };
4074 WCHAR szValueName[16], szValue[2];
4075 BOOL bSupported = FALSE, bInstalled = FALSE;
4076 HANDLE hKey;
4079 switch (dwFlags)
4081 case LGRPID_INSTALLED:
4082 case LGRPID_SUPPORTED:
4084 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4086 sprintfW( szValueName, szFormat, lgrpid );
4088 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4090 bSupported = TRUE;
4092 if (szValue[0] == '1')
4093 bInstalled = TRUE;
4096 if (hKey)
4097 NtClose( hKey );
4099 break;
4102 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4103 (dwFlags == LGRPID_INSTALLED && bInstalled))
4104 return TRUE;
4106 return FALSE;
4109 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4110 typedef struct
4112 LANGGROUPLOCALE_ENUMPROCA procA;
4113 LANGGROUPLOCALE_ENUMPROCW procW;
4114 DWORD dwFlags;
4115 LGRPID lgrpid;
4116 LONG_PTR lParam;
4117 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4119 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4120 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4122 static const WCHAR szAlternateSortsKeyName[] = {
4123 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4125 WCHAR szNumber[10], szValue[4];
4126 HANDLE hKey;
4127 BOOL bContinue = TRUE, bAlternate = FALSE;
4128 LGRPID lgrpid;
4129 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4131 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4133 SetLastError(ERROR_INVALID_PARAMETER);
4134 return FALSE;
4137 if (lpProcs->dwFlags)
4139 SetLastError(ERROR_INVALID_FLAGS);
4140 return FALSE;
4143 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4145 if (!hKey)
4146 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4148 while (bContinue)
4150 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4151 szValue, sizeof(szValue) ))
4153 lgrpid = strtoulW( szValue, NULL, 16 );
4155 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4156 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4158 if (lgrpid == lpProcs->lgrpid)
4160 LCID lcid;
4162 lcid = strtoulW( szNumber, NULL, 16 );
4164 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4165 * '00000437 ;Georgian'
4166 * At present we only pass the LCID string.
4169 if (lpProcs->procW)
4170 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4171 else
4173 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4175 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4177 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4181 ulIndex++;
4183 else
4185 /* Finished enumerating this key */
4186 if (!bAlternate)
4188 /* Enumerate alternate sorts also */
4189 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4190 bAlternate = TRUE;
4191 ulIndex = 0;
4193 else
4194 bContinue = FALSE; /* Finished both keys */
4197 if (!bContinue)
4198 break;
4201 if (hKey)
4202 NtClose( hKey );
4204 return TRUE;
4207 /******************************************************************************
4208 * EnumLanguageGroupLocalesA (KERNEL32.@)
4210 * Call a users function for every locale in a language group available on the system.
4212 * PARAMS
4213 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4214 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4215 * dwFlags [I] Reserved, set to 0
4216 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4218 * RETURNS
4219 * Success: TRUE.
4220 * Failure: FALSE. Use GetLastError() to determine the cause.
4222 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4223 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4225 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4227 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4229 callbacks.procA = pLangGrpLcEnumProc;
4230 callbacks.procW = NULL;
4231 callbacks.dwFlags = dwFlags;
4232 callbacks.lgrpid = lgrpid;
4233 callbacks.lParam = lParam;
4235 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4238 /******************************************************************************
4239 * EnumLanguageGroupLocalesW (KERNEL32.@)
4241 * See EnumLanguageGroupLocalesA.
4243 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4244 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4246 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4248 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4250 callbacks.procA = NULL;
4251 callbacks.procW = pLangGrpLcEnumProc;
4252 callbacks.dwFlags = dwFlags;
4253 callbacks.lgrpid = lgrpid;
4254 callbacks.lParam = lParam;
4256 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4259 /******************************************************************************
4260 * InvalidateNLSCache (KERNEL32.@)
4262 * Invalidate the cache of NLS values.
4264 * PARAMS
4265 * None.
4267 * RETURNS
4268 * Success: TRUE.
4269 * Failure: FALSE.
4271 BOOL WINAPI InvalidateNLSCache(void)
4273 FIXME("() stub\n");
4274 return FALSE;
4277 /******************************************************************************
4278 * GetUserGeoID (KERNEL32.@)
4280 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4282 GEOID ret = GEOID_NOT_AVAILABLE;
4283 static const WCHAR geoW[] = {'G','e','o',0};
4284 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4285 WCHAR bufferW[40], *end;
4286 DWORD count;
4287 HANDLE hkey, hSubkey = 0;
4288 UNICODE_STRING keyW;
4289 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4290 RtlInitUnicodeString( &keyW, nationW );
4291 count = sizeof(bufferW);
4293 if(!(hkey = create_registry_key())) return ret;
4295 switch( GeoClass ){
4296 case GEOCLASS_NATION:
4297 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4299 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4300 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4301 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4303 break;
4304 case GEOCLASS_REGION:
4305 FIXME("GEOCLASS_REGION not handled yet\n");
4306 break;
4309 NtClose(hkey);
4310 if (hSubkey) NtClose(hSubkey);
4311 return ret;
4314 /******************************************************************************
4315 * SetUserGeoID (KERNEL32.@)
4317 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4319 static const WCHAR geoW[] = {'G','e','o',0};
4320 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4321 static const WCHAR formatW[] = {'%','i',0};
4322 UNICODE_STRING nameW,keyW;
4323 WCHAR bufferW[10];
4324 OBJECT_ATTRIBUTES attr;
4325 HANDLE hkey;
4327 if(!(hkey = create_registry_key())) return FALSE;
4329 attr.Length = sizeof(attr);
4330 attr.RootDirectory = hkey;
4331 attr.ObjectName = &nameW;
4332 attr.Attributes = 0;
4333 attr.SecurityDescriptor = NULL;
4334 attr.SecurityQualityOfService = NULL;
4335 RtlInitUnicodeString( &nameW, geoW );
4336 RtlInitUnicodeString( &keyW, nationW );
4338 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4341 NtClose(attr.RootDirectory);
4342 return FALSE;
4345 sprintfW(bufferW, formatW, GeoID);
4346 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4347 NtClose(attr.RootDirectory);
4348 NtClose(hkey);
4349 return TRUE;
4352 typedef struct
4354 union
4356 UILANGUAGE_ENUMPROCA procA;
4357 UILANGUAGE_ENUMPROCW procW;
4358 } u;
4359 DWORD flags;
4360 LONG_PTR param;
4361 } ENUM_UILANG_CALLBACK;
4363 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4364 LPCSTR name, WORD LangID, LONG_PTR lParam )
4366 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4367 char buf[20];
4369 sprintf(buf, "%08x", (UINT)LangID);
4370 return enum_uilang->u.procA( buf, enum_uilang->param );
4373 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4374 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4376 static const WCHAR formatW[] = {'%','0','8','x',0};
4377 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4378 WCHAR buf[20];
4380 sprintfW( buf, formatW, (UINT)LangID );
4381 return enum_uilang->u.procW( buf, enum_uilang->param );
4384 /******************************************************************************
4385 * EnumUILanguagesA (KERNEL32.@)
4387 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4389 ENUM_UILANG_CALLBACK enum_uilang;
4391 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4393 if(!pUILangEnumProc) {
4394 SetLastError(ERROR_INVALID_PARAMETER);
4395 return FALSE;
4397 if(dwFlags) {
4398 SetLastError(ERROR_INVALID_FLAGS);
4399 return FALSE;
4402 enum_uilang.u.procA = pUILangEnumProc;
4403 enum_uilang.flags = dwFlags;
4404 enum_uilang.param = lParam;
4406 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4407 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4408 (LONG_PTR)&enum_uilang);
4409 return TRUE;
4412 /******************************************************************************
4413 * EnumUILanguagesW (KERNEL32.@)
4415 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4417 ENUM_UILANG_CALLBACK enum_uilang;
4419 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4422 if(!pUILangEnumProc) {
4423 SetLastError(ERROR_INVALID_PARAMETER);
4424 return FALSE;
4426 if(dwFlags) {
4427 SetLastError(ERROR_INVALID_FLAGS);
4428 return FALSE;
4431 enum_uilang.u.procW = pUILangEnumProc;
4432 enum_uilang.flags = dwFlags;
4433 enum_uilang.param = lParam;
4435 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4436 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4437 (LONG_PTR)&enum_uilang);
4438 return TRUE;
4441 enum locationkind {
4442 LOCATION_NATION = 0,
4443 LOCATION_REGION,
4444 LOCATION_BOTH
4447 struct geoinfo_t {
4448 GEOID id;
4449 WCHAR iso2W[3];
4450 WCHAR iso3W[4];
4451 GEOID parent;
4452 INT uncode;
4453 enum locationkind kind;
4456 static const struct geoinfo_t geoinfodata[] = {
4457 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4458 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4459 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4460 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4461 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4462 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4463 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4464 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4465 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4466 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4467 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4468 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4469 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4470 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4471 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4472 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4473 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4474 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4475 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4476 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4477 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4478 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4479 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4480 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4481 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4482 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4483 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4484 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4485 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4486 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4487 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4488 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4489 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4490 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4491 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4492 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4493 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4494 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4495 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4496 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4497 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4498 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4499 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4500 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4501 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4502 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4503 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4504 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4505 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4506 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4507 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4508 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4509 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4510 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4511 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4512 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4513 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4514 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4515 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4516 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4517 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4518 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4519 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4520 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4521 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4522 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4523 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4524 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4525 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4526 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4527 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4528 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4529 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4530 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4531 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4532 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4533 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4534 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4535 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4536 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4537 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4538 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4539 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4540 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4541 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4542 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4543 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4544 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4545 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4546 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4547 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4548 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4549 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4550 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4551 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4552 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4553 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4554 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4555 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4556 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4557 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4558 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4559 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4560 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4561 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4562 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4563 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4564 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4565 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4566 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4567 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4568 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4569 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4570 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4571 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4572 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4573 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4574 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4575 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4576 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4577 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4578 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4579 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4580 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4581 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4582 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4583 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4584 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4585 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4586 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4587 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4588 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4589 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4590 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4591 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4592 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4593 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4594 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4595 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4596 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4597 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4598 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4599 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4600 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4601 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4602 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4603 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4604 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4605 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4606 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4607 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4608 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4609 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4610 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4611 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4612 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4613 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4614 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4615 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4616 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4617 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4618 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4619 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4620 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4621 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4622 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4623 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4624 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4625 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4626 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4627 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4628 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4629 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4630 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4631 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4632 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4633 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4634 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4635 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4636 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4637 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4638 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4639 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4640 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4641 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4642 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4643 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4644 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4645 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4646 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4647 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4648 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4649 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4650 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4651 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4652 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4653 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4654 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4655 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4656 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4657 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4658 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4659 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4660 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4661 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4662 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4663 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4664 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4665 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4666 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4667 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4668 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4669 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4670 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4671 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4672 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4673 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4674 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4675 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4676 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4677 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4678 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4679 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4680 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4681 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4682 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4683 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4684 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4685 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4686 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4687 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4688 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4689 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4690 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4691 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4692 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4693 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4694 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4695 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4696 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4697 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4698 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4699 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4700 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4701 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4702 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4703 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4704 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4705 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4706 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4707 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4708 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4709 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4710 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4711 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4712 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4713 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4714 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4715 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4716 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4717 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4718 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4719 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4720 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4721 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4722 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4723 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4724 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4725 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4726 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4727 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4728 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4729 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4730 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4731 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4732 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4733 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4734 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4735 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4736 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4737 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4738 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4739 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4740 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4741 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4742 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4743 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4744 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4745 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4746 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4747 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4748 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4749 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4750 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4751 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4752 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4753 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4754 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4755 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4758 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4760 int min, max;
4762 min = 0;
4763 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4765 while (min <= max) {
4766 const struct geoinfo_t *ptr;
4767 int n = (min+max)/2;
4769 ptr = &geoinfodata[n];
4770 if (geoid == ptr->id)
4771 /* we don't need empty entries */
4772 return *ptr->iso2W ? ptr : NULL;
4774 if (ptr->id > geoid)
4775 max = n-1;
4776 else
4777 min = n+1;
4780 return NULL;
4783 /******************************************************************************
4784 * GetGeoInfoW (KERNEL32.@)
4786 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4788 const struct geoinfo_t *ptr;
4789 const WCHAR *str = NULL;
4790 WCHAR buffW[12];
4791 LONG val = 0;
4792 INT len;
4794 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4796 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4797 SetLastError(ERROR_INVALID_PARAMETER);
4798 return 0;
4801 switch (geotype) {
4802 case GEO_NATION:
4803 val = geoid;
4804 break;
4805 case GEO_ISO_UN_NUMBER:
4806 val = ptr->uncode;
4807 break;
4808 case GEO_PARENT:
4809 val = ptr->parent;
4810 break;
4811 case GEO_ISO2:
4812 case GEO_ISO3:
4814 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4815 break;
4817 case GEO_RFC1766:
4818 case GEO_LCID:
4819 case GEO_FRIENDLYNAME:
4820 case GEO_OFFICIALNAME:
4821 case GEO_TIMEZONES:
4822 case GEO_OFFICIALLANGUAGES:
4823 case GEO_LATITUDE:
4824 case GEO_LONGITUDE:
4825 FIXME("type %d is not supported\n", geotype);
4826 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4827 return 0;
4828 default:
4829 WARN("unrecognized type %d\n", geotype);
4830 SetLastError(ERROR_INVALID_FLAGS);
4831 return 0;
4834 if (val) {
4835 static const WCHAR fmtW[] = {'%','d',0};
4836 sprintfW(buffW, fmtW, val);
4837 str = buffW;
4840 len = strlenW(str) + 1;
4841 if (!data || !data_len)
4842 return len;
4844 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4845 if (data_len < len)
4846 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4847 return data_len < len ? 0 : len;
4850 /******************************************************************************
4851 * GetGeoInfoA (KERNEL32.@)
4853 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4855 WCHAR *buffW;
4856 INT len;
4858 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4860 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4861 if (!len)
4862 return 0;
4864 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4865 if (!buffW)
4866 return 0;
4868 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4869 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4870 if (!data || !data_len) {
4871 HeapFree(GetProcessHeap(), 0, buffW);
4872 return len;
4875 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4876 HeapFree(GetProcessHeap(), 0, buffW);
4878 if (data_len < len)
4879 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4880 return data_len < len ? 0 : len;
4883 /******************************************************************************
4884 * EnumSystemGeoID (KERNEL32.@)
4886 * Call a users function for every location available on the system.
4888 * PARAMS
4889 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4890 * parent [I] GEOID for the parent
4891 * enumproc [I] Callback function to call for each location
4893 * RETURNS
4894 * Success: TRUE.
4895 * Failure: FALSE. Use GetLastError() to determine the cause.
4897 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4899 INT i;
4901 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4903 if (!enumproc) {
4904 SetLastError(ERROR_INVALID_PARAMETER);
4905 return FALSE;
4908 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4909 SetLastError(ERROR_INVALID_FLAGS);
4910 return FALSE;
4913 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4914 const struct geoinfo_t *ptr = &geoinfodata[i];
4916 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4917 continue;
4919 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4920 continue;
4922 if (parent && ptr->parent != parent)
4923 continue;
4925 if (!enumproc(ptr->id))
4926 return TRUE;
4929 return TRUE;
4932 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4934 LCID userlcid;
4936 TRACE("%p, %d\n", localename, buffersize);
4938 userlcid = GetUserDefaultLCID();
4939 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4942 /******************************************************************************
4943 * NormalizeString (KERNEL32.@)
4945 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4946 LPWSTR lpDstString, INT cwDstLength)
4948 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4949 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4950 return 0;
4953 /******************************************************************************
4954 * IsNormalizedString (KERNEL32.@)
4956 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4958 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4959 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4960 return FALSE;
4963 enum {
4964 BASE = 36,
4965 TMIN = 1,
4966 TMAX = 26,
4967 SKEW = 38,
4968 DAMP = 700,
4969 INIT_BIAS = 72,
4970 INIT_N = 128
4973 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4975 INT k;
4977 delta /= (firsttime ? DAMP : 2);
4978 delta += delta/numpoints;
4980 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4981 delta /= BASE-TMIN;
4982 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4985 /******************************************************************************
4986 * IdnToAscii (KERNEL32.@)
4987 * Implementation of Punycode based on RFC 3492.
4989 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4990 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4992 static const WCHAR prefixW[] = {'x','n','-','-'};
4994 WCHAR *norm_str;
4995 INT i, label_start, label_end, norm_len, out_label, out = 0;
4997 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4998 lpASCIICharStr, cchASCIIChar);
5000 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5001 if(!norm_len)
5002 return 0;
5003 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5004 if(!norm_str) {
5005 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5006 return 0;
5008 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5009 cchUnicodeChar, norm_str, norm_len);
5010 if(!norm_len) {
5011 HeapFree(GetProcessHeap(), 0, norm_str);
5012 return 0;
5015 for(label_start=0; label_start<norm_len;) {
5016 INT n = INIT_N, bias = INIT_BIAS;
5017 INT delta = 0, b = 0, h;
5019 out_label = out;
5020 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5021 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5022 if(norm_str[i] < 0x80)
5023 b++;
5024 label_end = i;
5026 if(b == label_end-label_start) {
5027 if(label_end < norm_len)
5028 b++;
5029 if(!lpASCIICharStr) {
5030 out += b;
5031 }else if(out+b <= cchASCIIChar) {
5032 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5033 out += b;
5034 }else {
5035 HeapFree(GetProcessHeap(), 0, norm_str);
5036 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5037 return 0;
5039 label_start = label_end+1;
5040 continue;
5043 if(!lpASCIICharStr) {
5044 out += 5+b; /* strlen(xn--...-) */
5045 }else if(out+5+b <= cchASCIIChar) {
5046 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5047 out += 4;
5048 for(i=label_start; i<label_end; i++)
5049 if(norm_str[i] < 0x80)
5050 lpASCIICharStr[out++] = norm_str[i];
5051 lpASCIICharStr[out++] = '-';
5052 }else {
5053 HeapFree(GetProcessHeap(), 0, norm_str);
5054 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5055 return 0;
5057 if(!b)
5058 out--;
5060 for(h=b; h<label_end-label_start;) {
5061 INT m = 0xffff, q, k;
5063 for(i=label_start; i<label_end; i++) {
5064 if(norm_str[i]>=n && m>norm_str[i])
5065 m = norm_str[i];
5067 delta += (m-n)*(h+1);
5068 n = m;
5070 for(i=label_start; i<label_end; i++) {
5071 if(norm_str[i] < n) {
5072 delta++;
5073 }else if(norm_str[i] == n) {
5074 for(q=delta, k=BASE; ; k+=BASE) {
5075 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5076 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5077 if(!lpASCIICharStr) {
5078 out++;
5079 }else if(out+1 <= cchASCIIChar) {
5080 lpASCIICharStr[out++] = disp<='z'-'a' ?
5081 'a'+disp : '0'+disp-'z'+'a'-1;
5082 }else {
5083 HeapFree(GetProcessHeap(), 0, norm_str);
5084 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5085 return 0;
5087 if(q < t)
5088 break;
5089 q = (q-t)/(BASE-t);
5091 bias = adapt(delta, h+1, h==b);
5092 delta = 0;
5093 h++;
5096 delta++;
5097 n++;
5100 if(out-out_label > 63) {
5101 HeapFree(GetProcessHeap(), 0, norm_str);
5102 SetLastError(ERROR_INVALID_NAME);
5103 return 0;
5106 if(label_end < norm_len) {
5107 if(!lpASCIICharStr) {
5108 out++;
5109 }else if(out+1 <= cchASCIIChar) {
5110 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5111 }else {
5112 HeapFree(GetProcessHeap(), 0, norm_str);
5113 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5114 return 0;
5117 label_start = label_end+1;
5120 HeapFree(GetProcessHeap(), 0, norm_str);
5121 return out;
5124 /******************************************************************************
5125 * IdnToNameprepUnicode (KERNEL32.@)
5127 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5128 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5130 enum {
5131 UNASSIGNED = 0x1,
5132 PROHIBITED = 0x2,
5133 BIDI_RAL = 0x4,
5134 BIDI_L = 0x8
5137 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5138 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5139 const WCHAR *ptr;
5140 WORD flags;
5141 WCHAR buf[64], *map_str, norm_str[64], ch;
5142 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5143 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5145 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5146 lpNameprepCharStr, cchNameprepChar);
5148 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5149 SetLastError(ERROR_INVALID_FLAGS);
5150 return 0;
5153 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5154 SetLastError(ERROR_INVALID_PARAMETER);
5155 return 0;
5158 if(cchUnicodeChar == -1)
5159 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5160 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5161 SetLastError(ERROR_INVALID_NAME);
5162 return 0;
5165 for(label_start=0; label_start<cchUnicodeChar;) {
5166 ascii_only = TRUE;
5167 for(i=label_start; i<cchUnicodeChar; i++) {
5168 ch = lpUnicodeCharStr[i];
5170 if(i!=cchUnicodeChar-1 && !ch) {
5171 SetLastError(ERROR_INVALID_NAME);
5172 return 0;
5174 /* check if ch is one of label separators defined in RFC3490 */
5175 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5176 break;
5178 if(ch > 0x7f) {
5179 ascii_only = FALSE;
5180 continue;
5183 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5184 continue;
5185 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5186 || (ch>='0' && ch<='9') || ch=='-')
5187 continue;
5189 SetLastError(ERROR_INVALID_NAME);
5190 return 0;
5192 label_end = i;
5193 /* last label may be empty */
5194 if(label_start==label_end && ch) {
5195 SetLastError(ERROR_INVALID_NAME);
5196 return 0;
5199 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5200 lpUnicodeCharStr[label_end-1]=='-')) {
5201 SetLastError(ERROR_INVALID_NAME);
5202 return 0;
5205 if(ascii_only) {
5206 /* maximal label length is 63 characters */
5207 if(label_end-label_start > 63) {
5208 SetLastError(ERROR_INVALID_NAME);
5209 return 0;
5211 if(label_end < cchUnicodeChar)
5212 label_end++;
5214 if(!lpNameprepCharStr) {
5215 out += label_end-label_start;
5216 }else if(out+label_end-label_start <= cchNameprepChar) {
5217 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5218 (label_end-label_start)*sizeof(WCHAR));
5219 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5220 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5221 out += label_end-label_start;
5222 }else {
5223 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5224 return 0;
5227 label_start = label_end;
5228 continue;
5231 map_len = 0;
5232 for(i=label_start; i<label_end; i++) {
5233 ch = lpUnicodeCharStr[i];
5234 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5235 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5237 if(!ptr[0]) map_len++;
5238 else if(!ptr[1]) map_len++;
5239 else if(!ptr[2]) map_len += 2;
5240 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5242 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5243 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5244 if(!map_str) {
5245 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5246 return 0;
5248 }else {
5249 map_str = buf;
5251 map_len = 0;
5252 for(i=label_start; i<label_end; i++) {
5253 ch = lpUnicodeCharStr[i];
5254 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5255 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5257 if(!ptr[0]) {
5258 map_str[map_len++] = ch;
5259 }else if(!ptr[1]) {
5260 map_str[map_len++] = ptr[0];
5261 }else if(!ptr[2]) {
5262 map_str[map_len++] = ptr[0];
5263 map_str[map_len++] = ptr[1];
5264 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5265 map_str[map_len++] = ptr[0];
5266 map_str[map_len++] = ptr[1];
5267 map_str[map_len++] = ptr[2];
5271 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5272 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5273 if(map_str != buf)
5274 HeapFree(GetProcessHeap(), 0, map_str);
5275 if(!norm_len) {
5276 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5277 SetLastError(ERROR_INVALID_NAME);
5278 return 0;
5281 if(label_end < cchUnicodeChar) {
5282 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5283 label_end++;
5286 if(!lpNameprepCharStr) {
5287 out += norm_len;
5288 }else if(out+norm_len <= cchNameprepChar) {
5289 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5290 out += norm_len;
5291 }else {
5292 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5293 return 0;
5296 have_bidi_ral = prohibit_bidi_ral = FALSE;
5297 mask = PROHIBITED;
5298 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5299 mask |= UNASSIGNED;
5300 for(i=0; i<norm_len; i++) {
5301 ch = norm_str[i];
5302 flags = get_table_entry( nameprep_char_type, ch );
5304 if(flags & mask) {
5305 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5306 : ERROR_NO_UNICODE_TRANSLATION);
5307 return 0;
5310 if(flags & BIDI_RAL)
5311 have_bidi_ral = TRUE;
5312 if(flags & BIDI_L)
5313 prohibit_bidi_ral = TRUE;
5316 if(have_bidi_ral) {
5317 ch = norm_str[0];
5318 flags = get_table_entry( nameprep_char_type, ch );
5319 if((flags & BIDI_RAL) == 0)
5320 prohibit_bidi_ral = TRUE;
5322 ch = norm_str[norm_len-1];
5323 flags = get_table_entry( nameprep_char_type, ch );
5324 if((flags & BIDI_RAL) == 0)
5325 prohibit_bidi_ral = TRUE;
5328 if(have_bidi_ral && prohibit_bidi_ral) {
5329 SetLastError(ERROR_INVALID_NAME);
5330 return 0;
5333 label_start = label_end;
5336 return out;
5339 /******************************************************************************
5340 * IdnToUnicode (KERNEL32.@)
5342 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5343 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5345 extern const unsigned short nameprep_char_type[];
5347 INT i, label_start, label_end, out_label, out = 0;
5348 WCHAR ch;
5350 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5351 lpUnicodeCharStr, cchUnicodeChar);
5353 for(label_start=0; label_start<cchASCIIChar;) {
5354 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5356 out_label = out;
5357 for(i=label_start; i<cchASCIIChar; i++) {
5358 ch = lpASCIICharStr[i];
5360 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5361 SetLastError(ERROR_INVALID_NAME);
5362 return 0;
5365 if(!ch || ch=='.')
5366 break;
5367 if(ch == '-')
5368 delim = i;
5370 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5371 continue;
5372 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5373 || (ch>='0' && ch<='9') || ch=='-')
5374 continue;
5376 SetLastError(ERROR_INVALID_NAME);
5377 return 0;
5379 label_end = i;
5380 /* last label may be empty */
5381 if(label_start==label_end && ch) {
5382 SetLastError(ERROR_INVALID_NAME);
5383 return 0;
5386 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5387 lpASCIICharStr[label_end-1]=='-')) {
5388 SetLastError(ERROR_INVALID_NAME);
5389 return 0;
5391 if(label_end-label_start > 63) {
5392 SetLastError(ERROR_INVALID_NAME);
5393 return 0;
5396 if(label_end-label_start<4 ||
5397 tolowerW(lpASCIICharStr[label_start])!='x' ||
5398 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5399 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5400 if(label_end < cchASCIIChar)
5401 label_end++;
5403 if(!lpUnicodeCharStr) {
5404 out += label_end-label_start;
5405 }else if(out+label_end-label_start <= cchUnicodeChar) {
5406 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5407 (label_end-label_start)*sizeof(WCHAR));
5408 out += label_end-label_start;
5409 }else {
5410 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5411 return 0;
5414 label_start = label_end;
5415 continue;
5418 if(delim == label_start+3)
5419 delim++;
5420 if(!lpUnicodeCharStr) {
5421 out += delim-label_start-4;
5422 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5423 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5424 (delim-label_start-4)*sizeof(WCHAR));
5425 out += delim-label_start-4;
5426 }else {
5427 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5428 return 0;
5430 if(out != out_label)
5431 delim++;
5433 for(i=delim; i<label_end;) {
5434 old_pos = pos;
5435 w = 1;
5436 for(k=BASE; ; k+=BASE) {
5437 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5438 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5439 SetLastError(ERROR_INVALID_NAME);
5440 return 0;
5442 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5443 pos += digit*w;
5444 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5445 if(digit < t)
5446 break;
5447 w *= BASE-t;
5449 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5450 n += pos/(out-out_label+1);
5451 pos %= out-out_label+1;
5453 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5454 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5455 SetLastError(ERROR_INVALID_NAME);
5456 return 0;
5458 if(!lpUnicodeCharStr) {
5459 out++;
5460 }else if(out+1 <= cchASCIIChar) {
5461 memmove(lpUnicodeCharStr+out_label+pos+1,
5462 lpUnicodeCharStr+out_label+pos,
5463 (out-out_label-pos)*sizeof(WCHAR));
5464 lpUnicodeCharStr[out_label+pos] = n;
5465 out++;
5466 }else {
5467 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5468 return 0;
5470 pos++;
5473 if(out-out_label > 63) {
5474 SetLastError(ERROR_INVALID_NAME);
5475 return 0;
5478 if(label_end < cchASCIIChar) {
5479 if(!lpUnicodeCharStr) {
5480 out++;
5481 }else if(out+1 <= cchUnicodeChar) {
5482 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5483 }else {
5484 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5485 return 0;
5488 label_start = label_end+1;
5491 return out;
5495 /******************************************************************************
5496 * GetUserPreferredUILanguages (KERNEL32.@)
5498 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5500 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5501 return FALSE;
5504 /******************************************************************************
5505 * GetFileMUIPath (KERNEL32.@)
5508 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5509 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5511 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5512 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5514 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5516 return FALSE;
5519 /******************************************************************************
5520 * GetFileMUIInfo (KERNEL32.@)
5523 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5525 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5527 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5528 return FALSE;