Release 1.9.12.
[wine.git] / dlls / kernel32 / locale.c
blobf4ff6d396703a5eec2c5d816d2e60badd549de9e
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 /***********************************************************************
915 * setup_unix_locales
917 static UINT setup_unix_locales(void)
919 struct locale_name locale_name;
920 WCHAR buffer[128], ctype_buff[128];
921 char *locale;
922 UINT unix_cp = 0;
924 if ((locale = setlocale( LC_CTYPE, NULL )))
926 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
927 parse_locale_name( ctype_buff, &locale_name );
928 lcid_LC_CTYPE = locale_name.lcid;
929 unix_cp = locale_name.codepage;
931 if (!lcid_LC_CTYPE) /* this one needs a default value */
932 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
934 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
935 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
937 #define GET_UNIX_LOCALE(cat) do \
938 if ((locale = setlocale( cat, NULL ))) \
940 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
941 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
942 else { \
943 parse_locale_name( buffer, &locale_name ); \
944 lcid_##cat = locale_name.lcid; \
945 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
946 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
948 } while (0)
950 GET_UNIX_LOCALE( LC_COLLATE );
951 GET_UNIX_LOCALE( LC_MESSAGES );
952 GET_UNIX_LOCALE( LC_MONETARY );
953 GET_UNIX_LOCALE( LC_NUMERIC );
954 GET_UNIX_LOCALE( LC_TIME );
955 #ifdef LC_PAPER
956 GET_UNIX_LOCALE( LC_PAPER );
957 #endif
958 #ifdef LC_MEASUREMENT
959 GET_UNIX_LOCALE( LC_MEASUREMENT );
960 #endif
961 #ifdef LC_TELEPHONE
962 GET_UNIX_LOCALE( LC_TELEPHONE );
963 #endif
965 #undef GET_UNIX_LOCALE
967 return unix_cp;
971 /***********************************************************************
972 * GetUserDefaultLangID (KERNEL32.@)
974 * Get the default language Id for the current user.
976 * PARAMS
977 * None.
979 * RETURNS
980 * The current LANGID of the default language for the current user.
982 LANGID WINAPI GetUserDefaultLangID(void)
984 return LANGIDFROMLCID(GetUserDefaultLCID());
988 /***********************************************************************
989 * GetSystemDefaultLangID (KERNEL32.@)
991 * Get the default language Id for the system.
993 * PARAMS
994 * None.
996 * RETURNS
997 * The current LANGID of the default language for the system.
999 LANGID WINAPI GetSystemDefaultLangID(void)
1001 return LANGIDFROMLCID(GetSystemDefaultLCID());
1005 /***********************************************************************
1006 * GetUserDefaultLCID (KERNEL32.@)
1008 * Get the default locale Id for the current user.
1010 * PARAMS
1011 * None.
1013 * RETURNS
1014 * The current LCID of the default locale for the current user.
1016 LCID WINAPI GetUserDefaultLCID(void)
1018 LCID lcid;
1019 NtQueryDefaultLocale( TRUE, &lcid );
1020 return lcid;
1024 /***********************************************************************
1025 * GetSystemDefaultLCID (KERNEL32.@)
1027 * Get the default locale Id for the system.
1029 * PARAMS
1030 * None.
1032 * RETURNS
1033 * The current LCID of the default locale for the system.
1035 LCID WINAPI GetSystemDefaultLCID(void)
1037 LCID lcid;
1038 NtQueryDefaultLocale( FALSE, &lcid );
1039 return lcid;
1042 /***********************************************************************
1043 * GetSystemDefaultLocaleName (KERNEL32.@)
1045 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1047 LCID lcid = GetSystemDefaultLCID();
1048 return LCIDToLocaleName(lcid, localename, len, 0);
1051 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1053 LCTYPE type;
1054 int lsize;
1056 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1058 if (flags & MUI_LANGUAGE_ID)
1059 type = LOCALE_ILANGUAGE;
1060 else
1061 type = LOCALE_SNAME;
1063 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1064 if (!lsize)
1066 /* keep last error from callee */
1067 return FALSE;
1069 lsize++;
1070 if (!*size)
1072 *size = lsize;
1073 *count = 1;
1074 return TRUE;
1077 if (lsize > *size)
1079 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1080 return FALSE;
1083 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1085 /* keep last error from callee */
1086 return FALSE;
1089 buffer[lsize-1] = 0;
1090 *size = lsize;
1091 *count = 1;
1092 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1093 return TRUE;
1097 /***********************************************************************
1098 * GetSystemPreferredUILanguages (KERNEL32.@)
1100 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1102 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1104 SetLastError(ERROR_INVALID_PARAMETER);
1105 return FALSE;
1107 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1109 SetLastError(ERROR_INVALID_PARAMETER);
1110 return FALSE;
1112 if (*size && !buffer)
1114 SetLastError(ERROR_INVALID_PARAMETER);
1115 return FALSE;
1118 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1121 /***********************************************************************
1122 * SetThreadPreferredUILanguages (KERNEL32.@)
1124 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1126 FIXME( "%u, %p, %p\n", flags, buffer, count );
1127 return TRUE;
1130 /***********************************************************************
1131 * GetThreadPreferredUILanguages (KERNEL32.@)
1133 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1135 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1136 return get_dummy_preferred_ui_language( flags, count, buf, size );
1139 /***********************************************************************
1140 * GetUserDefaultUILanguage (KERNEL32.@)
1142 * Get the default user interface language Id for the current user.
1144 * PARAMS
1145 * None.
1147 * RETURNS
1148 * The current LANGID of the default UI language for the current user.
1150 LANGID WINAPI GetUserDefaultUILanguage(void)
1152 LANGID lang;
1153 NtQueryDefaultUILanguage( &lang );
1154 return lang;
1158 /***********************************************************************
1159 * GetSystemDefaultUILanguage (KERNEL32.@)
1161 * Get the default user interface language Id for the system.
1163 * PARAMS
1164 * None.
1166 * RETURNS
1167 * The current LANGID of the default UI language for the system. This is
1168 * typically the same language used during the installation process.
1170 LANGID WINAPI GetSystemDefaultUILanguage(void)
1172 LANGID lang;
1173 NtQueryInstallUILanguage( &lang );
1174 return lang;
1178 /***********************************************************************
1179 * LocaleNameToLCID (KERNEL32.@)
1181 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1183 struct locale_name locale_name;
1185 if (flags) FIXME( "unsupported flags %x\n", flags );
1187 if (name == LOCALE_NAME_USER_DEFAULT)
1188 return GetUserDefaultLCID();
1190 /* string parsing */
1191 parse_locale_name( name, &locale_name );
1193 TRACE( "found lcid %x for %s, matches %d\n",
1194 locale_name.lcid, debugstr_w(name), locale_name.matches );
1196 if (!locale_name.matches)
1198 SetLastError(ERROR_INVALID_PARAMETER);
1199 return 0;
1202 if (locale_name.matches == 1)
1203 WARN( "locale %s not recognized, defaulting to %s\n",
1204 debugstr_w(name), debugstr_w(locale_name.lang) );
1206 return locale_name.lcid;
1210 /***********************************************************************
1211 * LCIDToLocaleName (KERNEL32.@)
1213 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1215 if (flags) FIXME( "unsupported flags %x\n", flags );
1217 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1221 /******************************************************************************
1222 * get_locale_registry_value
1224 * Gets the registry value name and cache for a given lctype.
1226 static struct registry_value *get_locale_registry_value( DWORD lctype )
1228 int i;
1229 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1230 if (registry_values[i].lctype == lctype)
1231 return &registry_values[i];
1232 return NULL;
1236 /******************************************************************************
1237 * get_registry_locale_info
1239 * Retrieve user-modified locale info from the registry.
1240 * Return length, 0 on error, -1 if not found.
1242 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1244 DWORD size;
1245 INT ret;
1246 HANDLE hkey;
1247 NTSTATUS status;
1248 UNICODE_STRING nameW;
1249 KEY_VALUE_PARTIAL_INFORMATION *info;
1250 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1252 RtlEnterCriticalSection( &cache_section );
1254 if (!registry_value->cached_value)
1256 if (!(hkey = create_registry_key()))
1258 RtlLeaveCriticalSection( &cache_section );
1259 return -1;
1262 RtlInitUnicodeString( &nameW, registry_value->name );
1263 size = info_size + len * sizeof(WCHAR);
1265 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1267 NtClose( hkey );
1268 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1269 RtlLeaveCriticalSection( &cache_section );
1270 return 0;
1273 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1275 /* try again with a bigger buffer when we have to return the correct size */
1276 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1278 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1279 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1281 info = new_info;
1282 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1286 NtClose( hkey );
1288 if (!status)
1290 INT length = (size - info_size) / sizeof(WCHAR);
1291 LPWSTR cached_value;
1293 if (!length || ((WCHAR *)&info->Data)[length-1])
1294 length++;
1296 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1298 if (!cached_value)
1300 HeapFree( GetProcessHeap(), 0, info );
1301 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1302 RtlLeaveCriticalSection( &cache_section );
1303 return 0;
1306 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1307 cached_value[length-1] = 0;
1308 HeapFree( GetProcessHeap(), 0, info );
1309 registry_value->cached_value = cached_value;
1311 else
1313 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1315 ret = (size - info_size) / sizeof(WCHAR);
1317 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1319 ret = -1;
1321 else
1323 SetLastError( RtlNtStatusToDosError(status) );
1324 ret = 0;
1326 HeapFree( GetProcessHeap(), 0, info );
1327 RtlLeaveCriticalSection( &cache_section );
1328 return ret;
1332 ret = lstrlenW( registry_value->cached_value ) + 1;
1334 if (buffer)
1336 if (ret > len)
1338 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1339 ret = 0;
1341 else
1343 lstrcpyW( buffer, registry_value->cached_value );
1347 RtlLeaveCriticalSection( &cache_section );
1349 return ret;
1353 /******************************************************************************
1354 * GetLocaleInfoA (KERNEL32.@)
1356 * Get information about an aspect of a locale.
1358 * PARAMS
1359 * lcid [I] LCID of the locale
1360 * lctype [I] LCTYPE_ flags from "winnls.h"
1361 * buffer [O] Destination for the information
1362 * len [I] Length of buffer in characters
1364 * RETURNS
1365 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1366 * with the information.
1367 * Failure: 0. Use GetLastError() to determine the cause.
1369 * NOTES
1370 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1371 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1372 * which is a bit string.
1374 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1376 WCHAR *bufferW;
1377 INT lenW, ret;
1379 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1381 if (len < 0 || (len && !buffer))
1383 SetLastError( ERROR_INVALID_PARAMETER );
1384 return 0;
1386 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1387 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1389 SetLastError( ERROR_INVALID_FLAGS );
1390 return 0;
1393 if (!len) buffer = NULL;
1395 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1397 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1399 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1400 return 0;
1402 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1404 if ((lctype & LOCALE_RETURN_NUMBER) ||
1405 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1407 /* it's not an ASCII string, just bytes */
1408 ret *= sizeof(WCHAR);
1409 if (buffer)
1411 if (ret <= len) memcpy( buffer, bufferW, ret );
1412 else
1414 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1415 ret = 0;
1419 else
1421 UINT codepage = CP_ACP;
1422 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1423 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1426 HeapFree( GetProcessHeap(), 0, bufferW );
1427 return ret;
1430 static int get_value_base_by_lctype( LCTYPE lctype )
1432 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1435 /******************************************************************************
1436 * GetLocaleInfoW (KERNEL32.@)
1438 * See GetLocaleInfoA.
1440 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1442 LANGID lang_id;
1443 HRSRC hrsrc;
1444 HGLOBAL hmem;
1445 INT ret;
1446 UINT lcflags;
1447 const WCHAR *p;
1448 unsigned int i;
1450 if (len < 0 || (len && !buffer))
1452 SetLastError( ERROR_INVALID_PARAMETER );
1453 return 0;
1455 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1456 !is_genitive_name_supported( lctype ))
1458 SetLastError( ERROR_INVALID_FLAGS );
1459 return 0;
1462 if (!len) buffer = NULL;
1464 lcid = convert_default_lcid( lcid, lctype );
1466 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1467 lctype &= 0xffff;
1469 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1471 /* first check for overrides in the registry */
1473 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1474 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1476 struct registry_value *value = get_locale_registry_value(lctype);
1478 if (value)
1480 if (lcflags & LOCALE_RETURN_NUMBER)
1482 WCHAR tmp[16];
1483 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1484 if (ret > 0)
1486 WCHAR *end;
1487 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1488 if (*end) /* invalid number */
1490 SetLastError( ERROR_INVALID_FLAGS );
1491 return 0;
1493 ret = sizeof(UINT)/sizeof(WCHAR);
1494 if (!buffer) return ret;
1495 if (ret > len)
1497 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1498 return 0;
1500 memcpy( buffer, &number, sizeof(number) );
1503 else ret = get_registry_locale_info( value, buffer, len );
1505 if (ret != -1) return ret;
1509 /* now load it from kernel resources */
1511 lang_id = LANGIDFROMLCID( lcid );
1513 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1514 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1515 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1517 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1518 ULongToPtr((lctype >> 4) + 1), lang_id )))
1520 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1521 return 0;
1523 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1524 return 0;
1526 p = LockResource( hmem );
1527 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1529 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1530 else if (is_genitive_name_supported( lctype ) && *p)
1532 /* genitive form's stored after a null separator from a nominative */
1533 for (i = 1; i <= *p; i++) if (!p[i]) break;
1535 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1537 ret = *p - i + 1;
1538 p += i;
1540 else ret = i;
1542 else
1543 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1545 if (!buffer) return ret;
1547 if (ret > len)
1549 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1550 return 0;
1553 if (lcflags & LOCALE_RETURN_NUMBER)
1555 UINT number;
1556 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1557 if (!tmp) return 0;
1558 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1559 tmp[*p] = 0;
1560 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1561 if (!*end)
1562 memcpy( buffer, &number, sizeof(number) );
1563 else /* invalid number */
1565 SetLastError( ERROR_INVALID_FLAGS );
1566 ret = 0;
1568 HeapFree( GetProcessHeap(), 0, tmp );
1570 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1571 lcid, lctype, buffer, len, number );
1573 else
1575 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1576 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1578 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1579 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1581 return ret;
1584 /******************************************************************************
1585 * GetLocaleInfoEx (KERNEL32.@)
1587 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1589 LCID lcid = LocaleNameToLCID(locale, 0);
1591 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1593 if (!lcid) return 0;
1595 /* special handling for neutral locale names */
1596 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1598 if (len && len < 3)
1600 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1601 return 0;
1604 if (len) strcpyW(buffer, locale);
1605 return 3;
1608 return GetLocaleInfoW(lcid, info, buffer, len);
1611 /******************************************************************************
1612 * SetLocaleInfoA [KERNEL32.@]
1614 * Set information about an aspect of a locale.
1616 * PARAMS
1617 * lcid [I] LCID of the locale
1618 * lctype [I] LCTYPE_ flags from "winnls.h"
1619 * data [I] Information to set
1621 * RETURNS
1622 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1623 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1624 * Failure: FALSE. Use GetLastError() to determine the cause.
1626 * NOTES
1627 * - Values are only be set for the current user locale; the system locale
1628 * settings cannot be changed.
1629 * - Any settings changed by this call are lost when the locale is changed by
1630 * the control panel (in Wine, this happens every time you change LANG).
1631 * - The native implementation of this function does not check that lcid matches
1632 * the current user locale, and simply sets the new values. Wine warns you in
1633 * this case, but behaves the same.
1635 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1637 UINT codepage = CP_ACP;
1638 WCHAR *strW;
1639 DWORD len;
1640 BOOL ret;
1642 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1644 if (!data)
1646 SetLastError( ERROR_INVALID_PARAMETER );
1647 return FALSE;
1649 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1650 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1652 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1653 return FALSE;
1655 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1656 ret = SetLocaleInfoW( lcid, lctype, strW );
1657 HeapFree( GetProcessHeap(), 0, strW );
1658 return ret;
1662 /******************************************************************************
1663 * SetLocaleInfoW (KERNEL32.@)
1665 * See SetLocaleInfoA.
1667 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1669 struct registry_value *value;
1670 static const WCHAR intlW[] = {'i','n','t','l',0 };
1671 UNICODE_STRING valueW;
1672 NTSTATUS status;
1673 HANDLE hkey;
1675 lctype &= 0xffff;
1676 value = get_locale_registry_value( lctype );
1678 if (!data || !value)
1680 SetLastError( ERROR_INVALID_PARAMETER );
1681 return FALSE;
1684 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1686 SetLastError( ERROR_INVALID_FLAGS );
1687 return FALSE;
1690 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1692 /* FIXME: should check that data to set is sane */
1694 /* FIXME: profile functions should map to registry */
1695 WriteProfileStringW( intlW, value->name, data );
1697 if (!(hkey = create_registry_key())) return FALSE;
1698 RtlInitUnicodeString( &valueW, value->name );
1699 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1701 RtlEnterCriticalSection( &cache_section );
1702 HeapFree( GetProcessHeap(), 0, value->cached_value );
1703 value->cached_value = NULL;
1704 RtlLeaveCriticalSection( &cache_section );
1706 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1708 /* Set I-value from S value */
1709 WCHAR *lpD, *lpM, *lpY;
1710 WCHAR szBuff[2];
1712 lpD = strrchrW(data, 'd');
1713 lpM = strrchrW(data, 'M');
1714 lpY = strrchrW(data, 'y');
1716 if (lpD <= lpM)
1718 szBuff[0] = '1'; /* D-M-Y */
1720 else
1722 if (lpY <= lpM)
1723 szBuff[0] = '2'; /* Y-M-D */
1724 else
1725 szBuff[0] = '0'; /* M-D-Y */
1728 szBuff[1] = '\0';
1730 if (lctype == LOCALE_SSHORTDATE)
1731 lctype = LOCALE_IDATE;
1732 else
1733 lctype = LOCALE_ILDATE;
1735 value = get_locale_registry_value( lctype );
1737 WriteProfileStringW( intlW, value->name, szBuff );
1739 RtlInitUnicodeString( &valueW, value->name );
1740 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1742 RtlEnterCriticalSection( &cache_section );
1743 HeapFree( GetProcessHeap(), 0, value->cached_value );
1744 value->cached_value = NULL;
1745 RtlLeaveCriticalSection( &cache_section );
1748 NtClose( hkey );
1750 if (status) SetLastError( RtlNtStatusToDosError(status) );
1751 return !status;
1755 /******************************************************************************
1756 * GetACP (KERNEL32.@)
1758 * Get the current Ansi code page Id for the system.
1760 * PARAMS
1761 * None.
1763 * RETURNS
1764 * The current Ansi code page identifier for the system.
1766 UINT WINAPI GetACP(void)
1768 assert( ansi_cptable );
1769 return ansi_cptable->info.codepage;
1773 /******************************************************************************
1774 * SetCPGlobal (KERNEL32.@)
1776 * Set the current Ansi code page Id for the system.
1778 * PARAMS
1779 * acp [I] code page ID to be the new ACP.
1781 * RETURNS
1782 * The previous ACP.
1784 UINT WINAPI SetCPGlobal( UINT acp )
1786 UINT ret = GetACP();
1787 const union cptable *new_cptable = wine_cp_get_table( acp );
1789 if (new_cptable) ansi_cptable = new_cptable;
1790 return ret;
1794 /***********************************************************************
1795 * GetOEMCP (KERNEL32.@)
1797 * Get the current OEM code page Id for the system.
1799 * PARAMS
1800 * None.
1802 * RETURNS
1803 * The current OEM code page identifier for the system.
1805 UINT WINAPI GetOEMCP(void)
1807 assert( oem_cptable );
1808 return oem_cptable->info.codepage;
1812 /***********************************************************************
1813 * IsValidCodePage (KERNEL32.@)
1815 * Determine if a given code page identifier is valid.
1817 * PARAMS
1818 * codepage [I] Code page Id to verify.
1820 * RETURNS
1821 * TRUE, If codepage is valid and available on the system,
1822 * FALSE otherwise.
1824 BOOL WINAPI IsValidCodePage( UINT codepage )
1826 switch(codepage) {
1827 case CP_UTF7:
1828 case CP_UTF8:
1829 return TRUE;
1830 default:
1831 return wine_cp_get_table( codepage ) != NULL;
1836 /***********************************************************************
1837 * IsDBCSLeadByteEx (KERNEL32.@)
1839 * Determine if a character is a lead byte in a given code page.
1841 * PARAMS
1842 * codepage [I] Code page for the test.
1843 * testchar [I] Character to test
1845 * RETURNS
1846 * TRUE, if testchar is a lead byte in codepage,
1847 * FALSE otherwise.
1849 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1851 const union cptable *table = get_codepage_table( codepage );
1852 return table && wine_is_dbcs_leadbyte( table, testchar );
1856 /***********************************************************************
1857 * IsDBCSLeadByte (KERNEL32.@)
1858 * IsDBCSLeadByte (KERNEL.207)
1860 * Determine if a character is a lead byte.
1862 * PARAMS
1863 * testchar [I] Character to test
1865 * RETURNS
1866 * TRUE, if testchar is a lead byte in the ANSI code page,
1867 * FALSE otherwise.
1869 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1871 if (!ansi_cptable) return FALSE;
1872 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1876 /***********************************************************************
1877 * GetCPInfo (KERNEL32.@)
1879 * Get information about a code page.
1881 * PARAMS
1882 * codepage [I] Code page number
1883 * cpinfo [O] Destination for code page information
1885 * RETURNS
1886 * Success: TRUE. cpinfo is updated with the information about codepage.
1887 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1889 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1891 const union cptable *table;
1893 if (!cpinfo)
1895 SetLastError( ERROR_INVALID_PARAMETER );
1896 return FALSE;
1899 if (!(table = get_codepage_table( codepage )))
1901 switch(codepage)
1903 case CP_UTF7:
1904 case CP_UTF8:
1905 cpinfo->DefaultChar[0] = 0x3f;
1906 cpinfo->DefaultChar[1] = 0;
1907 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1908 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1909 return TRUE;
1912 SetLastError( ERROR_INVALID_PARAMETER );
1913 return FALSE;
1915 if (table->info.def_char & 0xff00)
1917 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1918 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1920 else
1922 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1923 cpinfo->DefaultChar[1] = 0;
1925 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1926 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1927 else
1928 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1930 return TRUE;
1933 /***********************************************************************
1934 * GetCPInfoExA (KERNEL32.@)
1936 * Get extended information about a code page.
1938 * PARAMS
1939 * codepage [I] Code page number
1940 * dwFlags [I] Reserved, must to 0.
1941 * cpinfo [O] Destination for code page information
1943 * RETURNS
1944 * Success: TRUE. cpinfo is updated with the information about codepage.
1945 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1947 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1949 CPINFOEXW cpinfoW;
1951 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1952 return FALSE;
1954 /* the layout is the same except for CodePageName */
1955 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1956 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1957 return TRUE;
1960 /***********************************************************************
1961 * GetCPInfoExW (KERNEL32.@)
1963 * Unicode version of GetCPInfoExA.
1965 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1967 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1968 return FALSE;
1970 switch(codepage)
1972 case CP_UTF7:
1974 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1976 cpinfo->CodePage = CP_UTF7;
1977 cpinfo->UnicodeDefaultChar = 0x3f;
1978 strcpyW(cpinfo->CodePageName, utf7);
1979 break;
1982 case CP_UTF8:
1984 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1986 cpinfo->CodePage = CP_UTF8;
1987 cpinfo->UnicodeDefaultChar = 0x3f;
1988 strcpyW(cpinfo->CodePageName, utf8);
1989 break;
1992 default:
1994 const union cptable *table = get_codepage_table( codepage );
1996 cpinfo->CodePage = table->info.codepage;
1997 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1998 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1999 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2000 break;
2003 return TRUE;
2006 /***********************************************************************
2007 * EnumSystemCodePagesA (KERNEL32.@)
2009 * Call a user defined function for every code page installed on the system.
2011 * PARAMS
2012 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2013 * flags [I] Reserved, set to 0.
2015 * RETURNS
2016 * TRUE, If all code pages have been enumerated, or
2017 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2019 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2021 const union cptable *table;
2022 char buffer[10];
2023 int index = 0;
2025 for (;;)
2027 if (!(table = wine_cp_enum_table( index++ ))) break;
2028 sprintf( buffer, "%d", table->info.codepage );
2029 if (!lpfnCodePageEnum( buffer )) break;
2031 return TRUE;
2035 /***********************************************************************
2036 * EnumSystemCodePagesW (KERNEL32.@)
2038 * See EnumSystemCodePagesA.
2040 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2042 const union cptable *table;
2043 WCHAR buffer[10], *p;
2044 int page, index = 0;
2046 for (;;)
2048 if (!(table = wine_cp_enum_table( index++ ))) break;
2049 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2050 *--p = 0;
2051 page = table->info.codepage;
2054 *--p = '0' + (page % 10);
2055 page /= 10;
2056 } while( page );
2057 if (!lpfnCodePageEnum( p )) break;
2059 return TRUE;
2063 /***********************************************************************
2064 * utf7_write_w
2066 * Helper for utf7_mbstowcs
2068 * RETURNS
2069 * TRUE on success, FALSE on error
2071 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2073 if (dstlen > 0)
2075 if (*index >= dstlen)
2076 return FALSE;
2078 dst[*index] = character;
2081 (*index)++;
2083 return TRUE;
2086 /***********************************************************************
2087 * utf7_mbstowcs
2089 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2091 * RETURNS
2092 * On success, the number of characters written
2093 * On dst buffer overflow, -1
2095 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2097 static const signed char base64_decoding_table[] =
2099 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2100 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2101 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2102 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2103 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2104 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2105 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2106 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2109 const char *source_end = src + srclen;
2110 int dest_index = 0;
2112 DWORD byte_pair = 0;
2113 short offset = 0;
2115 while (src < source_end)
2117 if (*src == '+')
2119 src++;
2120 if (src >= source_end)
2121 break;
2123 if (*src == '-')
2125 /* just a plus sign escaped as +- */
2126 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2127 return -1;
2128 src++;
2129 continue;
2134 signed char sextet = *src;
2135 if (sextet == '-')
2137 /* skip over the dash and end base64 decoding
2138 * the current, unfinished byte pair is discarded */
2139 src++;
2140 offset = 0;
2141 break;
2143 if (sextet < 0)
2145 /* the next character of src is < 0 and therefore not part of a base64 sequence
2146 * the current, unfinished byte pair is NOT discarded in this case
2147 * this is probably a bug in Windows */
2148 break;
2151 sextet = base64_decoding_table[sextet];
2152 if (sextet == -1)
2154 /* -1 means that the next character of src is not part of a base64 sequence
2155 * in other words, all sextets in this base64 sequence have been processed
2156 * the current, unfinished byte pair is discarded */
2157 offset = 0;
2158 break;
2161 byte_pair = (byte_pair << 6) | sextet;
2162 offset += 6;
2164 if (offset >= 16)
2166 /* this byte pair is done */
2167 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2168 return -1;
2169 offset -= 16;
2172 src++;
2174 while (src < source_end);
2176 else
2178 /* we have to convert to unsigned char in case *src < 0 */
2179 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2180 return -1;
2181 src++;
2185 return dest_index;
2188 /***********************************************************************
2189 * MultiByteToWideChar (KERNEL32.@)
2191 * Convert a multibyte character string into a Unicode string.
2193 * PARAMS
2194 * page [I] Codepage character set to convert from
2195 * flags [I] Character mapping flags
2196 * src [I] Source string buffer
2197 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2198 * dst [O] Destination buffer
2199 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2201 * RETURNS
2202 * Success: If dstlen > 0, the number of characters written to dst.
2203 * If dstlen == 0, the number of characters needed to perform the
2204 * conversion. In both cases the count includes the terminating NUL.
2205 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2206 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2207 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2208 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2209 * possible for src.
2211 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2212 LPWSTR dst, INT dstlen )
2214 const union cptable *table;
2215 int ret;
2217 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2219 SetLastError( ERROR_INVALID_PARAMETER );
2220 return 0;
2223 if (srclen < 0) srclen = strlen(src) + 1;
2225 switch(page)
2227 case CP_SYMBOL:
2228 if (flags)
2230 SetLastError( ERROR_INVALID_FLAGS );
2231 return 0;
2233 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2234 break;
2235 case CP_UTF7:
2236 if (flags)
2238 SetLastError( ERROR_INVALID_FLAGS );
2239 return 0;
2241 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2242 break;
2243 case CP_UNIXCP:
2244 if (unix_cptable)
2246 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2247 break;
2249 #ifdef __APPLE__
2250 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2251 #endif
2252 /* fall through */
2253 case CP_UTF8:
2254 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2255 break;
2256 default:
2257 if (!(table = get_codepage_table( page )))
2259 SetLastError( ERROR_INVALID_PARAMETER );
2260 return 0;
2262 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2263 break;
2266 if (ret < 0)
2268 switch(ret)
2270 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2271 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2273 ret = 0;
2275 TRACE("cp %d %s -> %s, ret = %d\n",
2276 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2277 return ret;
2281 /***********************************************************************
2282 * utf7_can_directly_encode
2284 * Helper for utf7_wcstombs
2286 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2288 static const BOOL directly_encodable_table[] =
2290 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2291 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2292 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2293 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2294 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2295 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2296 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2297 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2300 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2303 /***********************************************************************
2304 * utf7_write_c
2306 * Helper for utf7_wcstombs
2308 * RETURNS
2309 * TRUE on success, FALSE on error
2311 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2313 if (dstlen > 0)
2315 if (*index >= dstlen)
2316 return FALSE;
2318 dst[*index] = character;
2321 (*index)++;
2323 return TRUE;
2326 /***********************************************************************
2327 * utf7_wcstombs
2329 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2331 * RETURNS
2332 * On success, the number of characters written
2333 * On dst buffer overflow, -1
2335 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2337 static const char base64_encoding_table[] =
2338 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2340 const WCHAR *source_end = src + srclen;
2341 int dest_index = 0;
2343 while (src < source_end)
2345 if (*src == '+')
2347 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2348 return -1;
2349 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2350 return -1;
2351 src++;
2353 else if (utf7_can_directly_encode(*src))
2355 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2356 return -1;
2357 src++;
2359 else
2361 unsigned int offset = 0;
2362 DWORD byte_pair = 0;
2364 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2365 return -1;
2367 while (src < source_end && !utf7_can_directly_encode(*src))
2369 byte_pair = (byte_pair << 16) | *src;
2370 offset += 16;
2371 while (offset >= 6)
2373 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2374 return -1;
2375 offset -= 6;
2377 src++;
2380 if (offset)
2382 /* Windows won't create a padded base64 character if there's no room for the - sign
2383 * as well ; this is probably a bug in Windows */
2384 if (dstlen > 0 && dest_index + 1 >= dstlen)
2385 return -1;
2387 byte_pair <<= (6 - offset);
2388 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2389 return -1;
2392 /* Windows always explicitly terminates the base64 sequence
2393 even though RFC 2152 (page 3, rule 2) does not require this */
2394 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2395 return -1;
2399 return dest_index;
2402 /***********************************************************************
2403 * WideCharToMultiByte (KERNEL32.@)
2405 * Convert a Unicode character string into a multibyte string.
2407 * PARAMS
2408 * page [I] Code page character set to convert to
2409 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2410 * src [I] Source string buffer
2411 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2412 * dst [O] Destination buffer
2413 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2414 * defchar [I] Default character to use for conversion if no exact
2415 * conversion can be made
2416 * used [O] Set if default character was used in the conversion
2418 * RETURNS
2419 * Success: If dstlen > 0, the number of characters written to dst.
2420 * If dstlen == 0, number of characters needed to perform the
2421 * conversion. In both cases the count includes the terminating NUL.
2422 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2423 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2424 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2425 * parameter was given.
2427 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2428 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2430 const union cptable *table;
2431 int ret, used_tmp;
2433 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2435 SetLastError( ERROR_INVALID_PARAMETER );
2436 return 0;
2439 if (srclen < 0) srclen = strlenW(src) + 1;
2441 switch(page)
2443 case CP_SYMBOL:
2444 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2445 if (flags)
2447 SetLastError( ERROR_INVALID_FLAGS );
2448 return 0;
2450 if (defchar || used)
2452 SetLastError( ERROR_INVALID_PARAMETER );
2453 return 0;
2455 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2456 break;
2457 case CP_UTF7:
2458 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2459 if (defchar || used)
2461 SetLastError( ERROR_INVALID_PARAMETER );
2462 return 0;
2464 if (flags)
2466 SetLastError( ERROR_INVALID_FLAGS );
2467 return 0;
2469 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2470 break;
2471 case CP_UNIXCP:
2472 if (unix_cptable)
2474 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2475 defchar, used ? &used_tmp : NULL );
2476 break;
2478 /* fall through */
2479 case CP_UTF8:
2480 if (defchar || used)
2482 SetLastError( ERROR_INVALID_PARAMETER );
2483 return 0;
2485 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2486 break;
2487 default:
2488 if (!(table = get_codepage_table( page )))
2490 SetLastError( ERROR_INVALID_PARAMETER );
2491 return 0;
2493 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2494 defchar, used ? &used_tmp : NULL );
2495 if (used) *used = used_tmp;
2496 break;
2499 if (ret < 0)
2501 switch(ret)
2503 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2504 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2506 ret = 0;
2508 TRACE("cp %d %s -> %s, ret = %d\n",
2509 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2510 return ret;
2514 /***********************************************************************
2515 * GetThreadLocale (KERNEL32.@)
2517 * Get the current threads locale.
2519 * PARAMS
2520 * None.
2522 * RETURNS
2523 * The LCID currently associated with the calling thread.
2525 LCID WINAPI GetThreadLocale(void)
2527 LCID ret = NtCurrentTeb()->CurrentLocale;
2528 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2529 return ret;
2532 /**********************************************************************
2533 * SetThreadLocale (KERNEL32.@)
2535 * Set the current threads locale.
2537 * PARAMS
2538 * lcid [I] LCID of the locale to set
2540 * RETURNS
2541 * Success: TRUE. The threads locale is set to lcid.
2542 * Failure: FALSE. Use GetLastError() to determine the cause.
2544 BOOL WINAPI SetThreadLocale( LCID lcid )
2546 TRACE("(0x%04X)\n", lcid);
2548 lcid = ConvertDefaultLocale(lcid);
2550 if (lcid != GetThreadLocale())
2552 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2554 SetLastError(ERROR_INVALID_PARAMETER);
2555 return FALSE;
2558 NtCurrentTeb()->CurrentLocale = lcid;
2560 return TRUE;
2563 /**********************************************************************
2564 * SetThreadUILanguage (KERNEL32.@)
2566 * Set the current threads UI language.
2568 * PARAMS
2569 * langid [I] LANGID of the language to set, or 0 to use
2570 * the available language which is best supported
2571 * for console applications
2573 * RETURNS
2574 * Success: The return value is the same as the input value.
2575 * Failure: The return value differs from the input value.
2576 * Use GetLastError() to determine the cause.
2578 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2580 TRACE("(0x%04x) stub - returning success\n", langid);
2581 return langid;
2584 /******************************************************************************
2585 * ConvertDefaultLocale (KERNEL32.@)
2587 * Convert a default locale identifier into a real identifier.
2589 * PARAMS
2590 * lcid [I] LCID identifier of the locale to convert
2592 * RETURNS
2593 * lcid unchanged, if not a default locale or its sublanguage is
2594 * not SUBLANG_NEUTRAL.
2595 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2596 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2597 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2599 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2601 LANGID langid;
2603 switch (lcid)
2605 case LOCALE_INVARIANT:
2606 /* keep as-is */
2607 break;
2608 case LOCALE_SYSTEM_DEFAULT:
2609 lcid = GetSystemDefaultLCID();
2610 break;
2611 case LOCALE_USER_DEFAULT:
2612 case LOCALE_NEUTRAL:
2613 lcid = GetUserDefaultLCID();
2614 break;
2615 default:
2616 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2617 langid = LANGIDFROMLCID(lcid);
2618 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2620 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2621 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2624 return lcid;
2628 /******************************************************************************
2629 * IsValidLocale (KERNEL32.@)
2631 * Determine if a locale is valid.
2633 * PARAMS
2634 * lcid [I] LCID of the locale to check
2635 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2637 * RETURNS
2638 * TRUE, if lcid is valid,
2639 * FALSE, otherwise.
2641 * NOTES
2642 * Wine does not currently make the distinction between supported and installed. All
2643 * languages supported are installed by default.
2645 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2647 /* check if language is registered in the kernel32 resources */
2648 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2649 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2652 /******************************************************************************
2653 * IsValidLocaleName (KERNEL32.@)
2655 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2657 struct locale_name locale_name;
2659 if (!locale)
2660 return FALSE;
2662 /* string parsing */
2663 parse_locale_name( locale, &locale_name );
2665 TRACE( "found lcid %x for %s, matches %d\n",
2666 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2668 return locale_name.matches > 0;
2671 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2672 LPCSTR name, WORD LangID, LONG_PTR lParam )
2674 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2675 char buf[20];
2677 sprintf(buf, "%08x", (UINT)LangID);
2678 return lpfnLocaleEnum( buf );
2681 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2682 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2684 static const WCHAR formatW[] = {'%','0','8','x',0};
2685 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2686 WCHAR buf[20];
2687 sprintfW( buf, formatW, (UINT)LangID );
2688 return lpfnLocaleEnum( buf );
2691 /******************************************************************************
2692 * EnumSystemLocalesA (KERNEL32.@)
2694 * Call a users function for each locale available on the system.
2696 * PARAMS
2697 * lpfnLocaleEnum [I] Callback function to call for each locale
2698 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2700 * RETURNS
2701 * Success: TRUE.
2702 * Failure: FALSE. Use GetLastError() to determine the cause.
2704 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2706 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2707 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2708 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2709 (LONG_PTR)lpfnLocaleEnum);
2710 return TRUE;
2714 /******************************************************************************
2715 * EnumSystemLocalesW (KERNEL32.@)
2717 * See EnumSystemLocalesA.
2719 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2721 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2722 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2723 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2724 (LONG_PTR)lpfnLocaleEnum);
2725 return TRUE;
2729 struct enum_locale_ex_data
2731 LOCALE_ENUMPROCEX proc;
2732 DWORD flags;
2733 LPARAM lparam;
2736 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2737 LPCWSTR name, WORD lang, LONG_PTR lparam )
2739 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2740 WCHAR buffer[256];
2741 DWORD neutral;
2742 unsigned int flags;
2744 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2745 buffer, sizeof(buffer) / sizeof(WCHAR) );
2746 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2747 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2748 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2749 neutral = 0;
2750 flags = LOCALE_WINDOWS;
2751 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2752 if (data->flags && !(data->flags & flags)) return TRUE;
2753 return data->proc( buffer, flags, data->lparam );
2756 /******************************************************************************
2757 * EnumSystemLocalesEx (KERNEL32.@)
2759 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2761 struct enum_locale_ex_data data;
2763 if (reserved)
2765 SetLastError( ERROR_INVALID_PARAMETER );
2766 return FALSE;
2768 data.proc = proc;
2769 data.flags = flags;
2770 data.lparam = lparam;
2771 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2772 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2773 enum_locale_ex_proc, (LONG_PTR)&data );
2774 return TRUE;
2778 /***********************************************************************
2779 * VerLanguageNameA (KERNEL32.@)
2781 * Get the name of a language.
2783 * PARAMS
2784 * wLang [I] LANGID of the language
2785 * szLang [O] Destination for the language name
2787 * RETURNS
2788 * Success: The size of the language name. If szLang is non-NULL, it is filled
2789 * with the name.
2790 * Failure: 0. Use GetLastError() to determine the cause.
2793 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2795 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2799 /***********************************************************************
2800 * VerLanguageNameW (KERNEL32.@)
2802 * See VerLanguageNameA.
2804 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2806 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2810 /******************************************************************************
2811 * GetStringTypeW (KERNEL32.@)
2813 * See GetStringTypeA.
2815 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2817 static const unsigned char type2_map[16] =
2819 C2_NOTAPPLICABLE, /* unassigned */
2820 C2_LEFTTORIGHT, /* L */
2821 C2_RIGHTTOLEFT, /* R */
2822 C2_EUROPENUMBER, /* EN */
2823 C2_EUROPESEPARATOR, /* ES */
2824 C2_EUROPETERMINATOR, /* ET */
2825 C2_ARABICNUMBER, /* AN */
2826 C2_COMMONSEPARATOR, /* CS */
2827 C2_BLOCKSEPARATOR, /* B */
2828 C2_SEGMENTSEPARATOR, /* S */
2829 C2_WHITESPACE, /* WS */
2830 C2_OTHERNEUTRAL, /* ON */
2831 C2_RIGHTTOLEFT, /* AL */
2832 C2_NOTAPPLICABLE, /* NSM */
2833 C2_NOTAPPLICABLE, /* BN */
2834 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2837 if (!src)
2839 SetLastError( ERROR_INVALID_PARAMETER );
2840 return FALSE;
2843 if (count == -1) count = strlenW(src) + 1;
2844 switch(type)
2846 case CT_CTYPE1:
2847 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2848 break;
2849 case CT_CTYPE2:
2850 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2851 break;
2852 case CT_CTYPE3:
2854 WARN("CT_CTYPE3: semi-stub.\n");
2855 while (count--)
2857 int c = *src;
2858 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2860 type1 = get_char_typeW( *src++ ) & 0xfff;
2861 /* try to construct type3 from type1 */
2862 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2863 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2864 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2865 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2866 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2867 if (c == 0x0640) type3 |= C3_KASHIDA;
2868 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2870 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
2871 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
2873 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2874 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2875 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2876 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2877 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2878 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2879 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2880 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2882 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2883 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2884 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2885 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2886 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2887 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2888 *chartype++ = type3;
2890 break;
2892 default:
2893 SetLastError( ERROR_INVALID_PARAMETER );
2894 return FALSE;
2896 return TRUE;
2900 /******************************************************************************
2901 * GetStringTypeExW (KERNEL32.@)
2903 * See GetStringTypeExA.
2905 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2907 /* locale is ignored for Unicode */
2908 return GetStringTypeW( type, src, count, chartype );
2912 /******************************************************************************
2913 * GetStringTypeA (KERNEL32.@)
2915 * Get characteristics of the characters making up a string.
2917 * PARAMS
2918 * locale [I] Locale Id for the string
2919 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2920 * src [I] String to analyse
2921 * count [I] Length of src in chars, or -1 if src is NUL terminated
2922 * chartype [O] Destination for the calculated characteristics
2924 * RETURNS
2925 * Success: TRUE. chartype is filled with the requested characteristics of each char
2926 * in src.
2927 * Failure: FALSE. Use GetLastError() to determine the cause.
2929 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2931 UINT cp;
2932 INT countW;
2933 LPWSTR srcW;
2934 BOOL ret = FALSE;
2936 if(count == -1) count = strlen(src) + 1;
2938 if (!(cp = get_lcid_codepage( locale )))
2940 FIXME("For locale %04x using current ANSI code page\n", locale);
2941 cp = GetACP();
2944 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2945 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2947 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2949 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2950 * string, with multibyte characters there maybe be more bytes in count
2951 * than character space in the buffer!
2953 ret = GetStringTypeW(type, srcW, countW, chartype);
2954 HeapFree(GetProcessHeap(), 0, srcW);
2956 return ret;
2959 /******************************************************************************
2960 * GetStringTypeExA (KERNEL32.@)
2962 * Get characteristics of the characters making up a string.
2964 * PARAMS
2965 * locale [I] Locale Id for the string
2966 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2967 * src [I] String to analyse
2968 * count [I] Length of src in chars, or -1 if src is NUL terminated
2969 * chartype [O] Destination for the calculated characteristics
2971 * RETURNS
2972 * Success: TRUE. chartype is filled with the requested characteristics of each char
2973 * in src.
2974 * Failure: FALSE. Use GetLastError() to determine the cause.
2976 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2978 return GetStringTypeA(locale, type, src, count, chartype);
2981 /*************************************************************************
2982 * LCMapStringEx (KERNEL32.@)
2984 * Map characters in a locale sensitive string.
2986 * PARAMS
2987 * name [I] Locale name for the conversion.
2988 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2989 * src [I] String to map
2990 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2991 * dst [O] Destination for mapped string
2992 * dstlen [I] Length of dst in characters
2993 * version [I] reserved, must be NULL
2994 * reserved [I] reserved, must be NULL
2995 * lparam [I] reserved, must be 0
2997 * RETURNS
2998 * Success: The length of the mapped string in dst, including the NUL terminator.
2999 * Failure: 0. Use GetLastError() to determine the cause.
3001 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3002 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3004 LPWSTR dst_ptr;
3006 if (version) FIXME("unsupported version structure %p\n", version);
3007 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3008 if (lparam)
3010 static int once;
3011 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3014 if (!src || !srclen || dstlen < 0)
3016 SetLastError(ERROR_INVALID_PARAMETER);
3017 return 0;
3020 /* mutually exclusive flags */
3021 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3022 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3023 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3024 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
3026 SetLastError(ERROR_INVALID_FLAGS);
3027 return 0;
3030 if (!dstlen) dst = NULL;
3032 if (flags & LCMAP_SORTKEY)
3034 INT ret;
3035 if (src == dst)
3037 SetLastError(ERROR_INVALID_FLAGS);
3038 return 0;
3041 if (srclen < 0) srclen = strlenW(src);
3043 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3044 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3046 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3047 if (ret == 0)
3048 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3049 else
3050 ret++;
3051 return ret;
3054 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3055 if (flags & SORT_STRINGSORT)
3057 SetLastError(ERROR_INVALID_FLAGS);
3058 return 0;
3061 if (srclen < 0) srclen = strlenW(src) + 1;
3063 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3064 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3066 if (!dst) /* return required string length */
3068 INT len;
3070 for (len = 0; srclen; src++, srclen--)
3072 WCHAR wch = *src;
3073 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3074 * and skips white space and punctuation characters for
3075 * NORM_IGNORESYMBOLS.
3077 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3078 continue;
3079 len++;
3081 return len;
3084 if (flags & LCMAP_UPPERCASE)
3086 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3088 WCHAR wch = *src;
3089 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3090 continue;
3091 *dst_ptr++ = toupperW(wch);
3092 dstlen--;
3095 else if (flags & LCMAP_LOWERCASE)
3097 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3099 WCHAR wch = *src;
3100 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3101 continue;
3102 *dst_ptr++ = tolowerW(wch);
3103 dstlen--;
3106 else
3108 if (src == dst)
3110 SetLastError(ERROR_INVALID_FLAGS);
3111 return 0;
3113 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3115 WCHAR wch = *src;
3116 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3117 continue;
3118 *dst_ptr++ = wch;
3119 dstlen--;
3123 if (srclen)
3125 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3126 return 0;
3129 return dst_ptr - dst;
3132 /*************************************************************************
3133 * LCMapStringW (KERNEL32.@)
3135 * See LCMapStringA.
3137 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3138 LPWSTR dst, INT dstlen)
3140 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3141 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3143 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3146 /*************************************************************************
3147 * LCMapStringA (KERNEL32.@)
3149 * Map characters in a locale sensitive string.
3151 * PARAMS
3152 * lcid [I] LCID for the conversion.
3153 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3154 * src [I] String to map
3155 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3156 * dst [O] Destination for mapped string
3157 * dstlen [I] Length of dst in characters
3159 * RETURNS
3160 * Success: The length of the mapped string in dst, including the NUL terminator.
3161 * Failure: 0. Use GetLastError() to determine the cause.
3163 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3164 LPSTR dst, INT dstlen)
3166 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3167 LPWSTR srcW, dstW;
3168 INT ret = 0, srclenW, dstlenW;
3169 UINT locale_cp = CP_ACP;
3171 if (!src || !srclen || dstlen < 0)
3173 SetLastError(ERROR_INVALID_PARAMETER);
3174 return 0;
3177 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3179 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3180 if (srclenW)
3181 srcW = bufW;
3182 else
3184 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3185 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3186 if (!srcW)
3188 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3189 return 0;
3191 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3194 if (flags & LCMAP_SORTKEY)
3196 if (src == dst)
3198 SetLastError(ERROR_INVALID_FLAGS);
3199 goto map_string_exit;
3201 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3202 if (ret == 0)
3203 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3204 else
3205 ret++;
3206 goto map_string_exit;
3209 if (flags & SORT_STRINGSORT)
3211 SetLastError(ERROR_INVALID_FLAGS);
3212 goto map_string_exit;
3215 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3216 if (!dstlenW)
3217 goto map_string_exit;
3219 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3220 if (!dstW)
3222 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3223 goto map_string_exit;
3226 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3227 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3228 HeapFree(GetProcessHeap(), 0, dstW);
3230 map_string_exit:
3231 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3232 return ret;
3235 /*************************************************************************
3236 * FoldStringA (KERNEL32.@)
3238 * Map characters in a string.
3240 * PARAMS
3241 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3242 * src [I] String to map
3243 * srclen [I] Length of src, or -1 if src is NUL terminated
3244 * dst [O] Destination for mapped string
3245 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3247 * RETURNS
3248 * Success: The length of the string written to dst, including the terminating NUL. If
3249 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3250 * and dst may be NULL.
3251 * Failure: 0. Use GetLastError() to determine the cause.
3253 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3254 LPSTR dst, INT dstlen)
3256 INT ret = 0, srclenW = 0;
3257 WCHAR *srcW = NULL, *dstW = NULL;
3259 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3261 SetLastError(ERROR_INVALID_PARAMETER);
3262 return 0;
3265 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3266 src, srclen, NULL, 0);
3267 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3269 if (!srcW)
3271 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3272 goto FoldStringA_exit;
3275 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3276 src, srclen, srcW, srclenW);
3278 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3280 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3281 if (ret && dstlen)
3283 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3285 if (!dstW)
3287 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3288 goto FoldStringA_exit;
3291 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3292 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3294 ret = 0;
3295 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3299 HeapFree(GetProcessHeap(), 0, dstW);
3301 FoldStringA_exit:
3302 HeapFree(GetProcessHeap(), 0, srcW);
3303 return ret;
3306 /*************************************************************************
3307 * FoldStringW (KERNEL32.@)
3309 * See FoldStringA.
3311 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3312 LPWSTR dst, INT dstlen)
3314 int ret;
3316 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3318 case 0:
3319 if (dwFlags)
3320 break;
3321 /* Fall through for dwFlags == 0 */
3322 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3323 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3324 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3325 SetLastError(ERROR_INVALID_FLAGS);
3326 return 0;
3329 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3331 SetLastError(ERROR_INVALID_PARAMETER);
3332 return 0;
3335 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3336 if (!ret)
3337 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3338 return ret;
3341 /******************************************************************************
3342 * CompareStringW (KERNEL32.@)
3344 * See CompareStringA.
3346 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3347 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3349 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3352 /******************************************************************************
3353 * CompareStringEx (KERNEL32.@)
3355 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3356 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3358 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3359 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3360 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3361 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3362 INT ret;
3363 static int once;
3365 if (version) FIXME("unexpected version parameter\n");
3366 if (reserved) FIXME("unexpected reserved value\n");
3367 if (lParam) FIXME("unexpected lParam\n");
3369 if (!str1 || !str2)
3371 SetLastError(ERROR_INVALID_PARAMETER);
3372 return 0;
3375 if (flags & ~(supported_flags|semistub_flags))
3377 SetLastError(ERROR_INVALID_FLAGS);
3378 return 0;
3381 if (flags & semistub_flags)
3383 if (!once++)
3384 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3387 if (len1 < 0) len1 = strlenW(str1);
3388 if (len2 < 0) len2 = strlenW(str2);
3390 ret = wine_compare_string(flags, str1, len1, str2, len2);
3392 if (ret) /* need to translate result */
3393 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3394 return CSTR_EQUAL;
3397 /******************************************************************************
3398 * CompareStringA (KERNEL32.@)
3400 * Compare two locale sensitive strings.
3402 * PARAMS
3403 * lcid [I] LCID for the comparison
3404 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3405 * str1 [I] First string to compare
3406 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3407 * str2 [I] Second string to compare
3408 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3410 * RETURNS
3411 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3412 * str1 is less than, equal to or greater than str2 respectively.
3413 * Failure: FALSE. Use GetLastError() to determine the cause.
3415 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3416 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3418 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3419 WCHAR *buf2W = buf1W + 130;
3420 LPWSTR str1W, str2W;
3421 INT len1W = 0, len2W = 0, ret;
3422 UINT locale_cp = CP_ACP;
3424 if (!str1 || !str2)
3426 SetLastError(ERROR_INVALID_PARAMETER);
3427 return 0;
3429 if (len1 < 0) len1 = strlen(str1);
3430 if (len2 < 0) len2 = strlen(str2);
3432 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3434 if (len1)
3436 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3437 if (len1W)
3438 str1W = buf1W;
3439 else
3441 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3442 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3443 if (!str1W)
3445 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3446 return 0;
3448 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3451 else
3453 len1W = 0;
3454 str1W = buf1W;
3457 if (len2)
3459 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3460 if (len2W)
3461 str2W = buf2W;
3462 else
3464 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3465 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3466 if (!str2W)
3468 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3469 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3470 return 0;
3472 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3475 else
3477 len2W = 0;
3478 str2W = buf2W;
3481 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3483 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3484 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3485 return ret;
3488 /******************************************************************************
3489 * CompareStringOrdinal (KERNEL32.@)
3491 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3493 int ret, len;
3495 if (!str1 || !str2)
3497 SetLastError(ERROR_INVALID_PARAMETER);
3498 return 0;
3500 if (len1 < 0) len1 = strlenW(str1);
3501 if (len2 < 0) len2 = strlenW(str2);
3503 len = min(len1, len2);
3504 if (ignore_case)
3506 ret = memicmpW(str1, str2, len);
3508 else
3510 ret = 0;
3511 for (; len > 0; len--)
3512 if ((ret = (*str1++ - *str2++))) break;
3514 if (!ret) ret = len1 - len2;
3516 if (ret < 0) return CSTR_LESS_THAN;
3517 if (ret > 0) return CSTR_GREATER_THAN;
3518 return CSTR_EQUAL;
3521 /*************************************************************************
3522 * lstrcmp (KERNEL32.@)
3523 * lstrcmpA (KERNEL32.@)
3525 * Compare two strings using the current thread locale.
3527 * PARAMS
3528 * str1 [I] First string to compare
3529 * str2 [I] Second string to compare
3531 * RETURNS
3532 * Success: A number less than, equal to or greater than 0 depending on whether
3533 * str1 is less than, equal to or greater than str2 respectively.
3534 * Failure: FALSE. Use GetLastError() to determine the cause.
3536 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3538 int ret;
3540 if ((str1 == NULL) && (str2 == NULL)) return 0;
3541 if (str1 == NULL) return -1;
3542 if (str2 == NULL) return 1;
3544 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3545 if (ret) ret -= 2;
3547 return ret;
3550 /*************************************************************************
3551 * lstrcmpi (KERNEL32.@)
3552 * lstrcmpiA (KERNEL32.@)
3554 * Compare two strings using the current thread locale, ignoring case.
3556 * PARAMS
3557 * str1 [I] First string to compare
3558 * str2 [I] Second string to compare
3560 * RETURNS
3561 * Success: A number less than, equal to or greater than 0 depending on whether
3562 * str2 is less than, equal to or greater than str1 respectively.
3563 * Failure: FALSE. Use GetLastError() to determine the cause.
3565 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3567 int ret;
3569 if ((str1 == NULL) && (str2 == NULL)) return 0;
3570 if (str1 == NULL) return -1;
3571 if (str2 == NULL) return 1;
3573 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3574 if (ret) ret -= 2;
3576 return ret;
3579 /*************************************************************************
3580 * lstrcmpW (KERNEL32.@)
3582 * See lstrcmpA.
3584 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3586 int ret;
3588 if ((str1 == NULL) && (str2 == NULL)) return 0;
3589 if (str1 == NULL) return -1;
3590 if (str2 == NULL) return 1;
3592 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3593 if (ret) ret -= 2;
3595 return ret;
3598 /*************************************************************************
3599 * lstrcmpiW (KERNEL32.@)
3601 * See lstrcmpiA.
3603 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3605 int ret;
3607 if ((str1 == NULL) && (str2 == NULL)) return 0;
3608 if (str1 == NULL) return -1;
3609 if (str2 == NULL) return 1;
3611 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3612 if (ret) ret -= 2;
3614 return ret;
3617 /******************************************************************************
3618 * LOCALE_Init
3620 void LOCALE_Init(void)
3622 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3623 const union cptable *unix_cp );
3625 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3627 #ifdef __APPLE__
3628 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3629 char user_locale[50];
3631 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3632 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3633 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3634 CFStringRef user_locale_string_ref;
3636 if (user_locale_country_ref)
3638 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3639 user_locale_lang_ref, user_locale_country_ref);
3641 else
3643 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3646 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3647 strcat(user_locale, ".UTF-8");
3649 setenv( "LANG", user_locale, 0 );
3650 TRACE( "setting locale to '%s'\n", user_locale );
3651 #endif /* __APPLE__ */
3653 setlocale( LC_ALL, "" );
3655 unix_cp = setup_unix_locales();
3656 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3658 #ifdef __APPLE__
3659 if (!unix_cp)
3660 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3662 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3663 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3665 /* Retrieve the preferred language as chosen in System Preferences. */
3666 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3667 leave things be. */
3668 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3669 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3670 CFStringRef user_language_string_ref;
3671 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3672 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3673 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3674 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3676 struct locale_name locale_name;
3677 WCHAR buffer[128];
3678 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3679 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3680 parse_locale_name( buffer, &locale_name );
3681 lcid_LC_MESSAGES = locale_name.lcid;
3682 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3684 if (preferred_langs)
3685 CFRelease( preferred_langs );
3686 if (canonical_lang_string_ref)
3687 CFRelease( canonical_lang_string_ref );
3690 CFRelease( user_locale_ref );
3691 CFRelease( user_locale_string_ref );
3692 #endif
3694 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3695 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3696 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3698 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3699 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3700 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3701 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3702 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3703 if (!unix_cp)
3704 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3705 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3707 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3708 ansi_cptable = wine_cp_get_table( 1252 );
3709 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3710 oem_cptable = wine_cp_get_table( 437 );
3711 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3712 mac_cptable = wine_cp_get_table( 10000 );
3713 if (unix_cp != CP_UTF8)
3715 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3716 unix_cptable = wine_cp_get_table( 28591 );
3719 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3721 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3722 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3723 mac_cptable->info.codepage, unix_cp );
3725 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3728 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3730 UNICODE_STRING keyName;
3731 OBJECT_ATTRIBUTES attr;
3732 HANDLE hkey;
3734 RtlInitUnicodeString( &keyName, szKeyName );
3735 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3737 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3738 hkey = 0;
3740 return hkey;
3743 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3744 LPWSTR szValueName, ULONG valueNameSize,
3745 LPWSTR szValueData, ULONG valueDataSize)
3747 BYTE buffer[80];
3748 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3749 DWORD dwLen;
3751 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3752 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3753 info->NameLength > valueNameSize ||
3754 info->DataLength > valueDataSize)
3756 return FALSE;
3759 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3761 memcpy( szValueName, info->Name, info->NameLength);
3762 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3763 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3764 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3766 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3767 return TRUE;
3770 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3772 BYTE buffer[128];
3773 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3774 DWORD dwSize = sizeof(buffer);
3775 UNICODE_STRING valueName;
3777 RtlInitUnicodeString( &valueName, szValueName );
3779 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3780 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3781 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3782 info->DataLength == sizeof(DWORD))
3784 memcpy(lpVal, info->Data, sizeof(DWORD));
3785 return TRUE;
3788 return FALSE;
3791 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3793 LANGID langId;
3794 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3795 HRSRC hResource;
3796 BOOL bRet = FALSE;
3798 /* FIXME: Is it correct to use the system default langid? */
3799 langId = GetSystemDefaultLangID();
3801 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3802 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3804 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3806 if (hResource)
3808 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3810 if (hResDir)
3812 ULONG iResourceIndex = lgrpid & 0xf;
3813 LPCWSTR lpResEntry = LockResource( hResDir );
3814 ULONG i;
3816 for (i = 0; i < iResourceIndex; i++)
3817 lpResEntry += *lpResEntry + 1;
3819 if (*lpResEntry < nameSize)
3821 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3822 szName[*lpResEntry] = '\0';
3823 bRet = TRUE;
3827 FreeResource( hResource );
3829 return bRet;
3832 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3833 typedef struct
3835 LANGUAGEGROUP_ENUMPROCA procA;
3836 LANGUAGEGROUP_ENUMPROCW procW;
3837 DWORD dwFlags;
3838 LONG_PTR lParam;
3839 } ENUMLANGUAGEGROUP_CALLBACKS;
3841 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3842 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3844 WCHAR szNumber[10], szValue[4];
3845 HANDLE hKey;
3846 BOOL bContinue = TRUE;
3847 ULONG ulIndex = 0;
3849 if (!lpProcs)
3851 SetLastError(ERROR_INVALID_PARAMETER);
3852 return FALSE;
3855 switch (lpProcs->dwFlags)
3857 case 0:
3858 /* Default to LGRPID_INSTALLED */
3859 lpProcs->dwFlags = LGRPID_INSTALLED;
3860 /* Fall through... */
3861 case LGRPID_INSTALLED:
3862 case LGRPID_SUPPORTED:
3863 break;
3864 default:
3865 SetLastError(ERROR_INVALID_FLAGS);
3866 return FALSE;
3869 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3871 if (!hKey)
3872 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3874 while (bContinue)
3876 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3877 szValue, sizeof(szValue) ))
3879 BOOL bInstalled = szValue[0] == '1';
3880 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3882 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3883 bInstalled ? "" : "not ");
3885 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3887 WCHAR szGrpName[48];
3889 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3890 szGrpName[0] = '\0';
3892 if (lpProcs->procW)
3893 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3894 lpProcs->lParam );
3895 else
3897 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3898 char szGrpNameA[48];
3900 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3901 * or whether the language names are ever localised. Assume CP_ACP.
3904 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3905 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3907 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3908 lpProcs->lParam );
3912 ulIndex++;
3914 else
3915 bContinue = FALSE;
3917 if (!bContinue)
3918 break;
3921 if (hKey)
3922 NtClose( hKey );
3924 return TRUE;
3927 /******************************************************************************
3928 * EnumSystemLanguageGroupsA (KERNEL32.@)
3930 * Call a users function for each language group available on the system.
3932 * PARAMS
3933 * pLangGrpEnumProc [I] Callback function to call for each language group
3934 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3935 * lParam [I] User parameter to pass to pLangGrpEnumProc
3937 * RETURNS
3938 * Success: TRUE.
3939 * Failure: FALSE. Use GetLastError() to determine the cause.
3941 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3942 DWORD dwFlags, LONG_PTR lParam)
3944 ENUMLANGUAGEGROUP_CALLBACKS procs;
3946 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3948 procs.procA = pLangGrpEnumProc;
3949 procs.procW = NULL;
3950 procs.dwFlags = dwFlags;
3951 procs.lParam = lParam;
3953 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3956 /******************************************************************************
3957 * EnumSystemLanguageGroupsW (KERNEL32.@)
3959 * See EnumSystemLanguageGroupsA.
3961 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3962 DWORD dwFlags, LONG_PTR lParam)
3964 ENUMLANGUAGEGROUP_CALLBACKS procs;
3966 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3968 procs.procA = NULL;
3969 procs.procW = pLangGrpEnumProc;
3970 procs.dwFlags = dwFlags;
3971 procs.lParam = lParam;
3973 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3976 /******************************************************************************
3977 * IsValidLanguageGroup (KERNEL32.@)
3979 * Determine if a language group is supported and/or installed.
3981 * PARAMS
3982 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3983 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3985 * RETURNS
3986 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3987 * FALSE otherwise.
3989 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3991 static const WCHAR szFormat[] = { '%','x','\0' };
3992 WCHAR szValueName[16], szValue[2];
3993 BOOL bSupported = FALSE, bInstalled = FALSE;
3994 HANDLE hKey;
3997 switch (dwFlags)
3999 case LGRPID_INSTALLED:
4000 case LGRPID_SUPPORTED:
4002 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4004 sprintfW( szValueName, szFormat, lgrpid );
4006 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4008 bSupported = TRUE;
4010 if (szValue[0] == '1')
4011 bInstalled = TRUE;
4014 if (hKey)
4015 NtClose( hKey );
4017 break;
4020 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4021 (dwFlags == LGRPID_INSTALLED && bInstalled))
4022 return TRUE;
4024 return FALSE;
4027 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4028 typedef struct
4030 LANGGROUPLOCALE_ENUMPROCA procA;
4031 LANGGROUPLOCALE_ENUMPROCW procW;
4032 DWORD dwFlags;
4033 LGRPID lgrpid;
4034 LONG_PTR lParam;
4035 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4037 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4038 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4040 static const WCHAR szAlternateSortsKeyName[] = {
4041 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4043 WCHAR szNumber[10], szValue[4];
4044 HANDLE hKey;
4045 BOOL bContinue = TRUE, bAlternate = FALSE;
4046 LGRPID lgrpid;
4047 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4049 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4051 SetLastError(ERROR_INVALID_PARAMETER);
4052 return FALSE;
4055 if (lpProcs->dwFlags)
4057 SetLastError(ERROR_INVALID_FLAGS);
4058 return FALSE;
4061 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4063 if (!hKey)
4064 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4066 while (bContinue)
4068 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4069 szValue, sizeof(szValue) ))
4071 lgrpid = strtoulW( szValue, NULL, 16 );
4073 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4074 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4076 if (lgrpid == lpProcs->lgrpid)
4078 LCID lcid;
4080 lcid = strtoulW( szNumber, NULL, 16 );
4082 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4083 * '00000437 ;Georgian'
4084 * At present we only pass the LCID string.
4087 if (lpProcs->procW)
4088 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4089 else
4091 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4093 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4095 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4099 ulIndex++;
4101 else
4103 /* Finished enumerating this key */
4104 if (!bAlternate)
4106 /* Enumerate alternate sorts also */
4107 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4108 bAlternate = TRUE;
4109 ulIndex = 0;
4111 else
4112 bContinue = FALSE; /* Finished both keys */
4115 if (!bContinue)
4116 break;
4119 if (hKey)
4120 NtClose( hKey );
4122 return TRUE;
4125 /******************************************************************************
4126 * EnumLanguageGroupLocalesA (KERNEL32.@)
4128 * Call a users function for every locale in a language group available on the system.
4130 * PARAMS
4131 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4132 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4133 * dwFlags [I] Reserved, set to 0
4134 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4136 * RETURNS
4137 * Success: TRUE.
4138 * Failure: FALSE. Use GetLastError() to determine the cause.
4140 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4141 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4143 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4145 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4147 callbacks.procA = pLangGrpLcEnumProc;
4148 callbacks.procW = NULL;
4149 callbacks.dwFlags = dwFlags;
4150 callbacks.lgrpid = lgrpid;
4151 callbacks.lParam = lParam;
4153 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4156 /******************************************************************************
4157 * EnumLanguageGroupLocalesW (KERNEL32.@)
4159 * See EnumLanguageGroupLocalesA.
4161 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4162 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4164 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4166 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4168 callbacks.procA = NULL;
4169 callbacks.procW = pLangGrpLcEnumProc;
4170 callbacks.dwFlags = dwFlags;
4171 callbacks.lgrpid = lgrpid;
4172 callbacks.lParam = lParam;
4174 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4177 /******************************************************************************
4178 * InvalidateNLSCache (KERNEL32.@)
4180 * Invalidate the cache of NLS values.
4182 * PARAMS
4183 * None.
4185 * RETURNS
4186 * Success: TRUE.
4187 * Failure: FALSE.
4189 BOOL WINAPI InvalidateNLSCache(void)
4191 FIXME("() stub\n");
4192 return FALSE;
4195 /******************************************************************************
4196 * GetUserGeoID (KERNEL32.@)
4198 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4200 GEOID ret = GEOID_NOT_AVAILABLE;
4201 static const WCHAR geoW[] = {'G','e','o',0};
4202 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4203 WCHAR bufferW[40], *end;
4204 DWORD count;
4205 HANDLE hkey, hSubkey = 0;
4206 UNICODE_STRING keyW;
4207 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4208 RtlInitUnicodeString( &keyW, nationW );
4209 count = sizeof(bufferW);
4211 if(!(hkey = create_registry_key())) return ret;
4213 switch( GeoClass ){
4214 case GEOCLASS_NATION:
4215 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4217 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4218 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4219 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4221 break;
4222 case GEOCLASS_REGION:
4223 FIXME("GEOCLASS_REGION not handled yet\n");
4224 break;
4227 NtClose(hkey);
4228 if (hSubkey) NtClose(hSubkey);
4229 return ret;
4232 /******************************************************************************
4233 * SetUserGeoID (KERNEL32.@)
4235 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4237 static const WCHAR geoW[] = {'G','e','o',0};
4238 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4239 static const WCHAR formatW[] = {'%','i',0};
4240 UNICODE_STRING nameW,keyW;
4241 WCHAR bufferW[10];
4242 OBJECT_ATTRIBUTES attr;
4243 HANDLE hkey;
4245 if(!(hkey = create_registry_key())) return FALSE;
4247 attr.Length = sizeof(attr);
4248 attr.RootDirectory = hkey;
4249 attr.ObjectName = &nameW;
4250 attr.Attributes = 0;
4251 attr.SecurityDescriptor = NULL;
4252 attr.SecurityQualityOfService = NULL;
4253 RtlInitUnicodeString( &nameW, geoW );
4254 RtlInitUnicodeString( &keyW, nationW );
4256 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4259 NtClose(attr.RootDirectory);
4260 return FALSE;
4263 sprintfW(bufferW, formatW, GeoID);
4264 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4265 NtClose(attr.RootDirectory);
4266 NtClose(hkey);
4267 return TRUE;
4270 typedef struct
4272 union
4274 UILANGUAGE_ENUMPROCA procA;
4275 UILANGUAGE_ENUMPROCW procW;
4276 } u;
4277 DWORD flags;
4278 LONG_PTR param;
4279 } ENUM_UILANG_CALLBACK;
4281 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4282 LPCSTR name, WORD LangID, LONG_PTR lParam )
4284 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4285 char buf[20];
4287 sprintf(buf, "%08x", (UINT)LangID);
4288 return enum_uilang->u.procA( buf, enum_uilang->param );
4291 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4292 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4294 static const WCHAR formatW[] = {'%','0','8','x',0};
4295 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4296 WCHAR buf[20];
4298 sprintfW( buf, formatW, (UINT)LangID );
4299 return enum_uilang->u.procW( buf, enum_uilang->param );
4302 /******************************************************************************
4303 * EnumUILanguagesA (KERNEL32.@)
4305 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4307 ENUM_UILANG_CALLBACK enum_uilang;
4309 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4311 if(!pUILangEnumProc) {
4312 SetLastError(ERROR_INVALID_PARAMETER);
4313 return FALSE;
4315 if(dwFlags) {
4316 SetLastError(ERROR_INVALID_FLAGS);
4317 return FALSE;
4320 enum_uilang.u.procA = pUILangEnumProc;
4321 enum_uilang.flags = dwFlags;
4322 enum_uilang.param = lParam;
4324 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4325 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4326 (LONG_PTR)&enum_uilang);
4327 return TRUE;
4330 /******************************************************************************
4331 * EnumUILanguagesW (KERNEL32.@)
4333 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4335 ENUM_UILANG_CALLBACK enum_uilang;
4337 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4340 if(!pUILangEnumProc) {
4341 SetLastError(ERROR_INVALID_PARAMETER);
4342 return FALSE;
4344 if(dwFlags) {
4345 SetLastError(ERROR_INVALID_FLAGS);
4346 return FALSE;
4349 enum_uilang.u.procW = pUILangEnumProc;
4350 enum_uilang.flags = dwFlags;
4351 enum_uilang.param = lParam;
4353 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4354 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4355 (LONG_PTR)&enum_uilang);
4356 return TRUE;
4359 enum locationkind {
4360 LOCATION_NATION = 0,
4361 LOCATION_REGION,
4362 LOCATION_BOTH
4365 struct geoinfo_t {
4366 GEOID id;
4367 WCHAR iso2W[3];
4368 WCHAR iso3W[4];
4369 GEOID parent;
4370 INT uncode;
4371 enum locationkind kind;
4374 static const struct geoinfo_t geoinfodata[] = {
4375 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4376 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4377 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4378 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4379 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4380 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4381 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4382 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4383 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4384 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4385 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4386 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4387 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4388 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4389 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4390 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4391 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4392 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4393 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4394 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4395 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4396 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4397 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4398 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4399 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4400 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4401 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4402 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4403 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4404 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4405 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4406 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4407 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4408 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4409 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4410 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4411 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4412 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4413 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4414 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4415 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4416 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4417 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4418 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4419 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4420 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4421 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4422 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4423 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4424 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4425 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4426 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4427 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4428 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4429 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4430 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4431 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4432 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4433 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4434 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4435 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4436 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4437 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4438 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4439 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4440 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4441 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4442 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4443 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4444 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4445 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4446 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4447 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4448 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4449 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4450 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4451 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4452 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4453 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4454 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4455 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4456 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4457 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4458 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4459 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4460 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4461 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4462 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4463 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4464 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4465 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4466 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4467 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4468 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4469 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4470 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4471 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4472 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4473 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4474 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4475 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4476 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4477 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4478 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4479 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4480 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4481 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4482 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4483 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4484 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4485 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4486 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4487 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4488 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4489 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4490 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4491 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4492 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4493 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4494 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4495 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4496 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4497 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4498 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4499 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4500 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4501 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4502 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4503 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4504 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4505 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4506 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4507 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4508 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4509 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4510 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4511 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4512 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4513 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4514 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4515 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4516 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4517 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4518 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4519 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4520 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4521 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4522 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4523 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4524 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4525 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4526 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4527 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4528 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4529 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4530 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4531 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4532 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4533 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4534 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4535 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4536 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4537 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4538 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4539 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4540 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4541 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4542 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4543 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4544 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4545 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4546 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4547 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4548 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4549 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4550 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4551 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4552 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4553 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4554 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4555 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4556 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4557 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4558 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4559 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4560 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4561 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4562 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4563 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4564 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4565 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4566 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4567 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4568 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4569 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4570 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4571 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4572 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4573 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4574 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4575 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4576 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4577 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4578 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4579 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4580 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4581 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4582 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4583 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4584 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4585 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4586 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4587 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4588 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4589 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4590 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4591 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4592 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4593 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4594 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4595 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4596 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4597 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4598 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4599 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4600 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4601 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4602 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4603 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4604 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4605 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4606 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4607 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4608 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4609 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4610 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4611 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4612 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4613 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4614 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4615 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4616 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4617 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4618 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4619 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4620 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4621 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4622 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4623 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4624 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4625 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4626 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4627 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4628 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4629 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4630 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4631 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4632 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4633 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4634 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4635 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4636 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4637 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4638 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4639 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4640 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4641 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4642 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4643 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4644 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4645 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4646 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4647 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4648 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4649 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4650 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4651 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4652 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4653 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4654 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4655 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4656 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4657 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4658 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4659 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4660 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4661 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4662 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4663 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4664 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4665 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4666 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4667 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4668 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4669 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4670 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4671 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4672 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4673 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4676 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4678 int min, max;
4680 min = 0;
4681 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4683 while (min <= max) {
4684 const struct geoinfo_t *ptr;
4685 int n = (min+max)/2;
4687 ptr = &geoinfodata[n];
4688 if (geoid == ptr->id)
4689 /* we don't need empty entries */
4690 return *ptr->iso2W ? ptr : NULL;
4692 if (ptr->id > geoid)
4693 max = n-1;
4694 else
4695 min = n+1;
4698 return NULL;
4701 /******************************************************************************
4702 * GetGeoInfoW (KERNEL32.@)
4704 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4706 const struct geoinfo_t *ptr;
4707 const WCHAR *str = NULL;
4708 WCHAR buffW[12];
4709 LONG val = 0;
4710 INT len;
4712 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4714 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4715 SetLastError(ERROR_INVALID_PARAMETER);
4716 return 0;
4719 switch (geotype) {
4720 case GEO_NATION:
4721 val = geoid;
4722 break;
4723 case GEO_ISO_UN_NUMBER:
4724 val = ptr->uncode;
4725 break;
4726 case GEO_PARENT:
4727 val = ptr->parent;
4728 break;
4729 case GEO_ISO2:
4730 case GEO_ISO3:
4732 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4733 break;
4735 case GEO_RFC1766:
4736 case GEO_LCID:
4737 case GEO_FRIENDLYNAME:
4738 case GEO_OFFICIALNAME:
4739 case GEO_TIMEZONES:
4740 case GEO_OFFICIALLANGUAGES:
4741 case GEO_LATITUDE:
4742 case GEO_LONGITUDE:
4743 FIXME("type %d is not supported\n", geotype);
4744 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4745 return 0;
4746 default:
4747 WARN("unrecognized type %d\n", geotype);
4748 SetLastError(ERROR_INVALID_FLAGS);
4749 return 0;
4752 if (val) {
4753 static const WCHAR fmtW[] = {'%','d',0};
4754 sprintfW(buffW, fmtW, val);
4755 str = buffW;
4758 len = strlenW(str) + 1;
4759 if (!data || !data_len)
4760 return len;
4762 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4763 if (data_len < len)
4764 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4765 return data_len < len ? 0 : len;
4768 /******************************************************************************
4769 * GetGeoInfoA (KERNEL32.@)
4771 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4773 WCHAR *buffW;
4774 INT len;
4776 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4778 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4779 if (!len)
4780 return 0;
4782 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4783 if (!buffW)
4784 return 0;
4786 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4787 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4788 if (!data || !data_len) {
4789 HeapFree(GetProcessHeap(), 0, buffW);
4790 return len;
4793 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4794 HeapFree(GetProcessHeap(), 0, buffW);
4796 if (data_len < len)
4797 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4798 return data_len < len ? 0 : len;
4801 /******************************************************************************
4802 * EnumSystemGeoID (KERNEL32.@)
4804 * Call a users function for every location available on the system.
4806 * PARAMS
4807 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4808 * parent [I] GEOID for the parent
4809 * enumproc [I] Callback function to call for each location
4811 * RETURNS
4812 * Success: TRUE.
4813 * Failure: FALSE. Use GetLastError() to determine the cause.
4815 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4817 INT i;
4819 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4821 if (!enumproc) {
4822 SetLastError(ERROR_INVALID_PARAMETER);
4823 return FALSE;
4826 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4827 SetLastError(ERROR_INVALID_FLAGS);
4828 return FALSE;
4831 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4832 const struct geoinfo_t *ptr = &geoinfodata[i];
4834 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4835 continue;
4837 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4838 continue;
4840 if (parent && ptr->parent != parent)
4841 continue;
4843 if (!enumproc(ptr->id))
4844 return TRUE;
4847 return TRUE;
4850 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4852 LCID userlcid;
4854 TRACE("%p, %d\n", localename, buffersize);
4856 userlcid = GetUserDefaultLCID();
4857 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4860 /******************************************************************************
4861 * NormalizeString (KERNEL32.@)
4863 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4864 LPWSTR lpDstString, INT cwDstLength)
4866 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4867 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4868 return 0;
4871 /******************************************************************************
4872 * IsNormalizedString (KERNEL32.@)
4874 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4876 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4877 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4878 return FALSE;
4881 enum {
4882 BASE = 36,
4883 TMIN = 1,
4884 TMAX = 26,
4885 SKEW = 38,
4886 DAMP = 700,
4887 INIT_BIAS = 72,
4888 INIT_N = 128
4891 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4893 INT k;
4895 delta /= (firsttime ? DAMP : 2);
4896 delta += delta/numpoints;
4898 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4899 delta /= BASE-TMIN;
4900 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4903 /******************************************************************************
4904 * IdnToAscii (KERNEL32.@)
4905 * Implementation of Punycode based on RFC 3492.
4907 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4908 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4910 static const WCHAR prefixW[] = {'x','n','-','-'};
4912 WCHAR *norm_str;
4913 INT i, label_start, label_end, norm_len, out_label, out = 0;
4915 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4916 lpASCIICharStr, cchASCIIChar);
4918 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4919 if(!norm_len)
4920 return 0;
4921 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4922 if(!norm_str) {
4923 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4924 return 0;
4926 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4927 cchUnicodeChar, norm_str, norm_len);
4928 if(!norm_len) {
4929 HeapFree(GetProcessHeap(), 0, norm_str);
4930 return 0;
4933 for(label_start=0; label_start<norm_len;) {
4934 INT n = INIT_N, bias = INIT_BIAS;
4935 INT delta = 0, b = 0, h;
4937 out_label = out;
4938 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4939 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4940 if(norm_str[i] < 0x80)
4941 b++;
4942 label_end = i;
4944 if(b == label_end-label_start) {
4945 if(label_end < norm_len)
4946 b++;
4947 if(!lpASCIICharStr) {
4948 out += b;
4949 }else if(out+b <= cchASCIIChar) {
4950 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4951 out += b;
4952 }else {
4953 HeapFree(GetProcessHeap(), 0, norm_str);
4954 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4955 return 0;
4957 label_start = label_end+1;
4958 continue;
4961 if(!lpASCIICharStr) {
4962 out += 5+b; /* strlen(xn--...-) */
4963 }else if(out+5+b <= cchASCIIChar) {
4964 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4965 out += 4;
4966 for(i=label_start; i<label_end; i++)
4967 if(norm_str[i] < 0x80)
4968 lpASCIICharStr[out++] = norm_str[i];
4969 lpASCIICharStr[out++] = '-';
4970 }else {
4971 HeapFree(GetProcessHeap(), 0, norm_str);
4972 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4973 return 0;
4975 if(!b)
4976 out--;
4978 for(h=b; h<label_end-label_start;) {
4979 INT m = 0xffff, q, k;
4981 for(i=label_start; i<label_end; i++) {
4982 if(norm_str[i]>=n && m>norm_str[i])
4983 m = norm_str[i];
4985 delta += (m-n)*(h+1);
4986 n = m;
4988 for(i=label_start; i<label_end; i++) {
4989 if(norm_str[i] < n) {
4990 delta++;
4991 }else if(norm_str[i] == n) {
4992 for(q=delta, k=BASE; ; k+=BASE) {
4993 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4994 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4995 if(!lpASCIICharStr) {
4996 out++;
4997 }else if(out+1 <= cchASCIIChar) {
4998 lpASCIICharStr[out++] = disp<='z'-'a' ?
4999 'a'+disp : '0'+disp-'z'+'a'-1;
5000 }else {
5001 HeapFree(GetProcessHeap(), 0, norm_str);
5002 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5003 return 0;
5005 if(q < t)
5006 break;
5007 q = (q-t)/(BASE-t);
5009 bias = adapt(delta, h+1, h==b);
5010 delta = 0;
5011 h++;
5014 delta++;
5015 n++;
5018 if(out-out_label > 63) {
5019 HeapFree(GetProcessHeap(), 0, norm_str);
5020 SetLastError(ERROR_INVALID_NAME);
5021 return 0;
5024 if(label_end < norm_len) {
5025 if(!lpASCIICharStr) {
5026 out++;
5027 }else if(out+1 <= cchASCIIChar) {
5028 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5029 }else {
5030 HeapFree(GetProcessHeap(), 0, norm_str);
5031 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5032 return 0;
5035 label_start = label_end+1;
5038 HeapFree(GetProcessHeap(), 0, norm_str);
5039 return out;
5042 /******************************************************************************
5043 * IdnToNameprepUnicode (KERNEL32.@)
5045 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5046 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5048 enum {
5049 UNASSIGNED = 0x1,
5050 PROHIBITED = 0x2,
5051 BIDI_RAL = 0x4,
5052 BIDI_L = 0x8
5055 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5056 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5057 const WCHAR *ptr;
5058 WORD flags;
5059 WCHAR buf[64], *map_str, norm_str[64], ch;
5060 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5061 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5063 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5064 lpNameprepCharStr, cchNameprepChar);
5066 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5067 SetLastError(ERROR_INVALID_FLAGS);
5068 return 0;
5071 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5072 SetLastError(ERROR_INVALID_PARAMETER);
5073 return 0;
5076 if(cchUnicodeChar == -1)
5077 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5078 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5079 SetLastError(ERROR_INVALID_NAME);
5080 return 0;
5083 for(label_start=0; label_start<cchUnicodeChar;) {
5084 ascii_only = TRUE;
5085 for(i=label_start; i<cchUnicodeChar; i++) {
5086 ch = lpUnicodeCharStr[i];
5088 if(i!=cchUnicodeChar-1 && !ch) {
5089 SetLastError(ERROR_INVALID_NAME);
5090 return 0;
5092 /* check if ch is one of label separators defined in RFC3490 */
5093 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5094 break;
5096 if(ch > 0x7f) {
5097 ascii_only = FALSE;
5098 continue;
5101 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5102 continue;
5103 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5104 || (ch>='0' && ch<='9') || ch=='-')
5105 continue;
5107 SetLastError(ERROR_INVALID_NAME);
5108 return 0;
5110 label_end = i;
5111 /* last label may be empty */
5112 if(label_start==label_end && ch) {
5113 SetLastError(ERROR_INVALID_NAME);
5114 return 0;
5117 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5118 lpUnicodeCharStr[label_end-1]=='-')) {
5119 SetLastError(ERROR_INVALID_NAME);
5120 return 0;
5123 if(ascii_only) {
5124 /* maximal label length is 63 characters */
5125 if(label_end-label_start > 63) {
5126 SetLastError(ERROR_INVALID_NAME);
5127 return 0;
5129 if(label_end < cchUnicodeChar)
5130 label_end++;
5132 if(!lpNameprepCharStr) {
5133 out += label_end-label_start;
5134 }else if(out+label_end-label_start <= cchNameprepChar) {
5135 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5136 (label_end-label_start)*sizeof(WCHAR));
5137 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5138 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5139 out += label_end-label_start;
5140 }else {
5141 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5142 return 0;
5145 label_start = label_end;
5146 continue;
5149 map_len = 0;
5150 for(i=label_start; i<label_end; i++) {
5151 ch = lpUnicodeCharStr[i];
5152 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5153 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5155 if(!ptr[0]) map_len++;
5156 else if(!ptr[1]) map_len++;
5157 else if(!ptr[2]) map_len += 2;
5158 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5160 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5161 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5162 if(!map_str) {
5163 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5164 return 0;
5166 }else {
5167 map_str = buf;
5169 map_len = 0;
5170 for(i=label_start; i<label_end; i++) {
5171 ch = lpUnicodeCharStr[i];
5172 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5173 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5175 if(!ptr[0]) {
5176 map_str[map_len++] = ch;
5177 }else if(!ptr[1]) {
5178 map_str[map_len++] = ptr[0];
5179 }else if(!ptr[2]) {
5180 map_str[map_len++] = ptr[0];
5181 map_str[map_len++] = ptr[1];
5182 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5183 map_str[map_len++] = ptr[0];
5184 map_str[map_len++] = ptr[1];
5185 map_str[map_len++] = ptr[2];
5189 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5190 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5191 if(map_str != buf)
5192 HeapFree(GetProcessHeap(), 0, map_str);
5193 if(!norm_len) {
5194 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5195 SetLastError(ERROR_INVALID_NAME);
5196 return 0;
5199 if(label_end < cchUnicodeChar) {
5200 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5201 label_end++;
5204 if(!lpNameprepCharStr) {
5205 out += norm_len;
5206 }else if(out+norm_len <= cchNameprepChar) {
5207 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5208 out += norm_len;
5209 }else {
5210 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5211 return 0;
5214 have_bidi_ral = prohibit_bidi_ral = FALSE;
5215 mask = PROHIBITED;
5216 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5217 mask |= UNASSIGNED;
5218 for(i=0; i<norm_len; i++) {
5219 ch = norm_str[i];
5220 flags = get_table_entry( nameprep_char_type, ch );
5222 if(flags & mask) {
5223 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5224 : ERROR_NO_UNICODE_TRANSLATION);
5225 return 0;
5228 if(flags & BIDI_RAL)
5229 have_bidi_ral = TRUE;
5230 if(flags & BIDI_L)
5231 prohibit_bidi_ral = TRUE;
5234 if(have_bidi_ral) {
5235 ch = norm_str[0];
5236 flags = get_table_entry( nameprep_char_type, ch );
5237 if((flags & BIDI_RAL) == 0)
5238 prohibit_bidi_ral = TRUE;
5240 ch = norm_str[norm_len-1];
5241 flags = get_table_entry( nameprep_char_type, ch );
5242 if((flags & BIDI_RAL) == 0)
5243 prohibit_bidi_ral = TRUE;
5246 if(have_bidi_ral && prohibit_bidi_ral) {
5247 SetLastError(ERROR_INVALID_NAME);
5248 return 0;
5251 label_start = label_end;
5254 return out;
5257 /******************************************************************************
5258 * IdnToUnicode (KERNEL32.@)
5260 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5261 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5263 extern const unsigned short nameprep_char_type[];
5265 INT i, label_start, label_end, out_label, out = 0;
5266 WCHAR ch;
5268 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5269 lpUnicodeCharStr, cchUnicodeChar);
5271 for(label_start=0; label_start<cchASCIIChar;) {
5272 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5274 out_label = out;
5275 for(i=label_start; i<cchASCIIChar; i++) {
5276 ch = lpASCIICharStr[i];
5278 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5279 SetLastError(ERROR_INVALID_NAME);
5280 return 0;
5283 if(!ch || ch=='.')
5284 break;
5285 if(ch == '-')
5286 delim = i;
5288 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5289 continue;
5290 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5291 || (ch>='0' && ch<='9') || ch=='-')
5292 continue;
5294 SetLastError(ERROR_INVALID_NAME);
5295 return 0;
5297 label_end = i;
5298 /* last label may be empty */
5299 if(label_start==label_end && ch) {
5300 SetLastError(ERROR_INVALID_NAME);
5301 return 0;
5304 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5305 lpASCIICharStr[label_end-1]=='-')) {
5306 SetLastError(ERROR_INVALID_NAME);
5307 return 0;
5309 if(label_end-label_start > 63) {
5310 SetLastError(ERROR_INVALID_NAME);
5311 return 0;
5314 if(label_end-label_start<4 ||
5315 tolowerW(lpASCIICharStr[label_start])!='x' ||
5316 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5317 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5318 if(label_end < cchASCIIChar)
5319 label_end++;
5321 if(!lpUnicodeCharStr) {
5322 out += label_end-label_start;
5323 }else if(out+label_end-label_start <= cchUnicodeChar) {
5324 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5325 (label_end-label_start)*sizeof(WCHAR));
5326 out += label_end-label_start;
5327 }else {
5328 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5329 return 0;
5332 label_start = label_end;
5333 continue;
5336 if(delim == label_start+3)
5337 delim++;
5338 if(!lpUnicodeCharStr) {
5339 out += delim-label_start-4;
5340 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5341 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5342 (delim-label_start-4)*sizeof(WCHAR));
5343 out += delim-label_start-4;
5344 }else {
5345 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5346 return 0;
5348 if(out != out_label)
5349 delim++;
5351 for(i=delim; i<label_end;) {
5352 old_pos = pos;
5353 w = 1;
5354 for(k=BASE; ; k+=BASE) {
5355 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5356 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5357 SetLastError(ERROR_INVALID_NAME);
5358 return 0;
5360 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5361 pos += digit*w;
5362 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5363 if(digit < t)
5364 break;
5365 w *= BASE-t;
5367 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5368 n += pos/(out-out_label+1);
5369 pos %= out-out_label+1;
5371 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5372 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5373 SetLastError(ERROR_INVALID_NAME);
5374 return 0;
5376 if(!lpUnicodeCharStr) {
5377 out++;
5378 }else if(out+1 <= cchASCIIChar) {
5379 memmove(lpUnicodeCharStr+out_label+pos+1,
5380 lpUnicodeCharStr+out_label+pos,
5381 (out-out_label-pos)*sizeof(WCHAR));
5382 lpUnicodeCharStr[out_label+pos] = n;
5383 out++;
5384 }else {
5385 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5386 return 0;
5388 pos++;
5391 if(out-out_label > 63) {
5392 SetLastError(ERROR_INVALID_NAME);
5393 return 0;
5396 if(label_end < cchASCIIChar) {
5397 if(!lpUnicodeCharStr) {
5398 out++;
5399 }else if(out+1 <= cchUnicodeChar) {
5400 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5401 }else {
5402 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5403 return 0;
5406 label_start = label_end+1;
5409 return out;
5413 /******************************************************************************
5414 * GetUserPreferredUILanguages (KERNEL32.@)
5416 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5418 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5419 return FALSE;
5422 /******************************************************************************
5423 * GetFileMUIPath (KERNEL32.@)
5426 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5427 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5429 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5430 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5432 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5434 return FALSE;
5437 /******************************************************************************
5438 * GetFileMUIInfo (KERNEL32.@)
5441 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5443 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5445 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5446 return FALSE;