wined3d: Explicitly calculate the sub-resource level in wined3d_surface_upload_data().
[wine.git] / dlls / kernel32 / locale.c
blobe4c323ead632002dff6680e58a5c87b29b104ab6
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;
1400 if (flags) FIXME( "unsupported flags %x\n", flags );
1402 if (name == LOCALE_NAME_USER_DEFAULT)
1403 return GetUserDefaultLCID();
1405 /* string parsing */
1406 parse_locale_name( name, &locale_name );
1408 TRACE( "found lcid %x for %s, matches %d\n",
1409 locale_name.lcid, debugstr_w(name), locale_name.matches );
1411 if (!locale_name.matches)
1413 SetLastError(ERROR_INVALID_PARAMETER);
1414 return 0;
1417 if (locale_name.matches == 1)
1418 WARN( "locale %s not recognized, defaulting to %s\n",
1419 debugstr_w(name), debugstr_w(locale_name.lang) );
1421 return locale_name.lcid;
1425 /***********************************************************************
1426 * LCIDToLocaleName (KERNEL32.@)
1428 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1430 if (flags) FIXME( "unsupported flags %x\n", flags );
1432 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1436 /******************************************************************************
1437 * get_locale_registry_value
1439 * Gets the registry value name and cache for a given lctype.
1441 static struct registry_value *get_locale_registry_value( DWORD lctype )
1443 int i;
1444 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1445 if (registry_values[i].lctype == lctype)
1446 return &registry_values[i];
1447 return NULL;
1451 /******************************************************************************
1452 * get_registry_locale_info
1454 * Retrieve user-modified locale info from the registry.
1455 * Return length, 0 on error, -1 if not found.
1457 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1459 DWORD size;
1460 INT ret;
1461 HANDLE hkey;
1462 NTSTATUS status;
1463 UNICODE_STRING nameW;
1464 KEY_VALUE_PARTIAL_INFORMATION *info;
1465 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1467 RtlEnterCriticalSection( &cache_section );
1469 if (!registry_value->cached_value)
1471 if (!(hkey = create_registry_key()))
1473 RtlLeaveCriticalSection( &cache_section );
1474 return -1;
1477 RtlInitUnicodeString( &nameW, registry_value->name );
1478 size = info_size + len * sizeof(WCHAR);
1480 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1482 NtClose( hkey );
1483 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1484 RtlLeaveCriticalSection( &cache_section );
1485 return 0;
1488 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1490 /* try again with a bigger buffer when we have to return the correct size */
1491 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1493 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1494 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1496 info = new_info;
1497 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1501 NtClose( hkey );
1503 if (!status)
1505 INT length = (size - info_size) / sizeof(WCHAR);
1506 LPWSTR cached_value;
1508 if (!length || ((WCHAR *)&info->Data)[length-1])
1509 length++;
1511 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1513 if (!cached_value)
1515 HeapFree( GetProcessHeap(), 0, info );
1516 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1517 RtlLeaveCriticalSection( &cache_section );
1518 return 0;
1521 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1522 cached_value[length-1] = 0;
1523 HeapFree( GetProcessHeap(), 0, info );
1524 registry_value->cached_value = cached_value;
1526 else
1528 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1530 ret = (size - info_size) / sizeof(WCHAR);
1532 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1534 ret = -1;
1536 else
1538 SetLastError( RtlNtStatusToDosError(status) );
1539 ret = 0;
1541 HeapFree( GetProcessHeap(), 0, info );
1542 RtlLeaveCriticalSection( &cache_section );
1543 return ret;
1547 ret = lstrlenW( registry_value->cached_value ) + 1;
1549 if (buffer)
1551 if (ret > len)
1553 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1554 ret = 0;
1556 else
1558 lstrcpyW( buffer, registry_value->cached_value );
1562 RtlLeaveCriticalSection( &cache_section );
1564 return ret;
1568 /******************************************************************************
1569 * GetLocaleInfoA (KERNEL32.@)
1571 * Get information about an aspect of a locale.
1573 * PARAMS
1574 * lcid [I] LCID of the locale
1575 * lctype [I] LCTYPE_ flags from "winnls.h"
1576 * buffer [O] Destination for the information
1577 * len [I] Length of buffer in characters
1579 * RETURNS
1580 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1581 * with the information.
1582 * Failure: 0. Use GetLastError() to determine the cause.
1584 * NOTES
1585 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1586 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1587 * which is a bit string.
1589 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1591 WCHAR *bufferW;
1592 INT lenW, ret;
1594 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1596 if (len < 0 || (len && !buffer))
1598 SetLastError( ERROR_INVALID_PARAMETER );
1599 return 0;
1601 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1602 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1604 SetLastError( ERROR_INVALID_FLAGS );
1605 return 0;
1608 if (!len) buffer = NULL;
1610 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1612 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1614 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1615 return 0;
1617 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1619 if ((lctype & LOCALE_RETURN_NUMBER) ||
1620 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1622 /* it's not an ASCII string, just bytes */
1623 ret *= sizeof(WCHAR);
1624 if (buffer)
1626 if (ret <= len) memcpy( buffer, bufferW, ret );
1627 else
1629 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1630 ret = 0;
1634 else
1636 UINT codepage = CP_ACP;
1637 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1638 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1641 HeapFree( GetProcessHeap(), 0, bufferW );
1642 return ret;
1645 static int get_value_base_by_lctype( LCTYPE lctype )
1647 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1650 /******************************************************************************
1651 * GetLocaleInfoW (KERNEL32.@)
1653 * See GetLocaleInfoA.
1655 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1657 LANGID lang_id;
1658 HRSRC hrsrc;
1659 HGLOBAL hmem;
1660 INT ret;
1661 UINT lcflags;
1662 const WCHAR *p;
1663 unsigned int i;
1665 if (len < 0 || (len && !buffer))
1667 SetLastError( ERROR_INVALID_PARAMETER );
1668 return 0;
1670 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1671 !is_genitive_name_supported( lctype ))
1673 SetLastError( ERROR_INVALID_FLAGS );
1674 return 0;
1677 if (!len) buffer = NULL;
1679 lcid = convert_default_lcid( lcid, lctype );
1681 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1682 lctype &= 0xffff;
1684 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1686 /* first check for overrides in the registry */
1688 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1689 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1691 struct registry_value *value = get_locale_registry_value(lctype);
1693 if (value)
1695 if (lcflags & LOCALE_RETURN_NUMBER)
1697 WCHAR tmp[16];
1698 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1699 if (ret > 0)
1701 WCHAR *end;
1702 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1703 if (*end) /* invalid number */
1705 SetLastError( ERROR_INVALID_FLAGS );
1706 return 0;
1708 ret = sizeof(UINT)/sizeof(WCHAR);
1709 if (!buffer) return ret;
1710 if (ret > len)
1712 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1713 return 0;
1715 memcpy( buffer, &number, sizeof(number) );
1718 else ret = get_registry_locale_info( value, buffer, len );
1720 if (ret != -1) return ret;
1724 /* now load it from kernel resources */
1726 lang_id = LANGIDFROMLCID( lcid );
1728 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1729 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL) lang_id = get_default_sublang( lang_id );
1731 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1732 ULongToPtr((lctype >> 4) + 1), lang_id )))
1734 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1735 return 0;
1737 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1738 return 0;
1740 p = LockResource( hmem );
1741 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1743 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1744 else if (is_genitive_name_supported( lctype ) && *p)
1746 /* genitive form's stored after a null separator from a nominative */
1747 for (i = 1; i <= *p; i++) if (!p[i]) break;
1749 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1751 ret = *p - i + 1;
1752 p += i;
1754 else ret = i;
1756 else
1757 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1759 if (!buffer) return ret;
1761 if (ret > len)
1763 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1764 return 0;
1767 if (lcflags & LOCALE_RETURN_NUMBER)
1769 UINT number;
1770 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1771 if (!tmp) return 0;
1772 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1773 tmp[*p] = 0;
1774 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1775 if (!*end)
1776 memcpy( buffer, &number, sizeof(number) );
1777 else /* invalid number */
1779 SetLastError( ERROR_INVALID_FLAGS );
1780 ret = 0;
1782 HeapFree( GetProcessHeap(), 0, tmp );
1784 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1785 lcid, lctype, buffer, len, number );
1787 else
1789 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1790 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1792 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1793 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1795 return ret;
1798 /******************************************************************************
1799 * GetLocaleInfoEx (KERNEL32.@)
1801 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1803 LCID lcid = LocaleNameToLCID(locale, 0);
1805 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1807 if (!lcid) return 0;
1809 /* special handling for neutral locale names */
1810 if (locale && strlenW(locale) == 2)
1812 switch (info)
1814 case LOCALE_SNAME:
1815 if (len && len < 3)
1817 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1818 return 0;
1820 if (len) strcpyW(buffer, locale);
1821 return 3;
1822 case LOCALE_SPARENT:
1823 if (len) buffer[0] = 0;
1824 return 1;
1828 return GetLocaleInfoW(lcid, info, buffer, len);
1831 /******************************************************************************
1832 * SetLocaleInfoA [KERNEL32.@]
1834 * Set information about an aspect of a locale.
1836 * PARAMS
1837 * lcid [I] LCID of the locale
1838 * lctype [I] LCTYPE_ flags from "winnls.h"
1839 * data [I] Information to set
1841 * RETURNS
1842 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1843 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1844 * Failure: FALSE. Use GetLastError() to determine the cause.
1846 * NOTES
1847 * - Values are only be set for the current user locale; the system locale
1848 * settings cannot be changed.
1849 * - Any settings changed by this call are lost when the locale is changed by
1850 * the control panel (in Wine, this happens every time you change LANG).
1851 * - The native implementation of this function does not check that lcid matches
1852 * the current user locale, and simply sets the new values. Wine warns you in
1853 * this case, but behaves the same.
1855 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1857 UINT codepage = CP_ACP;
1858 WCHAR *strW;
1859 DWORD len;
1860 BOOL ret;
1862 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1864 if (!data)
1866 SetLastError( ERROR_INVALID_PARAMETER );
1867 return FALSE;
1869 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1870 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1872 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1873 return FALSE;
1875 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1876 ret = SetLocaleInfoW( lcid, lctype, strW );
1877 HeapFree( GetProcessHeap(), 0, strW );
1878 return ret;
1882 /******************************************************************************
1883 * SetLocaleInfoW (KERNEL32.@)
1885 * See SetLocaleInfoA.
1887 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1889 struct registry_value *value;
1890 static const WCHAR intlW[] = {'i','n','t','l',0 };
1891 UNICODE_STRING valueW;
1892 NTSTATUS status;
1893 HANDLE hkey;
1895 lctype &= 0xffff;
1896 value = get_locale_registry_value( lctype );
1898 if (!data || !value)
1900 SetLastError( ERROR_INVALID_PARAMETER );
1901 return FALSE;
1904 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1906 SetLastError( ERROR_INVALID_FLAGS );
1907 return FALSE;
1910 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1912 /* FIXME: should check that data to set is sane */
1914 /* FIXME: profile functions should map to registry */
1915 WriteProfileStringW( intlW, value->name, data );
1917 if (!(hkey = create_registry_key())) return FALSE;
1918 RtlInitUnicodeString( &valueW, value->name );
1919 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1921 RtlEnterCriticalSection( &cache_section );
1922 HeapFree( GetProcessHeap(), 0, value->cached_value );
1923 value->cached_value = NULL;
1924 RtlLeaveCriticalSection( &cache_section );
1926 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1928 /* Set I-value from S value */
1929 WCHAR *lpD, *lpM, *lpY;
1930 WCHAR szBuff[2];
1932 lpD = strrchrW(data, 'd');
1933 lpM = strrchrW(data, 'M');
1934 lpY = strrchrW(data, 'y');
1936 if (lpD <= lpM)
1938 szBuff[0] = '1'; /* D-M-Y */
1940 else
1942 if (lpY <= lpM)
1943 szBuff[0] = '2'; /* Y-M-D */
1944 else
1945 szBuff[0] = '0'; /* M-D-Y */
1948 szBuff[1] = '\0';
1950 if (lctype == LOCALE_SSHORTDATE)
1951 lctype = LOCALE_IDATE;
1952 else
1953 lctype = LOCALE_ILDATE;
1955 value = get_locale_registry_value( lctype );
1957 WriteProfileStringW( intlW, value->name, szBuff );
1959 RtlInitUnicodeString( &valueW, value->name );
1960 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1962 RtlEnterCriticalSection( &cache_section );
1963 HeapFree( GetProcessHeap(), 0, value->cached_value );
1964 value->cached_value = NULL;
1965 RtlLeaveCriticalSection( &cache_section );
1968 NtClose( hkey );
1970 if (status) SetLastError( RtlNtStatusToDosError(status) );
1971 return !status;
1975 /******************************************************************************
1976 * GetACP (KERNEL32.@)
1978 * Get the current Ansi code page Id for the system.
1980 * PARAMS
1981 * None.
1983 * RETURNS
1984 * The current Ansi code page identifier for the system.
1986 UINT WINAPI GetACP(void)
1988 assert( ansi_cptable );
1989 return ansi_cptable->info.codepage;
1993 /******************************************************************************
1994 * SetCPGlobal (KERNEL32.@)
1996 * Set the current Ansi code page Id for the system.
1998 * PARAMS
1999 * acp [I] code page ID to be the new ACP.
2001 * RETURNS
2002 * The previous ACP.
2004 UINT WINAPI SetCPGlobal( UINT acp )
2006 UINT ret = GetACP();
2007 const union cptable *new_cptable = wine_cp_get_table( acp );
2009 if (new_cptable) ansi_cptable = new_cptable;
2010 return ret;
2014 /***********************************************************************
2015 * GetOEMCP (KERNEL32.@)
2017 * Get the current OEM code page Id for the system.
2019 * PARAMS
2020 * None.
2022 * RETURNS
2023 * The current OEM code page identifier for the system.
2025 UINT WINAPI GetOEMCP(void)
2027 assert( oem_cptable );
2028 return oem_cptable->info.codepage;
2032 /***********************************************************************
2033 * IsValidCodePage (KERNEL32.@)
2035 * Determine if a given code page identifier is valid.
2037 * PARAMS
2038 * codepage [I] Code page Id to verify.
2040 * RETURNS
2041 * TRUE, If codepage is valid and available on the system,
2042 * FALSE otherwise.
2044 BOOL WINAPI IsValidCodePage( UINT codepage )
2046 switch(codepage) {
2047 case CP_UTF7:
2048 case CP_UTF8:
2049 return TRUE;
2050 default:
2051 return wine_cp_get_table( codepage ) != NULL;
2056 /***********************************************************************
2057 * IsDBCSLeadByteEx (KERNEL32.@)
2059 * Determine if a character is a lead byte in a given code page.
2061 * PARAMS
2062 * codepage [I] Code page for the test.
2063 * testchar [I] Character to test
2065 * RETURNS
2066 * TRUE, if testchar is a lead byte in codepage,
2067 * FALSE otherwise.
2069 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
2071 const union cptable *table = get_codepage_table( codepage );
2072 return table && wine_is_dbcs_leadbyte( table, testchar );
2076 /***********************************************************************
2077 * IsDBCSLeadByte (KERNEL32.@)
2078 * IsDBCSLeadByte (KERNEL.207)
2080 * Determine if a character is a lead byte.
2082 * PARAMS
2083 * testchar [I] Character to test
2085 * RETURNS
2086 * TRUE, if testchar is a lead byte in the ANSI code page,
2087 * FALSE otherwise.
2089 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2091 if (!ansi_cptable) return FALSE;
2092 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2096 /***********************************************************************
2097 * GetCPInfo (KERNEL32.@)
2099 * Get information about a code page.
2101 * PARAMS
2102 * codepage [I] Code page number
2103 * cpinfo [O] Destination for code page information
2105 * RETURNS
2106 * Success: TRUE. cpinfo is updated with the information about codepage.
2107 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2109 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2111 const union cptable *table;
2113 if (!cpinfo)
2115 SetLastError( ERROR_INVALID_PARAMETER );
2116 return FALSE;
2119 if (!(table = get_codepage_table( codepage )))
2121 switch(codepage)
2123 case CP_UTF7:
2124 case CP_UTF8:
2125 cpinfo->DefaultChar[0] = 0x3f;
2126 cpinfo->DefaultChar[1] = 0;
2127 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2128 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2129 return TRUE;
2132 SetLastError( ERROR_INVALID_PARAMETER );
2133 return FALSE;
2135 if (table->info.def_char & 0xff00)
2137 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2138 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2140 else
2142 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2143 cpinfo->DefaultChar[1] = 0;
2145 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2146 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2147 else
2148 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2150 return TRUE;
2153 /***********************************************************************
2154 * GetCPInfoExA (KERNEL32.@)
2156 * Get extended information about a code page.
2158 * PARAMS
2159 * codepage [I] Code page number
2160 * dwFlags [I] Reserved, must to 0.
2161 * cpinfo [O] Destination for code page information
2163 * RETURNS
2164 * Success: TRUE. cpinfo is updated with the information about codepage.
2165 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2167 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2169 CPINFOEXW cpinfoW;
2171 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2172 return FALSE;
2174 /* the layout is the same except for CodePageName */
2175 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2176 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2177 return TRUE;
2180 /***********************************************************************
2181 * GetCPInfoExW (KERNEL32.@)
2183 * Unicode version of GetCPInfoExA.
2185 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2187 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2188 return FALSE;
2190 switch(codepage)
2192 case CP_UTF7:
2194 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2196 cpinfo->CodePage = CP_UTF7;
2197 cpinfo->UnicodeDefaultChar = 0x3f;
2198 strcpyW(cpinfo->CodePageName, utf7);
2199 break;
2202 case CP_UTF8:
2204 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2206 cpinfo->CodePage = CP_UTF8;
2207 cpinfo->UnicodeDefaultChar = 0x3f;
2208 strcpyW(cpinfo->CodePageName, utf8);
2209 break;
2212 default:
2214 const union cptable *table = get_codepage_table( codepage );
2216 cpinfo->CodePage = table->info.codepage;
2217 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2218 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2219 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2220 break;
2223 return TRUE;
2226 /***********************************************************************
2227 * EnumSystemCodePagesA (KERNEL32.@)
2229 * Call a user defined function for every code page installed on the system.
2231 * PARAMS
2232 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2233 * flags [I] Reserved, set to 0.
2235 * RETURNS
2236 * TRUE, If all code pages have been enumerated, or
2237 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2239 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2241 const union cptable *table;
2242 char buffer[10];
2243 int index = 0;
2245 for (;;)
2247 if (!(table = wine_cp_enum_table( index++ ))) break;
2248 sprintf( buffer, "%d", table->info.codepage );
2249 if (!lpfnCodePageEnum( buffer )) break;
2251 return TRUE;
2255 /***********************************************************************
2256 * EnumSystemCodePagesW (KERNEL32.@)
2258 * See EnumSystemCodePagesA.
2260 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2262 const union cptable *table;
2263 WCHAR buffer[10], *p;
2264 int page, index = 0;
2266 for (;;)
2268 if (!(table = wine_cp_enum_table( index++ ))) break;
2269 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2270 *--p = 0;
2271 page = table->info.codepage;
2274 *--p = '0' + (page % 10);
2275 page /= 10;
2276 } while( page );
2277 if (!lpfnCodePageEnum( p )) break;
2279 return TRUE;
2283 /***********************************************************************
2284 * utf7_write_w
2286 * Helper for utf7_mbstowcs
2288 * RETURNS
2289 * TRUE on success, FALSE on error
2291 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2293 if (dstlen > 0)
2295 if (*index >= dstlen)
2296 return FALSE;
2298 dst[*index] = character;
2301 (*index)++;
2303 return TRUE;
2306 /***********************************************************************
2307 * utf7_mbstowcs
2309 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2311 * RETURNS
2312 * On success, the number of characters written
2313 * On dst buffer overflow, -1
2315 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2317 static const signed char base64_decoding_table[] =
2319 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2320 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2321 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2322 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2323 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2324 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2325 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2326 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2329 const char *source_end = src + srclen;
2330 int dest_index = 0;
2332 DWORD byte_pair = 0;
2333 short offset = 0;
2335 while (src < source_end)
2337 if (*src == '+')
2339 src++;
2340 if (src >= source_end)
2341 break;
2343 if (*src == '-')
2345 /* just a plus sign escaped as +- */
2346 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2347 return -1;
2348 src++;
2349 continue;
2354 signed char sextet = *src;
2355 if (sextet == '-')
2357 /* skip over the dash and end base64 decoding
2358 * the current, unfinished byte pair is discarded */
2359 src++;
2360 offset = 0;
2361 break;
2363 if (sextet < 0)
2365 /* the next character of src is < 0 and therefore not part of a base64 sequence
2366 * the current, unfinished byte pair is NOT discarded in this case
2367 * this is probably a bug in Windows */
2368 break;
2371 sextet = base64_decoding_table[sextet];
2372 if (sextet == -1)
2374 /* -1 means that the next character of src is not part of a base64 sequence
2375 * in other words, all sextets in this base64 sequence have been processed
2376 * the current, unfinished byte pair is discarded */
2377 offset = 0;
2378 break;
2381 byte_pair = (byte_pair << 6) | sextet;
2382 offset += 6;
2384 if (offset >= 16)
2386 /* this byte pair is done */
2387 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2388 return -1;
2389 offset -= 16;
2392 src++;
2394 while (src < source_end);
2396 else
2398 /* we have to convert to unsigned char in case *src < 0 */
2399 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2400 return -1;
2401 src++;
2405 return dest_index;
2408 /***********************************************************************
2409 * MultiByteToWideChar (KERNEL32.@)
2411 * Convert a multibyte character string into a Unicode string.
2413 * PARAMS
2414 * page [I] Codepage character set to convert from
2415 * flags [I] Character mapping flags
2416 * src [I] Source string buffer
2417 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2418 * dst [O] Destination buffer
2419 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2421 * RETURNS
2422 * Success: If dstlen > 0, the number of characters written to dst.
2423 * If dstlen == 0, the number of characters needed to perform the
2424 * conversion. In both cases the count includes the terminating NUL.
2425 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2426 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2427 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2428 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2429 * possible for src.
2431 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2432 LPWSTR dst, INT dstlen )
2434 const union cptable *table;
2435 int ret;
2437 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2439 SetLastError( ERROR_INVALID_PARAMETER );
2440 return 0;
2443 if (srclen < 0) srclen = strlen(src) + 1;
2445 switch(page)
2447 case CP_SYMBOL:
2448 if (flags)
2450 SetLastError( ERROR_INVALID_FLAGS );
2451 return 0;
2453 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2454 break;
2455 case CP_UTF7:
2456 if (flags)
2458 SetLastError( ERROR_INVALID_FLAGS );
2459 return 0;
2461 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2462 break;
2463 case CP_UNIXCP:
2464 if (unix_cptable)
2466 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2467 break;
2469 #ifdef __APPLE__
2470 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2471 #endif
2472 /* fall through */
2473 case CP_UTF8:
2474 if (flags & ~MB_FLAGSMASK)
2476 SetLastError( ERROR_INVALID_FLAGS );
2477 return 0;
2479 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2480 break;
2481 default:
2482 if (!(table = get_codepage_table( page )))
2484 SetLastError( ERROR_INVALID_PARAMETER );
2485 return 0;
2487 if (flags & ~MB_FLAGSMASK)
2489 SetLastError( ERROR_INVALID_FLAGS );
2490 return 0;
2492 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2493 break;
2496 if (ret < 0)
2498 switch(ret)
2500 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2501 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2503 ret = 0;
2505 TRACE("cp %d %s -> %s, ret = %d\n",
2506 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2507 return ret;
2511 /***********************************************************************
2512 * utf7_can_directly_encode
2514 * Helper for utf7_wcstombs
2516 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2518 static const BOOL directly_encodable_table[] =
2520 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2521 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2522 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2523 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2524 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2525 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2526 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2527 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2530 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2533 /***********************************************************************
2534 * utf7_write_c
2536 * Helper for utf7_wcstombs
2538 * RETURNS
2539 * TRUE on success, FALSE on error
2541 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2543 if (dstlen > 0)
2545 if (*index >= dstlen)
2546 return FALSE;
2548 dst[*index] = character;
2551 (*index)++;
2553 return TRUE;
2556 /***********************************************************************
2557 * utf7_wcstombs
2559 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2561 * RETURNS
2562 * On success, the number of characters written
2563 * On dst buffer overflow, -1
2565 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2567 static const char base64_encoding_table[] =
2568 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2570 const WCHAR *source_end = src + srclen;
2571 int dest_index = 0;
2573 while (src < source_end)
2575 if (*src == '+')
2577 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2578 return -1;
2579 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2580 return -1;
2581 src++;
2583 else if (utf7_can_directly_encode(*src))
2585 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2586 return -1;
2587 src++;
2589 else
2591 unsigned int offset = 0;
2592 DWORD byte_pair = 0;
2594 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2595 return -1;
2597 while (src < source_end && !utf7_can_directly_encode(*src))
2599 byte_pair = (byte_pair << 16) | *src;
2600 offset += 16;
2601 while (offset >= 6)
2603 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2604 return -1;
2605 offset -= 6;
2607 src++;
2610 if (offset)
2612 /* Windows won't create a padded base64 character if there's no room for the - sign
2613 * as well ; this is probably a bug in Windows */
2614 if (dstlen > 0 && dest_index + 1 >= dstlen)
2615 return -1;
2617 byte_pair <<= (6 - offset);
2618 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2619 return -1;
2622 /* Windows always explicitly terminates the base64 sequence
2623 even though RFC 2152 (page 3, rule 2) does not require this */
2624 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2625 return -1;
2629 return dest_index;
2632 /***********************************************************************
2633 * WideCharToMultiByte (KERNEL32.@)
2635 * Convert a Unicode character string into a multibyte string.
2637 * PARAMS
2638 * page [I] Code page character set to convert to
2639 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2640 * src [I] Source string buffer
2641 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2642 * dst [O] Destination buffer
2643 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2644 * defchar [I] Default character to use for conversion if no exact
2645 * conversion can be made
2646 * used [O] Set if default character was used in the conversion
2648 * RETURNS
2649 * Success: If dstlen > 0, the number of characters written to dst.
2650 * If dstlen == 0, number of characters needed to perform the
2651 * conversion. In both cases the count includes the terminating NUL.
2652 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2653 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2654 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2655 * parameter was given.
2657 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2658 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2660 const union cptable *table;
2661 int ret, used_tmp;
2663 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2665 SetLastError( ERROR_INVALID_PARAMETER );
2666 return 0;
2669 if (srclen < 0) srclen = strlenW(src) + 1;
2671 switch(page)
2673 case CP_SYMBOL:
2674 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2675 if (flags)
2677 SetLastError( ERROR_INVALID_FLAGS );
2678 return 0;
2680 if (defchar || used)
2682 SetLastError( ERROR_INVALID_PARAMETER );
2683 return 0;
2685 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2686 break;
2687 case CP_UTF7:
2688 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2689 if (defchar || used)
2691 SetLastError( ERROR_INVALID_PARAMETER );
2692 return 0;
2694 if (flags)
2696 SetLastError( ERROR_INVALID_FLAGS );
2697 return 0;
2699 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2700 break;
2701 case CP_UNIXCP:
2702 if (unix_cptable)
2704 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2705 defchar, used ? &used_tmp : NULL );
2706 break;
2708 /* fall through */
2709 case CP_UTF8:
2710 if (defchar || used)
2712 SetLastError( ERROR_INVALID_PARAMETER );
2713 return 0;
2715 if (flags & ~WC_FLAGSMASK)
2717 SetLastError( ERROR_INVALID_FLAGS );
2718 return 0;
2720 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2721 break;
2722 default:
2723 if (!(table = get_codepage_table( page )))
2725 SetLastError( ERROR_INVALID_PARAMETER );
2726 return 0;
2728 if (flags & ~WC_FLAGSMASK)
2730 SetLastError( ERROR_INVALID_FLAGS );
2731 return 0;
2733 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2734 defchar, used ? &used_tmp : NULL );
2735 if (used) *used = used_tmp;
2736 break;
2739 if (ret < 0)
2741 switch(ret)
2743 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2744 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2746 ret = 0;
2748 TRACE("cp %d %s -> %s, ret = %d\n",
2749 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2750 return ret;
2754 /***********************************************************************
2755 * GetThreadLocale (KERNEL32.@)
2757 * Get the current threads locale.
2759 * PARAMS
2760 * None.
2762 * RETURNS
2763 * The LCID currently associated with the calling thread.
2765 LCID WINAPI GetThreadLocale(void)
2767 LCID ret = NtCurrentTeb()->CurrentLocale;
2768 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2769 return ret;
2772 /**********************************************************************
2773 * SetThreadLocale (KERNEL32.@)
2775 * Set the current threads locale.
2777 * PARAMS
2778 * lcid [I] LCID of the locale to set
2780 * RETURNS
2781 * Success: TRUE. The threads locale is set to lcid.
2782 * Failure: FALSE. Use GetLastError() to determine the cause.
2784 BOOL WINAPI SetThreadLocale( LCID lcid )
2786 TRACE("(0x%04X)\n", lcid);
2788 lcid = ConvertDefaultLocale(lcid);
2790 if (lcid != GetThreadLocale())
2792 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2794 SetLastError(ERROR_INVALID_PARAMETER);
2795 return FALSE;
2798 NtCurrentTeb()->CurrentLocale = lcid;
2800 return TRUE;
2803 /**********************************************************************
2804 * SetThreadUILanguage (KERNEL32.@)
2806 * Set the current threads UI language.
2808 * PARAMS
2809 * langid [I] LANGID of the language to set, or 0 to use
2810 * the available language which is best supported
2811 * for console applications
2813 * RETURNS
2814 * Success: The return value is the same as the input value.
2815 * Failure: The return value differs from the input value.
2816 * Use GetLastError() to determine the cause.
2818 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2820 TRACE("(0x%04x) stub - returning success\n", langid);
2821 return langid;
2824 /******************************************************************************
2825 * ConvertDefaultLocale (KERNEL32.@)
2827 * Convert a default locale identifier into a real identifier.
2829 * PARAMS
2830 * lcid [I] LCID identifier of the locale to convert
2832 * RETURNS
2833 * lcid unchanged, if not a default locale or its sublanguage is
2834 * not SUBLANG_NEUTRAL.
2835 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2836 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2837 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2839 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2841 LANGID langid;
2843 switch (lcid)
2845 case LOCALE_INVARIANT:
2846 /* keep as-is */
2847 break;
2848 case LOCALE_SYSTEM_DEFAULT:
2849 lcid = GetSystemDefaultLCID();
2850 break;
2851 case LOCALE_USER_DEFAULT:
2852 case LOCALE_NEUTRAL:
2853 lcid = GetUserDefaultLCID();
2854 break;
2855 default:
2856 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2857 langid = LANGIDFROMLCID(lcid);
2858 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2860 langid = get_default_sublang( langid );
2861 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2864 return lcid;
2868 /******************************************************************************
2869 * IsValidLocale (KERNEL32.@)
2871 * Determine if a locale is valid.
2873 * PARAMS
2874 * lcid [I] LCID of the locale to check
2875 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2877 * RETURNS
2878 * TRUE, if lcid is valid,
2879 * FALSE, otherwise.
2881 * NOTES
2882 * Wine does not currently make the distinction between supported and installed. All
2883 * languages supported are installed by default.
2885 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2887 /* check if language is registered in the kernel32 resources */
2888 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2889 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2892 /******************************************************************************
2893 * IsValidLocaleName (KERNEL32.@)
2895 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2897 struct locale_name locale_name;
2899 if (!locale)
2900 return FALSE;
2902 /* string parsing */
2903 parse_locale_name( locale, &locale_name );
2905 TRACE( "found lcid %x for %s, matches %d\n",
2906 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2908 return locale_name.matches > 0;
2911 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2912 LPCSTR name, WORD LangID, LONG_PTR lParam )
2914 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2915 char buf[20];
2917 sprintf(buf, "%08x", (UINT)LangID);
2918 return lpfnLocaleEnum( buf );
2921 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2922 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2924 static const WCHAR formatW[] = {'%','0','8','x',0};
2925 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2926 WCHAR buf[20];
2927 sprintfW( buf, formatW, (UINT)LangID );
2928 return lpfnLocaleEnum( buf );
2931 /******************************************************************************
2932 * EnumSystemLocalesA (KERNEL32.@)
2934 * Call a users function for each locale available on the system.
2936 * PARAMS
2937 * lpfnLocaleEnum [I] Callback function to call for each locale
2938 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2940 * RETURNS
2941 * Success: TRUE.
2942 * Failure: FALSE. Use GetLastError() to determine the cause.
2944 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2946 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2947 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2948 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2949 (LONG_PTR)lpfnLocaleEnum);
2950 return TRUE;
2954 /******************************************************************************
2955 * EnumSystemLocalesW (KERNEL32.@)
2957 * See EnumSystemLocalesA.
2959 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2961 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2962 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2963 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2964 (LONG_PTR)lpfnLocaleEnum);
2965 return TRUE;
2969 struct enum_locale_ex_data
2971 LOCALE_ENUMPROCEX proc;
2972 DWORD flags;
2973 LPARAM lparam;
2976 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2977 LPCWSTR name, WORD lang, LONG_PTR lparam )
2979 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2980 WCHAR buffer[256];
2981 DWORD neutral;
2982 unsigned int flags;
2984 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2985 buffer, sizeof(buffer) / sizeof(WCHAR) );
2986 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2987 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2988 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2989 neutral = 0;
2990 flags = LOCALE_WINDOWS;
2991 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2992 if (data->flags && !(data->flags & flags)) return TRUE;
2993 return data->proc( buffer, flags, data->lparam );
2996 /******************************************************************************
2997 * EnumSystemLocalesEx (KERNEL32.@)
2999 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
3001 struct enum_locale_ex_data data;
3003 if (reserved)
3005 SetLastError( ERROR_INVALID_PARAMETER );
3006 return FALSE;
3008 data.proc = proc;
3009 data.flags = flags;
3010 data.lparam = lparam;
3011 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3012 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
3013 enum_locale_ex_proc, (LONG_PTR)&data );
3014 return TRUE;
3018 /***********************************************************************
3019 * VerLanguageNameA (KERNEL32.@)
3021 * Get the name of a language.
3023 * PARAMS
3024 * wLang [I] LANGID of the language
3025 * szLang [O] Destination for the language name
3027 * RETURNS
3028 * Success: The size of the language name. If szLang is non-NULL, it is filled
3029 * with the name.
3030 * Failure: 0. Use GetLastError() to determine the cause.
3033 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
3035 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3039 /***********************************************************************
3040 * VerLanguageNameW (KERNEL32.@)
3042 * See VerLanguageNameA.
3044 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
3046 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3050 /******************************************************************************
3051 * GetStringTypeW (KERNEL32.@)
3053 * See GetStringTypeA.
3055 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3057 static const unsigned char type2_map[16] =
3059 C2_NOTAPPLICABLE, /* unassigned */
3060 C2_LEFTTORIGHT, /* L */
3061 C2_RIGHTTOLEFT, /* R */
3062 C2_EUROPENUMBER, /* EN */
3063 C2_EUROPESEPARATOR, /* ES */
3064 C2_EUROPETERMINATOR, /* ET */
3065 C2_ARABICNUMBER, /* AN */
3066 C2_COMMONSEPARATOR, /* CS */
3067 C2_BLOCKSEPARATOR, /* B */
3068 C2_SEGMENTSEPARATOR, /* S */
3069 C2_WHITESPACE, /* WS */
3070 C2_OTHERNEUTRAL, /* ON */
3071 C2_RIGHTTOLEFT, /* AL */
3072 C2_NOTAPPLICABLE, /* NSM */
3073 C2_NOTAPPLICABLE, /* BN */
3074 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
3077 if (!src)
3079 SetLastError( ERROR_INVALID_PARAMETER );
3080 return FALSE;
3083 if (count == -1) count = strlenW(src) + 1;
3084 switch(type)
3086 case CT_CTYPE1:
3087 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
3088 break;
3089 case CT_CTYPE2:
3090 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
3091 break;
3092 case CT_CTYPE3:
3094 WARN("CT_CTYPE3: semi-stub.\n");
3095 while (count--)
3097 int c = *src;
3098 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
3100 type1 = get_char_typeW( *src++ ) & 0xfff;
3101 /* try to construct type3 from type1 */
3102 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
3103 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
3104 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
3105 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
3106 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3107 if (c == 0x0640) type3 |= C3_KASHIDA;
3108 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3110 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3111 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3113 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3114 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3115 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3116 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3117 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3118 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3119 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3120 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3122 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3123 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3124 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3125 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3126 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3127 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3128 *chartype++ = type3;
3130 break;
3132 default:
3133 SetLastError( ERROR_INVALID_PARAMETER );
3134 return FALSE;
3136 return TRUE;
3140 /******************************************************************************
3141 * GetStringTypeExW (KERNEL32.@)
3143 * See GetStringTypeExA.
3145 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3147 /* locale is ignored for Unicode */
3148 return GetStringTypeW( type, src, count, chartype );
3152 /******************************************************************************
3153 * GetStringTypeA (KERNEL32.@)
3155 * Get characteristics of the characters making up a string.
3157 * PARAMS
3158 * locale [I] Locale Id for the string
3159 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3160 * src [I] String to analyse
3161 * count [I] Length of src in chars, or -1 if src is NUL terminated
3162 * chartype [O] Destination for the calculated characteristics
3164 * RETURNS
3165 * Success: TRUE. chartype is filled with the requested characteristics of each char
3166 * in src.
3167 * Failure: FALSE. Use GetLastError() to determine the cause.
3169 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3171 UINT cp;
3172 INT countW;
3173 LPWSTR srcW;
3174 BOOL ret = FALSE;
3176 if(count == -1) count = strlen(src) + 1;
3178 if (!(cp = get_lcid_codepage( locale )))
3180 FIXME("For locale %04x using current ANSI code page\n", locale);
3181 cp = GetACP();
3184 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3185 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3187 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3189 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3190 * string, with multibyte characters there maybe be more bytes in count
3191 * than character space in the buffer!
3193 ret = GetStringTypeW(type, srcW, countW, chartype);
3194 HeapFree(GetProcessHeap(), 0, srcW);
3196 return ret;
3199 /******************************************************************************
3200 * GetStringTypeExA (KERNEL32.@)
3202 * Get characteristics of the characters making up a string.
3204 * PARAMS
3205 * locale [I] Locale Id for the string
3206 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3207 * src [I] String to analyse
3208 * count [I] Length of src in chars, or -1 if src is NUL terminated
3209 * chartype [O] Destination for the calculated characteristics
3211 * RETURNS
3212 * Success: TRUE. chartype is filled with the requested characteristics of each char
3213 * in src.
3214 * Failure: FALSE. Use GetLastError() to determine the cause.
3216 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3218 return GetStringTypeA(locale, type, src, count, chartype);
3221 /* compose a full-width katakana. return consumed source characters. */
3222 static INT compose_katakana( LPCWSTR src, INT srclen, LPWSTR dst )
3224 const static BYTE katakana_map[] = {
3225 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
3226 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
3227 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
3228 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
3229 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
3230 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
3231 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
3232 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
3234 WCHAR before, dummy;
3236 if (!dst)
3237 dst = &dummy;
3239 switch (*src)
3241 case 0x309b: case 0x309c:
3242 *dst = *src - 2;
3243 return 1;
3244 case 0x30f0: case 0x30f1: case 0x30fd:
3245 *dst = *src;
3246 break;
3247 default:
3249 int shift = *src - 0xff61;
3250 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]) )
3251 return 0;
3252 else
3253 *dst = katakana_map[shift] | 0x3000;
3257 if (srclen <= 1)
3258 return 1;
3260 before = *dst;
3262 /* datakuten (voiced sound) */
3263 if (*(src + 1) == 0xff9e)
3265 if ((*src >= 0xff76 && *src <= 0xff84) ||
3266 (*src >= 0xff8a && *src <= 0xff8e) ||
3267 *src == 0x30fd)
3268 *dst += 1;
3269 else if (*src == 0xff73)
3270 *dst = 0x30f4; /* KATAKANA LETTER VU */
3271 else if (*src == 0xff9c)
3272 *dst = 0x30f7; /* KATAKANA LETTER VA */
3273 else if (*src == 0x30f0)
3274 *dst = 0x30f8; /* KATAKANA LETTER VI */
3275 else if (*src == 0x30f1)
3276 *dst = 0x30f9; /* KATAKANA LETTER VE */
3277 else if (*src == 0xff66)
3278 *dst = 0x30fa; /* KATAKANA LETTER VO */
3281 /* handakuten (semi-voiced sound) */
3282 if (*(src + 1) == 0xff9f)
3283 if (*src >= 0xff8a && *src <= 0xff8e)
3284 *dst += 2;
3286 return (*dst != before) ? 2 : 1;
3289 /* map one or two half-width characters to one full-width character */
3290 static INT map_to_fullwidth( LPCWSTR src, INT srclen, LPWSTR dst )
3292 INT n;
3294 if (*src <= '~' && *src > ' ' && *src != '\\')
3295 *dst = *src - 0x20 + 0xff00;
3296 else if (*src == ' ')
3297 *dst = 0x3000;
3298 else if (*src <= 0x00af && *src >= 0x00a2)
3300 const static BYTE misc_symbols_table[] = {
3301 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3302 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3304 if (misc_symbols_table[*src - 0x00a2])
3305 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3306 else
3307 *dst = *src;
3309 else if (*src == 0x20a9) /* WON SIGN */
3310 *dst = 0xffe6;
3311 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3312 return n;
3313 else if (*src >= 0xffa0 && *src <= 0xffdc)
3315 const static BYTE hangul_mapping_table[] = {
3316 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3317 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3318 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3319 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3320 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3321 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3322 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3323 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3326 if (hangul_mapping_table[*src - 0xffa0])
3327 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3328 else
3329 *dst = *src;
3331 else
3332 *dst = *src;
3334 return 1;
3337 /* decompose a full-width katakana character into one or two half-width characters. */
3338 static INT decompose_katakana( WCHAR c, LPWSTR dst, INT dstlen )
3340 const static BYTE katakana_map[] = {
3341 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3342 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3343 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3344 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3345 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3346 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3347 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3348 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3349 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3350 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3351 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3352 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3353 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3355 INT len = 0, shift = c - 0x3099;
3356 BYTE k;
3358 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]))
3359 return 0;
3361 k = katakana_map[shift];
3363 if (!k)
3365 if (dstlen > 0)
3366 *dst = c;
3367 len++;
3369 else if (k > 0x60)
3371 if (dstlen > 0)
3372 *dst = k | 0xff00;
3373 len++;
3375 else
3377 if (dstlen >= 2)
3379 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3380 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3382 len += 2;
3384 return len;
3387 /* map single full-width character to single or double half-width characters. */
3388 static INT map_to_halfwidth(WCHAR c, LPWSTR dst, INT dstlen)
3390 INT n = decompose_katakana(c, dst, dstlen);
3391 if (n > 0)
3392 return n;
3394 if (c == 0x3000)
3395 *dst = ' ';
3396 else if (c == 0x3001)
3397 *dst = 0xff64;
3398 else if (c == 0x3002)
3399 *dst = 0xff61;
3400 else if (c == 0x300c || c == 0x300d)
3401 *dst = (c - 0x300c) + 0xff62;
3402 else if (c >= 0x3131 && c <= 0x3163)
3404 *dst = c - 0x3131 + 0xffa1;
3405 if (*dst >= 0xffbf) *dst += 3;
3406 if (*dst >= 0xffc8) *dst += 2;
3407 if (*dst >= 0xffd0) *dst += 2;
3408 if (*dst >= 0xffd8) *dst += 2;
3410 else if (c == 0x3164)
3411 *dst = 0xffa0;
3412 else if (c == 0x2019)
3413 *dst = '\'';
3414 else if (c == 0x201d)
3415 *dst = '"';
3416 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3417 *dst = c - 0xff00 + 0x20;
3418 else if (c >= 0xffe0 && c <= 0xffe6)
3420 const static WCHAR misc_symbol_map[] = {
3421 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9
3423 *dst = misc_symbol_map[c - 0xffe0];
3425 else
3426 *dst = c;
3428 return 1;
3431 /*************************************************************************
3432 * LCMapStringEx (KERNEL32.@)
3434 * Map characters in a locale sensitive string.
3436 * PARAMS
3437 * name [I] Locale name for the conversion.
3438 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3439 * src [I] String to map
3440 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3441 * dst [O] Destination for mapped string
3442 * dstlen [I] Length of dst in characters
3443 * version [I] reserved, must be NULL
3444 * reserved [I] reserved, must be NULL
3445 * lparam [I] reserved, must be 0
3447 * RETURNS
3448 * Success: The length of the mapped string in dst, including the NUL terminator.
3449 * Failure: 0. Use GetLastError() to determine the cause.
3451 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3452 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3454 LPWSTR dst_ptr;
3455 INT len;
3457 if (version) FIXME("unsupported version structure %p\n", version);
3458 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3459 if (lparam)
3461 static int once;
3462 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3465 if (!src || !srclen || dstlen < 0)
3467 SetLastError(ERROR_INVALID_PARAMETER);
3468 return 0;
3471 /* mutually exclusive flags */
3472 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3473 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3474 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3475 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
3476 !flags)
3478 SetLastError(ERROR_INVALID_FLAGS);
3479 return 0;
3482 if (!dstlen) dst = NULL;
3484 if (flags & LCMAP_SORTKEY)
3486 INT ret;
3487 if (src == dst)
3489 SetLastError(ERROR_INVALID_FLAGS);
3490 return 0;
3493 if (srclen < 0) srclen = strlenW(src);
3495 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3496 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3498 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3499 if (ret == 0)
3500 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3501 else
3502 ret++;
3503 return ret;
3506 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3507 if (flags & SORT_STRINGSORT)
3509 SetLastError(ERROR_INVALID_FLAGS);
3510 return 0;
3512 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
3513 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
3514 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
3515 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
3517 SetLastError(ERROR_INVALID_FLAGS);
3518 return 0;
3521 if (srclen < 0) srclen = strlenW(src) + 1;
3523 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3524 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3526 if (!dst) /* return required string length */
3528 if (flags & NORM_IGNORESYMBOLS)
3530 for (len = 0; srclen; src++, srclen--)
3532 WCHAR wch = *src;
3533 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3534 * and skips white space and punctuation characters for
3535 * NORM_IGNORESYMBOLS.
3537 if (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE))
3538 continue;
3539 len++;
3542 else if (flags & LCMAP_FULLWIDTH)
3544 for (len = 0; srclen; src++, srclen--, len++)
3546 if (compose_katakana(src, srclen, NULL) == 2)
3548 src++;
3549 srclen--;
3553 else if (flags & LCMAP_HALFWIDTH)
3555 for (len = 0; srclen; src++, srclen--, len++)
3556 if (decompose_katakana(*src, NULL, 0) == 2)
3557 len++;
3559 else
3560 len = srclen;
3561 return len;
3564 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3566 SetLastError(ERROR_INVALID_FLAGS);
3567 return 0;
3570 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
3572 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3574 WCHAR wch = *src;
3575 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3576 continue;
3577 *dst_ptr++ = wch;
3578 len--;
3580 goto done;
3583 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
3585 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
3587 WCHAR wch;
3588 if (flags & LCMAP_FULLWIDTH)
3590 /* map half-width character to full-width one,
3591 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
3592 if (map_to_fullwidth(src, srclen, &wch) == 2)
3594 src++;
3595 srclen--;
3598 else
3599 wch = *src;
3601 if (flags & LCMAP_KATAKANA)
3603 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
3604 we can't use C3_HIRAGANA as some characters can't map to katakana */
3605 if ((wch >= 0x3041 && wch <= 0x3096) ||
3606 wch == 0x309D || wch == 0x309E)
3607 wch += 0x60;
3609 else if (flags & LCMAP_HIRAGANA)
3611 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
3612 we can't use C3_KATAKANA as some characters can't map to hiragana */
3613 if ((wch >= 0x30A1 && wch <= 0x30F6) ||
3614 wch == 0x30FD || wch == 0x30FE)
3615 wch -= 0x60;
3618 if (flags & LCMAP_HALFWIDTH)
3620 /* map full-width character to half-width one,
3621 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
3622 if (map_to_halfwidth(wch, dst_ptr, dstlen) == 2)
3624 dstlen--;
3625 dst_ptr++;
3626 if (!dstlen)
3628 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3629 return 0;
3633 else
3634 *dst_ptr = wch;
3636 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)))
3637 goto done;
3639 srclen = dst_ptr - dst;
3640 src = dst;
3643 if (flags & LCMAP_UPPERCASE)
3645 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3647 *dst_ptr++ = toupperW(*src);
3648 len--;
3651 else if (flags & LCMAP_LOWERCASE)
3653 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3655 *dst_ptr++ = tolowerW(*src);
3656 len--;
3659 else
3661 len = min(srclen, dstlen);
3662 memcpy(dst, src, len * sizeof(WCHAR));
3663 dst_ptr = dst + len;
3664 srclen -= len;
3667 done:
3668 if (srclen)
3670 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3671 return 0;
3674 return dst_ptr - dst;
3677 /*************************************************************************
3678 * LCMapStringW (KERNEL32.@)
3680 * See LCMapStringA.
3682 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3683 LPWSTR dst, INT dstlen)
3685 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3686 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3688 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3691 /*************************************************************************
3692 * LCMapStringA (KERNEL32.@)
3694 * Map characters in a locale sensitive string.
3696 * PARAMS
3697 * lcid [I] LCID for the conversion.
3698 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3699 * src [I] String to map
3700 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3701 * dst [O] Destination for mapped string
3702 * dstlen [I] Length of dst in characters
3704 * RETURNS
3705 * Success: The length of the mapped string in dst, including the NUL terminator.
3706 * Failure: 0. Use GetLastError() to determine the cause.
3708 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3709 LPSTR dst, INT dstlen)
3711 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3712 LPWSTR srcW, dstW;
3713 INT ret = 0, srclenW, dstlenW;
3714 UINT locale_cp = CP_ACP;
3716 if (!src || !srclen || dstlen < 0)
3718 SetLastError(ERROR_INVALID_PARAMETER);
3719 return 0;
3722 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3724 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3725 if (srclenW)
3726 srcW = bufW;
3727 else
3729 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3730 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3731 if (!srcW)
3733 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3734 return 0;
3736 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3739 if (flags & LCMAP_SORTKEY)
3741 if (src == dst)
3743 SetLastError(ERROR_INVALID_FLAGS);
3744 goto map_string_exit;
3746 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3747 if (ret == 0)
3748 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3749 else
3750 ret++;
3751 goto map_string_exit;
3754 if (flags & SORT_STRINGSORT)
3756 SetLastError(ERROR_INVALID_FLAGS);
3757 goto map_string_exit;
3760 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3761 if (!dstlenW)
3762 goto map_string_exit;
3764 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3765 if (!dstW)
3767 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3768 goto map_string_exit;
3771 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3772 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3773 HeapFree(GetProcessHeap(), 0, dstW);
3775 map_string_exit:
3776 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3777 return ret;
3780 /*************************************************************************
3781 * FoldStringA (KERNEL32.@)
3783 * Map characters in a string.
3785 * PARAMS
3786 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3787 * src [I] String to map
3788 * srclen [I] Length of src, or -1 if src is NUL terminated
3789 * dst [O] Destination for mapped string
3790 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3792 * RETURNS
3793 * Success: The length of the string written to dst, including the terminating NUL. If
3794 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3795 * and dst may be NULL.
3796 * Failure: 0. Use GetLastError() to determine the cause.
3798 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3799 LPSTR dst, INT dstlen)
3801 INT ret = 0, srclenW = 0;
3802 WCHAR *srcW = NULL, *dstW = NULL;
3804 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3806 SetLastError(ERROR_INVALID_PARAMETER);
3807 return 0;
3810 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3811 src, srclen, NULL, 0);
3812 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3814 if (!srcW)
3816 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3817 goto FoldStringA_exit;
3820 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3821 src, srclen, srcW, srclenW);
3823 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3825 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3826 if (ret && dstlen)
3828 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3830 if (!dstW)
3832 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3833 goto FoldStringA_exit;
3836 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3837 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3839 ret = 0;
3840 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3844 HeapFree(GetProcessHeap(), 0, dstW);
3846 FoldStringA_exit:
3847 HeapFree(GetProcessHeap(), 0, srcW);
3848 return ret;
3851 /*************************************************************************
3852 * FoldStringW (KERNEL32.@)
3854 * See FoldStringA.
3856 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3857 LPWSTR dst, INT dstlen)
3859 int ret;
3861 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3863 case 0:
3864 if (dwFlags)
3865 break;
3866 /* Fall through for dwFlags == 0 */
3867 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3868 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3869 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3870 SetLastError(ERROR_INVALID_FLAGS);
3871 return 0;
3874 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3876 SetLastError(ERROR_INVALID_PARAMETER);
3877 return 0;
3880 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3881 if (!ret)
3882 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3883 return ret;
3886 /******************************************************************************
3887 * CompareStringW (KERNEL32.@)
3889 * See CompareStringA.
3891 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3892 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3894 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3897 /******************************************************************************
3898 * CompareStringEx (KERNEL32.@)
3900 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3901 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3903 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3904 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3905 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3906 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3907 INT ret;
3908 static int once;
3910 if (version) FIXME("unexpected version parameter\n");
3911 if (reserved) FIXME("unexpected reserved value\n");
3912 if (lParam) FIXME("unexpected lParam\n");
3914 if (!str1 || !str2)
3916 SetLastError(ERROR_INVALID_PARAMETER);
3917 return 0;
3920 if (flags & ~(supported_flags|semistub_flags))
3922 SetLastError(ERROR_INVALID_FLAGS);
3923 return 0;
3926 if (flags & semistub_flags)
3928 if (!once++)
3929 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3932 if (len1 < 0) len1 = strlenW(str1);
3933 if (len2 < 0) len2 = strlenW(str2);
3935 ret = wine_compare_string(flags, str1, len1, str2, len2);
3937 if (ret) /* need to translate result */
3938 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3939 return CSTR_EQUAL;
3942 /******************************************************************************
3943 * CompareStringA (KERNEL32.@)
3945 * Compare two locale sensitive strings.
3947 * PARAMS
3948 * lcid [I] LCID for the comparison
3949 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3950 * str1 [I] First string to compare
3951 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3952 * str2 [I] Second string to compare
3953 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3955 * RETURNS
3956 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3957 * str1 is less than, equal to or greater than str2 respectively.
3958 * Failure: FALSE. Use GetLastError() to determine the cause.
3960 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3961 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3963 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3964 WCHAR *buf2W = buf1W + 130;
3965 LPWSTR str1W, str2W;
3966 INT len1W = 0, len2W = 0, ret;
3967 UINT locale_cp = CP_ACP;
3969 if (!str1 || !str2)
3971 SetLastError(ERROR_INVALID_PARAMETER);
3972 return 0;
3974 if (len1 < 0) len1 = strlen(str1);
3975 if (len2 < 0) len2 = strlen(str2);
3977 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3979 if (len1)
3981 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3982 if (len1W)
3983 str1W = buf1W;
3984 else
3986 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3987 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3988 if (!str1W)
3990 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3991 return 0;
3993 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3996 else
3998 len1W = 0;
3999 str1W = buf1W;
4002 if (len2)
4004 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
4005 if (len2W)
4006 str2W = buf2W;
4007 else
4009 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
4010 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
4011 if (!str2W)
4013 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4014 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4015 return 0;
4017 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
4020 else
4022 len2W = 0;
4023 str2W = buf2W;
4026 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
4028 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4029 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
4030 return ret;
4033 /******************************************************************************
4034 * CompareStringOrdinal (KERNEL32.@)
4036 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
4038 int ret;
4040 if (!str1 || !str2)
4042 SetLastError(ERROR_INVALID_PARAMETER);
4043 return 0;
4045 if (len1 < 0) len1 = strlenW(str1);
4046 if (len2 < 0) len2 = strlenW(str2);
4048 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4049 if (ret < 0) return CSTR_LESS_THAN;
4050 if (ret > 0) return CSTR_GREATER_THAN;
4051 return CSTR_EQUAL;
4054 /*************************************************************************
4055 * lstrcmp (KERNEL32.@)
4056 * lstrcmpA (KERNEL32.@)
4058 * Compare two strings using the current thread locale.
4060 * PARAMS
4061 * str1 [I] First string to compare
4062 * str2 [I] Second string to compare
4064 * RETURNS
4065 * Success: A number less than, equal to or greater than 0 depending on whether
4066 * str1 is less than, equal to or greater than str2 respectively.
4067 * Failure: FALSE. Use GetLastError() to determine the cause.
4069 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
4071 int ret;
4073 if ((str1 == NULL) && (str2 == NULL)) return 0;
4074 if (str1 == NULL) return -1;
4075 if (str2 == NULL) return 1;
4077 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4078 if (ret) ret -= 2;
4080 return ret;
4083 /*************************************************************************
4084 * lstrcmpi (KERNEL32.@)
4085 * lstrcmpiA (KERNEL32.@)
4087 * Compare two strings using the current thread locale, ignoring case.
4089 * PARAMS
4090 * str1 [I] First string to compare
4091 * str2 [I] Second string to compare
4093 * RETURNS
4094 * Success: A number less than, equal to or greater than 0 depending on whether
4095 * str2 is less than, equal to or greater than str1 respectively.
4096 * Failure: FALSE. Use GetLastError() to determine the cause.
4098 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
4100 int ret;
4102 if ((str1 == NULL) && (str2 == NULL)) return 0;
4103 if (str1 == NULL) return -1;
4104 if (str2 == NULL) return 1;
4106 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4107 if (ret) ret -= 2;
4109 return ret;
4112 /*************************************************************************
4113 * lstrcmpW (KERNEL32.@)
4115 * See lstrcmpA.
4117 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
4119 int ret;
4121 if ((str1 == NULL) && (str2 == NULL)) return 0;
4122 if (str1 == NULL) return -1;
4123 if (str2 == NULL) return 1;
4125 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
4126 if (ret) ret -= 2;
4128 return ret;
4131 /*************************************************************************
4132 * lstrcmpiW (KERNEL32.@)
4134 * See lstrcmpiA.
4136 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
4138 int ret;
4140 if ((str1 == NULL) && (str2 == NULL)) return 0;
4141 if (str1 == NULL) return -1;
4142 if (str2 == NULL) return 1;
4144 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
4145 if (ret) ret -= 2;
4147 return ret;
4150 /******************************************************************************
4151 * LOCALE_Init
4153 void LOCALE_Init(void)
4155 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
4156 const union cptable *unix_cp );
4158 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
4160 setlocale( LC_ALL, "" );
4162 #ifdef __APPLE__
4163 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
4164 if (!has_env("LANG"))
4166 const char* mac_locale = get_mac_locale();
4168 setenv( "LANG", mac_locale, 1 );
4169 if (setlocale( LC_ALL, "" ))
4170 TRACE( "setting LANG to '%s'\n", mac_locale );
4171 else
4173 /* no C library locale matching Mac locale; don't pass garbage to children */
4174 unsetenv("LANG");
4175 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
4178 #endif /* __APPLE__ */
4180 unix_cp = setup_unix_locales();
4181 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
4183 #ifdef __APPLE__
4184 if (!unix_cp)
4185 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
4186 #endif
4188 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
4189 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
4190 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
4192 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
4193 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
4194 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
4195 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
4196 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
4197 if (!unix_cp)
4198 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
4199 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
4201 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
4202 ansi_cptable = wine_cp_get_table( 1252 );
4203 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
4204 oem_cptable = wine_cp_get_table( 437 );
4205 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
4206 mac_cptable = wine_cp_get_table( 10000 );
4207 if (unix_cp != CP_UTF8)
4209 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
4210 unix_cptable = wine_cp_get_table( 28591 );
4213 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
4215 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
4216 ansi_cptable->info.codepage, oem_cptable->info.codepage,
4217 mac_cptable->info.codepage, unix_cp );
4219 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
4222 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4224 UNICODE_STRING keyName;
4225 OBJECT_ATTRIBUTES attr;
4226 HANDLE hkey;
4228 RtlInitUnicodeString( &keyName, szKeyName );
4229 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
4231 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
4232 hkey = 0;
4234 return hkey;
4237 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4238 LPWSTR szValueName, ULONG valueNameSize,
4239 LPWSTR szValueData, ULONG valueDataSize)
4241 BYTE buffer[80];
4242 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
4243 DWORD dwLen;
4245 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
4246 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
4247 info->NameLength > valueNameSize ||
4248 info->DataLength > valueDataSize)
4250 return FALSE;
4253 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
4255 memcpy( szValueName, info->Name, info->NameLength);
4256 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
4257 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
4258 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
4260 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
4261 return TRUE;
4264 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
4266 BYTE buffer[128];
4267 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
4268 DWORD dwSize = sizeof(buffer);
4269 UNICODE_STRING valueName;
4271 RtlInitUnicodeString( &valueName, szValueName );
4273 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
4274 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
4275 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
4276 info->DataLength == sizeof(DWORD))
4278 memcpy(lpVal, info->Data, sizeof(DWORD));
4279 return TRUE;
4282 return FALSE;
4285 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
4287 LANGID langId;
4288 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
4289 HRSRC hResource;
4290 BOOL bRet = FALSE;
4292 /* FIXME: Is it correct to use the system default langid? */
4293 langId = GetSystemDefaultLangID();
4295 if (SUBLANGID(langId) == SUBLANG_NEUTRAL) langId = get_default_sublang( langId );
4297 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
4299 if (hResource)
4301 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
4303 if (hResDir)
4305 ULONG iResourceIndex = lgrpid & 0xf;
4306 LPCWSTR lpResEntry = LockResource( hResDir );
4307 ULONG i;
4309 for (i = 0; i < iResourceIndex; i++)
4310 lpResEntry += *lpResEntry + 1;
4312 if (*lpResEntry < nameSize)
4314 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
4315 szName[*lpResEntry] = '\0';
4316 bRet = TRUE;
4320 FreeResource( hResource );
4322 return bRet;
4325 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
4326 typedef struct
4328 LANGUAGEGROUP_ENUMPROCA procA;
4329 LANGUAGEGROUP_ENUMPROCW procW;
4330 DWORD dwFlags;
4331 LONG_PTR lParam;
4332 } ENUMLANGUAGEGROUP_CALLBACKS;
4334 /* Internal implementation of EnumSystemLanguageGroupsA/W */
4335 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
4337 WCHAR szNumber[10], szValue[4];
4338 HANDLE hKey;
4339 BOOL bContinue = TRUE;
4340 ULONG ulIndex = 0;
4342 if (!lpProcs)
4344 SetLastError(ERROR_INVALID_PARAMETER);
4345 return FALSE;
4348 switch (lpProcs->dwFlags)
4350 case 0:
4351 /* Default to LGRPID_INSTALLED */
4352 lpProcs->dwFlags = LGRPID_INSTALLED;
4353 /* Fall through... */
4354 case LGRPID_INSTALLED:
4355 case LGRPID_SUPPORTED:
4356 break;
4357 default:
4358 SetLastError(ERROR_INVALID_FLAGS);
4359 return FALSE;
4362 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4364 if (!hKey)
4365 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4367 while (bContinue)
4369 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4370 szValue, sizeof(szValue) ))
4372 BOOL bInstalled = szValue[0] == '1';
4373 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4375 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4376 bInstalled ? "" : "not ");
4378 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4380 WCHAR szGrpName[48];
4382 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
4383 szGrpName[0] = '\0';
4385 if (lpProcs->procW)
4386 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4387 lpProcs->lParam );
4388 else
4390 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4391 char szGrpNameA[48];
4393 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4394 * or whether the language names are ever localised. Assume CP_ACP.
4397 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4398 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4400 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4401 lpProcs->lParam );
4405 ulIndex++;
4407 else
4408 bContinue = FALSE;
4410 if (!bContinue)
4411 break;
4414 if (hKey)
4415 NtClose( hKey );
4417 return TRUE;
4420 /******************************************************************************
4421 * EnumSystemLanguageGroupsA (KERNEL32.@)
4423 * Call a users function for each language group available on the system.
4425 * PARAMS
4426 * pLangGrpEnumProc [I] Callback function to call for each language group
4427 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4428 * lParam [I] User parameter to pass to pLangGrpEnumProc
4430 * RETURNS
4431 * Success: TRUE.
4432 * Failure: FALSE. Use GetLastError() to determine the cause.
4434 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4435 DWORD dwFlags, LONG_PTR lParam)
4437 ENUMLANGUAGEGROUP_CALLBACKS procs;
4439 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4441 procs.procA = pLangGrpEnumProc;
4442 procs.procW = NULL;
4443 procs.dwFlags = dwFlags;
4444 procs.lParam = lParam;
4446 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4449 /******************************************************************************
4450 * EnumSystemLanguageGroupsW (KERNEL32.@)
4452 * See EnumSystemLanguageGroupsA.
4454 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4455 DWORD dwFlags, LONG_PTR lParam)
4457 ENUMLANGUAGEGROUP_CALLBACKS procs;
4459 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4461 procs.procA = NULL;
4462 procs.procW = pLangGrpEnumProc;
4463 procs.dwFlags = dwFlags;
4464 procs.lParam = lParam;
4466 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4469 /******************************************************************************
4470 * IsValidLanguageGroup (KERNEL32.@)
4472 * Determine if a language group is supported and/or installed.
4474 * PARAMS
4475 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4476 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4478 * RETURNS
4479 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4480 * FALSE otherwise.
4482 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4484 static const WCHAR szFormat[] = { '%','x','\0' };
4485 WCHAR szValueName[16], szValue[2];
4486 BOOL bSupported = FALSE, bInstalled = FALSE;
4487 HANDLE hKey;
4490 switch (dwFlags)
4492 case LGRPID_INSTALLED:
4493 case LGRPID_SUPPORTED:
4495 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4497 sprintfW( szValueName, szFormat, lgrpid );
4499 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4501 bSupported = TRUE;
4503 if (szValue[0] == '1')
4504 bInstalled = TRUE;
4507 if (hKey)
4508 NtClose( hKey );
4510 break;
4513 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4514 (dwFlags == LGRPID_INSTALLED && bInstalled))
4515 return TRUE;
4517 return FALSE;
4520 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4521 typedef struct
4523 LANGGROUPLOCALE_ENUMPROCA procA;
4524 LANGGROUPLOCALE_ENUMPROCW procW;
4525 DWORD dwFlags;
4526 LGRPID lgrpid;
4527 LONG_PTR lParam;
4528 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4530 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4531 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4533 static const WCHAR szAlternateSortsKeyName[] = {
4534 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4536 WCHAR szNumber[10], szValue[4];
4537 HANDLE hKey;
4538 BOOL bContinue = TRUE, bAlternate = FALSE;
4539 LGRPID lgrpid;
4540 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4542 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4544 SetLastError(ERROR_INVALID_PARAMETER);
4545 return FALSE;
4548 if (lpProcs->dwFlags)
4550 SetLastError(ERROR_INVALID_FLAGS);
4551 return FALSE;
4554 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4556 if (!hKey)
4557 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4559 while (bContinue)
4561 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4562 szValue, sizeof(szValue) ))
4564 lgrpid = strtoulW( szValue, NULL, 16 );
4566 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4567 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4569 if (lgrpid == lpProcs->lgrpid)
4571 LCID lcid;
4573 lcid = strtoulW( szNumber, NULL, 16 );
4575 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4576 * '00000437 ;Georgian'
4577 * At present we only pass the LCID string.
4580 if (lpProcs->procW)
4581 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4582 else
4584 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4586 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4588 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4592 ulIndex++;
4594 else
4596 /* Finished enumerating this key */
4597 if (!bAlternate)
4599 /* Enumerate alternate sorts also */
4600 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4601 bAlternate = TRUE;
4602 ulIndex = 0;
4604 else
4605 bContinue = FALSE; /* Finished both keys */
4608 if (!bContinue)
4609 break;
4612 if (hKey)
4613 NtClose( hKey );
4615 return TRUE;
4618 /******************************************************************************
4619 * EnumLanguageGroupLocalesA (KERNEL32.@)
4621 * Call a users function for every locale in a language group available on the system.
4623 * PARAMS
4624 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4625 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4626 * dwFlags [I] Reserved, set to 0
4627 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4629 * RETURNS
4630 * Success: TRUE.
4631 * Failure: FALSE. Use GetLastError() to determine the cause.
4633 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4634 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4636 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4638 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4640 callbacks.procA = pLangGrpLcEnumProc;
4641 callbacks.procW = NULL;
4642 callbacks.dwFlags = dwFlags;
4643 callbacks.lgrpid = lgrpid;
4644 callbacks.lParam = lParam;
4646 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4649 /******************************************************************************
4650 * EnumLanguageGroupLocalesW (KERNEL32.@)
4652 * See EnumLanguageGroupLocalesA.
4654 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4655 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4657 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4659 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4661 callbacks.procA = NULL;
4662 callbacks.procW = pLangGrpLcEnumProc;
4663 callbacks.dwFlags = dwFlags;
4664 callbacks.lgrpid = lgrpid;
4665 callbacks.lParam = lParam;
4667 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4670 /******************************************************************************
4671 * InvalidateNLSCache (KERNEL32.@)
4673 * Invalidate the cache of NLS values.
4675 * PARAMS
4676 * None.
4678 * RETURNS
4679 * Success: TRUE.
4680 * Failure: FALSE.
4682 BOOL WINAPI InvalidateNLSCache(void)
4684 FIXME("() stub\n");
4685 return FALSE;
4688 /******************************************************************************
4689 * GetUserGeoID (KERNEL32.@)
4691 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4693 GEOID ret = GEOID_NOT_AVAILABLE;
4694 static const WCHAR geoW[] = {'G','e','o',0};
4695 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4696 WCHAR bufferW[40], *end;
4697 DWORD count;
4698 HANDLE hkey, hSubkey = 0;
4699 UNICODE_STRING keyW;
4700 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4701 RtlInitUnicodeString( &keyW, nationW );
4702 count = sizeof(bufferW);
4704 if(!(hkey = create_registry_key())) return ret;
4706 switch( GeoClass ){
4707 case GEOCLASS_NATION:
4708 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4710 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4711 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4712 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4714 break;
4715 case GEOCLASS_REGION:
4716 FIXME("GEOCLASS_REGION not handled yet\n");
4717 break;
4720 NtClose(hkey);
4721 if (hSubkey) NtClose(hSubkey);
4722 return ret;
4725 /******************************************************************************
4726 * SetUserGeoID (KERNEL32.@)
4728 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4730 static const WCHAR geoW[] = {'G','e','o',0};
4731 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4732 static const WCHAR formatW[] = {'%','i',0};
4733 UNICODE_STRING nameW,keyW;
4734 WCHAR bufferW[10];
4735 OBJECT_ATTRIBUTES attr;
4736 HANDLE hkey;
4738 if(!(hkey = create_registry_key())) return FALSE;
4740 attr.Length = sizeof(attr);
4741 attr.RootDirectory = hkey;
4742 attr.ObjectName = &nameW;
4743 attr.Attributes = 0;
4744 attr.SecurityDescriptor = NULL;
4745 attr.SecurityQualityOfService = NULL;
4746 RtlInitUnicodeString( &nameW, geoW );
4747 RtlInitUnicodeString( &keyW, nationW );
4749 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4752 NtClose(attr.RootDirectory);
4753 return FALSE;
4756 sprintfW(bufferW, formatW, GeoID);
4757 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4758 NtClose(attr.RootDirectory);
4759 NtClose(hkey);
4760 return TRUE;
4763 typedef struct
4765 union
4767 UILANGUAGE_ENUMPROCA procA;
4768 UILANGUAGE_ENUMPROCW procW;
4769 } u;
4770 DWORD flags;
4771 LONG_PTR param;
4772 } ENUM_UILANG_CALLBACK;
4774 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4775 LPCSTR name, WORD LangID, LONG_PTR lParam )
4777 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4778 char buf[20];
4780 sprintf(buf, "%08x", (UINT)LangID);
4781 return enum_uilang->u.procA( buf, enum_uilang->param );
4784 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4785 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4787 static const WCHAR formatW[] = {'%','0','8','x',0};
4788 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4789 WCHAR buf[20];
4791 sprintfW( buf, formatW, (UINT)LangID );
4792 return enum_uilang->u.procW( buf, enum_uilang->param );
4795 /******************************************************************************
4796 * EnumUILanguagesA (KERNEL32.@)
4798 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4800 ENUM_UILANG_CALLBACK enum_uilang;
4802 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4804 if(!pUILangEnumProc) {
4805 SetLastError(ERROR_INVALID_PARAMETER);
4806 return FALSE;
4808 if(dwFlags) {
4809 SetLastError(ERROR_INVALID_FLAGS);
4810 return FALSE;
4813 enum_uilang.u.procA = pUILangEnumProc;
4814 enum_uilang.flags = dwFlags;
4815 enum_uilang.param = lParam;
4817 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4818 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4819 (LONG_PTR)&enum_uilang);
4820 return TRUE;
4823 /******************************************************************************
4824 * EnumUILanguagesW (KERNEL32.@)
4826 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4828 ENUM_UILANG_CALLBACK enum_uilang;
4830 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4833 if(!pUILangEnumProc) {
4834 SetLastError(ERROR_INVALID_PARAMETER);
4835 return FALSE;
4837 if(dwFlags) {
4838 SetLastError(ERROR_INVALID_FLAGS);
4839 return FALSE;
4842 enum_uilang.u.procW = pUILangEnumProc;
4843 enum_uilang.flags = dwFlags;
4844 enum_uilang.param = lParam;
4846 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4847 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4848 (LONG_PTR)&enum_uilang);
4849 return TRUE;
4852 enum locationkind {
4853 LOCATION_NATION = 0,
4854 LOCATION_REGION,
4855 LOCATION_BOTH
4858 struct geoinfo_t {
4859 GEOID id;
4860 WCHAR iso2W[3];
4861 WCHAR iso3W[4];
4862 GEOID parent;
4863 INT uncode;
4864 enum locationkind kind;
4867 static const struct geoinfo_t geoinfodata[] = {
4868 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4869 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4870 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4871 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4872 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4873 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4874 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4875 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4876 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4877 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4878 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4879 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4880 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4881 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4882 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4883 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4884 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4885 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4886 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4887 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4888 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4889 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4890 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4891 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4892 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4893 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4894 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4895 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4896 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4897 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4898 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4899 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4900 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4901 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4902 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4903 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4904 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4905 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4906 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4907 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4908 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4909 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4910 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4911 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4912 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4913 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4914 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4915 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4916 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4917 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4918 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4919 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4920 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4921 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4922 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4923 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4924 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4925 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4926 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4927 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4928 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4929 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4930 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4931 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4932 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4933 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4934 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4935 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4936 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4937 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4938 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4939 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4940 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4941 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4942 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4943 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4944 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4945 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4946 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4947 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4948 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4949 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4950 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4951 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4952 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4953 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4954 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4955 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4956 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4957 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4958 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4959 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4960 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4961 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4962 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4963 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4964 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4965 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4966 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4967 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4968 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4969 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4970 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4971 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4972 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4973 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4974 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4975 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4976 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4977 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4978 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4979 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4980 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4981 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4982 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4983 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4984 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4985 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4986 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4987 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4988 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4989 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4990 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4991 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4992 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4993 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4994 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4995 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4996 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4997 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4998 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4999 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
5000 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
5001 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
5002 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
5003 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
5004 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
5005 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
5006 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
5007 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
5008 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
5009 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
5010 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
5011 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
5012 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
5013 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
5014 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
5015 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
5016 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
5017 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
5018 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
5019 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
5020 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
5021 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
5022 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
5023 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
5024 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
5025 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
5026 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
5027 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
5028 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
5029 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
5030 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
5031 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
5032 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
5033 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
5034 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
5035 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
5036 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
5037 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
5038 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
5039 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
5040 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
5041 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
5042 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
5043 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
5044 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
5045 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
5046 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
5047 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
5048 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
5049 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
5050 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
5051 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
5052 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
5053 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
5054 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
5055 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
5056 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
5057 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
5058 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
5059 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
5060 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
5061 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
5062 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
5063 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
5064 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
5065 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
5066 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
5067 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
5068 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
5069 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
5070 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
5071 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
5072 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
5073 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
5074 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
5075 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
5076 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
5077 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
5078 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
5079 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
5080 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
5081 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
5082 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
5083 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
5084 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
5085 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
5086 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
5087 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
5088 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
5089 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
5090 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
5091 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
5092 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
5093 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
5094 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
5095 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
5096 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
5097 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
5098 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
5099 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
5100 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
5101 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
5102 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
5103 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
5104 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
5105 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
5106 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
5107 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
5108 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
5109 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
5110 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
5111 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
5112 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
5113 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
5114 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
5115 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
5116 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
5117 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
5118 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
5119 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
5120 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
5121 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
5122 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
5123 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
5124 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
5125 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
5126 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
5127 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
5128 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
5129 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
5130 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
5131 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
5132 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
5133 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
5134 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
5135 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
5136 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
5137 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
5138 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
5139 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
5140 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
5141 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
5142 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
5143 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
5144 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
5145 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
5146 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
5147 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
5148 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
5149 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
5150 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
5151 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
5152 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
5153 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
5154 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
5155 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
5156 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
5157 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
5158 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
5159 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
5160 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
5161 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
5162 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
5163 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
5164 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
5165 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
5166 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
5169 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
5171 int min, max;
5173 min = 0;
5174 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
5176 while (min <= max) {
5177 const struct geoinfo_t *ptr;
5178 int n = (min+max)/2;
5180 ptr = &geoinfodata[n];
5181 if (geoid == ptr->id)
5182 /* we don't need empty entries */
5183 return *ptr->iso2W ? ptr : NULL;
5185 if (ptr->id > geoid)
5186 max = n-1;
5187 else
5188 min = n+1;
5191 return NULL;
5194 /******************************************************************************
5195 * GetGeoInfoW (KERNEL32.@)
5197 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
5199 const struct geoinfo_t *ptr;
5200 const WCHAR *str = NULL;
5201 WCHAR buffW[12];
5202 LONG val = 0;
5203 INT len;
5205 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5207 if (!(ptr = get_geoinfo_dataptr(geoid))) {
5208 SetLastError(ERROR_INVALID_PARAMETER);
5209 return 0;
5212 switch (geotype) {
5213 case GEO_NATION:
5214 val = geoid;
5215 break;
5216 case GEO_ISO_UN_NUMBER:
5217 val = ptr->uncode;
5218 break;
5219 case GEO_PARENT:
5220 val = ptr->parent;
5221 break;
5222 case GEO_ISO2:
5223 case GEO_ISO3:
5225 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
5226 break;
5228 case GEO_RFC1766:
5229 case GEO_LCID:
5230 case GEO_FRIENDLYNAME:
5231 case GEO_OFFICIALNAME:
5232 case GEO_TIMEZONES:
5233 case GEO_OFFICIALLANGUAGES:
5234 case GEO_LATITUDE:
5235 case GEO_LONGITUDE:
5236 FIXME("type %d is not supported\n", geotype);
5237 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5238 return 0;
5239 default:
5240 WARN("unrecognized type %d\n", geotype);
5241 SetLastError(ERROR_INVALID_FLAGS);
5242 return 0;
5245 if (val) {
5246 static const WCHAR fmtW[] = {'%','d',0};
5247 sprintfW(buffW, fmtW, val);
5248 str = buffW;
5251 len = strlenW(str) + 1;
5252 if (!data || !data_len)
5253 return len;
5255 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
5256 if (data_len < len)
5257 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5258 return data_len < len ? 0 : len;
5261 /******************************************************************************
5262 * GetGeoInfoA (KERNEL32.@)
5264 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
5266 WCHAR *buffW;
5267 INT len;
5269 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5271 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
5272 if (!len)
5273 return 0;
5275 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
5276 if (!buffW)
5277 return 0;
5279 GetGeoInfoW(geoid, geotype, buffW, len, lang);
5280 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
5281 if (!data || !data_len) {
5282 HeapFree(GetProcessHeap(), 0, buffW);
5283 return len;
5286 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
5287 HeapFree(GetProcessHeap(), 0, buffW);
5289 if (data_len < len)
5290 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5291 return data_len < len ? 0 : len;
5294 /******************************************************************************
5295 * EnumSystemGeoID (KERNEL32.@)
5297 * Call a users function for every location available on the system.
5299 * PARAMS
5300 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
5301 * parent [I] GEOID for the parent
5302 * enumproc [I] Callback function to call for each location
5304 * RETURNS
5305 * Success: TRUE.
5306 * Failure: FALSE. Use GetLastError() to determine the cause.
5308 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
5310 INT i;
5312 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
5314 if (!enumproc) {
5315 SetLastError(ERROR_INVALID_PARAMETER);
5316 return FALSE;
5319 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
5320 SetLastError(ERROR_INVALID_FLAGS);
5321 return FALSE;
5324 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
5325 const struct geoinfo_t *ptr = &geoinfodata[i];
5327 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
5328 continue;
5330 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
5331 continue;
5333 if (parent && ptr->parent != parent)
5334 continue;
5336 if (!enumproc(ptr->id))
5337 return TRUE;
5340 return TRUE;
5343 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
5345 LCID userlcid;
5347 TRACE("%p, %d\n", localename, buffersize);
5349 userlcid = GetUserDefaultLCID();
5350 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
5353 /******************************************************************************
5354 * NormalizeString (KERNEL32.@)
5356 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
5357 LPWSTR lpDstString, INT cwDstLength)
5359 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
5360 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5361 return 0;
5364 /******************************************************************************
5365 * IsNormalizedString (KERNEL32.@)
5367 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
5369 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5370 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5371 return FALSE;
5374 enum {
5375 BASE = 36,
5376 TMIN = 1,
5377 TMAX = 26,
5378 SKEW = 38,
5379 DAMP = 700,
5380 INIT_BIAS = 72,
5381 INIT_N = 128
5384 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5386 INT k;
5388 delta /= (firsttime ? DAMP : 2);
5389 delta += delta/numpoints;
5391 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5392 delta /= BASE-TMIN;
5393 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5396 /******************************************************************************
5397 * IdnToAscii (KERNEL32.@)
5398 * Implementation of Punycode based on RFC 3492.
5400 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5401 LPWSTR lpASCIICharStr, INT cchASCIIChar)
5403 static const WCHAR prefixW[] = {'x','n','-','-'};
5405 WCHAR *norm_str;
5406 INT i, label_start, label_end, norm_len, out_label, out = 0;
5408 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5409 lpASCIICharStr, cchASCIIChar);
5411 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5412 if(!norm_len)
5413 return 0;
5414 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5415 if(!norm_str) {
5416 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5417 return 0;
5419 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5420 cchUnicodeChar, norm_str, norm_len);
5421 if(!norm_len) {
5422 HeapFree(GetProcessHeap(), 0, norm_str);
5423 return 0;
5426 for(label_start=0; label_start<norm_len;) {
5427 INT n = INIT_N, bias = INIT_BIAS;
5428 INT delta = 0, b = 0, h;
5430 out_label = out;
5431 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5432 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5433 if(norm_str[i] < 0x80)
5434 b++;
5435 label_end = i;
5437 if(b == label_end-label_start) {
5438 if(label_end < norm_len)
5439 b++;
5440 if(!lpASCIICharStr) {
5441 out += b;
5442 }else if(out+b <= cchASCIIChar) {
5443 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5444 out += b;
5445 }else {
5446 HeapFree(GetProcessHeap(), 0, norm_str);
5447 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5448 return 0;
5450 label_start = label_end+1;
5451 continue;
5454 if(!lpASCIICharStr) {
5455 out += 5+b; /* strlen(xn--...-) */
5456 }else if(out+5+b <= cchASCIIChar) {
5457 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5458 out += 4;
5459 for(i=label_start; i<label_end; i++)
5460 if(norm_str[i] < 0x80)
5461 lpASCIICharStr[out++] = norm_str[i];
5462 lpASCIICharStr[out++] = '-';
5463 }else {
5464 HeapFree(GetProcessHeap(), 0, norm_str);
5465 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5466 return 0;
5468 if(!b)
5469 out--;
5471 for(h=b; h<label_end-label_start;) {
5472 INT m = 0xffff, q, k;
5474 for(i=label_start; i<label_end; i++) {
5475 if(norm_str[i]>=n && m>norm_str[i])
5476 m = norm_str[i];
5478 delta += (m-n)*(h+1);
5479 n = m;
5481 for(i=label_start; i<label_end; i++) {
5482 if(norm_str[i] < n) {
5483 delta++;
5484 }else if(norm_str[i] == n) {
5485 for(q=delta, k=BASE; ; k+=BASE) {
5486 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5487 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5488 if(!lpASCIICharStr) {
5489 out++;
5490 }else if(out+1 <= cchASCIIChar) {
5491 lpASCIICharStr[out++] = disp<='z'-'a' ?
5492 'a'+disp : '0'+disp-'z'+'a'-1;
5493 }else {
5494 HeapFree(GetProcessHeap(), 0, norm_str);
5495 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5496 return 0;
5498 if(q < t)
5499 break;
5500 q = (q-t)/(BASE-t);
5502 bias = adapt(delta, h+1, h==b);
5503 delta = 0;
5504 h++;
5507 delta++;
5508 n++;
5511 if(out-out_label > 63) {
5512 HeapFree(GetProcessHeap(), 0, norm_str);
5513 SetLastError(ERROR_INVALID_NAME);
5514 return 0;
5517 if(label_end < norm_len) {
5518 if(!lpASCIICharStr) {
5519 out++;
5520 }else if(out+1 <= cchASCIIChar) {
5521 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5522 }else {
5523 HeapFree(GetProcessHeap(), 0, norm_str);
5524 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5525 return 0;
5528 label_start = label_end+1;
5531 HeapFree(GetProcessHeap(), 0, norm_str);
5532 return out;
5535 /******************************************************************************
5536 * IdnToNameprepUnicode (KERNEL32.@)
5538 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5539 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5541 enum {
5542 UNASSIGNED = 0x1,
5543 PROHIBITED = 0x2,
5544 BIDI_RAL = 0x4,
5545 BIDI_L = 0x8
5548 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5549 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5550 const WCHAR *ptr;
5551 WORD flags;
5552 WCHAR buf[64], *map_str, norm_str[64], ch;
5553 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5554 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5556 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5557 lpNameprepCharStr, cchNameprepChar);
5559 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5560 SetLastError(ERROR_INVALID_FLAGS);
5561 return 0;
5564 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5565 SetLastError(ERROR_INVALID_PARAMETER);
5566 return 0;
5569 if(cchUnicodeChar == -1)
5570 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5571 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5572 SetLastError(ERROR_INVALID_NAME);
5573 return 0;
5576 for(label_start=0; label_start<cchUnicodeChar;) {
5577 ascii_only = TRUE;
5578 for(i=label_start; i<cchUnicodeChar; i++) {
5579 ch = lpUnicodeCharStr[i];
5581 if(i!=cchUnicodeChar-1 && !ch) {
5582 SetLastError(ERROR_INVALID_NAME);
5583 return 0;
5585 /* check if ch is one of label separators defined in RFC3490 */
5586 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5587 break;
5589 if(ch > 0x7f) {
5590 ascii_only = FALSE;
5591 continue;
5594 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5595 continue;
5596 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5597 || (ch>='0' && ch<='9') || ch=='-')
5598 continue;
5600 SetLastError(ERROR_INVALID_NAME);
5601 return 0;
5603 label_end = i;
5604 /* last label may be empty */
5605 if(label_start==label_end && ch) {
5606 SetLastError(ERROR_INVALID_NAME);
5607 return 0;
5610 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5611 lpUnicodeCharStr[label_end-1]=='-')) {
5612 SetLastError(ERROR_INVALID_NAME);
5613 return 0;
5616 if(ascii_only) {
5617 /* maximal label length is 63 characters */
5618 if(label_end-label_start > 63) {
5619 SetLastError(ERROR_INVALID_NAME);
5620 return 0;
5622 if(label_end < cchUnicodeChar)
5623 label_end++;
5625 if(!lpNameprepCharStr) {
5626 out += label_end-label_start;
5627 }else if(out+label_end-label_start <= cchNameprepChar) {
5628 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5629 (label_end-label_start)*sizeof(WCHAR));
5630 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5631 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5632 out += label_end-label_start;
5633 }else {
5634 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5635 return 0;
5638 label_start = label_end;
5639 continue;
5642 map_len = 0;
5643 for(i=label_start; i<label_end; i++) {
5644 ch = lpUnicodeCharStr[i];
5645 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5646 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5648 if(!ptr[0]) map_len++;
5649 else if(!ptr[1]) map_len++;
5650 else if(!ptr[2]) map_len += 2;
5651 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5653 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5654 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5655 if(!map_str) {
5656 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5657 return 0;
5659 }else {
5660 map_str = buf;
5662 map_len = 0;
5663 for(i=label_start; i<label_end; i++) {
5664 ch = lpUnicodeCharStr[i];
5665 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5666 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5668 if(!ptr[0]) {
5669 map_str[map_len++] = ch;
5670 }else if(!ptr[1]) {
5671 map_str[map_len++] = ptr[0];
5672 }else if(!ptr[2]) {
5673 map_str[map_len++] = ptr[0];
5674 map_str[map_len++] = ptr[1];
5675 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5676 map_str[map_len++] = ptr[0];
5677 map_str[map_len++] = ptr[1];
5678 map_str[map_len++] = ptr[2];
5682 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5683 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5684 if(map_str != buf)
5685 HeapFree(GetProcessHeap(), 0, map_str);
5686 if(!norm_len) {
5687 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5688 SetLastError(ERROR_INVALID_NAME);
5689 return 0;
5692 if(label_end < cchUnicodeChar) {
5693 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5694 label_end++;
5697 if(!lpNameprepCharStr) {
5698 out += norm_len;
5699 }else if(out+norm_len <= cchNameprepChar) {
5700 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5701 out += norm_len;
5702 }else {
5703 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5704 return 0;
5707 have_bidi_ral = prohibit_bidi_ral = FALSE;
5708 mask = PROHIBITED;
5709 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5710 mask |= UNASSIGNED;
5711 for(i=0; i<norm_len; i++) {
5712 ch = norm_str[i];
5713 flags = get_table_entry( nameprep_char_type, ch );
5715 if(flags & mask) {
5716 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5717 : ERROR_NO_UNICODE_TRANSLATION);
5718 return 0;
5721 if(flags & BIDI_RAL)
5722 have_bidi_ral = TRUE;
5723 if(flags & BIDI_L)
5724 prohibit_bidi_ral = TRUE;
5727 if(have_bidi_ral) {
5728 ch = norm_str[0];
5729 flags = get_table_entry( nameprep_char_type, ch );
5730 if((flags & BIDI_RAL) == 0)
5731 prohibit_bidi_ral = TRUE;
5733 ch = norm_str[norm_len-1];
5734 flags = get_table_entry( nameprep_char_type, ch );
5735 if((flags & BIDI_RAL) == 0)
5736 prohibit_bidi_ral = TRUE;
5739 if(have_bidi_ral && prohibit_bidi_ral) {
5740 SetLastError(ERROR_INVALID_NAME);
5741 return 0;
5744 label_start = label_end;
5747 return out;
5750 /******************************************************************************
5751 * IdnToUnicode (KERNEL32.@)
5753 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5754 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5756 extern const unsigned short nameprep_char_type[];
5758 INT i, label_start, label_end, out_label, out = 0;
5759 WCHAR ch;
5761 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5762 lpUnicodeCharStr, cchUnicodeChar);
5764 for(label_start=0; label_start<cchASCIIChar;) {
5765 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5767 out_label = out;
5768 for(i=label_start; i<cchASCIIChar; i++) {
5769 ch = lpASCIICharStr[i];
5771 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5772 SetLastError(ERROR_INVALID_NAME);
5773 return 0;
5776 if(!ch || ch=='.')
5777 break;
5778 if(ch == '-')
5779 delim = i;
5781 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5782 continue;
5783 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5784 || (ch>='0' && ch<='9') || ch=='-')
5785 continue;
5787 SetLastError(ERROR_INVALID_NAME);
5788 return 0;
5790 label_end = i;
5791 /* last label may be empty */
5792 if(label_start==label_end && ch) {
5793 SetLastError(ERROR_INVALID_NAME);
5794 return 0;
5797 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5798 lpASCIICharStr[label_end-1]=='-')) {
5799 SetLastError(ERROR_INVALID_NAME);
5800 return 0;
5802 if(label_end-label_start > 63) {
5803 SetLastError(ERROR_INVALID_NAME);
5804 return 0;
5807 if(label_end-label_start<4 ||
5808 tolowerW(lpASCIICharStr[label_start])!='x' ||
5809 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5810 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5811 if(label_end < cchASCIIChar)
5812 label_end++;
5814 if(!lpUnicodeCharStr) {
5815 out += label_end-label_start;
5816 }else if(out+label_end-label_start <= cchUnicodeChar) {
5817 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5818 (label_end-label_start)*sizeof(WCHAR));
5819 out += label_end-label_start;
5820 }else {
5821 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5822 return 0;
5825 label_start = label_end;
5826 continue;
5829 if(delim == label_start+3)
5830 delim++;
5831 if(!lpUnicodeCharStr) {
5832 out += delim-label_start-4;
5833 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5834 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5835 (delim-label_start-4)*sizeof(WCHAR));
5836 out += delim-label_start-4;
5837 }else {
5838 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5839 return 0;
5841 if(out != out_label)
5842 delim++;
5844 for(i=delim; i<label_end;) {
5845 old_pos = pos;
5846 w = 1;
5847 for(k=BASE; ; k+=BASE) {
5848 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5849 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5850 SetLastError(ERROR_INVALID_NAME);
5851 return 0;
5853 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5854 pos += digit*w;
5855 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5856 if(digit < t)
5857 break;
5858 w *= BASE-t;
5860 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5861 n += pos/(out-out_label+1);
5862 pos %= out-out_label+1;
5864 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5865 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5866 SetLastError(ERROR_INVALID_NAME);
5867 return 0;
5869 if(!lpUnicodeCharStr) {
5870 out++;
5871 }else if(out+1 <= cchASCIIChar) {
5872 memmove(lpUnicodeCharStr+out_label+pos+1,
5873 lpUnicodeCharStr+out_label+pos,
5874 (out-out_label-pos)*sizeof(WCHAR));
5875 lpUnicodeCharStr[out_label+pos] = n;
5876 out++;
5877 }else {
5878 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5879 return 0;
5881 pos++;
5884 if(out-out_label > 63) {
5885 SetLastError(ERROR_INVALID_NAME);
5886 return 0;
5889 if(label_end < cchASCIIChar) {
5890 if(!lpUnicodeCharStr) {
5891 out++;
5892 }else if(out+1 <= cchUnicodeChar) {
5893 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5894 }else {
5895 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5896 return 0;
5899 label_start = label_end+1;
5902 return out;
5906 /******************************************************************************
5907 * GetFileMUIPath (KERNEL32.@)
5910 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5911 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5913 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5914 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5916 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5918 return FALSE;
5921 /******************************************************************************
5922 * GetFileMUIInfo (KERNEL32.@)
5925 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5927 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5929 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5930 return FALSE;
5933 /******************************************************************************
5934 * ResolveLocaleName (KERNEL32.@)
5937 INT WINAPI ResolveLocaleName(LPCWSTR name, LPWSTR localename, INT len)
5939 FIXME("stub: %s, %p, %d\n", wine_dbgstr_w(name), localename, len);
5941 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5942 return 0;