kernel32: Improve stub for SetThreadUILanguage.
[wine.git] / dlls / kernel32 / locale.c
blob5a6ff35b75b832fdd24f321fef23d8b6a6ce40df
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)
57 #define MB_FLAGSMASK (MB_PRECOMPOSED|MB_COMPOSITE|MB_USEGLYPHCHARS|MB_ERR_INVALID_CHARS)
58 #define WC_FLAGSMASK (WC_DISCARDNS|WC_SEPCHARS|WC_DEFAULTCHAR|WC_ERR_INVALID_CHARS|\
59 WC_COMPOSITECHECK|WC_NO_BEST_FIT_CHARS)
61 /* current code pages */
62 static const union cptable *ansi_cptable;
63 static const union cptable *oem_cptable;
64 static const union cptable *mac_cptable;
65 static const union cptable *unix_cptable; /* NULL if UTF8 */
67 static const WCHAR szLocaleKeyName[] = {
68 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
69 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
70 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
73 static const WCHAR szLangGroupsKeyName[] = {
74 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
75 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
76 'C','o','n','t','r','o','l','\\','N','l','s','\\',
77 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
80 /* Charset to codepage map, sorted by name. */
81 static const struct charset_entry
83 const char *charset_name;
84 UINT codepage;
85 } charset_names[] =
87 { "BIG5", 950 },
88 { "CP1250", 1250 },
89 { "CP1251", 1251 },
90 { "CP1252", 1252 },
91 { "CP1253", 1253 },
92 { "CP1254", 1254 },
93 { "CP1255", 1255 },
94 { "CP1256", 1256 },
95 { "CP1257", 1257 },
96 { "CP1258", 1258 },
97 { "CP932", 932 },
98 { "CP936", 936 },
99 { "CP949", 949 },
100 { "CP950", 950 },
101 { "EUCJP", 20932 },
102 { "GB2312", 936 },
103 { "IBM037", 37 },
104 { "IBM1026", 1026 },
105 { "IBM424", 424 },
106 { "IBM437", 437 },
107 { "IBM500", 500 },
108 { "IBM850", 850 },
109 { "IBM852", 852 },
110 { "IBM855", 855 },
111 { "IBM857", 857 },
112 { "IBM860", 860 },
113 { "IBM861", 861 },
114 { "IBM862", 862 },
115 { "IBM863", 863 },
116 { "IBM864", 864 },
117 { "IBM865", 865 },
118 { "IBM866", 866 },
119 { "IBM869", 869 },
120 { "IBM874", 874 },
121 { "IBM875", 875 },
122 { "ISO88591", 28591 },
123 { "ISO885910", 28600 },
124 { "ISO885913", 28603 },
125 { "ISO885914", 28604 },
126 { "ISO885915", 28605 },
127 { "ISO885916", 28606 },
128 { "ISO88592", 28592 },
129 { "ISO88593", 28593 },
130 { "ISO88594", 28594 },
131 { "ISO88595", 28595 },
132 { "ISO88596", 28596 },
133 { "ISO88597", 28597 },
134 { "ISO88598", 28598 },
135 { "ISO88599", 28599 },
136 { "KOI8R", 20866 },
137 { "KOI8U", 21866 },
138 { "UTF8", CP_UTF8 }
142 struct locale_name
144 WCHAR win_name[128]; /* Windows name ("en-US") */
145 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
146 WCHAR *country; /* country ("US") */
147 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
148 WCHAR *script; /* script ("Latn") for Windows format only */
149 WCHAR *modifier; /* modifier or sort order */
150 LCID lcid; /* corresponding LCID */
151 int matches; /* number of elements matching LCID (0..4) */
152 UINT codepage; /* codepage corresponding to charset */
155 /* locale ids corresponding to the various Unix locale parameters */
156 static LCID lcid_LC_COLLATE;
157 static LCID lcid_LC_CTYPE;
158 static LCID lcid_LC_MESSAGES;
159 static LCID lcid_LC_MONETARY;
160 static LCID lcid_LC_NUMERIC;
161 static LCID lcid_LC_TIME;
162 static LCID lcid_LC_PAPER;
163 static LCID lcid_LC_MEASUREMENT;
164 static LCID lcid_LC_TELEPHONE;
166 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
167 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
168 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
169 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
170 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
171 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
172 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
173 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
174 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
175 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
176 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
177 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
178 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
179 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
180 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
181 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
182 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
183 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
184 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
185 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
186 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
187 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
188 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
189 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
190 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
191 static const WCHAR sListW[] = {'s','L','i','s','t',0};
192 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
193 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
194 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
195 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
196 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
197 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
198 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
199 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
200 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
201 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
202 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
203 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
204 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
206 static struct registry_value
208 DWORD lctype;
209 const WCHAR *name;
210 WCHAR *cached_value;
211 } registry_values[] =
213 { LOCALE_ICALENDARTYPE, iCalendarTypeW },
214 { LOCALE_ICURRDIGITS, iCurrDigitsW },
215 { LOCALE_ICURRENCY, iCurrencyW },
216 { LOCALE_IDIGITS, iDigitsW },
217 { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
218 { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
219 { LOCALE_ILZERO, iLZeroW },
220 { LOCALE_IMEASURE, iMeasureW },
221 { LOCALE_INEGCURR, iNegCurrW },
222 { LOCALE_INEGNUMBER, iNegNumberW },
223 { LOCALE_IPAPERSIZE, iPaperSizeW },
224 { LOCALE_ITIME, iTimeW },
225 { LOCALE_S1159, s1159W },
226 { LOCALE_S2359, s2359W },
227 { LOCALE_SCURRENCY, sCurrencyW },
228 { LOCALE_SDATE, sDateW },
229 { LOCALE_SDECIMAL, sDecimalW },
230 { LOCALE_SGROUPING, sGroupingW },
231 { LOCALE_SLIST, sListW },
232 { LOCALE_SLONGDATE, sLongDateW },
233 { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
234 { LOCALE_SMONGROUPING, sMonGroupingW },
235 { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
236 { LOCALE_SNEGATIVESIGN, sNegativeSignW },
237 { LOCALE_SPOSITIVESIGN, sPositiveSignW },
238 { LOCALE_SSHORTDATE, sShortDateW },
239 { LOCALE_STHOUSAND, sThousandW },
240 { LOCALE_STIME, sTimeW },
241 { LOCALE_STIMEFORMAT, sTimeFormatW },
242 { LOCALE_SYEARMONTH, sYearMonthW },
243 /* The following are not listed under MSDN as supported,
244 * but seem to be used and also stored in the registry.
246 { LOCALE_ICOUNTRY, iCountryW },
247 { LOCALE_IDATE, iDateW },
248 { LOCALE_ILDATE, iLDateW },
249 { LOCALE_ITLZERO, iTLZeroW },
250 { LOCALE_SCOUNTRY, sCountryW },
251 { LOCALE_SABBREVLANGNAME, sLanguageW },
252 /* The following are used in XP and later */
253 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
254 { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
255 { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
258 static CRITICAL_SECTION cache_section;
259 static CRITICAL_SECTION_DEBUG critsect_debug =
261 0, 0, &cache_section,
262 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
263 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
265 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
267 /* Copy Ascii string to Unicode without using codepages */
268 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
270 while (n > 1 && *src)
272 *dst++ = (unsigned char)*src++;
273 n--;
275 if (n) *dst = 0;
278 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
280 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
283 /***********************************************************************
284 * get_lcid_codepage
286 * Retrieve the ANSI codepage for a given locale.
288 static inline UINT get_lcid_codepage( LCID lcid )
290 UINT ret;
291 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
292 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
293 return ret;
297 /***********************************************************************
298 * get_codepage_table
300 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
302 static const union cptable *get_codepage_table( unsigned int codepage )
304 const union cptable *ret = NULL;
306 assert( ansi_cptable ); /* init must have been done already */
308 switch(codepage)
310 case CP_ACP:
311 return ansi_cptable;
312 case CP_OEMCP:
313 return oem_cptable;
314 case CP_MACCP:
315 return mac_cptable;
316 case CP_UTF7:
317 case CP_UTF8:
318 break;
319 case CP_THREAD_ACP:
320 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
321 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
322 if (!codepage) return ansi_cptable;
323 /* fall through */
324 default:
325 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
326 if (codepage == oem_cptable->info.codepage) return oem_cptable;
327 if (codepage == mac_cptable->info.codepage) return mac_cptable;
328 ret = wine_cp_get_table( codepage );
329 break;
331 return ret;
335 /***********************************************************************
336 * charset_cmp (internal)
338 static int charset_cmp( const void *name, const void *entry )
340 const struct charset_entry *charset = entry;
341 return strcasecmp( name, charset->charset_name );
344 /***********************************************************************
345 * find_charset
347 static UINT find_charset( const WCHAR *name )
349 const struct charset_entry *entry;
350 char charset_name[16];
351 size_t i, j;
353 /* remove punctuation characters from charset name */
354 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
355 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
356 charset_name[j] = 0;
358 entry = bsearch( charset_name, charset_names,
359 sizeof(charset_names)/sizeof(charset_names[0]),
360 sizeof(charset_names[0]), charset_cmp );
361 if (entry) return entry->codepage;
362 return 0;
365 static LANGID get_default_sublang( LANGID lang )
367 switch (lang)
369 case MAKELANGID( LANG_SPANISH, SUBLANG_NEUTRAL ):
370 return MAKELANGID( LANG_SPANISH, SUBLANG_SPANISH_MODERN );
371 case MAKELANGID( LANG_CHINESE, SUBLANG_NEUTRAL ):
372 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
373 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE ):
374 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
375 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ):
376 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_MACAU ):
377 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG );
379 if (SUBLANGID( lang ) == SUBLANG_NEUTRAL) lang = MAKELANGID( PRIMARYLANGID(lang), SUBLANG_DEFAULT );
380 return lang;
383 /***********************************************************************
384 * find_locale_id_callback
386 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
387 LPCWSTR name, LANGID lang, LPARAM lParam )
389 struct locale_name *data = (struct locale_name *)lParam;
390 WCHAR buffer[128];
391 int matches = 0;
392 LCID lcid = MAKELCID( lang, SORT_DEFAULT ); /* FIXME: handle sort order */
394 if (PRIMARYLANGID(lang) == LANG_NEUTRAL) return TRUE; /* continue search */
396 /* first check exact name */
397 if (data->win_name[0] &&
398 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
399 buffer, sizeof(buffer)/sizeof(WCHAR) ))
401 if (!strcmpiW( data->win_name, buffer ))
403 matches = 4; /* everything matches */
404 goto done;
408 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
409 buffer, sizeof(buffer)/sizeof(WCHAR) ))
410 return TRUE;
411 if (strcmpiW( buffer, data->lang )) return TRUE;
412 matches++; /* language name matched */
414 if (data->script)
416 if (GetLocaleInfoW( lcid, LOCALE_SSCRIPTS | LOCALE_NOUSEROVERRIDE,
417 buffer, sizeof(buffer)/sizeof(WCHAR) ))
419 const WCHAR *p = buffer;
420 unsigned int len = strlenW( data->script );
421 while (*p)
423 if (!strncmpiW( p, data->script, len ) && (!p[len] || p[len] == ';')) break;
424 if (!(p = strchrW( p, ';'))) goto done;
425 p++;
427 if (!*p) goto done;
428 matches++; /* script matched */
432 if (data->country)
434 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
435 buffer, sizeof(buffer)/sizeof(WCHAR) ))
437 if (strcmpiW( buffer, data->country )) goto done;
438 matches++; /* country name matched */
441 else /* match default language */
443 LANGID def_lang = data->script ? lang : MAKELANGID( PRIMARYLANGID(lang), LANG_NEUTRAL );
444 if (lang == get_default_sublang( def_lang )) matches++;
447 if (data->codepage)
449 UINT unix_cp;
450 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
451 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
453 if (unix_cp == data->codepage) matches++;
457 /* FIXME: check sort order */
459 done:
460 if (matches > data->matches)
462 data->lcid = lcid;
463 data->matches = matches;
465 return (data->matches < 4); /* no need to continue for perfect match */
469 /***********************************************************************
470 * parse_locale_name
472 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
473 * Unix format is: lang[_country][.charset][@modifier]
474 * Windows format is: lang[-script][-country][_modifier]
476 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
478 static const WCHAR sepW[] = {'-','_','.','@',0};
479 static const WCHAR winsepW[] = {'-','_',0};
480 static const WCHAR posixW[] = {'P','O','S','I','X',0};
481 static const WCHAR cW[] = {'C',0};
482 static const WCHAR latinW[] = {'l','a','t','i','n',0};
483 static const WCHAR latnW[] = {'-','L','a','t','n',0};
484 WCHAR *p;
486 TRACE("%s\n", debugstr_w(str));
488 name->country = name->charset = name->script = name->modifier = NULL;
489 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
490 name->matches = 0;
491 name->codepage = 0;
492 name->win_name[0] = 0;
493 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
495 if (!*name->lang)
497 name->lcid = LOCALE_INVARIANT;
498 name->matches = 4;
499 return;
502 if (!(p = strpbrkW( name->lang, sepW )))
504 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
506 name->matches = 4; /* perfect match for default English lcid */
507 return;
509 strcpyW( name->win_name, name->lang );
511 else if (*p == '-') /* Windows format */
513 strcpyW( name->win_name, name->lang );
514 *p++ = 0;
515 name->country = p;
516 if ((p = strpbrkW( p, winsepW )) && *p == '-')
518 *p++ = 0;
519 name->script = name->country;
520 name->country = p;
521 p = strpbrkW( p, winsepW );
523 if (p)
525 *p++ = 0;
526 name->modifier = p;
528 /* second value can be script or country, check length to resolve the ambiguity */
529 if (!name->script && strlenW( name->country ) == 4)
531 name->script = name->country;
532 name->country = NULL;
535 else /* Unix format */
537 if (*p == '_')
539 *p++ = 0;
540 name->country = p;
541 p = strpbrkW( p, sepW + 2 );
543 if (p && *p == '.')
545 *p++ = 0;
546 name->charset = p;
547 p = strchrW( p, '@' );
549 if (p)
551 *p++ = 0;
552 name->modifier = p;
555 if (name->charset)
556 name->codepage = find_charset( name->charset );
558 /* rebuild a Windows name if possible */
560 if (name->charset) goto done; /* can't specify charset in Windows format */
561 if (name->modifier && strcmpW( name->modifier, latinW ))
562 goto done; /* only Latn script supported for now */
563 strcpyW( name->win_name, name->lang );
564 if (name->modifier) strcatW( name->win_name, latnW );
565 if (name->country)
567 p = name->win_name + strlenW(name->win_name);
568 *p++ = '-';
569 strcpyW( p, name->country );
572 done:
573 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
574 find_locale_id_callback, (LPARAM)name );
578 /***********************************************************************
579 * convert_default_lcid
581 * Get the default LCID to use for a given lctype in GetLocaleInfo.
583 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
585 if (lcid == LOCALE_SYSTEM_DEFAULT ||
586 lcid == LOCALE_USER_DEFAULT ||
587 lcid == LOCALE_NEUTRAL)
589 LCID default_id = 0;
591 switch(lctype & 0xffff)
593 case LOCALE_SSORTNAME:
594 default_id = lcid_LC_COLLATE;
595 break;
597 case LOCALE_FONTSIGNATURE:
598 case LOCALE_IDEFAULTANSICODEPAGE:
599 case LOCALE_IDEFAULTCODEPAGE:
600 case LOCALE_IDEFAULTEBCDICCODEPAGE:
601 case LOCALE_IDEFAULTMACCODEPAGE:
602 case LOCALE_IDEFAULTUNIXCODEPAGE:
603 default_id = lcid_LC_CTYPE;
604 break;
606 case LOCALE_ICURRDIGITS:
607 case LOCALE_ICURRENCY:
608 case LOCALE_IINTLCURRDIGITS:
609 case LOCALE_INEGCURR:
610 case LOCALE_INEGSEPBYSPACE:
611 case LOCALE_INEGSIGNPOSN:
612 case LOCALE_INEGSYMPRECEDES:
613 case LOCALE_IPOSSEPBYSPACE:
614 case LOCALE_IPOSSIGNPOSN:
615 case LOCALE_IPOSSYMPRECEDES:
616 case LOCALE_SCURRENCY:
617 case LOCALE_SINTLSYMBOL:
618 case LOCALE_SMONDECIMALSEP:
619 case LOCALE_SMONGROUPING:
620 case LOCALE_SMONTHOUSANDSEP:
621 case LOCALE_SNATIVECURRNAME:
622 default_id = lcid_LC_MONETARY;
623 break;
625 case LOCALE_IDIGITS:
626 case LOCALE_IDIGITSUBSTITUTION:
627 case LOCALE_ILZERO:
628 case LOCALE_INEGNUMBER:
629 case LOCALE_SDECIMAL:
630 case LOCALE_SGROUPING:
631 case LOCALE_SNAN:
632 case LOCALE_SNATIVEDIGITS:
633 case LOCALE_SNEGATIVESIGN:
634 case LOCALE_SNEGINFINITY:
635 case LOCALE_SPOSINFINITY:
636 case LOCALE_SPOSITIVESIGN:
637 case LOCALE_STHOUSAND:
638 default_id = lcid_LC_NUMERIC;
639 break;
641 case LOCALE_ICALENDARTYPE:
642 case LOCALE_ICENTURY:
643 case LOCALE_IDATE:
644 case LOCALE_IDAYLZERO:
645 case LOCALE_IFIRSTDAYOFWEEK:
646 case LOCALE_IFIRSTWEEKOFYEAR:
647 case LOCALE_ILDATE:
648 case LOCALE_IMONLZERO:
649 case LOCALE_IOPTIONALCALENDAR:
650 case LOCALE_ITIME:
651 case LOCALE_ITIMEMARKPOSN:
652 case LOCALE_ITLZERO:
653 case LOCALE_S1159:
654 case LOCALE_S2359:
655 case LOCALE_SABBREVDAYNAME1:
656 case LOCALE_SABBREVDAYNAME2:
657 case LOCALE_SABBREVDAYNAME3:
658 case LOCALE_SABBREVDAYNAME4:
659 case LOCALE_SABBREVDAYNAME5:
660 case LOCALE_SABBREVDAYNAME6:
661 case LOCALE_SABBREVDAYNAME7:
662 case LOCALE_SABBREVMONTHNAME1:
663 case LOCALE_SABBREVMONTHNAME2:
664 case LOCALE_SABBREVMONTHNAME3:
665 case LOCALE_SABBREVMONTHNAME4:
666 case LOCALE_SABBREVMONTHNAME5:
667 case LOCALE_SABBREVMONTHNAME6:
668 case LOCALE_SABBREVMONTHNAME7:
669 case LOCALE_SABBREVMONTHNAME8:
670 case LOCALE_SABBREVMONTHNAME9:
671 case LOCALE_SABBREVMONTHNAME10:
672 case LOCALE_SABBREVMONTHNAME11:
673 case LOCALE_SABBREVMONTHNAME12:
674 case LOCALE_SABBREVMONTHNAME13:
675 case LOCALE_SDATE:
676 case LOCALE_SDAYNAME1:
677 case LOCALE_SDAYNAME2:
678 case LOCALE_SDAYNAME3:
679 case LOCALE_SDAYNAME4:
680 case LOCALE_SDAYNAME5:
681 case LOCALE_SDAYNAME6:
682 case LOCALE_SDAYNAME7:
683 case LOCALE_SDURATION:
684 case LOCALE_SLONGDATE:
685 case LOCALE_SMONTHNAME1:
686 case LOCALE_SMONTHNAME2:
687 case LOCALE_SMONTHNAME3:
688 case LOCALE_SMONTHNAME4:
689 case LOCALE_SMONTHNAME5:
690 case LOCALE_SMONTHNAME6:
691 case LOCALE_SMONTHNAME7:
692 case LOCALE_SMONTHNAME8:
693 case LOCALE_SMONTHNAME9:
694 case LOCALE_SMONTHNAME10:
695 case LOCALE_SMONTHNAME11:
696 case LOCALE_SMONTHNAME12:
697 case LOCALE_SMONTHNAME13:
698 case LOCALE_SSHORTDATE:
699 case LOCALE_SSHORTESTDAYNAME1:
700 case LOCALE_SSHORTESTDAYNAME2:
701 case LOCALE_SSHORTESTDAYNAME3:
702 case LOCALE_SSHORTESTDAYNAME4:
703 case LOCALE_SSHORTESTDAYNAME5:
704 case LOCALE_SSHORTESTDAYNAME6:
705 case LOCALE_SSHORTESTDAYNAME7:
706 case LOCALE_STIME:
707 case LOCALE_STIMEFORMAT:
708 case LOCALE_SYEARMONTH:
709 default_id = lcid_LC_TIME;
710 break;
712 case LOCALE_IPAPERSIZE:
713 default_id = lcid_LC_PAPER;
714 break;
716 case LOCALE_IMEASURE:
717 default_id = lcid_LC_MEASUREMENT;
718 break;
720 case LOCALE_ICOUNTRY:
721 default_id = lcid_LC_TELEPHONE;
722 break;
724 if (default_id) lcid = default_id;
726 return ConvertDefaultLocale( lcid );
729 /***********************************************************************
730 * is_genitive_name_supported
732 * Determine could LCTYPE basically support genitive name form or not.
734 static BOOL is_genitive_name_supported( LCTYPE lctype )
736 switch(lctype & 0xffff)
738 case LOCALE_SMONTHNAME1:
739 case LOCALE_SMONTHNAME2:
740 case LOCALE_SMONTHNAME3:
741 case LOCALE_SMONTHNAME4:
742 case LOCALE_SMONTHNAME5:
743 case LOCALE_SMONTHNAME6:
744 case LOCALE_SMONTHNAME7:
745 case LOCALE_SMONTHNAME8:
746 case LOCALE_SMONTHNAME9:
747 case LOCALE_SMONTHNAME10:
748 case LOCALE_SMONTHNAME11:
749 case LOCALE_SMONTHNAME12:
750 case LOCALE_SMONTHNAME13:
751 return TRUE;
752 default:
753 return FALSE;
757 /***********************************************************************
758 * create_registry_key
760 * Create the Control Panel\\International registry key.
762 static inline HANDLE create_registry_key(void)
764 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
765 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
766 OBJECT_ATTRIBUTES attr;
767 UNICODE_STRING nameW;
768 HANDLE cpl_key, hkey = 0;
770 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
772 attr.Length = sizeof(attr);
773 attr.RootDirectory = hkey;
774 attr.ObjectName = &nameW;
775 attr.Attributes = 0;
776 attr.SecurityDescriptor = NULL;
777 attr.SecurityQualityOfService = NULL;
778 RtlInitUnicodeString( &nameW, cplW );
780 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
782 NtClose( attr.RootDirectory );
783 attr.RootDirectory = cpl_key;
784 RtlInitUnicodeString( &nameW, intlW );
785 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
787 NtClose( attr.RootDirectory );
788 return hkey;
792 /* update the registry settings for a given locale parameter */
793 /* return TRUE if an update was needed */
794 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
795 const LCTYPE *values, UINT nb_values )
797 static const WCHAR formatW[] = { '%','0','8','x',0 };
798 WCHAR bufferW[40];
799 UNICODE_STRING nameW;
800 DWORD count, i;
802 RtlInitUnicodeString( &nameW, name );
803 count = sizeof(bufferW);
804 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
806 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
807 LPCWSTR text = (LPCWSTR)info->Data;
809 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
810 TRACE( "updating registry, locale %s changed %s -> %08x\n",
811 debugstr_w(name), debugstr_w(text), lcid );
813 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
814 sprintfW( bufferW, formatW, lcid );
815 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
817 for (i = 0; i < nb_values; i++)
819 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
820 sizeof(bufferW)/sizeof(WCHAR) );
821 SetLocaleInfoW( lcid, values[i], bufferW );
823 return TRUE;
827 /***********************************************************************
828 * LOCALE_InitRegistry
830 * Update registry contents on startup if the user locale has changed.
831 * This simulates the action of the Windows control panel.
833 void LOCALE_InitRegistry(void)
835 static const WCHAR acpW[] = {'A','C','P',0};
836 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
837 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
838 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
839 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
840 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
841 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
842 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
843 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
844 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
845 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
846 static const struct
848 LPCWSTR name;
849 USHORT value;
850 } update_cp_values[] = {
851 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
852 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
853 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
855 static const LCTYPE lc_messages_values[] = {
856 LOCALE_SABBREVLANGNAME,
857 LOCALE_SCOUNTRY,
858 LOCALE_SLIST };
859 static const LCTYPE lc_monetary_values[] = {
860 LOCALE_SCURRENCY,
861 LOCALE_ICURRENCY,
862 LOCALE_INEGCURR,
863 LOCALE_ICURRDIGITS,
864 LOCALE_ILZERO,
865 LOCALE_SMONDECIMALSEP,
866 LOCALE_SMONGROUPING,
867 LOCALE_SMONTHOUSANDSEP };
868 static const LCTYPE lc_numeric_values[] = {
869 LOCALE_SDECIMAL,
870 LOCALE_STHOUSAND,
871 LOCALE_IDIGITS,
872 LOCALE_IDIGITSUBSTITUTION,
873 LOCALE_SNATIVEDIGITS,
874 LOCALE_INEGNUMBER,
875 LOCALE_SNEGATIVESIGN,
876 LOCALE_SPOSITIVESIGN,
877 LOCALE_SGROUPING };
878 static const LCTYPE lc_time_values[] = {
879 LOCALE_S1159,
880 LOCALE_S2359,
881 LOCALE_STIME,
882 LOCALE_ITIME,
883 LOCALE_ITLZERO,
884 LOCALE_SSHORTDATE,
885 LOCALE_SLONGDATE,
886 LOCALE_SDATE,
887 LOCALE_ITIMEMARKPOSN,
888 LOCALE_ICALENDARTYPE,
889 LOCALE_IFIRSTDAYOFWEEK,
890 LOCALE_IFIRSTWEEKOFYEAR,
891 LOCALE_STIMEFORMAT,
892 LOCALE_SYEARMONTH,
893 LOCALE_IDATE };
894 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
895 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
896 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
898 UNICODE_STRING nameW;
899 WCHAR bufferW[80];
900 DWORD count, i;
901 HANDLE hkey;
902 LCID lcid = GetUserDefaultLCID();
904 if (!(hkey = create_registry_key()))
905 return; /* don't do anything if we can't create the registry key */
907 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
908 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
909 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
910 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
911 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
912 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
913 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
914 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
915 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
916 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
917 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
918 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
919 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
920 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
922 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
924 static const WCHAR codepageW[] =
925 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
926 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
927 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
929 OBJECT_ATTRIBUTES attr;
930 HANDLE nls_key;
931 DWORD len = 14;
933 RtlInitUnicodeString( &nameW, codepageW );
934 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
935 while (codepageW[len])
937 nameW.Length = len * sizeof(WCHAR);
938 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
939 NtClose( nls_key );
940 len++;
941 while (codepageW[len] && codepageW[len] != '\\') len++;
943 nameW.Length = len * sizeof(WCHAR);
944 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
946 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
948 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
949 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
950 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
951 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
953 NtClose( nls_key );
957 NtClose( hkey );
961 #ifdef __APPLE__
962 /***********************************************************************
963 * get_mac_locale
965 * Return a locale identifier string reflecting the Mac locale, in a form
966 * that parse_locale_name() will understand. So, strip out unusual
967 * things like script, variant, etc. Or, rather, just construct it as
968 * <lang>[_<country>].UTF-8.
970 static const char* get_mac_locale(void)
972 static char mac_locale[50];
974 if (!mac_locale[0])
976 CFLocaleRef locale = CFLocaleCopyCurrent();
977 CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
978 CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
979 CFStringRef locale_string;
981 if (country)
982 locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
983 else
984 locale_string = CFStringCreateCopy(NULL, lang);
986 CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
987 strcat(mac_locale, ".UTF-8");
989 CFRelease(locale);
990 CFRelease(locale_string);
993 return mac_locale;
997 /***********************************************************************
998 * has_env
1000 static BOOL has_env(const char* name)
1002 const char* value = getenv( name );
1003 return value && value[0];
1005 #endif
1008 /***********************************************************************
1009 * get_locale
1011 * Get the locale identifier for a given category. On most platforms,
1012 * this is just a thin wrapper around setlocale(). On OS X, though, it
1013 * is common for the Mac locale settings to not be supported by the C
1014 * library. So, we sometimes override the result with the Mac locale.
1016 static const char* get_locale(int category, const char* category_name)
1018 const char* ret = setlocale(category, NULL);
1020 #ifdef __ANDROID__
1021 if (!strcmp(ret, "C"))
1023 ret = getenv( category_name );
1024 if (!ret || !ret[0]) ret = getenv( "LC_ALL" );
1025 if (!ret || !ret[0]) ret = "C";
1027 #endif
1029 #ifdef __APPLE__
1030 /* If LC_ALL is set, respect it as a user override.
1031 If LC_* is set, respect it as a user override, except if it's LC_CTYPE
1032 and equal to UTF-8. That's because, when the Mac locale isn't supported
1033 by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
1034 parse_locale_name() doesn't handle that properly, so we override that
1035 with the Mac locale (which uses UTF-8 for the charset, anyway).
1036 Otherwise:
1037 For LC_MESSAGES, we override the C library because the user language
1038 setting is separate from the locale setting on which LANG was based.
1039 If the C library didn't get anything better from LANG than C or POSIX,
1040 override that. That probably means the Mac locale isn't supported by
1041 the C library. */
1042 if (!has_env( "LC_ALL" ) &&
1043 ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
1044 (!has_env( category_name ) &&
1045 (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
1047 const char* override = get_mac_locale();
1049 if (category == LC_MESSAGES)
1051 /* Retrieve the preferred language as chosen in System Preferences. */
1052 static char messages_locale[50];
1054 if (!messages_locale[0])
1056 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1057 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1059 CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1060 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1061 if (components)
1063 CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1064 CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1065 CFLocaleRef locale = NULL;
1066 CFStringRef locale_string;
1068 if (!country)
1070 locale = CFLocaleCopyCurrent();
1071 country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1074 if (country)
1075 locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1076 else
1077 locale_string = CFStringCreateCopy( NULL, lang );
1078 CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1079 strcat( messages_locale, ".UTF-8" );
1081 CFRelease( locale_string );
1082 if (locale) CFRelease( locale );
1083 CFRelease( components );
1086 if (preferred_langs)
1087 CFRelease( preferred_langs );
1090 if (messages_locale[0])
1091 override = messages_locale;
1094 TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1095 ret = override;
1097 #endif
1099 return ret;
1103 /***********************************************************************
1104 * setup_unix_locales
1106 static UINT setup_unix_locales(void)
1108 struct locale_name locale_name;
1109 WCHAR buffer[128], ctype_buff[128];
1110 const char *locale;
1111 UINT unix_cp = 0;
1113 if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1115 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
1116 parse_locale_name( ctype_buff, &locale_name );
1117 lcid_LC_CTYPE = locale_name.lcid;
1118 unix_cp = locale_name.codepage;
1120 if (!lcid_LC_CTYPE) /* this one needs a default value */
1121 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
1123 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1124 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
1126 #define GET_UNIX_LOCALE(cat) do \
1127 if ((locale = get_locale( cat, #cat ))) \
1129 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
1130 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1131 else { \
1132 parse_locale_name( buffer, &locale_name ); \
1133 lcid_##cat = locale_name.lcid; \
1134 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
1135 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1137 } while (0)
1139 GET_UNIX_LOCALE( LC_COLLATE );
1140 GET_UNIX_LOCALE( LC_MESSAGES );
1141 GET_UNIX_LOCALE( LC_MONETARY );
1142 GET_UNIX_LOCALE( LC_NUMERIC );
1143 GET_UNIX_LOCALE( LC_TIME );
1144 #ifdef LC_PAPER
1145 GET_UNIX_LOCALE( LC_PAPER );
1146 #endif
1147 #ifdef LC_MEASUREMENT
1148 GET_UNIX_LOCALE( LC_MEASUREMENT );
1149 #endif
1150 #ifdef LC_TELEPHONE
1151 GET_UNIX_LOCALE( LC_TELEPHONE );
1152 #endif
1154 #undef GET_UNIX_LOCALE
1156 return unix_cp;
1160 /***********************************************************************
1161 * GetUserDefaultLangID (KERNEL32.@)
1163 * Get the default language Id for the current user.
1165 * PARAMS
1166 * None.
1168 * RETURNS
1169 * The current LANGID of the default language for the current user.
1171 LANGID WINAPI GetUserDefaultLangID(void)
1173 return LANGIDFROMLCID(GetUserDefaultLCID());
1177 /***********************************************************************
1178 * GetSystemDefaultLangID (KERNEL32.@)
1180 * Get the default language Id for the system.
1182 * PARAMS
1183 * None.
1185 * RETURNS
1186 * The current LANGID of the default language for the system.
1188 LANGID WINAPI GetSystemDefaultLangID(void)
1190 return LANGIDFROMLCID(GetSystemDefaultLCID());
1194 /***********************************************************************
1195 * GetUserDefaultLCID (KERNEL32.@)
1197 * Get the default locale Id for the current user.
1199 * PARAMS
1200 * None.
1202 * RETURNS
1203 * The current LCID of the default locale for the current user.
1205 LCID WINAPI GetUserDefaultLCID(void)
1207 LCID lcid;
1208 NtQueryDefaultLocale( TRUE, &lcid );
1209 return lcid;
1213 /***********************************************************************
1214 * GetSystemDefaultLCID (KERNEL32.@)
1216 * Get the default locale Id for the system.
1218 * PARAMS
1219 * None.
1221 * RETURNS
1222 * The current LCID of the default locale for the system.
1224 LCID WINAPI GetSystemDefaultLCID(void)
1226 LCID lcid;
1227 NtQueryDefaultLocale( FALSE, &lcid );
1228 return lcid;
1231 /***********************************************************************
1232 * GetSystemDefaultLocaleName (KERNEL32.@)
1234 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1236 LCID lcid = GetSystemDefaultLCID();
1237 return LCIDToLocaleName(lcid, localename, len, 0);
1240 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1242 LCTYPE type;
1243 int lsize;
1245 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1247 if (flags & MUI_LANGUAGE_ID)
1248 type = LOCALE_ILANGUAGE;
1249 else
1250 type = LOCALE_SNAME;
1252 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1253 if (!lsize)
1255 /* keep last error from callee */
1256 return FALSE;
1258 lsize++;
1259 if (!*size)
1261 *size = lsize;
1262 *count = 1;
1263 return TRUE;
1266 if (lsize > *size)
1268 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1269 return FALSE;
1272 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1274 /* keep last error from callee */
1275 return FALSE;
1278 buffer[lsize-1] = 0;
1279 *size = lsize;
1280 *count = 1;
1281 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1282 return TRUE;
1286 /***********************************************************************
1287 * GetSystemPreferredUILanguages (KERNEL32.@)
1289 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1291 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1293 SetLastError(ERROR_INVALID_PARAMETER);
1294 return FALSE;
1296 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1298 SetLastError(ERROR_INVALID_PARAMETER);
1299 return FALSE;
1301 if (*size && !buffer)
1303 SetLastError(ERROR_INVALID_PARAMETER);
1304 return FALSE;
1307 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1310 /***********************************************************************
1311 * SetThreadPreferredUILanguages (KERNEL32.@)
1313 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1315 FIXME( "%u, %p, %p\n", flags, buffer, count );
1316 return TRUE;
1319 /***********************************************************************
1320 * GetThreadPreferredUILanguages (KERNEL32.@)
1322 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1324 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1325 return get_dummy_preferred_ui_language( flags, count, buf, size );
1328 /******************************************************************************
1329 * GetUserPreferredUILanguages (KERNEL32.@)
1331 BOOL WINAPI GetUserPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1333 TRACE( "%u %p %p %p\n", flags, count, buffer, size );
1335 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID))
1337 SetLastError(ERROR_INVALID_PARAMETER);
1338 return FALSE;
1340 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1342 SetLastError(ERROR_INVALID_PARAMETER);
1343 return FALSE;
1345 if (*size && !buffer)
1347 SetLastError(ERROR_INVALID_PARAMETER);
1348 return FALSE;
1351 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1354 /***********************************************************************
1355 * GetUserDefaultUILanguage (KERNEL32.@)
1357 * Get the default user interface language Id for the current user.
1359 * PARAMS
1360 * None.
1362 * RETURNS
1363 * The current LANGID of the default UI language for the current user.
1365 LANGID WINAPI GetUserDefaultUILanguage(void)
1367 LANGID lang;
1368 NtQueryDefaultUILanguage( &lang );
1369 return lang;
1373 /***********************************************************************
1374 * GetSystemDefaultUILanguage (KERNEL32.@)
1376 * Get the default user interface language Id for the system.
1378 * PARAMS
1379 * None.
1381 * RETURNS
1382 * The current LANGID of the default UI language for the system. This is
1383 * typically the same language used during the installation process.
1385 LANGID WINAPI GetSystemDefaultUILanguage(void)
1387 LANGID lang;
1388 NtQueryInstallUILanguage( &lang );
1389 return lang;
1393 /***********************************************************************
1394 * LocaleNameToLCID (KERNEL32.@)
1396 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1398 struct locale_name locale_name;
1399 static int once;
1401 if (flags && !once++)
1402 FIXME( "unsupported flags %x\n", flags );
1404 if (name == LOCALE_NAME_USER_DEFAULT)
1405 return GetUserDefaultLCID();
1407 /* string parsing */
1408 parse_locale_name( name, &locale_name );
1410 TRACE( "found lcid %x for %s, matches %d\n",
1411 locale_name.lcid, debugstr_w(name), locale_name.matches );
1413 if (!locale_name.matches)
1415 SetLastError(ERROR_INVALID_PARAMETER);
1416 return 0;
1419 if (locale_name.matches == 1)
1420 WARN( "locale %s not recognized, defaulting to %s\n",
1421 debugstr_w(name), debugstr_w(locale_name.lang) );
1423 return locale_name.lcid;
1427 /***********************************************************************
1428 * LCIDToLocaleName (KERNEL32.@)
1430 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1432 static int once;
1433 if (flags && !once++) FIXME( "unsupported flags %x\n", flags );
1435 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1439 /******************************************************************************
1440 * get_locale_registry_value
1442 * Gets the registry value name and cache for a given lctype.
1444 static struct registry_value *get_locale_registry_value( DWORD lctype )
1446 int i;
1447 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1448 if (registry_values[i].lctype == lctype)
1449 return &registry_values[i];
1450 return NULL;
1454 /******************************************************************************
1455 * get_registry_locale_info
1457 * Retrieve user-modified locale info from the registry.
1458 * Return length, 0 on error, -1 if not found.
1460 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1462 DWORD size;
1463 INT ret;
1464 HANDLE hkey;
1465 NTSTATUS status;
1466 UNICODE_STRING nameW;
1467 KEY_VALUE_PARTIAL_INFORMATION *info;
1468 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1470 RtlEnterCriticalSection( &cache_section );
1472 if (!registry_value->cached_value)
1474 if (!(hkey = create_registry_key()))
1476 RtlLeaveCriticalSection( &cache_section );
1477 return -1;
1480 RtlInitUnicodeString( &nameW, registry_value->name );
1481 size = info_size + len * sizeof(WCHAR);
1483 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1485 NtClose( hkey );
1486 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1487 RtlLeaveCriticalSection( &cache_section );
1488 return 0;
1491 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1493 /* try again with a bigger buffer when we have to return the correct size */
1494 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1496 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1497 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1499 info = new_info;
1500 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1504 NtClose( hkey );
1506 if (!status)
1508 INT length = (size - info_size) / sizeof(WCHAR);
1509 LPWSTR cached_value;
1511 if (!length || ((WCHAR *)&info->Data)[length-1])
1512 length++;
1514 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1516 if (!cached_value)
1518 HeapFree( GetProcessHeap(), 0, info );
1519 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1520 RtlLeaveCriticalSection( &cache_section );
1521 return 0;
1524 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1525 cached_value[length-1] = 0;
1526 HeapFree( GetProcessHeap(), 0, info );
1527 registry_value->cached_value = cached_value;
1529 else
1531 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1533 ret = (size - info_size) / sizeof(WCHAR);
1535 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1537 ret = -1;
1539 else
1541 SetLastError( RtlNtStatusToDosError(status) );
1542 ret = 0;
1544 HeapFree( GetProcessHeap(), 0, info );
1545 RtlLeaveCriticalSection( &cache_section );
1546 return ret;
1550 ret = lstrlenW( registry_value->cached_value ) + 1;
1552 if (buffer)
1554 if (ret > len)
1556 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1557 ret = 0;
1559 else
1561 lstrcpyW( buffer, registry_value->cached_value );
1565 RtlLeaveCriticalSection( &cache_section );
1567 return ret;
1571 /******************************************************************************
1572 * GetLocaleInfoA (KERNEL32.@)
1574 * Get information about an aspect of a locale.
1576 * PARAMS
1577 * lcid [I] LCID of the locale
1578 * lctype [I] LCTYPE_ flags from "winnls.h"
1579 * buffer [O] Destination for the information
1580 * len [I] Length of buffer in characters
1582 * RETURNS
1583 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1584 * with the information.
1585 * Failure: 0. Use GetLastError() to determine the cause.
1587 * NOTES
1588 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1589 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1590 * which is a bit string.
1592 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1594 WCHAR *bufferW;
1595 INT lenW, ret;
1597 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1599 if (len < 0 || (len && !buffer))
1601 SetLastError( ERROR_INVALID_PARAMETER );
1602 return 0;
1604 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1605 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1607 SetLastError( ERROR_INVALID_FLAGS );
1608 return 0;
1611 if (!len) buffer = NULL;
1613 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1615 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1617 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1618 return 0;
1620 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1622 if ((lctype & LOCALE_RETURN_NUMBER) ||
1623 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1625 /* it's not an ASCII string, just bytes */
1626 ret *= sizeof(WCHAR);
1627 if (buffer)
1629 if (ret <= len) memcpy( buffer, bufferW, ret );
1630 else
1632 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1633 ret = 0;
1637 else
1639 UINT codepage = CP_ACP;
1640 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1641 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1644 HeapFree( GetProcessHeap(), 0, bufferW );
1645 return ret;
1648 static int get_value_base_by_lctype( LCTYPE lctype )
1650 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1653 /******************************************************************************
1654 * GetLocaleInfoW (KERNEL32.@)
1656 * See GetLocaleInfoA.
1658 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1660 LANGID lang_id;
1661 HRSRC hrsrc;
1662 HGLOBAL hmem;
1663 INT ret;
1664 UINT lcflags;
1665 const WCHAR *p;
1666 unsigned int i;
1668 if (len < 0 || (len && !buffer))
1670 SetLastError( ERROR_INVALID_PARAMETER );
1671 return 0;
1673 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1674 !is_genitive_name_supported( lctype ))
1676 SetLastError( ERROR_INVALID_FLAGS );
1677 return 0;
1680 if (!len) buffer = NULL;
1682 lcid = convert_default_lcid( lcid, lctype );
1684 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1685 lctype &= 0xffff;
1687 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1689 /* first check for overrides in the registry */
1691 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1692 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1694 struct registry_value *value = get_locale_registry_value(lctype);
1696 if (value)
1698 if (lcflags & LOCALE_RETURN_NUMBER)
1700 WCHAR tmp[16];
1701 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1702 if (ret > 0)
1704 WCHAR *end;
1705 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1706 if (*end) /* invalid number */
1708 SetLastError( ERROR_INVALID_FLAGS );
1709 return 0;
1711 ret = sizeof(UINT)/sizeof(WCHAR);
1712 if (!buffer) return ret;
1713 if (ret > len)
1715 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1716 return 0;
1718 memcpy( buffer, &number, sizeof(number) );
1721 else ret = get_registry_locale_info( value, buffer, len );
1723 if (ret != -1) return ret;
1727 /* now load it from kernel resources */
1729 lang_id = LANGIDFROMLCID( lcid );
1731 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1732 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL) lang_id = get_default_sublang( lang_id );
1734 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1735 ULongToPtr((lctype >> 4) + 1), lang_id )))
1737 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1738 return 0;
1740 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1741 return 0;
1743 p = LockResource( hmem );
1744 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1746 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1747 else if (is_genitive_name_supported( lctype ) && *p)
1749 /* genitive form's stored after a null separator from a nominative */
1750 for (i = 1; i <= *p; i++) if (!p[i]) break;
1752 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1754 ret = *p - i + 1;
1755 p += i;
1757 else ret = i;
1759 else
1760 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1762 if (!buffer) return ret;
1764 if (ret > len)
1766 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1767 return 0;
1770 if (lcflags & LOCALE_RETURN_NUMBER)
1772 UINT number;
1773 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1774 if (!tmp) return 0;
1775 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1776 tmp[*p] = 0;
1777 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1778 if (!*end)
1779 memcpy( buffer, &number, sizeof(number) );
1780 else /* invalid number */
1782 SetLastError( ERROR_INVALID_FLAGS );
1783 ret = 0;
1785 HeapFree( GetProcessHeap(), 0, tmp );
1787 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1788 lcid, lctype, buffer, len, number );
1790 else
1792 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1793 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1795 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1796 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1798 return ret;
1801 /******************************************************************************
1802 * GetLocaleInfoEx (KERNEL32.@)
1804 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1806 LCID lcid = LocaleNameToLCID(locale, 0);
1808 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1810 if (!lcid) return 0;
1812 /* special handling for neutral locale names */
1813 if (locale && strlenW(locale) == 2)
1815 switch (info)
1817 case LOCALE_SNAME:
1818 if (len && len < 3)
1820 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1821 return 0;
1823 if (len) strcpyW(buffer, locale);
1824 return 3;
1825 case LOCALE_SPARENT:
1826 if (len) buffer[0] = 0;
1827 return 1;
1831 return GetLocaleInfoW(lcid, info, buffer, len);
1834 /******************************************************************************
1835 * SetLocaleInfoA [KERNEL32.@]
1837 * Set information about an aspect of a locale.
1839 * PARAMS
1840 * lcid [I] LCID of the locale
1841 * lctype [I] LCTYPE_ flags from "winnls.h"
1842 * data [I] Information to set
1844 * RETURNS
1845 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1846 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1847 * Failure: FALSE. Use GetLastError() to determine the cause.
1849 * NOTES
1850 * - Values are only be set for the current user locale; the system locale
1851 * settings cannot be changed.
1852 * - Any settings changed by this call are lost when the locale is changed by
1853 * the control panel (in Wine, this happens every time you change LANG).
1854 * - The native implementation of this function does not check that lcid matches
1855 * the current user locale, and simply sets the new values. Wine warns you in
1856 * this case, but behaves the same.
1858 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1860 UINT codepage = CP_ACP;
1861 WCHAR *strW;
1862 DWORD len;
1863 BOOL ret;
1865 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1867 if (!data)
1869 SetLastError( ERROR_INVALID_PARAMETER );
1870 return FALSE;
1872 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1873 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1875 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1876 return FALSE;
1878 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1879 ret = SetLocaleInfoW( lcid, lctype, strW );
1880 HeapFree( GetProcessHeap(), 0, strW );
1881 return ret;
1885 /******************************************************************************
1886 * SetLocaleInfoW (KERNEL32.@)
1888 * See SetLocaleInfoA.
1890 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1892 struct registry_value *value;
1893 static const WCHAR intlW[] = {'i','n','t','l',0 };
1894 UNICODE_STRING valueW;
1895 NTSTATUS status;
1896 HANDLE hkey;
1898 lctype &= 0xffff;
1899 value = get_locale_registry_value( lctype );
1901 if (!data || !value)
1903 SetLastError( ERROR_INVALID_PARAMETER );
1904 return FALSE;
1907 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1909 SetLastError( ERROR_INVALID_FLAGS );
1910 return FALSE;
1913 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1915 /* FIXME: should check that data to set is sane */
1917 /* FIXME: profile functions should map to registry */
1918 WriteProfileStringW( intlW, value->name, data );
1920 if (!(hkey = create_registry_key())) return FALSE;
1921 RtlInitUnicodeString( &valueW, value->name );
1922 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1924 RtlEnterCriticalSection( &cache_section );
1925 HeapFree( GetProcessHeap(), 0, value->cached_value );
1926 value->cached_value = NULL;
1927 RtlLeaveCriticalSection( &cache_section );
1929 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1931 /* Set I-value from S value */
1932 WCHAR *lpD, *lpM, *lpY;
1933 WCHAR szBuff[2];
1935 lpD = strrchrW(data, 'd');
1936 lpM = strrchrW(data, 'M');
1937 lpY = strrchrW(data, 'y');
1939 if (lpD <= lpM)
1941 szBuff[0] = '1'; /* D-M-Y */
1943 else
1945 if (lpY <= lpM)
1946 szBuff[0] = '2'; /* Y-M-D */
1947 else
1948 szBuff[0] = '0'; /* M-D-Y */
1951 szBuff[1] = '\0';
1953 if (lctype == LOCALE_SSHORTDATE)
1954 lctype = LOCALE_IDATE;
1955 else
1956 lctype = LOCALE_ILDATE;
1958 value = get_locale_registry_value( lctype );
1960 WriteProfileStringW( intlW, value->name, szBuff );
1962 RtlInitUnicodeString( &valueW, value->name );
1963 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1965 RtlEnterCriticalSection( &cache_section );
1966 HeapFree( GetProcessHeap(), 0, value->cached_value );
1967 value->cached_value = NULL;
1968 RtlLeaveCriticalSection( &cache_section );
1971 NtClose( hkey );
1973 if (status) SetLastError( RtlNtStatusToDosError(status) );
1974 return !status;
1978 /******************************************************************************
1979 * GetACP (KERNEL32.@)
1981 * Get the current Ansi code page Id for the system.
1983 * PARAMS
1984 * None.
1986 * RETURNS
1987 * The current Ansi code page identifier for the system.
1989 UINT WINAPI GetACP(void)
1991 assert( ansi_cptable );
1992 return ansi_cptable->info.codepage;
1996 /******************************************************************************
1997 * SetCPGlobal (KERNEL32.@)
1999 * Set the current Ansi code page Id for the system.
2001 * PARAMS
2002 * acp [I] code page ID to be the new ACP.
2004 * RETURNS
2005 * The previous ACP.
2007 UINT WINAPI SetCPGlobal( UINT acp )
2009 UINT ret = GetACP();
2010 const union cptable *new_cptable = wine_cp_get_table( acp );
2012 if (new_cptable) ansi_cptable = new_cptable;
2013 return ret;
2017 /***********************************************************************
2018 * GetOEMCP (KERNEL32.@)
2020 * Get the current OEM code page Id for the system.
2022 * PARAMS
2023 * None.
2025 * RETURNS
2026 * The current OEM code page identifier for the system.
2028 UINT WINAPI GetOEMCP(void)
2030 assert( oem_cptable );
2031 return oem_cptable->info.codepage;
2035 /***********************************************************************
2036 * IsValidCodePage (KERNEL32.@)
2038 * Determine if a given code page identifier is valid.
2040 * PARAMS
2041 * codepage [I] Code page Id to verify.
2043 * RETURNS
2044 * TRUE, If codepage is valid and available on the system,
2045 * FALSE otherwise.
2047 BOOL WINAPI IsValidCodePage( UINT codepage )
2049 switch(codepage) {
2050 case CP_UTF7:
2051 case CP_UTF8:
2052 return TRUE;
2053 default:
2054 return wine_cp_get_table( codepage ) != NULL;
2059 /***********************************************************************
2060 * IsDBCSLeadByteEx (KERNEL32.@)
2062 * Determine if a character is a lead byte in a given code page.
2064 * PARAMS
2065 * codepage [I] Code page for the test.
2066 * testchar [I] Character to test
2068 * RETURNS
2069 * TRUE, if testchar is a lead byte in codepage,
2070 * FALSE otherwise.
2072 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
2074 const union cptable *table = get_codepage_table( codepage );
2075 return table && wine_is_dbcs_leadbyte( table, testchar );
2079 /***********************************************************************
2080 * IsDBCSLeadByte (KERNEL32.@)
2081 * IsDBCSLeadByte (KERNEL.207)
2083 * Determine if a character is a lead byte.
2085 * PARAMS
2086 * testchar [I] Character to test
2088 * RETURNS
2089 * TRUE, if testchar is a lead byte in the ANSI code page,
2090 * FALSE otherwise.
2092 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2094 if (!ansi_cptable) return FALSE;
2095 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2099 /***********************************************************************
2100 * GetCPInfo (KERNEL32.@)
2102 * Get information about a code page.
2104 * PARAMS
2105 * codepage [I] Code page number
2106 * cpinfo [O] Destination for code page information
2108 * RETURNS
2109 * Success: TRUE. cpinfo is updated with the information about codepage.
2110 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2112 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2114 const union cptable *table;
2116 if (!cpinfo)
2118 SetLastError( ERROR_INVALID_PARAMETER );
2119 return FALSE;
2122 if (!(table = get_codepage_table( codepage )))
2124 switch(codepage)
2126 case CP_UTF7:
2127 case CP_UTF8:
2128 cpinfo->DefaultChar[0] = 0x3f;
2129 cpinfo->DefaultChar[1] = 0;
2130 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2131 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2132 return TRUE;
2135 SetLastError( ERROR_INVALID_PARAMETER );
2136 return FALSE;
2138 if (table->info.def_char & 0xff00)
2140 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2141 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2143 else
2145 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2146 cpinfo->DefaultChar[1] = 0;
2148 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2149 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2150 else
2151 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2153 return TRUE;
2156 /***********************************************************************
2157 * GetCPInfoExA (KERNEL32.@)
2159 * Get extended information about a code page.
2161 * PARAMS
2162 * codepage [I] Code page number
2163 * dwFlags [I] Reserved, must to 0.
2164 * cpinfo [O] Destination for code page information
2166 * RETURNS
2167 * Success: TRUE. cpinfo is updated with the information about codepage.
2168 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2170 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2172 CPINFOEXW cpinfoW;
2174 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2175 return FALSE;
2177 /* the layout is the same except for CodePageName */
2178 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2179 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2180 return TRUE;
2183 /***********************************************************************
2184 * GetCPInfoExW (KERNEL32.@)
2186 * Unicode version of GetCPInfoExA.
2188 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2190 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2191 return FALSE;
2193 switch(codepage)
2195 case CP_UTF7:
2197 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2199 cpinfo->CodePage = CP_UTF7;
2200 cpinfo->UnicodeDefaultChar = 0x3f;
2201 strcpyW(cpinfo->CodePageName, utf7);
2202 break;
2205 case CP_UTF8:
2207 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2209 cpinfo->CodePage = CP_UTF8;
2210 cpinfo->UnicodeDefaultChar = 0x3f;
2211 strcpyW(cpinfo->CodePageName, utf8);
2212 break;
2215 default:
2217 const union cptable *table = get_codepage_table( codepage );
2219 cpinfo->CodePage = table->info.codepage;
2220 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2221 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2222 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2223 break;
2226 return TRUE;
2229 /***********************************************************************
2230 * EnumSystemCodePagesA (KERNEL32.@)
2232 * Call a user defined function for every code page installed on the system.
2234 * PARAMS
2235 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2236 * flags [I] Reserved, set to 0.
2238 * RETURNS
2239 * TRUE, If all code pages have been enumerated, or
2240 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2242 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2244 const union cptable *table;
2245 char buffer[10];
2246 int index = 0;
2248 for (;;)
2250 if (!(table = wine_cp_enum_table( index++ ))) break;
2251 sprintf( buffer, "%d", table->info.codepage );
2252 if (!lpfnCodePageEnum( buffer )) break;
2254 return TRUE;
2258 /***********************************************************************
2259 * EnumSystemCodePagesW (KERNEL32.@)
2261 * See EnumSystemCodePagesA.
2263 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2265 const union cptable *table;
2266 WCHAR buffer[10], *p;
2267 int page, index = 0;
2269 for (;;)
2271 if (!(table = wine_cp_enum_table( index++ ))) break;
2272 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2273 *--p = 0;
2274 page = table->info.codepage;
2277 *--p = '0' + (page % 10);
2278 page /= 10;
2279 } while( page );
2280 if (!lpfnCodePageEnum( p )) break;
2282 return TRUE;
2286 /***********************************************************************
2287 * utf7_write_w
2289 * Helper for utf7_mbstowcs
2291 * RETURNS
2292 * TRUE on success, FALSE on error
2294 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2296 if (dstlen > 0)
2298 if (*index >= dstlen)
2299 return FALSE;
2301 dst[*index] = character;
2304 (*index)++;
2306 return TRUE;
2309 /***********************************************************************
2310 * utf7_mbstowcs
2312 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2314 * RETURNS
2315 * On success, the number of characters written
2316 * On dst buffer overflow, -1
2318 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2320 static const signed char base64_decoding_table[] =
2322 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2323 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2324 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2325 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2326 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2327 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2328 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2329 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2332 const char *source_end = src + srclen;
2333 int dest_index = 0;
2335 DWORD byte_pair = 0;
2336 short offset = 0;
2338 while (src < source_end)
2340 if (*src == '+')
2342 src++;
2343 if (src >= source_end)
2344 break;
2346 if (*src == '-')
2348 /* just a plus sign escaped as +- */
2349 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2350 return -1;
2351 src++;
2352 continue;
2357 signed char sextet = *src;
2358 if (sextet == '-')
2360 /* skip over the dash and end base64 decoding
2361 * the current, unfinished byte pair is discarded */
2362 src++;
2363 offset = 0;
2364 break;
2366 if (sextet < 0)
2368 /* the next character of src is < 0 and therefore not part of a base64 sequence
2369 * the current, unfinished byte pair is NOT discarded in this case
2370 * this is probably a bug in Windows */
2371 break;
2374 sextet = base64_decoding_table[sextet];
2375 if (sextet == -1)
2377 /* -1 means that the next character of src is not part of a base64 sequence
2378 * in other words, all sextets in this base64 sequence have been processed
2379 * the current, unfinished byte pair is discarded */
2380 offset = 0;
2381 break;
2384 byte_pair = (byte_pair << 6) | sextet;
2385 offset += 6;
2387 if (offset >= 16)
2389 /* this byte pair is done */
2390 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2391 return -1;
2392 offset -= 16;
2395 src++;
2397 while (src < source_end);
2399 else
2401 /* we have to convert to unsigned char in case *src < 0 */
2402 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2403 return -1;
2404 src++;
2408 return dest_index;
2411 /***********************************************************************
2412 * MultiByteToWideChar (KERNEL32.@)
2414 * Convert a multibyte character string into a Unicode string.
2416 * PARAMS
2417 * page [I] Codepage character set to convert from
2418 * flags [I] Character mapping flags
2419 * src [I] Source string buffer
2420 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2421 * dst [O] Destination buffer
2422 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2424 * RETURNS
2425 * Success: If dstlen > 0, the number of characters written to dst.
2426 * If dstlen == 0, the number of characters needed to perform the
2427 * conversion. In both cases the count includes the terminating NUL.
2428 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2429 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2430 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2431 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2432 * possible for src.
2434 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2435 LPWSTR dst, INT dstlen )
2437 const union cptable *table;
2438 int ret;
2440 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2442 SetLastError( ERROR_INVALID_PARAMETER );
2443 return 0;
2446 if (srclen < 0) srclen = strlen(src) + 1;
2448 switch(page)
2450 case CP_SYMBOL:
2451 if (flags)
2453 SetLastError( ERROR_INVALID_FLAGS );
2454 return 0;
2456 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2457 break;
2458 case CP_UTF7:
2459 if (flags)
2461 SetLastError( ERROR_INVALID_FLAGS );
2462 return 0;
2464 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2465 break;
2466 case CP_UNIXCP:
2467 if (unix_cptable)
2469 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2470 break;
2472 #ifdef __APPLE__
2473 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2474 #endif
2475 /* fall through */
2476 case CP_UTF8:
2477 if (flags & ~MB_FLAGSMASK)
2479 SetLastError( ERROR_INVALID_FLAGS );
2480 return 0;
2482 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2483 break;
2484 default:
2485 if (!(table = get_codepage_table( page )))
2487 SetLastError( ERROR_INVALID_PARAMETER );
2488 return 0;
2490 if (flags & ~MB_FLAGSMASK)
2492 SetLastError( ERROR_INVALID_FLAGS );
2493 return 0;
2495 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
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_an(src, srclen), debugstr_wn(dst, ret), ret);
2510 return ret;
2514 /***********************************************************************
2515 * utf7_can_directly_encode
2517 * Helper for utf7_wcstombs
2519 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2521 static const BOOL directly_encodable_table[] =
2523 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2524 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2525 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2526 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2527 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2528 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2529 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2530 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2533 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2536 /***********************************************************************
2537 * utf7_write_c
2539 * Helper for utf7_wcstombs
2541 * RETURNS
2542 * TRUE on success, FALSE on error
2544 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2546 if (dstlen > 0)
2548 if (*index >= dstlen)
2549 return FALSE;
2551 dst[*index] = character;
2554 (*index)++;
2556 return TRUE;
2559 /***********************************************************************
2560 * utf7_wcstombs
2562 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2564 * RETURNS
2565 * On success, the number of characters written
2566 * On dst buffer overflow, -1
2568 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2570 static const char base64_encoding_table[] =
2571 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2573 const WCHAR *source_end = src + srclen;
2574 int dest_index = 0;
2576 while (src < source_end)
2578 if (*src == '+')
2580 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2581 return -1;
2582 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2583 return -1;
2584 src++;
2586 else if (utf7_can_directly_encode(*src))
2588 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2589 return -1;
2590 src++;
2592 else
2594 unsigned int offset = 0;
2595 DWORD byte_pair = 0;
2597 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2598 return -1;
2600 while (src < source_end && !utf7_can_directly_encode(*src))
2602 byte_pair = (byte_pair << 16) | *src;
2603 offset += 16;
2604 while (offset >= 6)
2606 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2607 return -1;
2608 offset -= 6;
2610 src++;
2613 if (offset)
2615 /* Windows won't create a padded base64 character if there's no room for the - sign
2616 * as well ; this is probably a bug in Windows */
2617 if (dstlen > 0 && dest_index + 1 >= dstlen)
2618 return -1;
2620 byte_pair <<= (6 - offset);
2621 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2622 return -1;
2625 /* Windows always explicitly terminates the base64 sequence
2626 even though RFC 2152 (page 3, rule 2) does not require this */
2627 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2628 return -1;
2632 return dest_index;
2635 /***********************************************************************
2636 * WideCharToMultiByte (KERNEL32.@)
2638 * Convert a Unicode character string into a multibyte string.
2640 * PARAMS
2641 * page [I] Code page character set to convert to
2642 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2643 * src [I] Source string buffer
2644 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2645 * dst [O] Destination buffer
2646 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2647 * defchar [I] Default character to use for conversion if no exact
2648 * conversion can be made
2649 * used [O] Set if default character was used in the conversion
2651 * RETURNS
2652 * Success: If dstlen > 0, the number of characters written to dst.
2653 * If dstlen == 0, number of characters needed to perform the
2654 * conversion. In both cases the count includes the terminating NUL.
2655 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2656 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2657 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2658 * parameter was given.
2660 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2661 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2663 const union cptable *table;
2664 int ret, used_tmp;
2666 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2668 SetLastError( ERROR_INVALID_PARAMETER );
2669 return 0;
2672 if (srclen < 0) srclen = strlenW(src) + 1;
2674 switch(page)
2676 case CP_SYMBOL:
2677 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2678 if (flags)
2680 SetLastError( ERROR_INVALID_FLAGS );
2681 return 0;
2683 if (defchar || used)
2685 SetLastError( ERROR_INVALID_PARAMETER );
2686 return 0;
2688 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2689 break;
2690 case CP_UTF7:
2691 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2692 if (defchar || used)
2694 SetLastError( ERROR_INVALID_PARAMETER );
2695 return 0;
2697 if (flags)
2699 SetLastError( ERROR_INVALID_FLAGS );
2700 return 0;
2702 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2703 break;
2704 case CP_UNIXCP:
2705 if (unix_cptable)
2707 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2708 defchar, used ? &used_tmp : NULL );
2709 break;
2711 /* fall through */
2712 case CP_UTF8:
2713 if (defchar || used)
2715 SetLastError( ERROR_INVALID_PARAMETER );
2716 return 0;
2718 if (flags & ~WC_FLAGSMASK)
2720 SetLastError( ERROR_INVALID_FLAGS );
2721 return 0;
2723 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2724 break;
2725 default:
2726 if (!(table = get_codepage_table( page )))
2728 SetLastError( ERROR_INVALID_PARAMETER );
2729 return 0;
2731 if (flags & ~WC_FLAGSMASK)
2733 SetLastError( ERROR_INVALID_FLAGS );
2734 return 0;
2736 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2737 defchar, used ? &used_tmp : NULL );
2738 if (used) *used = used_tmp;
2739 break;
2742 if (ret < 0)
2744 switch(ret)
2746 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2747 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2749 ret = 0;
2751 TRACE("cp %d %s -> %s, ret = %d\n",
2752 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2753 return ret;
2757 /***********************************************************************
2758 * GetThreadLocale (KERNEL32.@)
2760 * Get the current threads locale.
2762 * PARAMS
2763 * None.
2765 * RETURNS
2766 * The LCID currently associated with the calling thread.
2768 LCID WINAPI GetThreadLocale(void)
2770 LCID ret = NtCurrentTeb()->CurrentLocale;
2771 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2772 return ret;
2775 /**********************************************************************
2776 * SetThreadLocale (KERNEL32.@)
2778 * Set the current threads locale.
2780 * PARAMS
2781 * lcid [I] LCID of the locale to set
2783 * RETURNS
2784 * Success: TRUE. The threads locale is set to lcid.
2785 * Failure: FALSE. Use GetLastError() to determine the cause.
2787 BOOL WINAPI SetThreadLocale( LCID lcid )
2789 TRACE("(0x%04X)\n", lcid);
2791 lcid = ConvertDefaultLocale(lcid);
2793 if (lcid != GetThreadLocale())
2795 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2797 SetLastError(ERROR_INVALID_PARAMETER);
2798 return FALSE;
2801 NtCurrentTeb()->CurrentLocale = lcid;
2803 return TRUE;
2806 /**********************************************************************
2807 * SetThreadUILanguage (KERNEL32.@)
2809 * Set the current threads UI language.
2811 * PARAMS
2812 * langid [I] LANGID of the language to set, or 0 to use
2813 * the available language which is best supported
2814 * for console applications
2816 * RETURNS
2817 * Success: The return value is the same as the input value.
2818 * Failure: The return value differs from the input value.
2819 * Use GetLastError() to determine the cause.
2821 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2823 TRACE("(0x%04x) stub - returning success\n", langid);
2825 if (!langid)
2826 return GetThreadUILanguage();
2827 else
2828 return langid;
2831 /******************************************************************************
2832 * ConvertDefaultLocale (KERNEL32.@)
2834 * Convert a default locale identifier into a real identifier.
2836 * PARAMS
2837 * lcid [I] LCID identifier of the locale to convert
2839 * RETURNS
2840 * lcid unchanged, if not a default locale or its sublanguage is
2841 * not SUBLANG_NEUTRAL.
2842 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2843 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2844 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2846 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2848 LANGID langid;
2850 switch (lcid)
2852 case LOCALE_INVARIANT:
2853 /* keep as-is */
2854 break;
2855 case LOCALE_SYSTEM_DEFAULT:
2856 lcid = GetSystemDefaultLCID();
2857 break;
2858 case LOCALE_USER_DEFAULT:
2859 case LOCALE_NEUTRAL:
2860 lcid = GetUserDefaultLCID();
2861 break;
2862 default:
2863 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2864 langid = LANGIDFROMLCID(lcid);
2865 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2867 langid = get_default_sublang( langid );
2868 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2871 return lcid;
2875 /******************************************************************************
2876 * IsValidLocale (KERNEL32.@)
2878 * Determine if a locale is valid.
2880 * PARAMS
2881 * lcid [I] LCID of the locale to check
2882 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2884 * RETURNS
2885 * TRUE, if lcid is valid,
2886 * FALSE, otherwise.
2888 * NOTES
2889 * Wine does not currently make the distinction between supported and installed. All
2890 * languages supported are installed by default.
2892 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2894 /* check if language is registered in the kernel32 resources */
2895 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2896 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2899 /******************************************************************************
2900 * IsValidLocaleName (KERNEL32.@)
2902 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2904 struct locale_name locale_name;
2906 if (!locale)
2907 return FALSE;
2909 /* string parsing */
2910 parse_locale_name( locale, &locale_name );
2912 TRACE( "found lcid %x for %s, matches %d\n",
2913 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2915 return locale_name.matches > 0;
2918 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2919 LPCSTR name, WORD LangID, LONG_PTR lParam )
2921 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2922 char buf[20];
2924 sprintf(buf, "%08x", (UINT)LangID);
2925 return lpfnLocaleEnum( buf );
2928 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2929 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2931 static const WCHAR formatW[] = {'%','0','8','x',0};
2932 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2933 WCHAR buf[20];
2934 sprintfW( buf, formatW, (UINT)LangID );
2935 return lpfnLocaleEnum( buf );
2938 /******************************************************************************
2939 * EnumSystemLocalesA (KERNEL32.@)
2941 * Call a users function for each locale available on the system.
2943 * PARAMS
2944 * lpfnLocaleEnum [I] Callback function to call for each locale
2945 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2947 * RETURNS
2948 * Success: TRUE.
2949 * Failure: FALSE. Use GetLastError() to determine the cause.
2951 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2953 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2954 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2955 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2956 (LONG_PTR)lpfnLocaleEnum);
2957 return TRUE;
2961 /******************************************************************************
2962 * EnumSystemLocalesW (KERNEL32.@)
2964 * See EnumSystemLocalesA.
2966 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2968 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2969 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2970 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2971 (LONG_PTR)lpfnLocaleEnum);
2972 return TRUE;
2976 struct enum_locale_ex_data
2978 LOCALE_ENUMPROCEX proc;
2979 DWORD flags;
2980 LPARAM lparam;
2983 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2984 LPCWSTR name, WORD lang, LONG_PTR lparam )
2986 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2987 WCHAR buffer[256];
2988 DWORD neutral;
2989 unsigned int flags;
2991 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2992 buffer, sizeof(buffer) / sizeof(WCHAR) );
2993 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2994 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2995 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2996 neutral = 0;
2997 flags = LOCALE_WINDOWS;
2998 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2999 if (data->flags && !(data->flags & flags)) return TRUE;
3000 return data->proc( buffer, flags, data->lparam );
3003 /******************************************************************************
3004 * EnumSystemLocalesEx (KERNEL32.@)
3006 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
3008 struct enum_locale_ex_data data;
3010 if (reserved)
3012 SetLastError( ERROR_INVALID_PARAMETER );
3013 return FALSE;
3015 data.proc = proc;
3016 data.flags = flags;
3017 data.lparam = lparam;
3018 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3019 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
3020 enum_locale_ex_proc, (LONG_PTR)&data );
3021 return TRUE;
3025 /***********************************************************************
3026 * VerLanguageNameA (KERNEL32.@)
3028 * Get the name of a language.
3030 * PARAMS
3031 * wLang [I] LANGID of the language
3032 * szLang [O] Destination for the language name
3034 * RETURNS
3035 * Success: The size of the language name. If szLang is non-NULL, it is filled
3036 * with the name.
3037 * Failure: 0. Use GetLastError() to determine the cause.
3040 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
3042 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3046 /***********************************************************************
3047 * VerLanguageNameW (KERNEL32.@)
3049 * See VerLanguageNameA.
3051 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
3053 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3057 /******************************************************************************
3058 * GetStringTypeW (KERNEL32.@)
3060 * See GetStringTypeA.
3062 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3064 static const unsigned char type2_map[16] =
3066 C2_NOTAPPLICABLE, /* unassigned */
3067 C2_LEFTTORIGHT, /* L */
3068 C2_RIGHTTOLEFT, /* R */
3069 C2_EUROPENUMBER, /* EN */
3070 C2_EUROPESEPARATOR, /* ES */
3071 C2_EUROPETERMINATOR, /* ET */
3072 C2_ARABICNUMBER, /* AN */
3073 C2_COMMONSEPARATOR, /* CS */
3074 C2_BLOCKSEPARATOR, /* B */
3075 C2_SEGMENTSEPARATOR, /* S */
3076 C2_WHITESPACE, /* WS */
3077 C2_OTHERNEUTRAL, /* ON */
3078 C2_RIGHTTOLEFT, /* AL */
3079 C2_NOTAPPLICABLE, /* NSM */
3080 C2_NOTAPPLICABLE, /* BN */
3081 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
3084 if (!src)
3086 SetLastError( ERROR_INVALID_PARAMETER );
3087 return FALSE;
3090 if (count == -1) count = strlenW(src) + 1;
3091 switch(type)
3093 case CT_CTYPE1:
3094 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
3095 break;
3096 case CT_CTYPE2:
3097 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
3098 break;
3099 case CT_CTYPE3:
3101 WARN("CT_CTYPE3: semi-stub.\n");
3102 while (count--)
3104 int c = *src;
3105 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
3107 type1 = get_char_typeW( *src++ ) & 0xfff;
3108 /* try to construct type3 from type1 */
3109 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
3110 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
3111 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
3112 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
3113 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3114 if (c == 0x0640) type3 |= C3_KASHIDA;
3115 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3117 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3118 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3120 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3121 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3122 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3123 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3124 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3125 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3126 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3127 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3129 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3130 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3131 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3132 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3133 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3134 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3135 *chartype++ = type3;
3137 break;
3139 default:
3140 SetLastError( ERROR_INVALID_PARAMETER );
3141 return FALSE;
3143 return TRUE;
3147 /******************************************************************************
3148 * GetStringTypeExW (KERNEL32.@)
3150 * See GetStringTypeExA.
3152 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3154 /* locale is ignored for Unicode */
3155 return GetStringTypeW( type, src, count, chartype );
3159 /******************************************************************************
3160 * GetStringTypeA (KERNEL32.@)
3162 * Get characteristics of the characters making up a string.
3164 * PARAMS
3165 * locale [I] Locale Id for the string
3166 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3167 * src [I] String to analyse
3168 * count [I] Length of src in chars, or -1 if src is NUL terminated
3169 * chartype [O] Destination for the calculated characteristics
3171 * RETURNS
3172 * Success: TRUE. chartype is filled with the requested characteristics of each char
3173 * in src.
3174 * Failure: FALSE. Use GetLastError() to determine the cause.
3176 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3178 UINT cp;
3179 INT countW;
3180 LPWSTR srcW;
3181 BOOL ret = FALSE;
3183 if(count == -1) count = strlen(src) + 1;
3185 if (!(cp = get_lcid_codepage( locale )))
3187 FIXME("For locale %04x using current ANSI code page\n", locale);
3188 cp = GetACP();
3191 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3192 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3194 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3196 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3197 * string, with multibyte characters there maybe be more bytes in count
3198 * than character space in the buffer!
3200 ret = GetStringTypeW(type, srcW, countW, chartype);
3201 HeapFree(GetProcessHeap(), 0, srcW);
3203 return ret;
3206 /******************************************************************************
3207 * GetStringTypeExA (KERNEL32.@)
3209 * Get characteristics of the characters making up a string.
3211 * PARAMS
3212 * locale [I] Locale Id for the string
3213 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3214 * src [I] String to analyse
3215 * count [I] Length of src in chars, or -1 if src is NUL terminated
3216 * chartype [O] Destination for the calculated characteristics
3218 * RETURNS
3219 * Success: TRUE. chartype is filled with the requested characteristics of each char
3220 * in src.
3221 * Failure: FALSE. Use GetLastError() to determine the cause.
3223 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3225 return GetStringTypeA(locale, type, src, count, chartype);
3228 /* compose a full-width katakana. return consumed source characters. */
3229 static INT compose_katakana( LPCWSTR src, INT srclen, LPWSTR dst )
3231 const static BYTE katakana_map[] = {
3232 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
3233 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
3234 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
3235 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
3236 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
3237 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
3238 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
3239 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
3241 WCHAR before, dummy;
3243 if (!dst)
3244 dst = &dummy;
3246 switch (*src)
3248 case 0x309b: case 0x309c:
3249 *dst = *src - 2;
3250 return 1;
3251 case 0x30f0: case 0x30f1: case 0x30fd:
3252 *dst = *src;
3253 break;
3254 default:
3256 int shift = *src - 0xff61;
3257 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]) )
3258 return 0;
3259 else
3260 *dst = katakana_map[shift] | 0x3000;
3264 if (srclen <= 1)
3265 return 1;
3267 before = *dst;
3269 /* datakuten (voiced sound) */
3270 if (*(src + 1) == 0xff9e)
3272 if ((*src >= 0xff76 && *src <= 0xff84) ||
3273 (*src >= 0xff8a && *src <= 0xff8e) ||
3274 *src == 0x30fd)
3275 *dst += 1;
3276 else if (*src == 0xff73)
3277 *dst = 0x30f4; /* KATAKANA LETTER VU */
3278 else if (*src == 0xff9c)
3279 *dst = 0x30f7; /* KATAKANA LETTER VA */
3280 else if (*src == 0x30f0)
3281 *dst = 0x30f8; /* KATAKANA LETTER VI */
3282 else if (*src == 0x30f1)
3283 *dst = 0x30f9; /* KATAKANA LETTER VE */
3284 else if (*src == 0xff66)
3285 *dst = 0x30fa; /* KATAKANA LETTER VO */
3288 /* handakuten (semi-voiced sound) */
3289 if (*(src + 1) == 0xff9f)
3290 if (*src >= 0xff8a && *src <= 0xff8e)
3291 *dst += 2;
3293 return (*dst != before) ? 2 : 1;
3296 /* map one or two half-width characters to one full-width character */
3297 static INT map_to_fullwidth( LPCWSTR src, INT srclen, LPWSTR dst )
3299 INT n;
3301 if (*src <= '~' && *src > ' ' && *src != '\\')
3302 *dst = *src - 0x20 + 0xff00;
3303 else if (*src == ' ')
3304 *dst = 0x3000;
3305 else if (*src <= 0x00af && *src >= 0x00a2)
3307 const static BYTE misc_symbols_table[] = {
3308 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3309 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3311 if (misc_symbols_table[*src - 0x00a2])
3312 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3313 else
3314 *dst = *src;
3316 else if (*src == 0x20a9) /* WON SIGN */
3317 *dst = 0xffe6;
3318 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3319 return n;
3320 else if (*src >= 0xffa0 && *src <= 0xffdc)
3322 const static BYTE hangul_mapping_table[] = {
3323 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3324 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3325 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3326 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3327 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3328 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3329 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3330 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3333 if (hangul_mapping_table[*src - 0xffa0])
3334 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3335 else
3336 *dst = *src;
3338 else
3339 *dst = *src;
3341 return 1;
3344 /* decompose a full-width katakana character into one or two half-width characters. */
3345 static INT decompose_katakana( WCHAR c, LPWSTR dst, INT dstlen )
3347 const static BYTE katakana_map[] = {
3348 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3349 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3350 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3351 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3352 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3353 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3354 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3355 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3356 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3357 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3358 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3359 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3360 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3362 INT len = 0, shift = c - 0x3099;
3363 BYTE k;
3365 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]))
3366 return 0;
3368 k = katakana_map[shift];
3370 if (!k)
3372 if (dstlen > 0)
3373 *dst = c;
3374 len++;
3376 else if (k > 0x60)
3378 if (dstlen > 0)
3379 *dst = k | 0xff00;
3380 len++;
3382 else
3384 if (dstlen >= 2)
3386 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3387 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3389 len += 2;
3391 return len;
3394 /* map single full-width character to single or double half-width characters. */
3395 static INT map_to_halfwidth(WCHAR c, LPWSTR dst, INT dstlen)
3397 INT n = decompose_katakana(c, dst, dstlen);
3398 if (n > 0)
3399 return n;
3401 if (c == 0x3000)
3402 *dst = ' ';
3403 else if (c == 0x3001)
3404 *dst = 0xff64;
3405 else if (c == 0x3002)
3406 *dst = 0xff61;
3407 else if (c == 0x300c || c == 0x300d)
3408 *dst = (c - 0x300c) + 0xff62;
3409 else if (c >= 0x3131 && c <= 0x3163)
3411 *dst = c - 0x3131 + 0xffa1;
3412 if (*dst >= 0xffbf) *dst += 3;
3413 if (*dst >= 0xffc8) *dst += 2;
3414 if (*dst >= 0xffd0) *dst += 2;
3415 if (*dst >= 0xffd8) *dst += 2;
3417 else if (c == 0x3164)
3418 *dst = 0xffa0;
3419 else if (c == 0x2019)
3420 *dst = '\'';
3421 else if (c == 0x201d)
3422 *dst = '"';
3423 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3424 *dst = c - 0xff00 + 0x20;
3425 else if (c >= 0xffe0 && c <= 0xffe6)
3427 const static WCHAR misc_symbol_map[] = {
3428 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9
3430 *dst = misc_symbol_map[c - 0xffe0];
3432 else
3433 *dst = c;
3435 return 1;
3438 /*************************************************************************
3439 * LCMapStringEx (KERNEL32.@)
3441 * Map characters in a locale sensitive string.
3443 * PARAMS
3444 * name [I] Locale name for the conversion.
3445 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3446 * src [I] String to map
3447 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3448 * dst [O] Destination for mapped string
3449 * dstlen [I] Length of dst in characters
3450 * version [I] reserved, must be NULL
3451 * reserved [I] reserved, must be NULL
3452 * lparam [I] reserved, must be 0
3454 * RETURNS
3455 * Success: The length of the mapped string in dst, including the NUL terminator.
3456 * Failure: 0. Use GetLastError() to determine the cause.
3458 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3459 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3461 LPWSTR dst_ptr;
3462 INT len;
3464 if (version) FIXME("unsupported version structure %p\n", version);
3465 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3466 if (lparam)
3468 static int once;
3469 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3472 if (!src || !srclen || dstlen < 0)
3474 SetLastError(ERROR_INVALID_PARAMETER);
3475 return 0;
3478 /* mutually exclusive flags */
3479 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3480 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3481 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3482 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
3483 !flags)
3485 SetLastError(ERROR_INVALID_FLAGS);
3486 return 0;
3489 if (!dstlen) dst = NULL;
3491 if (flags & LCMAP_SORTKEY)
3493 INT ret;
3494 if (src == dst)
3496 SetLastError(ERROR_INVALID_FLAGS);
3497 return 0;
3500 if (srclen < 0) srclen = strlenW(src);
3502 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3503 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3505 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3506 if (ret == 0)
3507 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3508 else
3509 ret++;
3510 return ret;
3513 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3514 if (flags & SORT_STRINGSORT)
3516 SetLastError(ERROR_INVALID_FLAGS);
3517 return 0;
3519 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
3520 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
3521 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
3522 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
3524 SetLastError(ERROR_INVALID_FLAGS);
3525 return 0;
3528 if (srclen < 0) srclen = strlenW(src) + 1;
3530 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3531 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3533 if (!dst) /* return required string length */
3535 if (flags & NORM_IGNORESYMBOLS)
3537 for (len = 0; srclen; src++, srclen--)
3539 WCHAR wch = *src;
3540 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3541 * and skips white space and punctuation characters for
3542 * NORM_IGNORESYMBOLS.
3544 if (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE))
3545 continue;
3546 len++;
3549 else if (flags & LCMAP_FULLWIDTH)
3551 for (len = 0; srclen; src++, srclen--, len++)
3553 if (compose_katakana(src, srclen, NULL) == 2)
3555 src++;
3556 srclen--;
3560 else if (flags & LCMAP_HALFWIDTH)
3562 for (len = 0; srclen; src++, srclen--, len++)
3563 if (decompose_katakana(*src, NULL, 0) == 2)
3564 len++;
3566 else
3567 len = srclen;
3568 return len;
3571 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3573 SetLastError(ERROR_INVALID_FLAGS);
3574 return 0;
3577 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
3579 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3581 WCHAR wch = *src;
3582 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3583 continue;
3584 *dst_ptr++ = wch;
3585 len--;
3587 goto done;
3590 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
3592 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
3594 WCHAR wch;
3595 if (flags & LCMAP_FULLWIDTH)
3597 /* map half-width character to full-width one,
3598 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
3599 if (map_to_fullwidth(src, srclen, &wch) == 2)
3601 src++;
3602 srclen--;
3605 else
3606 wch = *src;
3608 if (flags & LCMAP_KATAKANA)
3610 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
3611 we can't use C3_HIRAGANA as some characters can't map to katakana */
3612 if ((wch >= 0x3041 && wch <= 0x3096) ||
3613 wch == 0x309D || wch == 0x309E)
3614 wch += 0x60;
3616 else if (flags & LCMAP_HIRAGANA)
3618 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
3619 we can't use C3_KATAKANA as some characters can't map to hiragana */
3620 if ((wch >= 0x30A1 && wch <= 0x30F6) ||
3621 wch == 0x30FD || wch == 0x30FE)
3622 wch -= 0x60;
3625 if (flags & LCMAP_HALFWIDTH)
3627 /* map full-width character to half-width one,
3628 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
3629 if (map_to_halfwidth(wch, dst_ptr, dstlen) == 2)
3631 dstlen--;
3632 dst_ptr++;
3633 if (!dstlen)
3635 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3636 return 0;
3640 else
3641 *dst_ptr = wch;
3643 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)))
3644 goto done;
3646 srclen = dst_ptr - dst;
3647 src = dst;
3650 if (flags & LCMAP_UPPERCASE)
3652 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3654 *dst_ptr++ = toupperW(*src);
3655 len--;
3658 else if (flags & LCMAP_LOWERCASE)
3660 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3662 *dst_ptr++ = tolowerW(*src);
3663 len--;
3666 else
3668 len = min(srclen, dstlen);
3669 memcpy(dst, src, len * sizeof(WCHAR));
3670 dst_ptr = dst + len;
3671 srclen -= len;
3674 done:
3675 if (srclen)
3677 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3678 return 0;
3681 return dst_ptr - dst;
3684 /*************************************************************************
3685 * LCMapStringW (KERNEL32.@)
3687 * See LCMapStringA.
3689 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3690 LPWSTR dst, INT dstlen)
3692 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3693 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3695 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3698 /*************************************************************************
3699 * LCMapStringA (KERNEL32.@)
3701 * Map characters in a locale sensitive string.
3703 * PARAMS
3704 * lcid [I] LCID for the conversion.
3705 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3706 * src [I] String to map
3707 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3708 * dst [O] Destination for mapped string
3709 * dstlen [I] Length of dst in characters
3711 * RETURNS
3712 * Success: The length of the mapped string in dst, including the NUL terminator.
3713 * Failure: 0. Use GetLastError() to determine the cause.
3715 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3716 LPSTR dst, INT dstlen)
3718 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3719 LPWSTR srcW, dstW;
3720 INT ret = 0, srclenW, dstlenW;
3721 UINT locale_cp = CP_ACP;
3723 if (!src || !srclen || dstlen < 0)
3725 SetLastError(ERROR_INVALID_PARAMETER);
3726 return 0;
3729 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3731 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3732 if (srclenW)
3733 srcW = bufW;
3734 else
3736 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3737 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3738 if (!srcW)
3740 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3741 return 0;
3743 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3746 if (flags & LCMAP_SORTKEY)
3748 if (src == dst)
3750 SetLastError(ERROR_INVALID_FLAGS);
3751 goto map_string_exit;
3753 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3754 if (ret == 0)
3755 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3756 else
3757 ret++;
3758 goto map_string_exit;
3761 if (flags & SORT_STRINGSORT)
3763 SetLastError(ERROR_INVALID_FLAGS);
3764 goto map_string_exit;
3767 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3768 if (!dstlenW)
3769 goto map_string_exit;
3771 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3772 if (!dstW)
3774 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3775 goto map_string_exit;
3778 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3779 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3780 HeapFree(GetProcessHeap(), 0, dstW);
3782 map_string_exit:
3783 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3784 return ret;
3787 /*************************************************************************
3788 * FoldStringA (KERNEL32.@)
3790 * Map characters in a string.
3792 * PARAMS
3793 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3794 * src [I] String to map
3795 * srclen [I] Length of src, or -1 if src is NUL terminated
3796 * dst [O] Destination for mapped string
3797 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3799 * RETURNS
3800 * Success: The length of the string written to dst, including the terminating NUL. If
3801 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3802 * and dst may be NULL.
3803 * Failure: 0. Use GetLastError() to determine the cause.
3805 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3806 LPSTR dst, INT dstlen)
3808 INT ret = 0, srclenW = 0;
3809 WCHAR *srcW = NULL, *dstW = NULL;
3811 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3813 SetLastError(ERROR_INVALID_PARAMETER);
3814 return 0;
3817 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3818 src, srclen, NULL, 0);
3819 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3821 if (!srcW)
3823 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3824 goto FoldStringA_exit;
3827 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3828 src, srclen, srcW, srclenW);
3830 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3832 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3833 if (ret && dstlen)
3835 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3837 if (!dstW)
3839 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3840 goto FoldStringA_exit;
3843 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3844 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3846 ret = 0;
3847 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3851 HeapFree(GetProcessHeap(), 0, dstW);
3853 FoldStringA_exit:
3854 HeapFree(GetProcessHeap(), 0, srcW);
3855 return ret;
3858 /*************************************************************************
3859 * FoldStringW (KERNEL32.@)
3861 * See FoldStringA.
3863 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3864 LPWSTR dst, INT dstlen)
3866 int ret;
3868 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3870 case 0:
3871 if (dwFlags)
3872 break;
3873 /* Fall through for dwFlags == 0 */
3874 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3875 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3876 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3877 SetLastError(ERROR_INVALID_FLAGS);
3878 return 0;
3881 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3883 SetLastError(ERROR_INVALID_PARAMETER);
3884 return 0;
3887 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3888 if (!ret)
3889 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3890 return ret;
3893 /******************************************************************************
3894 * CompareStringW (KERNEL32.@)
3896 * See CompareStringA.
3898 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3899 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3901 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3904 /******************************************************************************
3905 * CompareStringEx (KERNEL32.@)
3907 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3908 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3910 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3911 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3912 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3913 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3914 INT ret;
3915 static int once;
3917 if (version) FIXME("unexpected version parameter\n");
3918 if (reserved) FIXME("unexpected reserved value\n");
3919 if (lParam) FIXME("unexpected lParam\n");
3921 if (!str1 || !str2)
3923 SetLastError(ERROR_INVALID_PARAMETER);
3924 return 0;
3927 if (flags & ~(supported_flags|semistub_flags))
3929 SetLastError(ERROR_INVALID_FLAGS);
3930 return 0;
3933 if (flags & semistub_flags)
3935 if (!once++)
3936 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3939 if (len1 < 0) len1 = strlenW(str1);
3940 if (len2 < 0) len2 = strlenW(str2);
3942 ret = wine_compare_string(flags, str1, len1, str2, len2);
3944 if (ret) /* need to translate result */
3945 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3946 return CSTR_EQUAL;
3949 /******************************************************************************
3950 * CompareStringA (KERNEL32.@)
3952 * Compare two locale sensitive strings.
3954 * PARAMS
3955 * lcid [I] LCID for the comparison
3956 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3957 * str1 [I] First string to compare
3958 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3959 * str2 [I] Second string to compare
3960 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3962 * RETURNS
3963 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3964 * str1 is less than, equal to or greater than str2 respectively.
3965 * Failure: FALSE. Use GetLastError() to determine the cause.
3967 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3968 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3970 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3971 WCHAR *buf2W = buf1W + 130;
3972 LPWSTR str1W, str2W;
3973 INT len1W = 0, len2W = 0, ret;
3974 UINT locale_cp = CP_ACP;
3976 if (!str1 || !str2)
3978 SetLastError(ERROR_INVALID_PARAMETER);
3979 return 0;
3981 if (len1 < 0) len1 = strlen(str1);
3982 if (len2 < 0) len2 = strlen(str2);
3984 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3986 if (len1)
3988 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3989 if (len1W)
3990 str1W = buf1W;
3991 else
3993 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3994 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3995 if (!str1W)
3997 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3998 return 0;
4000 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
4003 else
4005 len1W = 0;
4006 str1W = buf1W;
4009 if (len2)
4011 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
4012 if (len2W)
4013 str2W = buf2W;
4014 else
4016 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
4017 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
4018 if (!str2W)
4020 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4021 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4022 return 0;
4024 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
4027 else
4029 len2W = 0;
4030 str2W = buf2W;
4033 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
4035 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4036 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
4037 return ret;
4040 /******************************************************************************
4041 * CompareStringOrdinal (KERNEL32.@)
4043 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
4045 int ret;
4047 if (!str1 || !str2)
4049 SetLastError(ERROR_INVALID_PARAMETER);
4050 return 0;
4052 if (len1 < 0) len1 = strlenW(str1);
4053 if (len2 < 0) len2 = strlenW(str2);
4055 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4056 if (ret < 0) return CSTR_LESS_THAN;
4057 if (ret > 0) return CSTR_GREATER_THAN;
4058 return CSTR_EQUAL;
4061 /*************************************************************************
4062 * lstrcmp (KERNEL32.@)
4063 * lstrcmpA (KERNEL32.@)
4065 * Compare two strings using the current thread locale.
4067 * PARAMS
4068 * str1 [I] First string to compare
4069 * str2 [I] Second string to compare
4071 * RETURNS
4072 * Success: A number less than, equal to or greater than 0 depending on whether
4073 * str1 is less than, equal to or greater than str2 respectively.
4074 * Failure: FALSE. Use GetLastError() to determine the cause.
4076 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
4078 int ret;
4080 if ((str1 == NULL) && (str2 == NULL)) return 0;
4081 if (str1 == NULL) return -1;
4082 if (str2 == NULL) return 1;
4084 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4085 if (ret) ret -= 2;
4087 return ret;
4090 /*************************************************************************
4091 * lstrcmpi (KERNEL32.@)
4092 * lstrcmpiA (KERNEL32.@)
4094 * Compare two strings using the current thread locale, ignoring case.
4096 * PARAMS
4097 * str1 [I] First string to compare
4098 * str2 [I] Second string to compare
4100 * RETURNS
4101 * Success: A number less than, equal to or greater than 0 depending on whether
4102 * str2 is less than, equal to or greater than str1 respectively.
4103 * Failure: FALSE. Use GetLastError() to determine the cause.
4105 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
4107 int ret;
4109 if ((str1 == NULL) && (str2 == NULL)) return 0;
4110 if (str1 == NULL) return -1;
4111 if (str2 == NULL) return 1;
4113 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4114 if (ret) ret -= 2;
4116 return ret;
4119 /*************************************************************************
4120 * lstrcmpW (KERNEL32.@)
4122 * See lstrcmpA.
4124 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
4126 int ret;
4128 if ((str1 == NULL) && (str2 == NULL)) return 0;
4129 if (str1 == NULL) return -1;
4130 if (str2 == NULL) return 1;
4132 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
4133 if (ret) ret -= 2;
4135 return ret;
4138 /*************************************************************************
4139 * lstrcmpiW (KERNEL32.@)
4141 * See lstrcmpiA.
4143 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
4145 int ret;
4147 if ((str1 == NULL) && (str2 == NULL)) return 0;
4148 if (str1 == NULL) return -1;
4149 if (str2 == NULL) return 1;
4151 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
4152 if (ret) ret -= 2;
4154 return ret;
4157 /******************************************************************************
4158 * LOCALE_Init
4160 void LOCALE_Init(void)
4162 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
4163 const union cptable *unix_cp );
4165 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
4167 setlocale( LC_ALL, "" );
4169 #ifdef __APPLE__
4170 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
4171 if (!has_env("LANG"))
4173 const char* mac_locale = get_mac_locale();
4175 setenv( "LANG", mac_locale, 1 );
4176 if (setlocale( LC_ALL, "" ))
4177 TRACE( "setting LANG to '%s'\n", mac_locale );
4178 else
4180 /* no C library locale matching Mac locale; don't pass garbage to children */
4181 unsetenv("LANG");
4182 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
4185 #endif /* __APPLE__ */
4187 unix_cp = setup_unix_locales();
4188 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
4190 #ifdef __APPLE__
4191 if (!unix_cp)
4192 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
4193 #endif
4195 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
4196 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
4197 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
4199 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
4200 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
4201 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
4202 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
4203 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
4204 if (!unix_cp)
4205 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
4206 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
4208 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
4209 ansi_cptable = wine_cp_get_table( 1252 );
4210 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
4211 oem_cptable = wine_cp_get_table( 437 );
4212 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
4213 mac_cptable = wine_cp_get_table( 10000 );
4214 if (unix_cp != CP_UTF8)
4216 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
4217 unix_cptable = wine_cp_get_table( 28591 );
4220 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
4222 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
4223 ansi_cptable->info.codepage, oem_cptable->info.codepage,
4224 mac_cptable->info.codepage, unix_cp );
4226 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
4229 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4231 UNICODE_STRING keyName;
4232 OBJECT_ATTRIBUTES attr;
4233 HANDLE hkey;
4235 RtlInitUnicodeString( &keyName, szKeyName );
4236 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
4238 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
4239 hkey = 0;
4241 return hkey;
4244 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4245 LPWSTR szValueName, ULONG valueNameSize,
4246 LPWSTR szValueData, ULONG valueDataSize)
4248 BYTE buffer[80];
4249 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
4250 DWORD dwLen;
4252 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
4253 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
4254 info->NameLength > valueNameSize ||
4255 info->DataLength > valueDataSize)
4257 return FALSE;
4260 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
4262 memcpy( szValueName, info->Name, info->NameLength);
4263 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
4264 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
4265 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
4267 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
4268 return TRUE;
4271 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
4273 BYTE buffer[128];
4274 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
4275 DWORD dwSize = sizeof(buffer);
4276 UNICODE_STRING valueName;
4278 RtlInitUnicodeString( &valueName, szValueName );
4280 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
4281 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
4282 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
4283 info->DataLength == sizeof(DWORD))
4285 memcpy(lpVal, info->Data, sizeof(DWORD));
4286 return TRUE;
4289 return FALSE;
4292 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
4294 LANGID langId;
4295 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
4296 HRSRC hResource;
4297 BOOL bRet = FALSE;
4299 /* FIXME: Is it correct to use the system default langid? */
4300 langId = GetSystemDefaultLangID();
4302 if (SUBLANGID(langId) == SUBLANG_NEUTRAL) langId = get_default_sublang( langId );
4304 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
4306 if (hResource)
4308 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
4310 if (hResDir)
4312 ULONG iResourceIndex = lgrpid & 0xf;
4313 LPCWSTR lpResEntry = LockResource( hResDir );
4314 ULONG i;
4316 for (i = 0; i < iResourceIndex; i++)
4317 lpResEntry += *lpResEntry + 1;
4319 if (*lpResEntry < nameSize)
4321 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
4322 szName[*lpResEntry] = '\0';
4323 bRet = TRUE;
4327 FreeResource( hResource );
4329 return bRet;
4332 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
4333 typedef struct
4335 LANGUAGEGROUP_ENUMPROCA procA;
4336 LANGUAGEGROUP_ENUMPROCW procW;
4337 DWORD dwFlags;
4338 LONG_PTR lParam;
4339 } ENUMLANGUAGEGROUP_CALLBACKS;
4341 /* Internal implementation of EnumSystemLanguageGroupsA/W */
4342 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
4344 WCHAR szNumber[10], szValue[4];
4345 HANDLE hKey;
4346 BOOL bContinue = TRUE;
4347 ULONG ulIndex = 0;
4349 if (!lpProcs)
4351 SetLastError(ERROR_INVALID_PARAMETER);
4352 return FALSE;
4355 switch (lpProcs->dwFlags)
4357 case 0:
4358 /* Default to LGRPID_INSTALLED */
4359 lpProcs->dwFlags = LGRPID_INSTALLED;
4360 /* Fall through... */
4361 case LGRPID_INSTALLED:
4362 case LGRPID_SUPPORTED:
4363 break;
4364 default:
4365 SetLastError(ERROR_INVALID_FLAGS);
4366 return FALSE;
4369 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4371 if (!hKey)
4372 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4374 while (bContinue)
4376 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4377 szValue, sizeof(szValue) ))
4379 BOOL bInstalled = szValue[0] == '1';
4380 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4382 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4383 bInstalled ? "" : "not ");
4385 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4387 WCHAR szGrpName[48];
4389 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
4390 szGrpName[0] = '\0';
4392 if (lpProcs->procW)
4393 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4394 lpProcs->lParam );
4395 else
4397 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4398 char szGrpNameA[48];
4400 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4401 * or whether the language names are ever localised. Assume CP_ACP.
4404 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4405 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4407 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4408 lpProcs->lParam );
4412 ulIndex++;
4414 else
4415 bContinue = FALSE;
4417 if (!bContinue)
4418 break;
4421 if (hKey)
4422 NtClose( hKey );
4424 return TRUE;
4427 /******************************************************************************
4428 * EnumSystemLanguageGroupsA (KERNEL32.@)
4430 * Call a users function for each language group available on the system.
4432 * PARAMS
4433 * pLangGrpEnumProc [I] Callback function to call for each language group
4434 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4435 * lParam [I] User parameter to pass to pLangGrpEnumProc
4437 * RETURNS
4438 * Success: TRUE.
4439 * Failure: FALSE. Use GetLastError() to determine the cause.
4441 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4442 DWORD dwFlags, LONG_PTR lParam)
4444 ENUMLANGUAGEGROUP_CALLBACKS procs;
4446 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4448 procs.procA = pLangGrpEnumProc;
4449 procs.procW = NULL;
4450 procs.dwFlags = dwFlags;
4451 procs.lParam = lParam;
4453 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4456 /******************************************************************************
4457 * EnumSystemLanguageGroupsW (KERNEL32.@)
4459 * See EnumSystemLanguageGroupsA.
4461 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4462 DWORD dwFlags, LONG_PTR lParam)
4464 ENUMLANGUAGEGROUP_CALLBACKS procs;
4466 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4468 procs.procA = NULL;
4469 procs.procW = pLangGrpEnumProc;
4470 procs.dwFlags = dwFlags;
4471 procs.lParam = lParam;
4473 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4476 /******************************************************************************
4477 * IsValidLanguageGroup (KERNEL32.@)
4479 * Determine if a language group is supported and/or installed.
4481 * PARAMS
4482 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4483 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4485 * RETURNS
4486 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4487 * FALSE otherwise.
4489 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4491 static const WCHAR szFormat[] = { '%','x','\0' };
4492 WCHAR szValueName[16], szValue[2];
4493 BOOL bSupported = FALSE, bInstalled = FALSE;
4494 HANDLE hKey;
4497 switch (dwFlags)
4499 case LGRPID_INSTALLED:
4500 case LGRPID_SUPPORTED:
4502 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4504 sprintfW( szValueName, szFormat, lgrpid );
4506 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4508 bSupported = TRUE;
4510 if (szValue[0] == '1')
4511 bInstalled = TRUE;
4514 if (hKey)
4515 NtClose( hKey );
4517 break;
4520 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4521 (dwFlags == LGRPID_INSTALLED && bInstalled))
4522 return TRUE;
4524 return FALSE;
4527 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4528 typedef struct
4530 LANGGROUPLOCALE_ENUMPROCA procA;
4531 LANGGROUPLOCALE_ENUMPROCW procW;
4532 DWORD dwFlags;
4533 LGRPID lgrpid;
4534 LONG_PTR lParam;
4535 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4537 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4538 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4540 static const WCHAR szAlternateSortsKeyName[] = {
4541 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4543 WCHAR szNumber[10], szValue[4];
4544 HANDLE hKey;
4545 BOOL bContinue = TRUE, bAlternate = FALSE;
4546 LGRPID lgrpid;
4547 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4549 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4551 SetLastError(ERROR_INVALID_PARAMETER);
4552 return FALSE;
4555 if (lpProcs->dwFlags)
4557 SetLastError(ERROR_INVALID_FLAGS);
4558 return FALSE;
4561 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4563 if (!hKey)
4564 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4566 while (bContinue)
4568 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4569 szValue, sizeof(szValue) ))
4571 lgrpid = strtoulW( szValue, NULL, 16 );
4573 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4574 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4576 if (lgrpid == lpProcs->lgrpid)
4578 LCID lcid;
4580 lcid = strtoulW( szNumber, NULL, 16 );
4582 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4583 * '00000437 ;Georgian'
4584 * At present we only pass the LCID string.
4587 if (lpProcs->procW)
4588 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4589 else
4591 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4593 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4595 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4599 ulIndex++;
4601 else
4603 /* Finished enumerating this key */
4604 if (!bAlternate)
4606 /* Enumerate alternate sorts also */
4607 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4608 bAlternate = TRUE;
4609 ulIndex = 0;
4611 else
4612 bContinue = FALSE; /* Finished both keys */
4615 if (!bContinue)
4616 break;
4619 if (hKey)
4620 NtClose( hKey );
4622 return TRUE;
4625 /******************************************************************************
4626 * EnumLanguageGroupLocalesA (KERNEL32.@)
4628 * Call a users function for every locale in a language group available on the system.
4630 * PARAMS
4631 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4632 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4633 * dwFlags [I] Reserved, set to 0
4634 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4636 * RETURNS
4637 * Success: TRUE.
4638 * Failure: FALSE. Use GetLastError() to determine the cause.
4640 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4641 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4643 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4645 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4647 callbacks.procA = pLangGrpLcEnumProc;
4648 callbacks.procW = NULL;
4649 callbacks.dwFlags = dwFlags;
4650 callbacks.lgrpid = lgrpid;
4651 callbacks.lParam = lParam;
4653 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4656 /******************************************************************************
4657 * EnumLanguageGroupLocalesW (KERNEL32.@)
4659 * See EnumLanguageGroupLocalesA.
4661 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4662 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4664 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4666 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4668 callbacks.procA = NULL;
4669 callbacks.procW = pLangGrpLcEnumProc;
4670 callbacks.dwFlags = dwFlags;
4671 callbacks.lgrpid = lgrpid;
4672 callbacks.lParam = lParam;
4674 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4677 /******************************************************************************
4678 * InvalidateNLSCache (KERNEL32.@)
4680 * Invalidate the cache of NLS values.
4682 * PARAMS
4683 * None.
4685 * RETURNS
4686 * Success: TRUE.
4687 * Failure: FALSE.
4689 BOOL WINAPI InvalidateNLSCache(void)
4691 FIXME("() stub\n");
4692 return FALSE;
4695 /******************************************************************************
4696 * GetUserGeoID (KERNEL32.@)
4698 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4700 GEOID ret = GEOID_NOT_AVAILABLE;
4701 static const WCHAR geoW[] = {'G','e','o',0};
4702 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4703 WCHAR bufferW[40], *end;
4704 DWORD count;
4705 HANDLE hkey, hSubkey = 0;
4706 UNICODE_STRING keyW;
4707 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4708 RtlInitUnicodeString( &keyW, nationW );
4709 count = sizeof(bufferW);
4711 if(!(hkey = create_registry_key())) return ret;
4713 switch( GeoClass ){
4714 case GEOCLASS_NATION:
4715 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4717 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4718 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4719 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4721 break;
4722 case GEOCLASS_REGION:
4723 FIXME("GEOCLASS_REGION not handled yet\n");
4724 break;
4727 NtClose(hkey);
4728 if (hSubkey) NtClose(hSubkey);
4729 return ret;
4732 /******************************************************************************
4733 * SetUserGeoID (KERNEL32.@)
4735 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4737 static const WCHAR geoW[] = {'G','e','o',0};
4738 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4739 static const WCHAR formatW[] = {'%','i',0};
4740 UNICODE_STRING nameW,keyW;
4741 WCHAR bufferW[10];
4742 OBJECT_ATTRIBUTES attr;
4743 HANDLE hkey;
4745 if(!(hkey = create_registry_key())) return FALSE;
4747 attr.Length = sizeof(attr);
4748 attr.RootDirectory = hkey;
4749 attr.ObjectName = &nameW;
4750 attr.Attributes = 0;
4751 attr.SecurityDescriptor = NULL;
4752 attr.SecurityQualityOfService = NULL;
4753 RtlInitUnicodeString( &nameW, geoW );
4754 RtlInitUnicodeString( &keyW, nationW );
4756 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4759 NtClose(attr.RootDirectory);
4760 return FALSE;
4763 sprintfW(bufferW, formatW, GeoID);
4764 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4765 NtClose(attr.RootDirectory);
4766 NtClose(hkey);
4767 return TRUE;
4770 typedef struct
4772 union
4774 UILANGUAGE_ENUMPROCA procA;
4775 UILANGUAGE_ENUMPROCW procW;
4776 } u;
4777 DWORD flags;
4778 LONG_PTR param;
4779 } ENUM_UILANG_CALLBACK;
4781 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4782 LPCSTR name, WORD LangID, LONG_PTR lParam )
4784 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4785 char buf[20];
4787 sprintf(buf, "%08x", (UINT)LangID);
4788 return enum_uilang->u.procA( buf, enum_uilang->param );
4791 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4792 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4794 static const WCHAR formatW[] = {'%','0','8','x',0};
4795 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4796 WCHAR buf[20];
4798 sprintfW( buf, formatW, (UINT)LangID );
4799 return enum_uilang->u.procW( buf, enum_uilang->param );
4802 /******************************************************************************
4803 * EnumUILanguagesA (KERNEL32.@)
4805 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4807 ENUM_UILANG_CALLBACK enum_uilang;
4809 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4811 if(!pUILangEnumProc) {
4812 SetLastError(ERROR_INVALID_PARAMETER);
4813 return FALSE;
4815 if(dwFlags) {
4816 SetLastError(ERROR_INVALID_FLAGS);
4817 return FALSE;
4820 enum_uilang.u.procA = pUILangEnumProc;
4821 enum_uilang.flags = dwFlags;
4822 enum_uilang.param = lParam;
4824 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4825 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4826 (LONG_PTR)&enum_uilang);
4827 return TRUE;
4830 /******************************************************************************
4831 * EnumUILanguagesW (KERNEL32.@)
4833 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4835 ENUM_UILANG_CALLBACK enum_uilang;
4837 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4840 if(!pUILangEnumProc) {
4841 SetLastError(ERROR_INVALID_PARAMETER);
4842 return FALSE;
4844 if(dwFlags) {
4845 SetLastError(ERROR_INVALID_FLAGS);
4846 return FALSE;
4849 enum_uilang.u.procW = pUILangEnumProc;
4850 enum_uilang.flags = dwFlags;
4851 enum_uilang.param = lParam;
4853 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4854 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4855 (LONG_PTR)&enum_uilang);
4856 return TRUE;
4859 enum locationkind {
4860 LOCATION_NATION = 0,
4861 LOCATION_REGION,
4862 LOCATION_BOTH
4865 struct geoinfo_t {
4866 GEOID id;
4867 WCHAR iso2W[3];
4868 WCHAR iso3W[4];
4869 GEOID parent;
4870 INT uncode;
4871 enum locationkind kind;
4874 static const struct geoinfo_t geoinfodata[] = {
4875 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4876 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4877 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4878 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4879 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4880 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4881 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4882 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4883 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4884 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4885 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4886 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4887 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4888 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4889 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4890 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4891 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4892 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4893 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4894 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4895 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4896 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4897 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4898 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4899 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4900 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4901 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4902 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4903 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4904 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4905 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4906 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4907 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4908 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4909 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4910 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4911 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4912 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4913 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4914 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4915 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4916 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4917 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4918 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4919 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4920 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4921 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4922 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4923 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4924 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4925 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4926 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4927 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4928 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4929 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4930 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4931 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4932 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4933 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4934 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4935 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4936 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4937 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4938 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4939 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4940 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4941 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4942 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4943 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4944 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4945 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4946 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4947 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4948 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4949 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4950 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4951 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4952 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4953 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4954 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4955 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4956 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4957 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4958 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4959 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4960 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4961 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4962 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4963 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4964 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4965 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4966 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4967 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4968 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4969 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4970 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4971 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4972 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4973 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4974 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4975 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4976 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4977 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4978 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4979 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4980 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4981 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4982 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4983 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4984 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4985 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4986 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4987 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4988 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4989 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4990 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4991 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4992 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4993 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4994 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4995 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4996 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4997 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4998 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4999 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
5000 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
5001 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
5002 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
5003 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
5004 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
5005 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
5006 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
5007 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
5008 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
5009 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
5010 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
5011 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
5012 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
5013 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
5014 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
5015 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
5016 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
5017 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
5018 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
5019 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
5020 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
5021 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
5022 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
5023 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
5024 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
5025 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
5026 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
5027 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
5028 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
5029 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
5030 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
5031 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
5032 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
5033 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
5034 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
5035 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
5036 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
5037 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
5038 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
5039 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
5040 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
5041 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
5042 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
5043 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
5044 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
5045 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
5046 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
5047 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
5048 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
5049 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
5050 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
5051 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
5052 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
5053 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
5054 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
5055 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
5056 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
5057 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
5058 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
5059 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
5060 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
5061 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
5062 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
5063 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
5064 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
5065 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
5066 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
5067 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
5068 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
5069 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
5070 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
5071 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
5072 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
5073 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
5074 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
5075 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
5076 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
5077 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
5078 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
5079 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
5080 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
5081 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
5082 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
5083 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
5084 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
5085 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
5086 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
5087 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
5088 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
5089 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
5090 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
5091 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
5092 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
5093 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
5094 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
5095 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
5096 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
5097 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
5098 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
5099 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
5100 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
5101 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
5102 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
5103 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
5104 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
5105 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
5106 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
5107 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
5108 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
5109 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
5110 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
5111 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
5112 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
5113 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
5114 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
5115 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
5116 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
5117 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
5118 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
5119 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
5120 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
5121 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
5122 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
5123 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
5124 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
5125 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
5126 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
5127 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
5128 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
5129 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
5130 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
5131 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
5132 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
5133 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
5134 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
5135 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
5136 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
5137 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
5138 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
5139 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
5140 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
5141 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
5142 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
5143 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
5144 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
5145 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
5146 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
5147 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
5148 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
5149 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
5150 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
5151 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
5152 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
5153 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
5154 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
5155 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
5156 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
5157 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
5158 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
5159 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
5160 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
5161 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
5162 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
5163 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
5164 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
5165 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
5166 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
5167 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
5168 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
5169 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
5170 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
5171 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
5172 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
5173 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
5176 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
5178 int min, max;
5180 min = 0;
5181 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
5183 while (min <= max) {
5184 const struct geoinfo_t *ptr;
5185 int n = (min+max)/2;
5187 ptr = &geoinfodata[n];
5188 if (geoid == ptr->id)
5189 /* we don't need empty entries */
5190 return *ptr->iso2W ? ptr : NULL;
5192 if (ptr->id > geoid)
5193 max = n-1;
5194 else
5195 min = n+1;
5198 return NULL;
5201 /******************************************************************************
5202 * GetGeoInfoW (KERNEL32.@)
5204 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
5206 const struct geoinfo_t *ptr;
5207 const WCHAR *str = NULL;
5208 WCHAR buffW[12];
5209 LONG val = 0;
5210 INT len;
5212 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5214 if (!(ptr = get_geoinfo_dataptr(geoid))) {
5215 SetLastError(ERROR_INVALID_PARAMETER);
5216 return 0;
5219 switch (geotype) {
5220 case GEO_NATION:
5221 val = geoid;
5222 break;
5223 case GEO_ISO_UN_NUMBER:
5224 val = ptr->uncode;
5225 break;
5226 case GEO_PARENT:
5227 val = ptr->parent;
5228 break;
5229 case GEO_ISO2:
5230 case GEO_ISO3:
5232 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
5233 break;
5235 case GEO_RFC1766:
5236 case GEO_LCID:
5237 case GEO_FRIENDLYNAME:
5238 case GEO_OFFICIALNAME:
5239 case GEO_TIMEZONES:
5240 case GEO_OFFICIALLANGUAGES:
5241 case GEO_LATITUDE:
5242 case GEO_LONGITUDE:
5243 FIXME("type %d is not supported\n", geotype);
5244 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5245 return 0;
5246 default:
5247 WARN("unrecognized type %d\n", geotype);
5248 SetLastError(ERROR_INVALID_FLAGS);
5249 return 0;
5252 if (val) {
5253 static const WCHAR fmtW[] = {'%','d',0};
5254 sprintfW(buffW, fmtW, val);
5255 str = buffW;
5258 len = strlenW(str) + 1;
5259 if (!data || !data_len)
5260 return len;
5262 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
5263 if (data_len < len)
5264 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5265 return data_len < len ? 0 : len;
5268 /******************************************************************************
5269 * GetGeoInfoA (KERNEL32.@)
5271 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
5273 WCHAR *buffW;
5274 INT len;
5276 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5278 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
5279 if (!len)
5280 return 0;
5282 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
5283 if (!buffW)
5284 return 0;
5286 GetGeoInfoW(geoid, geotype, buffW, len, lang);
5287 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
5288 if (!data || !data_len) {
5289 HeapFree(GetProcessHeap(), 0, buffW);
5290 return len;
5293 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
5294 HeapFree(GetProcessHeap(), 0, buffW);
5296 if (data_len < len)
5297 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5298 return data_len < len ? 0 : len;
5301 /******************************************************************************
5302 * EnumSystemGeoID (KERNEL32.@)
5304 * Call a users function for every location available on the system.
5306 * PARAMS
5307 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
5308 * parent [I] GEOID for the parent
5309 * enumproc [I] Callback function to call for each location
5311 * RETURNS
5312 * Success: TRUE.
5313 * Failure: FALSE. Use GetLastError() to determine the cause.
5315 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
5317 INT i;
5319 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
5321 if (!enumproc) {
5322 SetLastError(ERROR_INVALID_PARAMETER);
5323 return FALSE;
5326 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
5327 SetLastError(ERROR_INVALID_FLAGS);
5328 return FALSE;
5331 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
5332 const struct geoinfo_t *ptr = &geoinfodata[i];
5334 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
5335 continue;
5337 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
5338 continue;
5340 if (parent && ptr->parent != parent)
5341 continue;
5343 if (!enumproc(ptr->id))
5344 return TRUE;
5347 return TRUE;
5350 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
5352 LCID userlcid;
5354 TRACE("%p, %d\n", localename, buffersize);
5356 userlcid = GetUserDefaultLCID();
5357 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
5360 /******************************************************************************
5361 * NormalizeString (KERNEL32.@)
5363 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
5364 LPWSTR lpDstString, INT cwDstLength)
5366 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
5367 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5368 return 0;
5371 /******************************************************************************
5372 * IsNormalizedString (KERNEL32.@)
5374 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
5376 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5377 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5378 return FALSE;
5381 enum {
5382 BASE = 36,
5383 TMIN = 1,
5384 TMAX = 26,
5385 SKEW = 38,
5386 DAMP = 700,
5387 INIT_BIAS = 72,
5388 INIT_N = 128
5391 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5393 INT k;
5395 delta /= (firsttime ? DAMP : 2);
5396 delta += delta/numpoints;
5398 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5399 delta /= BASE-TMIN;
5400 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5403 /******************************************************************************
5404 * IdnToAscii (KERNEL32.@)
5405 * Implementation of Punycode based on RFC 3492.
5407 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5408 LPWSTR lpASCIICharStr, INT cchASCIIChar)
5410 static const WCHAR prefixW[] = {'x','n','-','-'};
5412 WCHAR *norm_str;
5413 INT i, label_start, label_end, norm_len, out_label, out = 0;
5415 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5416 lpASCIICharStr, cchASCIIChar);
5418 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5419 if(!norm_len)
5420 return 0;
5421 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5422 if(!norm_str) {
5423 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5424 return 0;
5426 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5427 cchUnicodeChar, norm_str, norm_len);
5428 if(!norm_len) {
5429 HeapFree(GetProcessHeap(), 0, norm_str);
5430 return 0;
5433 for(label_start=0; label_start<norm_len;) {
5434 INT n = INIT_N, bias = INIT_BIAS;
5435 INT delta = 0, b = 0, h;
5437 out_label = out;
5438 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5439 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5440 if(norm_str[i] < 0x80)
5441 b++;
5442 label_end = i;
5444 if(b == label_end-label_start) {
5445 if(label_end < norm_len)
5446 b++;
5447 if(!lpASCIICharStr) {
5448 out += b;
5449 }else if(out+b <= cchASCIIChar) {
5450 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5451 out += b;
5452 }else {
5453 HeapFree(GetProcessHeap(), 0, norm_str);
5454 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5455 return 0;
5457 label_start = label_end+1;
5458 continue;
5461 if(!lpASCIICharStr) {
5462 out += 5+b; /* strlen(xn--...-) */
5463 }else if(out+5+b <= cchASCIIChar) {
5464 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5465 out += 4;
5466 for(i=label_start; i<label_end; i++)
5467 if(norm_str[i] < 0x80)
5468 lpASCIICharStr[out++] = norm_str[i];
5469 lpASCIICharStr[out++] = '-';
5470 }else {
5471 HeapFree(GetProcessHeap(), 0, norm_str);
5472 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5473 return 0;
5475 if(!b)
5476 out--;
5478 for(h=b; h<label_end-label_start;) {
5479 INT m = 0xffff, q, k;
5481 for(i=label_start; i<label_end; i++) {
5482 if(norm_str[i]>=n && m>norm_str[i])
5483 m = norm_str[i];
5485 delta += (m-n)*(h+1);
5486 n = m;
5488 for(i=label_start; i<label_end; i++) {
5489 if(norm_str[i] < n) {
5490 delta++;
5491 }else if(norm_str[i] == n) {
5492 for(q=delta, k=BASE; ; k+=BASE) {
5493 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5494 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5495 if(!lpASCIICharStr) {
5496 out++;
5497 }else if(out+1 <= cchASCIIChar) {
5498 lpASCIICharStr[out++] = disp<='z'-'a' ?
5499 'a'+disp : '0'+disp-'z'+'a'-1;
5500 }else {
5501 HeapFree(GetProcessHeap(), 0, norm_str);
5502 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5503 return 0;
5505 if(q < t)
5506 break;
5507 q = (q-t)/(BASE-t);
5509 bias = adapt(delta, h+1, h==b);
5510 delta = 0;
5511 h++;
5514 delta++;
5515 n++;
5518 if(out-out_label > 63) {
5519 HeapFree(GetProcessHeap(), 0, norm_str);
5520 SetLastError(ERROR_INVALID_NAME);
5521 return 0;
5524 if(label_end < norm_len) {
5525 if(!lpASCIICharStr) {
5526 out++;
5527 }else if(out+1 <= cchASCIIChar) {
5528 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5529 }else {
5530 HeapFree(GetProcessHeap(), 0, norm_str);
5531 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5532 return 0;
5535 label_start = label_end+1;
5538 HeapFree(GetProcessHeap(), 0, norm_str);
5539 return out;
5542 /******************************************************************************
5543 * IdnToNameprepUnicode (KERNEL32.@)
5545 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5546 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5548 enum {
5549 UNASSIGNED = 0x1,
5550 PROHIBITED = 0x2,
5551 BIDI_RAL = 0x4,
5552 BIDI_L = 0x8
5555 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5556 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5557 const WCHAR *ptr;
5558 WORD flags;
5559 WCHAR buf[64], *map_str, norm_str[64], ch;
5560 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5561 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5563 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5564 lpNameprepCharStr, cchNameprepChar);
5566 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5567 SetLastError(ERROR_INVALID_FLAGS);
5568 return 0;
5571 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5572 SetLastError(ERROR_INVALID_PARAMETER);
5573 return 0;
5576 if(cchUnicodeChar == -1)
5577 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5578 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5579 SetLastError(ERROR_INVALID_NAME);
5580 return 0;
5583 for(label_start=0; label_start<cchUnicodeChar;) {
5584 ascii_only = TRUE;
5585 for(i=label_start; i<cchUnicodeChar; i++) {
5586 ch = lpUnicodeCharStr[i];
5588 if(i!=cchUnicodeChar-1 && !ch) {
5589 SetLastError(ERROR_INVALID_NAME);
5590 return 0;
5592 /* check if ch is one of label separators defined in RFC3490 */
5593 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5594 break;
5596 if(ch > 0x7f) {
5597 ascii_only = FALSE;
5598 continue;
5601 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5602 continue;
5603 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5604 || (ch>='0' && ch<='9') || ch=='-')
5605 continue;
5607 SetLastError(ERROR_INVALID_NAME);
5608 return 0;
5610 label_end = i;
5611 /* last label may be empty */
5612 if(label_start==label_end && ch) {
5613 SetLastError(ERROR_INVALID_NAME);
5614 return 0;
5617 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5618 lpUnicodeCharStr[label_end-1]=='-')) {
5619 SetLastError(ERROR_INVALID_NAME);
5620 return 0;
5623 if(ascii_only) {
5624 /* maximal label length is 63 characters */
5625 if(label_end-label_start > 63) {
5626 SetLastError(ERROR_INVALID_NAME);
5627 return 0;
5629 if(label_end < cchUnicodeChar)
5630 label_end++;
5632 if(!lpNameprepCharStr) {
5633 out += label_end-label_start;
5634 }else if(out+label_end-label_start <= cchNameprepChar) {
5635 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5636 (label_end-label_start)*sizeof(WCHAR));
5637 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5638 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5639 out += label_end-label_start;
5640 }else {
5641 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5642 return 0;
5645 label_start = label_end;
5646 continue;
5649 map_len = 0;
5650 for(i=label_start; i<label_end; i++) {
5651 ch = lpUnicodeCharStr[i];
5652 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5653 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5655 if(!ptr[0]) map_len++;
5656 else if(!ptr[1]) map_len++;
5657 else if(!ptr[2]) map_len += 2;
5658 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5660 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5661 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5662 if(!map_str) {
5663 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5664 return 0;
5666 }else {
5667 map_str = buf;
5669 map_len = 0;
5670 for(i=label_start; i<label_end; i++) {
5671 ch = lpUnicodeCharStr[i];
5672 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5673 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5675 if(!ptr[0]) {
5676 map_str[map_len++] = ch;
5677 }else if(!ptr[1]) {
5678 map_str[map_len++] = ptr[0];
5679 }else if(!ptr[2]) {
5680 map_str[map_len++] = ptr[0];
5681 map_str[map_len++] = ptr[1];
5682 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5683 map_str[map_len++] = ptr[0];
5684 map_str[map_len++] = ptr[1];
5685 map_str[map_len++] = ptr[2];
5689 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5690 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5691 if(map_str != buf)
5692 HeapFree(GetProcessHeap(), 0, map_str);
5693 if(!norm_len) {
5694 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5695 SetLastError(ERROR_INVALID_NAME);
5696 return 0;
5699 if(label_end < cchUnicodeChar) {
5700 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5701 label_end++;
5704 if(!lpNameprepCharStr) {
5705 out += norm_len;
5706 }else if(out+norm_len <= cchNameprepChar) {
5707 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5708 out += norm_len;
5709 }else {
5710 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5711 return 0;
5714 have_bidi_ral = prohibit_bidi_ral = FALSE;
5715 mask = PROHIBITED;
5716 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5717 mask |= UNASSIGNED;
5718 for(i=0; i<norm_len; i++) {
5719 ch = norm_str[i];
5720 flags = get_table_entry( nameprep_char_type, ch );
5722 if(flags & mask) {
5723 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5724 : ERROR_NO_UNICODE_TRANSLATION);
5725 return 0;
5728 if(flags & BIDI_RAL)
5729 have_bidi_ral = TRUE;
5730 if(flags & BIDI_L)
5731 prohibit_bidi_ral = TRUE;
5734 if(have_bidi_ral) {
5735 ch = norm_str[0];
5736 flags = get_table_entry( nameprep_char_type, ch );
5737 if((flags & BIDI_RAL) == 0)
5738 prohibit_bidi_ral = TRUE;
5740 ch = norm_str[norm_len-1];
5741 flags = get_table_entry( nameprep_char_type, ch );
5742 if((flags & BIDI_RAL) == 0)
5743 prohibit_bidi_ral = TRUE;
5746 if(have_bidi_ral && prohibit_bidi_ral) {
5747 SetLastError(ERROR_INVALID_NAME);
5748 return 0;
5751 label_start = label_end;
5754 return out;
5757 /******************************************************************************
5758 * IdnToUnicode (KERNEL32.@)
5760 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5761 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5763 extern const unsigned short nameprep_char_type[];
5765 INT i, label_start, label_end, out_label, out = 0;
5766 WCHAR ch;
5768 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5769 lpUnicodeCharStr, cchUnicodeChar);
5771 for(label_start=0; label_start<cchASCIIChar;) {
5772 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5774 out_label = out;
5775 for(i=label_start; i<cchASCIIChar; i++) {
5776 ch = lpASCIICharStr[i];
5778 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5779 SetLastError(ERROR_INVALID_NAME);
5780 return 0;
5783 if(!ch || ch=='.')
5784 break;
5785 if(ch == '-')
5786 delim = i;
5788 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5789 continue;
5790 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5791 || (ch>='0' && ch<='9') || ch=='-')
5792 continue;
5794 SetLastError(ERROR_INVALID_NAME);
5795 return 0;
5797 label_end = i;
5798 /* last label may be empty */
5799 if(label_start==label_end && ch) {
5800 SetLastError(ERROR_INVALID_NAME);
5801 return 0;
5804 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5805 lpASCIICharStr[label_end-1]=='-')) {
5806 SetLastError(ERROR_INVALID_NAME);
5807 return 0;
5809 if(label_end-label_start > 63) {
5810 SetLastError(ERROR_INVALID_NAME);
5811 return 0;
5814 if(label_end-label_start<4 ||
5815 tolowerW(lpASCIICharStr[label_start])!='x' ||
5816 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5817 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5818 if(label_end < cchASCIIChar)
5819 label_end++;
5821 if(!lpUnicodeCharStr) {
5822 out += label_end-label_start;
5823 }else if(out+label_end-label_start <= cchUnicodeChar) {
5824 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5825 (label_end-label_start)*sizeof(WCHAR));
5826 out += label_end-label_start;
5827 }else {
5828 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5829 return 0;
5832 label_start = label_end;
5833 continue;
5836 if(delim == label_start+3)
5837 delim++;
5838 if(!lpUnicodeCharStr) {
5839 out += delim-label_start-4;
5840 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5841 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5842 (delim-label_start-4)*sizeof(WCHAR));
5843 out += delim-label_start-4;
5844 }else {
5845 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5846 return 0;
5848 if(out != out_label)
5849 delim++;
5851 for(i=delim; i<label_end;) {
5852 old_pos = pos;
5853 w = 1;
5854 for(k=BASE; ; k+=BASE) {
5855 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5856 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5857 SetLastError(ERROR_INVALID_NAME);
5858 return 0;
5860 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5861 pos += digit*w;
5862 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5863 if(digit < t)
5864 break;
5865 w *= BASE-t;
5867 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5868 n += pos/(out-out_label+1);
5869 pos %= out-out_label+1;
5871 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5872 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5873 SetLastError(ERROR_INVALID_NAME);
5874 return 0;
5876 if(!lpUnicodeCharStr) {
5877 out++;
5878 }else if(out+1 <= cchASCIIChar) {
5879 memmove(lpUnicodeCharStr+out_label+pos+1,
5880 lpUnicodeCharStr+out_label+pos,
5881 (out-out_label-pos)*sizeof(WCHAR));
5882 lpUnicodeCharStr[out_label+pos] = n;
5883 out++;
5884 }else {
5885 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5886 return 0;
5888 pos++;
5891 if(out-out_label > 63) {
5892 SetLastError(ERROR_INVALID_NAME);
5893 return 0;
5896 if(label_end < cchASCIIChar) {
5897 if(!lpUnicodeCharStr) {
5898 out++;
5899 }else if(out+1 <= cchUnicodeChar) {
5900 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5901 }else {
5902 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5903 return 0;
5906 label_start = label_end+1;
5909 return out;
5913 /******************************************************************************
5914 * GetFileMUIPath (KERNEL32.@)
5917 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5918 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5920 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5921 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5923 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5925 return FALSE;
5928 /******************************************************************************
5929 * GetFileMUIInfo (KERNEL32.@)
5932 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5934 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5936 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5937 return FALSE;
5940 /******************************************************************************
5941 * ResolveLocaleName (KERNEL32.@)
5944 INT WINAPI ResolveLocaleName(LPCWSTR name, LPWSTR localename, INT len)
5946 FIXME("stub: %s, %p, %d\n", wine_dbgstr_w(name), localename, len);
5948 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5949 return 0;
5952 /******************************************************************************
5953 * FindNLSStringEx (KERNEL32.@)
5956 INT WINAPI FindNLSStringEx(const WCHAR *localename, DWORD flags, const WCHAR *src,
5957 INT src_size, const WCHAR *value, INT value_size,
5958 INT *found, NLSVERSIONINFO *version_info, void *reserved,
5959 LPARAM sort_handle)
5962 /* FIXME: this function should normalize strings before calling CompareStringEx() */
5963 DWORD mask = flags;
5964 int offset, inc, count;
5966 TRACE("%s %x %s %d %s %d %p %p %p %ld\n", wine_dbgstr_w(localename), flags,
5967 wine_dbgstr_w(src), src_size, wine_dbgstr_w(value), value_size, found,
5968 version_info, reserved, sort_handle);
5970 if (version_info != NULL || reserved != NULL || sort_handle != 0 ||
5971 !IsValidLocaleName(localename) || src == NULL || src_size == 0 ||
5972 src_size < -1 || value == NULL || value_size == 0 || value_size < -1)
5974 SetLastError(ERROR_INVALID_PARAMETER);
5975 return -1;
5977 if (src_size == -1)
5978 src_size = strlenW(src);
5979 if (value_size == -1)
5980 value_size = strlenW(value);
5982 src_size -= value_size;
5983 if (src_size < 0) return -1;
5985 mask = flags & ~(FIND_FROMSTART | FIND_FROMEND | FIND_STARTSWITH | FIND_ENDSWITH);
5986 count = flags & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
5987 offset = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
5988 inc = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
5989 while (count--)
5991 if (CompareStringEx(localename, mask, src + offset, value_size, value, value_size, NULL, NULL, 0) == CSTR_EQUAL)
5993 if (found)
5994 *found = value_size;
5995 return offset;
5997 offset += inc;
6000 return -1;