kernel32: Implement LCMAP_KATAKANA.
[wine.git] / dlls / kernel32 / locale.c
blob842922a50b40acb477b7e3b267191f8584a70476
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 && locale && 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;
3138 INT len;
3140 if (version) FIXME("unsupported version structure %p\n", version);
3141 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3142 if (lparam)
3144 static int once;
3145 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3148 if (!src || !srclen || dstlen < 0)
3150 SetLastError(ERROR_INVALID_PARAMETER);
3151 return 0;
3154 /* mutually exclusive flags */
3155 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3156 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3157 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3158 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
3159 !flags)
3161 SetLastError(ERROR_INVALID_FLAGS);
3162 return 0;
3165 if (!dstlen) dst = NULL;
3167 if (flags & LCMAP_SORTKEY)
3169 INT ret;
3170 if (src == dst)
3172 SetLastError(ERROR_INVALID_FLAGS);
3173 return 0;
3176 if (srclen < 0) srclen = strlenW(src);
3178 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3179 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3181 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3182 if (ret == 0)
3183 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3184 else
3185 ret++;
3186 return ret;
3189 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3190 if (flags & SORT_STRINGSORT)
3192 SetLastError(ERROR_INVALID_FLAGS);
3193 return 0;
3195 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
3196 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
3197 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) &&
3198 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
3200 SetLastError(ERROR_INVALID_FLAGS);
3201 return 0;
3204 if (srclen < 0) srclen = strlenW(src) + 1;
3206 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3207 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3209 if (!dst) /* return required string length */
3211 if (flags & NORM_IGNORESYMBOLS)
3213 for (len = 0; srclen; src++, srclen--)
3215 WCHAR wch = *src;
3216 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3217 * and skips white space and punctuation characters for
3218 * NORM_IGNORESYMBOLS.
3220 if (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE))
3221 continue;
3222 len++;
3225 else
3226 len = srclen;
3227 return len;
3230 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3232 SetLastError(ERROR_INVALID_FLAGS);
3233 return 0;
3236 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
3238 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3240 WCHAR wch = *src;
3241 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3242 continue;
3243 *dst_ptr++ = wch;
3244 len--;
3246 goto done;
3249 if (flags & LCMAP_UPPERCASE)
3251 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3253 *dst_ptr++ = toupperW(*src);
3254 len--;
3257 else if (flags & LCMAP_LOWERCASE)
3259 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3261 *dst_ptr++ = tolowerW(*src);
3262 len--;
3265 else
3267 len = min(srclen, dstlen);
3268 memcpy(dst, src, len * sizeof(WCHAR));
3269 dst_ptr = dst + len;
3270 srclen -= len;
3273 if (flags & LCMAP_HIRAGANA)
3275 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
3276 we can't use C3_KATAKANA as some characters can't map to hiragana */
3277 for (len = dst_ptr - dst, dst_ptr = dst; len; len--, dst_ptr++)
3279 if ((*dst_ptr >= 0x30A1 && *dst_ptr <= 0x30F6) ||
3280 *dst_ptr == 0x30FD || *dst_ptr == 0x30FE)
3281 *dst_ptr -= 0x60;
3284 else if (flags & LCMAP_KATAKANA)
3286 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
3287 we can't use C3_HIRAGANA as some characters can't map to katakana */
3288 for (len = dst_ptr - dst, dst_ptr = dst; len; len--, dst_ptr++)
3290 if ((*dst_ptr >= 0x3041 && *dst_ptr <= 0x3096) ||
3291 *dst_ptr == 0x309D || *dst_ptr == 0x309E)
3292 *dst_ptr += 0x60;
3296 done:
3297 if (srclen)
3299 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3300 return 0;
3303 return dst_ptr - dst;
3306 /*************************************************************************
3307 * LCMapStringW (KERNEL32.@)
3309 * See LCMapStringA.
3311 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3312 LPWSTR dst, INT dstlen)
3314 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3315 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3317 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3320 /*************************************************************************
3321 * LCMapStringA (KERNEL32.@)
3323 * Map characters in a locale sensitive string.
3325 * PARAMS
3326 * lcid [I] LCID for the conversion.
3327 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3328 * src [I] String to map
3329 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3330 * dst [O] Destination for mapped string
3331 * dstlen [I] Length of dst in characters
3333 * RETURNS
3334 * Success: The length of the mapped string in dst, including the NUL terminator.
3335 * Failure: 0. Use GetLastError() to determine the cause.
3337 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3338 LPSTR dst, INT dstlen)
3340 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3341 LPWSTR srcW, dstW;
3342 INT ret = 0, srclenW, dstlenW;
3343 UINT locale_cp = CP_ACP;
3345 if (!src || !srclen || dstlen < 0)
3347 SetLastError(ERROR_INVALID_PARAMETER);
3348 return 0;
3351 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3353 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3354 if (srclenW)
3355 srcW = bufW;
3356 else
3358 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3359 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3360 if (!srcW)
3362 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3363 return 0;
3365 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3368 if (flags & LCMAP_SORTKEY)
3370 if (src == dst)
3372 SetLastError(ERROR_INVALID_FLAGS);
3373 goto map_string_exit;
3375 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3376 if (ret == 0)
3377 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3378 else
3379 ret++;
3380 goto map_string_exit;
3383 if (flags & SORT_STRINGSORT)
3385 SetLastError(ERROR_INVALID_FLAGS);
3386 goto map_string_exit;
3389 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3390 if (!dstlenW)
3391 goto map_string_exit;
3393 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3394 if (!dstW)
3396 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3397 goto map_string_exit;
3400 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3401 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3402 HeapFree(GetProcessHeap(), 0, dstW);
3404 map_string_exit:
3405 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3406 return ret;
3409 /*************************************************************************
3410 * FoldStringA (KERNEL32.@)
3412 * Map characters in a string.
3414 * PARAMS
3415 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3416 * src [I] String to map
3417 * srclen [I] Length of src, or -1 if src is NUL terminated
3418 * dst [O] Destination for mapped string
3419 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3421 * RETURNS
3422 * Success: The length of the string written to dst, including the terminating NUL. If
3423 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3424 * and dst may be NULL.
3425 * Failure: 0. Use GetLastError() to determine the cause.
3427 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3428 LPSTR dst, INT dstlen)
3430 INT ret = 0, srclenW = 0;
3431 WCHAR *srcW = NULL, *dstW = NULL;
3433 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3435 SetLastError(ERROR_INVALID_PARAMETER);
3436 return 0;
3439 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3440 src, srclen, NULL, 0);
3441 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3443 if (!srcW)
3445 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3446 goto FoldStringA_exit;
3449 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3450 src, srclen, srcW, srclenW);
3452 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3454 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3455 if (ret && dstlen)
3457 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3459 if (!dstW)
3461 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3462 goto FoldStringA_exit;
3465 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3466 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3468 ret = 0;
3469 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3473 HeapFree(GetProcessHeap(), 0, dstW);
3475 FoldStringA_exit:
3476 HeapFree(GetProcessHeap(), 0, srcW);
3477 return ret;
3480 /*************************************************************************
3481 * FoldStringW (KERNEL32.@)
3483 * See FoldStringA.
3485 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3486 LPWSTR dst, INT dstlen)
3488 int ret;
3490 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3492 case 0:
3493 if (dwFlags)
3494 break;
3495 /* Fall through for dwFlags == 0 */
3496 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3497 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3498 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3499 SetLastError(ERROR_INVALID_FLAGS);
3500 return 0;
3503 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3505 SetLastError(ERROR_INVALID_PARAMETER);
3506 return 0;
3509 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3510 if (!ret)
3511 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3512 return ret;
3515 /******************************************************************************
3516 * CompareStringW (KERNEL32.@)
3518 * See CompareStringA.
3520 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3521 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3523 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3526 /******************************************************************************
3527 * CompareStringEx (KERNEL32.@)
3529 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3530 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3532 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3533 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3534 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3535 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3536 INT ret;
3537 static int once;
3539 if (version) FIXME("unexpected version parameter\n");
3540 if (reserved) FIXME("unexpected reserved value\n");
3541 if (lParam) FIXME("unexpected lParam\n");
3543 if (!str1 || !str2)
3545 SetLastError(ERROR_INVALID_PARAMETER);
3546 return 0;
3549 if (flags & ~(supported_flags|semistub_flags))
3551 SetLastError(ERROR_INVALID_FLAGS);
3552 return 0;
3555 if (flags & semistub_flags)
3557 if (!once++)
3558 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3561 if (len1 < 0) len1 = strlenW(str1);
3562 if (len2 < 0) len2 = strlenW(str2);
3564 ret = wine_compare_string(flags, str1, len1, str2, len2);
3566 if (ret) /* need to translate result */
3567 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3568 return CSTR_EQUAL;
3571 /******************************************************************************
3572 * CompareStringA (KERNEL32.@)
3574 * Compare two locale sensitive strings.
3576 * PARAMS
3577 * lcid [I] LCID for the comparison
3578 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3579 * str1 [I] First string to compare
3580 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3581 * str2 [I] Second string to compare
3582 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3584 * RETURNS
3585 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3586 * str1 is less than, equal to or greater than str2 respectively.
3587 * Failure: FALSE. Use GetLastError() to determine the cause.
3589 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3590 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3592 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3593 WCHAR *buf2W = buf1W + 130;
3594 LPWSTR str1W, str2W;
3595 INT len1W = 0, len2W = 0, ret;
3596 UINT locale_cp = CP_ACP;
3598 if (!str1 || !str2)
3600 SetLastError(ERROR_INVALID_PARAMETER);
3601 return 0;
3603 if (len1 < 0) len1 = strlen(str1);
3604 if (len2 < 0) len2 = strlen(str2);
3606 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3608 if (len1)
3610 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3611 if (len1W)
3612 str1W = buf1W;
3613 else
3615 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3616 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3617 if (!str1W)
3619 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3620 return 0;
3622 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3625 else
3627 len1W = 0;
3628 str1W = buf1W;
3631 if (len2)
3633 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3634 if (len2W)
3635 str2W = buf2W;
3636 else
3638 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3639 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3640 if (!str2W)
3642 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3643 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3644 return 0;
3646 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3649 else
3651 len2W = 0;
3652 str2W = buf2W;
3655 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3657 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3658 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3659 return ret;
3662 /******************************************************************************
3663 * CompareStringOrdinal (KERNEL32.@)
3665 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3667 int ret;
3669 if (!str1 || !str2)
3671 SetLastError(ERROR_INVALID_PARAMETER);
3672 return 0;
3674 if (len1 < 0) len1 = strlenW(str1);
3675 if (len2 < 0) len2 = strlenW(str2);
3677 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
3678 if (ret < 0) return CSTR_LESS_THAN;
3679 if (ret > 0) return CSTR_GREATER_THAN;
3680 return CSTR_EQUAL;
3683 /*************************************************************************
3684 * lstrcmp (KERNEL32.@)
3685 * lstrcmpA (KERNEL32.@)
3687 * Compare two strings using the current thread locale.
3689 * PARAMS
3690 * str1 [I] First string to compare
3691 * str2 [I] Second string to compare
3693 * RETURNS
3694 * Success: A number less than, equal to or greater than 0 depending on whether
3695 * str1 is less than, equal to or greater than str2 respectively.
3696 * Failure: FALSE. Use GetLastError() to determine the cause.
3698 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3700 int ret;
3702 if ((str1 == NULL) && (str2 == NULL)) return 0;
3703 if (str1 == NULL) return -1;
3704 if (str2 == NULL) return 1;
3706 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3707 if (ret) ret -= 2;
3709 return ret;
3712 /*************************************************************************
3713 * lstrcmpi (KERNEL32.@)
3714 * lstrcmpiA (KERNEL32.@)
3716 * Compare two strings using the current thread locale, ignoring case.
3718 * PARAMS
3719 * str1 [I] First string to compare
3720 * str2 [I] Second string to compare
3722 * RETURNS
3723 * Success: A number less than, equal to or greater than 0 depending on whether
3724 * str2 is less than, equal to or greater than str1 respectively.
3725 * Failure: FALSE. Use GetLastError() to determine the cause.
3727 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3729 int ret;
3731 if ((str1 == NULL) && (str2 == NULL)) return 0;
3732 if (str1 == NULL) return -1;
3733 if (str2 == NULL) return 1;
3735 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3736 if (ret) ret -= 2;
3738 return ret;
3741 /*************************************************************************
3742 * lstrcmpW (KERNEL32.@)
3744 * See lstrcmpA.
3746 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3748 int ret;
3750 if ((str1 == NULL) && (str2 == NULL)) return 0;
3751 if (str1 == NULL) return -1;
3752 if (str2 == NULL) return 1;
3754 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3755 if (ret) ret -= 2;
3757 return ret;
3760 /*************************************************************************
3761 * lstrcmpiW (KERNEL32.@)
3763 * See lstrcmpiA.
3765 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3767 int ret;
3769 if ((str1 == NULL) && (str2 == NULL)) return 0;
3770 if (str1 == NULL) return -1;
3771 if (str2 == NULL) return 1;
3773 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3774 if (ret) ret -= 2;
3776 return ret;
3779 /******************************************************************************
3780 * LOCALE_Init
3782 void LOCALE_Init(void)
3784 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3785 const union cptable *unix_cp );
3787 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3789 setlocale( LC_ALL, "" );
3791 #ifdef __APPLE__
3792 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3793 if (!has_env("LANG"))
3795 const char* mac_locale = get_mac_locale();
3797 setenv( "LANG", mac_locale, 1 );
3798 if (setlocale( LC_ALL, "" ))
3799 TRACE( "setting LANG to '%s'\n", mac_locale );
3800 else
3802 /* no C library locale matching Mac locale; don't pass garbage to children */
3803 unsetenv("LANG");
3804 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
3807 #endif /* __APPLE__ */
3809 unix_cp = setup_unix_locales();
3810 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3812 #ifdef __APPLE__
3813 if (!unix_cp)
3814 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3815 #endif
3817 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3818 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3819 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3821 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3822 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3823 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3824 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3825 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3826 if (!unix_cp)
3827 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3828 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3830 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3831 ansi_cptable = wine_cp_get_table( 1252 );
3832 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3833 oem_cptable = wine_cp_get_table( 437 );
3834 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3835 mac_cptable = wine_cp_get_table( 10000 );
3836 if (unix_cp != CP_UTF8)
3838 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3839 unix_cptable = wine_cp_get_table( 28591 );
3842 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3844 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3845 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3846 mac_cptable->info.codepage, unix_cp );
3848 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3851 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3853 UNICODE_STRING keyName;
3854 OBJECT_ATTRIBUTES attr;
3855 HANDLE hkey;
3857 RtlInitUnicodeString( &keyName, szKeyName );
3858 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3860 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3861 hkey = 0;
3863 return hkey;
3866 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3867 LPWSTR szValueName, ULONG valueNameSize,
3868 LPWSTR szValueData, ULONG valueDataSize)
3870 BYTE buffer[80];
3871 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3872 DWORD dwLen;
3874 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3875 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3876 info->NameLength > valueNameSize ||
3877 info->DataLength > valueDataSize)
3879 return FALSE;
3882 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3884 memcpy( szValueName, info->Name, info->NameLength);
3885 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3886 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3887 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3889 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3890 return TRUE;
3893 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3895 BYTE buffer[128];
3896 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3897 DWORD dwSize = sizeof(buffer);
3898 UNICODE_STRING valueName;
3900 RtlInitUnicodeString( &valueName, szValueName );
3902 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3903 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3904 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3905 info->DataLength == sizeof(DWORD))
3907 memcpy(lpVal, info->Data, sizeof(DWORD));
3908 return TRUE;
3911 return FALSE;
3914 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3916 LANGID langId;
3917 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3918 HRSRC hResource;
3919 BOOL bRet = FALSE;
3921 /* FIXME: Is it correct to use the system default langid? */
3922 langId = GetSystemDefaultLangID();
3924 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3925 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3927 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3929 if (hResource)
3931 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3933 if (hResDir)
3935 ULONG iResourceIndex = lgrpid & 0xf;
3936 LPCWSTR lpResEntry = LockResource( hResDir );
3937 ULONG i;
3939 for (i = 0; i < iResourceIndex; i++)
3940 lpResEntry += *lpResEntry + 1;
3942 if (*lpResEntry < nameSize)
3944 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3945 szName[*lpResEntry] = '\0';
3946 bRet = TRUE;
3950 FreeResource( hResource );
3952 return bRet;
3955 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3956 typedef struct
3958 LANGUAGEGROUP_ENUMPROCA procA;
3959 LANGUAGEGROUP_ENUMPROCW procW;
3960 DWORD dwFlags;
3961 LONG_PTR lParam;
3962 } ENUMLANGUAGEGROUP_CALLBACKS;
3964 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3965 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3967 WCHAR szNumber[10], szValue[4];
3968 HANDLE hKey;
3969 BOOL bContinue = TRUE;
3970 ULONG ulIndex = 0;
3972 if (!lpProcs)
3974 SetLastError(ERROR_INVALID_PARAMETER);
3975 return FALSE;
3978 switch (lpProcs->dwFlags)
3980 case 0:
3981 /* Default to LGRPID_INSTALLED */
3982 lpProcs->dwFlags = LGRPID_INSTALLED;
3983 /* Fall through... */
3984 case LGRPID_INSTALLED:
3985 case LGRPID_SUPPORTED:
3986 break;
3987 default:
3988 SetLastError(ERROR_INVALID_FLAGS);
3989 return FALSE;
3992 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3994 if (!hKey)
3995 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3997 while (bContinue)
3999 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4000 szValue, sizeof(szValue) ))
4002 BOOL bInstalled = szValue[0] == '1';
4003 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4005 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4006 bInstalled ? "" : "not ");
4008 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4010 WCHAR szGrpName[48];
4012 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
4013 szGrpName[0] = '\0';
4015 if (lpProcs->procW)
4016 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4017 lpProcs->lParam );
4018 else
4020 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4021 char szGrpNameA[48];
4023 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4024 * or whether the language names are ever localised. Assume CP_ACP.
4027 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4028 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4030 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4031 lpProcs->lParam );
4035 ulIndex++;
4037 else
4038 bContinue = FALSE;
4040 if (!bContinue)
4041 break;
4044 if (hKey)
4045 NtClose( hKey );
4047 return TRUE;
4050 /******************************************************************************
4051 * EnumSystemLanguageGroupsA (KERNEL32.@)
4053 * Call a users function for each language group available on the system.
4055 * PARAMS
4056 * pLangGrpEnumProc [I] Callback function to call for each language group
4057 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4058 * lParam [I] User parameter to pass to pLangGrpEnumProc
4060 * RETURNS
4061 * Success: TRUE.
4062 * Failure: FALSE. Use GetLastError() to determine the cause.
4064 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4065 DWORD dwFlags, LONG_PTR lParam)
4067 ENUMLANGUAGEGROUP_CALLBACKS procs;
4069 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4071 procs.procA = pLangGrpEnumProc;
4072 procs.procW = NULL;
4073 procs.dwFlags = dwFlags;
4074 procs.lParam = lParam;
4076 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4079 /******************************************************************************
4080 * EnumSystemLanguageGroupsW (KERNEL32.@)
4082 * See EnumSystemLanguageGroupsA.
4084 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4085 DWORD dwFlags, LONG_PTR lParam)
4087 ENUMLANGUAGEGROUP_CALLBACKS procs;
4089 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4091 procs.procA = NULL;
4092 procs.procW = pLangGrpEnumProc;
4093 procs.dwFlags = dwFlags;
4094 procs.lParam = lParam;
4096 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4099 /******************************************************************************
4100 * IsValidLanguageGroup (KERNEL32.@)
4102 * Determine if a language group is supported and/or installed.
4104 * PARAMS
4105 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4106 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4108 * RETURNS
4109 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4110 * FALSE otherwise.
4112 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4114 static const WCHAR szFormat[] = { '%','x','\0' };
4115 WCHAR szValueName[16], szValue[2];
4116 BOOL bSupported = FALSE, bInstalled = FALSE;
4117 HANDLE hKey;
4120 switch (dwFlags)
4122 case LGRPID_INSTALLED:
4123 case LGRPID_SUPPORTED:
4125 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4127 sprintfW( szValueName, szFormat, lgrpid );
4129 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4131 bSupported = TRUE;
4133 if (szValue[0] == '1')
4134 bInstalled = TRUE;
4137 if (hKey)
4138 NtClose( hKey );
4140 break;
4143 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4144 (dwFlags == LGRPID_INSTALLED && bInstalled))
4145 return TRUE;
4147 return FALSE;
4150 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4151 typedef struct
4153 LANGGROUPLOCALE_ENUMPROCA procA;
4154 LANGGROUPLOCALE_ENUMPROCW procW;
4155 DWORD dwFlags;
4156 LGRPID lgrpid;
4157 LONG_PTR lParam;
4158 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4160 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4161 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4163 static const WCHAR szAlternateSortsKeyName[] = {
4164 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4166 WCHAR szNumber[10], szValue[4];
4167 HANDLE hKey;
4168 BOOL bContinue = TRUE, bAlternate = FALSE;
4169 LGRPID lgrpid;
4170 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4172 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4174 SetLastError(ERROR_INVALID_PARAMETER);
4175 return FALSE;
4178 if (lpProcs->dwFlags)
4180 SetLastError(ERROR_INVALID_FLAGS);
4181 return FALSE;
4184 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4186 if (!hKey)
4187 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4189 while (bContinue)
4191 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4192 szValue, sizeof(szValue) ))
4194 lgrpid = strtoulW( szValue, NULL, 16 );
4196 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4197 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4199 if (lgrpid == lpProcs->lgrpid)
4201 LCID lcid;
4203 lcid = strtoulW( szNumber, NULL, 16 );
4205 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4206 * '00000437 ;Georgian'
4207 * At present we only pass the LCID string.
4210 if (lpProcs->procW)
4211 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4212 else
4214 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4216 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4218 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4222 ulIndex++;
4224 else
4226 /* Finished enumerating this key */
4227 if (!bAlternate)
4229 /* Enumerate alternate sorts also */
4230 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4231 bAlternate = TRUE;
4232 ulIndex = 0;
4234 else
4235 bContinue = FALSE; /* Finished both keys */
4238 if (!bContinue)
4239 break;
4242 if (hKey)
4243 NtClose( hKey );
4245 return TRUE;
4248 /******************************************************************************
4249 * EnumLanguageGroupLocalesA (KERNEL32.@)
4251 * Call a users function for every locale in a language group available on the system.
4253 * PARAMS
4254 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4255 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4256 * dwFlags [I] Reserved, set to 0
4257 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4259 * RETURNS
4260 * Success: TRUE.
4261 * Failure: FALSE. Use GetLastError() to determine the cause.
4263 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4264 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4266 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4268 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4270 callbacks.procA = pLangGrpLcEnumProc;
4271 callbacks.procW = NULL;
4272 callbacks.dwFlags = dwFlags;
4273 callbacks.lgrpid = lgrpid;
4274 callbacks.lParam = lParam;
4276 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4279 /******************************************************************************
4280 * EnumLanguageGroupLocalesW (KERNEL32.@)
4282 * See EnumLanguageGroupLocalesA.
4284 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4285 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4287 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4289 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4291 callbacks.procA = NULL;
4292 callbacks.procW = pLangGrpLcEnumProc;
4293 callbacks.dwFlags = dwFlags;
4294 callbacks.lgrpid = lgrpid;
4295 callbacks.lParam = lParam;
4297 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4300 /******************************************************************************
4301 * InvalidateNLSCache (KERNEL32.@)
4303 * Invalidate the cache of NLS values.
4305 * PARAMS
4306 * None.
4308 * RETURNS
4309 * Success: TRUE.
4310 * Failure: FALSE.
4312 BOOL WINAPI InvalidateNLSCache(void)
4314 FIXME("() stub\n");
4315 return FALSE;
4318 /******************************************************************************
4319 * GetUserGeoID (KERNEL32.@)
4321 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4323 GEOID ret = GEOID_NOT_AVAILABLE;
4324 static const WCHAR geoW[] = {'G','e','o',0};
4325 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4326 WCHAR bufferW[40], *end;
4327 DWORD count;
4328 HANDLE hkey, hSubkey = 0;
4329 UNICODE_STRING keyW;
4330 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4331 RtlInitUnicodeString( &keyW, nationW );
4332 count = sizeof(bufferW);
4334 if(!(hkey = create_registry_key())) return ret;
4336 switch( GeoClass ){
4337 case GEOCLASS_NATION:
4338 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4340 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4341 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4342 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4344 break;
4345 case GEOCLASS_REGION:
4346 FIXME("GEOCLASS_REGION not handled yet\n");
4347 break;
4350 NtClose(hkey);
4351 if (hSubkey) NtClose(hSubkey);
4352 return ret;
4355 /******************************************************************************
4356 * SetUserGeoID (KERNEL32.@)
4358 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4360 static const WCHAR geoW[] = {'G','e','o',0};
4361 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4362 static const WCHAR formatW[] = {'%','i',0};
4363 UNICODE_STRING nameW,keyW;
4364 WCHAR bufferW[10];
4365 OBJECT_ATTRIBUTES attr;
4366 HANDLE hkey;
4368 if(!(hkey = create_registry_key())) return FALSE;
4370 attr.Length = sizeof(attr);
4371 attr.RootDirectory = hkey;
4372 attr.ObjectName = &nameW;
4373 attr.Attributes = 0;
4374 attr.SecurityDescriptor = NULL;
4375 attr.SecurityQualityOfService = NULL;
4376 RtlInitUnicodeString( &nameW, geoW );
4377 RtlInitUnicodeString( &keyW, nationW );
4379 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4382 NtClose(attr.RootDirectory);
4383 return FALSE;
4386 sprintfW(bufferW, formatW, GeoID);
4387 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4388 NtClose(attr.RootDirectory);
4389 NtClose(hkey);
4390 return TRUE;
4393 typedef struct
4395 union
4397 UILANGUAGE_ENUMPROCA procA;
4398 UILANGUAGE_ENUMPROCW procW;
4399 } u;
4400 DWORD flags;
4401 LONG_PTR param;
4402 } ENUM_UILANG_CALLBACK;
4404 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4405 LPCSTR name, WORD LangID, LONG_PTR lParam )
4407 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4408 char buf[20];
4410 sprintf(buf, "%08x", (UINT)LangID);
4411 return enum_uilang->u.procA( buf, enum_uilang->param );
4414 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4415 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4417 static const WCHAR formatW[] = {'%','0','8','x',0};
4418 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4419 WCHAR buf[20];
4421 sprintfW( buf, formatW, (UINT)LangID );
4422 return enum_uilang->u.procW( buf, enum_uilang->param );
4425 /******************************************************************************
4426 * EnumUILanguagesA (KERNEL32.@)
4428 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4430 ENUM_UILANG_CALLBACK enum_uilang;
4432 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4434 if(!pUILangEnumProc) {
4435 SetLastError(ERROR_INVALID_PARAMETER);
4436 return FALSE;
4438 if(dwFlags) {
4439 SetLastError(ERROR_INVALID_FLAGS);
4440 return FALSE;
4443 enum_uilang.u.procA = pUILangEnumProc;
4444 enum_uilang.flags = dwFlags;
4445 enum_uilang.param = lParam;
4447 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4448 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4449 (LONG_PTR)&enum_uilang);
4450 return TRUE;
4453 /******************************************************************************
4454 * EnumUILanguagesW (KERNEL32.@)
4456 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4458 ENUM_UILANG_CALLBACK enum_uilang;
4460 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4463 if(!pUILangEnumProc) {
4464 SetLastError(ERROR_INVALID_PARAMETER);
4465 return FALSE;
4467 if(dwFlags) {
4468 SetLastError(ERROR_INVALID_FLAGS);
4469 return FALSE;
4472 enum_uilang.u.procW = pUILangEnumProc;
4473 enum_uilang.flags = dwFlags;
4474 enum_uilang.param = lParam;
4476 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4477 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4478 (LONG_PTR)&enum_uilang);
4479 return TRUE;
4482 enum locationkind {
4483 LOCATION_NATION = 0,
4484 LOCATION_REGION,
4485 LOCATION_BOTH
4488 struct geoinfo_t {
4489 GEOID id;
4490 WCHAR iso2W[3];
4491 WCHAR iso3W[4];
4492 GEOID parent;
4493 INT uncode;
4494 enum locationkind kind;
4497 static const struct geoinfo_t geoinfodata[] = {
4498 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4499 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4500 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4501 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4502 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4503 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4504 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4505 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4506 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4507 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4508 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4509 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4510 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4511 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4512 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4513 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4514 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4515 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4516 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4517 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4518 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4519 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4520 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4521 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4522 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4523 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4524 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4525 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4526 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4527 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4528 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4529 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4530 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4531 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4532 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4533 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4534 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4535 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4536 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4537 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4538 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4539 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4540 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4541 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4542 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4543 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4544 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4545 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4546 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4547 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4548 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4549 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4550 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4551 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4552 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4553 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4554 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4555 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4556 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4557 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4558 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4559 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4560 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4561 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4562 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4563 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4564 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4565 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4566 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4567 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4568 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4569 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4570 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4571 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4572 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4573 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4574 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4575 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4576 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4577 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4578 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4579 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4580 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4581 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4582 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4583 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4584 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4585 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4586 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4587 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4588 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4589 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4590 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4591 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4592 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4593 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4594 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4595 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4596 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4597 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4598 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4599 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4600 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4601 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4602 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4603 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4604 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4605 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4606 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4607 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4608 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4609 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4610 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4611 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4612 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4613 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4614 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4615 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4616 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4617 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4618 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4619 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4620 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4621 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4622 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4623 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4624 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4625 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4626 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4627 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4628 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4629 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4630 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4631 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4632 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4633 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4634 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4635 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4636 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4637 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4638 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4639 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4640 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4641 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4642 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4643 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4644 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4645 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4646 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4647 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4648 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4649 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4650 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4651 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4652 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4653 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4654 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4655 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4656 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4657 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4658 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4659 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4660 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4661 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4662 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4663 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4664 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4665 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4666 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4667 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4668 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4669 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4670 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4671 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4672 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4673 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4674 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4675 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4676 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4677 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4678 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4679 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4680 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4681 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4682 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4683 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4684 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4685 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4686 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4687 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4688 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4689 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4690 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4691 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4692 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4693 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4694 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4695 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4696 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4697 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4698 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4699 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4700 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4701 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4702 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4703 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4704 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4705 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4706 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4707 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4708 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4709 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4710 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4711 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4712 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4713 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4714 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4715 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4716 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4717 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4718 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4719 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4720 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4721 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4722 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4723 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4724 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4725 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4726 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4727 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4728 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4729 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4730 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4731 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4732 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4733 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4734 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4735 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4736 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4737 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4738 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4739 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4740 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4741 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4742 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4743 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4744 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4745 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4746 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4747 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4748 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4749 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4750 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4751 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4752 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4753 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4754 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4755 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4756 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4757 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4758 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4759 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4760 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4761 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4762 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4763 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4764 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4765 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4766 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4767 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4768 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4769 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4770 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4771 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4772 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4773 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4774 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4775 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4776 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4777 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4778 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4779 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4780 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4781 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4782 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4783 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4784 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4785 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4786 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4787 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4788 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4789 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4790 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4791 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4792 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4793 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4794 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4795 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4796 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4799 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4801 int min, max;
4803 min = 0;
4804 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4806 while (min <= max) {
4807 const struct geoinfo_t *ptr;
4808 int n = (min+max)/2;
4810 ptr = &geoinfodata[n];
4811 if (geoid == ptr->id)
4812 /* we don't need empty entries */
4813 return *ptr->iso2W ? ptr : NULL;
4815 if (ptr->id > geoid)
4816 max = n-1;
4817 else
4818 min = n+1;
4821 return NULL;
4824 /******************************************************************************
4825 * GetGeoInfoW (KERNEL32.@)
4827 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4829 const struct geoinfo_t *ptr;
4830 const WCHAR *str = NULL;
4831 WCHAR buffW[12];
4832 LONG val = 0;
4833 INT len;
4835 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4837 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4838 SetLastError(ERROR_INVALID_PARAMETER);
4839 return 0;
4842 switch (geotype) {
4843 case GEO_NATION:
4844 val = geoid;
4845 break;
4846 case GEO_ISO_UN_NUMBER:
4847 val = ptr->uncode;
4848 break;
4849 case GEO_PARENT:
4850 val = ptr->parent;
4851 break;
4852 case GEO_ISO2:
4853 case GEO_ISO3:
4855 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4856 break;
4858 case GEO_RFC1766:
4859 case GEO_LCID:
4860 case GEO_FRIENDLYNAME:
4861 case GEO_OFFICIALNAME:
4862 case GEO_TIMEZONES:
4863 case GEO_OFFICIALLANGUAGES:
4864 case GEO_LATITUDE:
4865 case GEO_LONGITUDE:
4866 FIXME("type %d is not supported\n", geotype);
4867 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4868 return 0;
4869 default:
4870 WARN("unrecognized type %d\n", geotype);
4871 SetLastError(ERROR_INVALID_FLAGS);
4872 return 0;
4875 if (val) {
4876 static const WCHAR fmtW[] = {'%','d',0};
4877 sprintfW(buffW, fmtW, val);
4878 str = buffW;
4881 len = strlenW(str) + 1;
4882 if (!data || !data_len)
4883 return len;
4885 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4886 if (data_len < len)
4887 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4888 return data_len < len ? 0 : len;
4891 /******************************************************************************
4892 * GetGeoInfoA (KERNEL32.@)
4894 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4896 WCHAR *buffW;
4897 INT len;
4899 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4901 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4902 if (!len)
4903 return 0;
4905 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4906 if (!buffW)
4907 return 0;
4909 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4910 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4911 if (!data || !data_len) {
4912 HeapFree(GetProcessHeap(), 0, buffW);
4913 return len;
4916 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4917 HeapFree(GetProcessHeap(), 0, buffW);
4919 if (data_len < len)
4920 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4921 return data_len < len ? 0 : len;
4924 /******************************************************************************
4925 * EnumSystemGeoID (KERNEL32.@)
4927 * Call a users function for every location available on the system.
4929 * PARAMS
4930 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4931 * parent [I] GEOID for the parent
4932 * enumproc [I] Callback function to call for each location
4934 * RETURNS
4935 * Success: TRUE.
4936 * Failure: FALSE. Use GetLastError() to determine the cause.
4938 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4940 INT i;
4942 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4944 if (!enumproc) {
4945 SetLastError(ERROR_INVALID_PARAMETER);
4946 return FALSE;
4949 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4950 SetLastError(ERROR_INVALID_FLAGS);
4951 return FALSE;
4954 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4955 const struct geoinfo_t *ptr = &geoinfodata[i];
4957 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4958 continue;
4960 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4961 continue;
4963 if (parent && ptr->parent != parent)
4964 continue;
4966 if (!enumproc(ptr->id))
4967 return TRUE;
4970 return TRUE;
4973 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4975 LCID userlcid;
4977 TRACE("%p, %d\n", localename, buffersize);
4979 userlcid = GetUserDefaultLCID();
4980 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4983 /******************************************************************************
4984 * NormalizeString (KERNEL32.@)
4986 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4987 LPWSTR lpDstString, INT cwDstLength)
4989 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4990 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4991 return 0;
4994 /******************************************************************************
4995 * IsNormalizedString (KERNEL32.@)
4997 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4999 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5000 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5001 return FALSE;
5004 enum {
5005 BASE = 36,
5006 TMIN = 1,
5007 TMAX = 26,
5008 SKEW = 38,
5009 DAMP = 700,
5010 INIT_BIAS = 72,
5011 INIT_N = 128
5014 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5016 INT k;
5018 delta /= (firsttime ? DAMP : 2);
5019 delta += delta/numpoints;
5021 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5022 delta /= BASE-TMIN;
5023 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5026 /******************************************************************************
5027 * IdnToAscii (KERNEL32.@)
5028 * Implementation of Punycode based on RFC 3492.
5030 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5031 LPWSTR lpASCIICharStr, INT cchASCIIChar)
5033 static const WCHAR prefixW[] = {'x','n','-','-'};
5035 WCHAR *norm_str;
5036 INT i, label_start, label_end, norm_len, out_label, out = 0;
5038 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5039 lpASCIICharStr, cchASCIIChar);
5041 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5042 if(!norm_len)
5043 return 0;
5044 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5045 if(!norm_str) {
5046 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5047 return 0;
5049 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5050 cchUnicodeChar, norm_str, norm_len);
5051 if(!norm_len) {
5052 HeapFree(GetProcessHeap(), 0, norm_str);
5053 return 0;
5056 for(label_start=0; label_start<norm_len;) {
5057 INT n = INIT_N, bias = INIT_BIAS;
5058 INT delta = 0, b = 0, h;
5060 out_label = out;
5061 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5062 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5063 if(norm_str[i] < 0x80)
5064 b++;
5065 label_end = i;
5067 if(b == label_end-label_start) {
5068 if(label_end < norm_len)
5069 b++;
5070 if(!lpASCIICharStr) {
5071 out += b;
5072 }else if(out+b <= cchASCIIChar) {
5073 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5074 out += b;
5075 }else {
5076 HeapFree(GetProcessHeap(), 0, norm_str);
5077 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5078 return 0;
5080 label_start = label_end+1;
5081 continue;
5084 if(!lpASCIICharStr) {
5085 out += 5+b; /* strlen(xn--...-) */
5086 }else if(out+5+b <= cchASCIIChar) {
5087 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5088 out += 4;
5089 for(i=label_start; i<label_end; i++)
5090 if(norm_str[i] < 0x80)
5091 lpASCIICharStr[out++] = norm_str[i];
5092 lpASCIICharStr[out++] = '-';
5093 }else {
5094 HeapFree(GetProcessHeap(), 0, norm_str);
5095 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5096 return 0;
5098 if(!b)
5099 out--;
5101 for(h=b; h<label_end-label_start;) {
5102 INT m = 0xffff, q, k;
5104 for(i=label_start; i<label_end; i++) {
5105 if(norm_str[i]>=n && m>norm_str[i])
5106 m = norm_str[i];
5108 delta += (m-n)*(h+1);
5109 n = m;
5111 for(i=label_start; i<label_end; i++) {
5112 if(norm_str[i] < n) {
5113 delta++;
5114 }else if(norm_str[i] == n) {
5115 for(q=delta, k=BASE; ; k+=BASE) {
5116 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5117 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5118 if(!lpASCIICharStr) {
5119 out++;
5120 }else if(out+1 <= cchASCIIChar) {
5121 lpASCIICharStr[out++] = disp<='z'-'a' ?
5122 'a'+disp : '0'+disp-'z'+'a'-1;
5123 }else {
5124 HeapFree(GetProcessHeap(), 0, norm_str);
5125 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5126 return 0;
5128 if(q < t)
5129 break;
5130 q = (q-t)/(BASE-t);
5132 bias = adapt(delta, h+1, h==b);
5133 delta = 0;
5134 h++;
5137 delta++;
5138 n++;
5141 if(out-out_label > 63) {
5142 HeapFree(GetProcessHeap(), 0, norm_str);
5143 SetLastError(ERROR_INVALID_NAME);
5144 return 0;
5147 if(label_end < norm_len) {
5148 if(!lpASCIICharStr) {
5149 out++;
5150 }else if(out+1 <= cchASCIIChar) {
5151 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5152 }else {
5153 HeapFree(GetProcessHeap(), 0, norm_str);
5154 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5155 return 0;
5158 label_start = label_end+1;
5161 HeapFree(GetProcessHeap(), 0, norm_str);
5162 return out;
5165 /******************************************************************************
5166 * IdnToNameprepUnicode (KERNEL32.@)
5168 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5169 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5171 enum {
5172 UNASSIGNED = 0x1,
5173 PROHIBITED = 0x2,
5174 BIDI_RAL = 0x4,
5175 BIDI_L = 0x8
5178 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5179 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5180 const WCHAR *ptr;
5181 WORD flags;
5182 WCHAR buf[64], *map_str, norm_str[64], ch;
5183 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5184 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5186 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5187 lpNameprepCharStr, cchNameprepChar);
5189 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5190 SetLastError(ERROR_INVALID_FLAGS);
5191 return 0;
5194 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5195 SetLastError(ERROR_INVALID_PARAMETER);
5196 return 0;
5199 if(cchUnicodeChar == -1)
5200 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5201 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5202 SetLastError(ERROR_INVALID_NAME);
5203 return 0;
5206 for(label_start=0; label_start<cchUnicodeChar;) {
5207 ascii_only = TRUE;
5208 for(i=label_start; i<cchUnicodeChar; i++) {
5209 ch = lpUnicodeCharStr[i];
5211 if(i!=cchUnicodeChar-1 && !ch) {
5212 SetLastError(ERROR_INVALID_NAME);
5213 return 0;
5215 /* check if ch is one of label separators defined in RFC3490 */
5216 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5217 break;
5219 if(ch > 0x7f) {
5220 ascii_only = FALSE;
5221 continue;
5224 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5225 continue;
5226 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5227 || (ch>='0' && ch<='9') || ch=='-')
5228 continue;
5230 SetLastError(ERROR_INVALID_NAME);
5231 return 0;
5233 label_end = i;
5234 /* last label may be empty */
5235 if(label_start==label_end && ch) {
5236 SetLastError(ERROR_INVALID_NAME);
5237 return 0;
5240 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5241 lpUnicodeCharStr[label_end-1]=='-')) {
5242 SetLastError(ERROR_INVALID_NAME);
5243 return 0;
5246 if(ascii_only) {
5247 /* maximal label length is 63 characters */
5248 if(label_end-label_start > 63) {
5249 SetLastError(ERROR_INVALID_NAME);
5250 return 0;
5252 if(label_end < cchUnicodeChar)
5253 label_end++;
5255 if(!lpNameprepCharStr) {
5256 out += label_end-label_start;
5257 }else if(out+label_end-label_start <= cchNameprepChar) {
5258 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5259 (label_end-label_start)*sizeof(WCHAR));
5260 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5261 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5262 out += label_end-label_start;
5263 }else {
5264 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5265 return 0;
5268 label_start = label_end;
5269 continue;
5272 map_len = 0;
5273 for(i=label_start; i<label_end; i++) {
5274 ch = lpUnicodeCharStr[i];
5275 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5276 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5278 if(!ptr[0]) map_len++;
5279 else if(!ptr[1]) map_len++;
5280 else if(!ptr[2]) map_len += 2;
5281 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5283 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5284 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5285 if(!map_str) {
5286 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5287 return 0;
5289 }else {
5290 map_str = buf;
5292 map_len = 0;
5293 for(i=label_start; i<label_end; i++) {
5294 ch = lpUnicodeCharStr[i];
5295 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5296 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5298 if(!ptr[0]) {
5299 map_str[map_len++] = ch;
5300 }else if(!ptr[1]) {
5301 map_str[map_len++] = ptr[0];
5302 }else if(!ptr[2]) {
5303 map_str[map_len++] = ptr[0];
5304 map_str[map_len++] = ptr[1];
5305 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5306 map_str[map_len++] = ptr[0];
5307 map_str[map_len++] = ptr[1];
5308 map_str[map_len++] = ptr[2];
5312 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5313 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5314 if(map_str != buf)
5315 HeapFree(GetProcessHeap(), 0, map_str);
5316 if(!norm_len) {
5317 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5318 SetLastError(ERROR_INVALID_NAME);
5319 return 0;
5322 if(label_end < cchUnicodeChar) {
5323 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5324 label_end++;
5327 if(!lpNameprepCharStr) {
5328 out += norm_len;
5329 }else if(out+norm_len <= cchNameprepChar) {
5330 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5331 out += norm_len;
5332 }else {
5333 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5334 return 0;
5337 have_bidi_ral = prohibit_bidi_ral = FALSE;
5338 mask = PROHIBITED;
5339 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5340 mask |= UNASSIGNED;
5341 for(i=0; i<norm_len; i++) {
5342 ch = norm_str[i];
5343 flags = get_table_entry( nameprep_char_type, ch );
5345 if(flags & mask) {
5346 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5347 : ERROR_NO_UNICODE_TRANSLATION);
5348 return 0;
5351 if(flags & BIDI_RAL)
5352 have_bidi_ral = TRUE;
5353 if(flags & BIDI_L)
5354 prohibit_bidi_ral = TRUE;
5357 if(have_bidi_ral) {
5358 ch = norm_str[0];
5359 flags = get_table_entry( nameprep_char_type, ch );
5360 if((flags & BIDI_RAL) == 0)
5361 prohibit_bidi_ral = TRUE;
5363 ch = norm_str[norm_len-1];
5364 flags = get_table_entry( nameprep_char_type, ch );
5365 if((flags & BIDI_RAL) == 0)
5366 prohibit_bidi_ral = TRUE;
5369 if(have_bidi_ral && prohibit_bidi_ral) {
5370 SetLastError(ERROR_INVALID_NAME);
5371 return 0;
5374 label_start = label_end;
5377 return out;
5380 /******************************************************************************
5381 * IdnToUnicode (KERNEL32.@)
5383 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5384 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5386 extern const unsigned short nameprep_char_type[];
5388 INT i, label_start, label_end, out_label, out = 0;
5389 WCHAR ch;
5391 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5392 lpUnicodeCharStr, cchUnicodeChar);
5394 for(label_start=0; label_start<cchASCIIChar;) {
5395 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5397 out_label = out;
5398 for(i=label_start; i<cchASCIIChar; i++) {
5399 ch = lpASCIICharStr[i];
5401 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5402 SetLastError(ERROR_INVALID_NAME);
5403 return 0;
5406 if(!ch || ch=='.')
5407 break;
5408 if(ch == '-')
5409 delim = i;
5411 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5412 continue;
5413 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5414 || (ch>='0' && ch<='9') || ch=='-')
5415 continue;
5417 SetLastError(ERROR_INVALID_NAME);
5418 return 0;
5420 label_end = i;
5421 /* last label may be empty */
5422 if(label_start==label_end && ch) {
5423 SetLastError(ERROR_INVALID_NAME);
5424 return 0;
5427 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5428 lpASCIICharStr[label_end-1]=='-')) {
5429 SetLastError(ERROR_INVALID_NAME);
5430 return 0;
5432 if(label_end-label_start > 63) {
5433 SetLastError(ERROR_INVALID_NAME);
5434 return 0;
5437 if(label_end-label_start<4 ||
5438 tolowerW(lpASCIICharStr[label_start])!='x' ||
5439 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5440 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5441 if(label_end < cchASCIIChar)
5442 label_end++;
5444 if(!lpUnicodeCharStr) {
5445 out += label_end-label_start;
5446 }else if(out+label_end-label_start <= cchUnicodeChar) {
5447 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5448 (label_end-label_start)*sizeof(WCHAR));
5449 out += label_end-label_start;
5450 }else {
5451 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5452 return 0;
5455 label_start = label_end;
5456 continue;
5459 if(delim == label_start+3)
5460 delim++;
5461 if(!lpUnicodeCharStr) {
5462 out += delim-label_start-4;
5463 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5464 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5465 (delim-label_start-4)*sizeof(WCHAR));
5466 out += delim-label_start-4;
5467 }else {
5468 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5469 return 0;
5471 if(out != out_label)
5472 delim++;
5474 for(i=delim; i<label_end;) {
5475 old_pos = pos;
5476 w = 1;
5477 for(k=BASE; ; k+=BASE) {
5478 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5479 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5480 SetLastError(ERROR_INVALID_NAME);
5481 return 0;
5483 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5484 pos += digit*w;
5485 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5486 if(digit < t)
5487 break;
5488 w *= BASE-t;
5490 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5491 n += pos/(out-out_label+1);
5492 pos %= out-out_label+1;
5494 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5495 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5496 SetLastError(ERROR_INVALID_NAME);
5497 return 0;
5499 if(!lpUnicodeCharStr) {
5500 out++;
5501 }else if(out+1 <= cchASCIIChar) {
5502 memmove(lpUnicodeCharStr+out_label+pos+1,
5503 lpUnicodeCharStr+out_label+pos,
5504 (out-out_label-pos)*sizeof(WCHAR));
5505 lpUnicodeCharStr[out_label+pos] = n;
5506 out++;
5507 }else {
5508 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5509 return 0;
5511 pos++;
5514 if(out-out_label > 63) {
5515 SetLastError(ERROR_INVALID_NAME);
5516 return 0;
5519 if(label_end < cchASCIIChar) {
5520 if(!lpUnicodeCharStr) {
5521 out++;
5522 }else if(out+1 <= cchUnicodeChar) {
5523 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5524 }else {
5525 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5526 return 0;
5529 label_start = label_end+1;
5532 return out;
5536 /******************************************************************************
5537 * GetUserPreferredUILanguages (KERNEL32.@)
5539 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5541 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5542 return FALSE;
5545 /******************************************************************************
5546 * GetFileMUIPath (KERNEL32.@)
5549 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5550 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5552 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5553 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5555 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5557 return FALSE;
5560 /******************************************************************************
5561 * GetFileMUIInfo (KERNEL32.@)
5564 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5566 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5568 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5569 return FALSE;