kernel32/tests: Fix a dll reference leak.
[wine.git] / dlls / kernel32 / locale.c
blob64966e540cd7eba4ee475bf15b9841c2277d14ac
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 WORD get_default_sublang(LCID lang)
367 switch (PRIMARYLANGID(lang))
369 case LANG_SPANISH:
370 return SUBLANG_SPANISH_MODERN;
371 case LANG_CHINESE:
372 return SUBLANG_CHINESE_SIMPLIFIED;
373 default:
374 return SUBLANG_DEFAULT;
378 /***********************************************************************
379 * find_locale_id_callback
381 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
382 LPCWSTR name, WORD LangID, LPARAM lParam )
384 struct locale_name *data = (struct locale_name *)lParam;
385 WCHAR buffer[128];
386 int matches = 0;
387 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
389 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
391 /* first check exact name */
392 if (data->win_name[0] &&
393 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
394 buffer, sizeof(buffer)/sizeof(WCHAR) ))
396 if (!strcmpiW( data->win_name, buffer ))
398 matches = 4; /* everything matches */
399 goto done;
403 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
404 buffer, sizeof(buffer)/sizeof(WCHAR) ))
405 return TRUE;
406 if (strcmpiW( buffer, data->lang )) return TRUE;
407 matches++; /* language name matched */
409 if (data->country)
411 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
412 buffer, sizeof(buffer)/sizeof(WCHAR) ))
414 if (strcmpiW( buffer, data->country )) goto done;
415 matches++; /* country name matched */
418 else /* match default language */
420 if (SUBLANGID(LangID) == get_default_sublang( LangID )) matches++;
423 if (data->codepage)
425 UINT unix_cp;
426 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
427 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
429 if (unix_cp == data->codepage) matches++;
433 /* FIXME: check sort order */
435 done:
436 if (matches > data->matches)
438 data->lcid = lcid;
439 data->matches = matches;
441 return (data->matches < 4); /* no need to continue for perfect match */
445 /***********************************************************************
446 * parse_locale_name
448 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
449 * Unix format is: lang[_country][.charset][@modifier]
450 * Windows format is: lang[-script][-country][_modifier]
452 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
454 static const WCHAR sepW[] = {'-','_','.','@',0};
455 static const WCHAR winsepW[] = {'-','_',0};
456 static const WCHAR posixW[] = {'P','O','S','I','X',0};
457 static const WCHAR cW[] = {'C',0};
458 static const WCHAR latinW[] = {'l','a','t','i','n',0};
459 static const WCHAR latnW[] = {'-','L','a','t','n',0};
460 WCHAR *p;
462 TRACE("%s\n", debugstr_w(str));
464 name->country = name->charset = name->script = name->modifier = NULL;
465 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
466 name->matches = 0;
467 name->codepage = 0;
468 name->win_name[0] = 0;
469 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
471 if (!*name->lang)
473 name->lcid = LOCALE_INVARIANT;
474 name->matches = 4;
475 return;
478 if (!(p = strpbrkW( name->lang, sepW )))
480 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
482 name->matches = 4; /* perfect match for default English lcid */
483 return;
485 strcpyW( name->win_name, name->lang );
487 else if (*p == '-') /* Windows format */
489 strcpyW( name->win_name, name->lang );
490 *p++ = 0;
491 name->country = p;
492 if (!(p = strpbrkW( p, winsepW ))) goto done;
493 if (*p == '-')
495 *p++ = 0;
496 name->script = name->country;
497 name->country = p;
498 if (!(p = strpbrkW( p, winsepW ))) goto done;
500 *p++ = 0;
501 name->modifier = p;
503 else /* Unix format */
505 if (*p == '_')
507 *p++ = 0;
508 name->country = p;
509 p = strpbrkW( p, sepW + 2 );
511 if (p && *p == '.')
513 *p++ = 0;
514 name->charset = p;
515 p = strchrW( p, '@' );
517 if (p)
519 *p++ = 0;
520 name->modifier = p;
523 if (name->charset)
524 name->codepage = find_charset( name->charset );
526 /* rebuild a Windows name if possible */
528 if (name->charset) goto done; /* can't specify charset in Windows format */
529 if (name->modifier && strcmpW( name->modifier, latinW ))
530 goto done; /* only Latn script supported for now */
531 strcpyW( name->win_name, name->lang );
532 if (name->modifier) strcatW( name->win_name, latnW );
533 if (name->country)
535 p = name->win_name + strlenW(name->win_name);
536 *p++ = '-';
537 strcpyW( p, name->country );
540 done:
541 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
542 find_locale_id_callback, (LPARAM)name );
546 /***********************************************************************
547 * convert_default_lcid
549 * Get the default LCID to use for a given lctype in GetLocaleInfo.
551 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
553 if (lcid == LOCALE_SYSTEM_DEFAULT ||
554 lcid == LOCALE_USER_DEFAULT ||
555 lcid == LOCALE_NEUTRAL)
557 LCID default_id = 0;
559 switch(lctype & 0xffff)
561 case LOCALE_SSORTNAME:
562 default_id = lcid_LC_COLLATE;
563 break;
565 case LOCALE_FONTSIGNATURE:
566 case LOCALE_IDEFAULTANSICODEPAGE:
567 case LOCALE_IDEFAULTCODEPAGE:
568 case LOCALE_IDEFAULTEBCDICCODEPAGE:
569 case LOCALE_IDEFAULTMACCODEPAGE:
570 case LOCALE_IDEFAULTUNIXCODEPAGE:
571 default_id = lcid_LC_CTYPE;
572 break;
574 case LOCALE_ICURRDIGITS:
575 case LOCALE_ICURRENCY:
576 case LOCALE_IINTLCURRDIGITS:
577 case LOCALE_INEGCURR:
578 case LOCALE_INEGSEPBYSPACE:
579 case LOCALE_INEGSIGNPOSN:
580 case LOCALE_INEGSYMPRECEDES:
581 case LOCALE_IPOSSEPBYSPACE:
582 case LOCALE_IPOSSIGNPOSN:
583 case LOCALE_IPOSSYMPRECEDES:
584 case LOCALE_SCURRENCY:
585 case LOCALE_SINTLSYMBOL:
586 case LOCALE_SMONDECIMALSEP:
587 case LOCALE_SMONGROUPING:
588 case LOCALE_SMONTHOUSANDSEP:
589 case LOCALE_SNATIVECURRNAME:
590 default_id = lcid_LC_MONETARY;
591 break;
593 case LOCALE_IDIGITS:
594 case LOCALE_IDIGITSUBSTITUTION:
595 case LOCALE_ILZERO:
596 case LOCALE_INEGNUMBER:
597 case LOCALE_SDECIMAL:
598 case LOCALE_SGROUPING:
599 case LOCALE_SNAN:
600 case LOCALE_SNATIVEDIGITS:
601 case LOCALE_SNEGATIVESIGN:
602 case LOCALE_SNEGINFINITY:
603 case LOCALE_SPOSINFINITY:
604 case LOCALE_SPOSITIVESIGN:
605 case LOCALE_STHOUSAND:
606 default_id = lcid_LC_NUMERIC;
607 break;
609 case LOCALE_ICALENDARTYPE:
610 case LOCALE_ICENTURY:
611 case LOCALE_IDATE:
612 case LOCALE_IDAYLZERO:
613 case LOCALE_IFIRSTDAYOFWEEK:
614 case LOCALE_IFIRSTWEEKOFYEAR:
615 case LOCALE_ILDATE:
616 case LOCALE_IMONLZERO:
617 case LOCALE_IOPTIONALCALENDAR:
618 case LOCALE_ITIME:
619 case LOCALE_ITIMEMARKPOSN:
620 case LOCALE_ITLZERO:
621 case LOCALE_S1159:
622 case LOCALE_S2359:
623 case LOCALE_SABBREVDAYNAME1:
624 case LOCALE_SABBREVDAYNAME2:
625 case LOCALE_SABBREVDAYNAME3:
626 case LOCALE_SABBREVDAYNAME4:
627 case LOCALE_SABBREVDAYNAME5:
628 case LOCALE_SABBREVDAYNAME6:
629 case LOCALE_SABBREVDAYNAME7:
630 case LOCALE_SABBREVMONTHNAME1:
631 case LOCALE_SABBREVMONTHNAME2:
632 case LOCALE_SABBREVMONTHNAME3:
633 case LOCALE_SABBREVMONTHNAME4:
634 case LOCALE_SABBREVMONTHNAME5:
635 case LOCALE_SABBREVMONTHNAME6:
636 case LOCALE_SABBREVMONTHNAME7:
637 case LOCALE_SABBREVMONTHNAME8:
638 case LOCALE_SABBREVMONTHNAME9:
639 case LOCALE_SABBREVMONTHNAME10:
640 case LOCALE_SABBREVMONTHNAME11:
641 case LOCALE_SABBREVMONTHNAME12:
642 case LOCALE_SABBREVMONTHNAME13:
643 case LOCALE_SDATE:
644 case LOCALE_SDAYNAME1:
645 case LOCALE_SDAYNAME2:
646 case LOCALE_SDAYNAME3:
647 case LOCALE_SDAYNAME4:
648 case LOCALE_SDAYNAME5:
649 case LOCALE_SDAYNAME6:
650 case LOCALE_SDAYNAME7:
651 case LOCALE_SDURATION:
652 case LOCALE_SLONGDATE:
653 case LOCALE_SMONTHNAME1:
654 case LOCALE_SMONTHNAME2:
655 case LOCALE_SMONTHNAME3:
656 case LOCALE_SMONTHNAME4:
657 case LOCALE_SMONTHNAME5:
658 case LOCALE_SMONTHNAME6:
659 case LOCALE_SMONTHNAME7:
660 case LOCALE_SMONTHNAME8:
661 case LOCALE_SMONTHNAME9:
662 case LOCALE_SMONTHNAME10:
663 case LOCALE_SMONTHNAME11:
664 case LOCALE_SMONTHNAME12:
665 case LOCALE_SMONTHNAME13:
666 case LOCALE_SSHORTDATE:
667 case LOCALE_SSHORTESTDAYNAME1:
668 case LOCALE_SSHORTESTDAYNAME2:
669 case LOCALE_SSHORTESTDAYNAME3:
670 case LOCALE_SSHORTESTDAYNAME4:
671 case LOCALE_SSHORTESTDAYNAME5:
672 case LOCALE_SSHORTESTDAYNAME6:
673 case LOCALE_SSHORTESTDAYNAME7:
674 case LOCALE_STIME:
675 case LOCALE_STIMEFORMAT:
676 case LOCALE_SYEARMONTH:
677 default_id = lcid_LC_TIME;
678 break;
680 case LOCALE_IPAPERSIZE:
681 default_id = lcid_LC_PAPER;
682 break;
684 case LOCALE_IMEASURE:
685 default_id = lcid_LC_MEASUREMENT;
686 break;
688 case LOCALE_ICOUNTRY:
689 default_id = lcid_LC_TELEPHONE;
690 break;
692 if (default_id) lcid = default_id;
694 return ConvertDefaultLocale( lcid );
697 /***********************************************************************
698 * is_genitive_name_supported
700 * Determine could LCTYPE basically support genitive name form or not.
702 static BOOL is_genitive_name_supported( LCTYPE lctype )
704 switch(lctype & 0xffff)
706 case LOCALE_SMONTHNAME1:
707 case LOCALE_SMONTHNAME2:
708 case LOCALE_SMONTHNAME3:
709 case LOCALE_SMONTHNAME4:
710 case LOCALE_SMONTHNAME5:
711 case LOCALE_SMONTHNAME6:
712 case LOCALE_SMONTHNAME7:
713 case LOCALE_SMONTHNAME8:
714 case LOCALE_SMONTHNAME9:
715 case LOCALE_SMONTHNAME10:
716 case LOCALE_SMONTHNAME11:
717 case LOCALE_SMONTHNAME12:
718 case LOCALE_SMONTHNAME13:
719 return TRUE;
720 default:
721 return FALSE;
725 /***********************************************************************
726 * create_registry_key
728 * Create the Control Panel\\International registry key.
730 static inline HANDLE create_registry_key(void)
732 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
733 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
734 OBJECT_ATTRIBUTES attr;
735 UNICODE_STRING nameW;
736 HANDLE cpl_key, hkey = 0;
738 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
740 attr.Length = sizeof(attr);
741 attr.RootDirectory = hkey;
742 attr.ObjectName = &nameW;
743 attr.Attributes = 0;
744 attr.SecurityDescriptor = NULL;
745 attr.SecurityQualityOfService = NULL;
746 RtlInitUnicodeString( &nameW, cplW );
748 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
750 NtClose( attr.RootDirectory );
751 attr.RootDirectory = cpl_key;
752 RtlInitUnicodeString( &nameW, intlW );
753 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
755 NtClose( attr.RootDirectory );
756 return hkey;
760 /* update the registry settings for a given locale parameter */
761 /* return TRUE if an update was needed */
762 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
763 const LCTYPE *values, UINT nb_values )
765 static const WCHAR formatW[] = { '%','0','8','x',0 };
766 WCHAR bufferW[40];
767 UNICODE_STRING nameW;
768 DWORD count, i;
770 RtlInitUnicodeString( &nameW, name );
771 count = sizeof(bufferW);
772 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
774 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
775 LPCWSTR text = (LPCWSTR)info->Data;
777 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
778 TRACE( "updating registry, locale %s changed %s -> %08x\n",
779 debugstr_w(name), debugstr_w(text), lcid );
781 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
782 sprintfW( bufferW, formatW, lcid );
783 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
785 for (i = 0; i < nb_values; i++)
787 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
788 sizeof(bufferW)/sizeof(WCHAR) );
789 SetLocaleInfoW( lcid, values[i], bufferW );
791 return TRUE;
795 /***********************************************************************
796 * LOCALE_InitRegistry
798 * Update registry contents on startup if the user locale has changed.
799 * This simulates the action of the Windows control panel.
801 void LOCALE_InitRegistry(void)
803 static const WCHAR acpW[] = {'A','C','P',0};
804 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
805 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
806 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
807 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
808 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
809 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
810 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
811 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
812 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
813 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
814 static const struct
816 LPCWSTR name;
817 USHORT value;
818 } update_cp_values[] = {
819 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
820 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
821 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
823 static const LCTYPE lc_messages_values[] = {
824 LOCALE_SABBREVLANGNAME,
825 LOCALE_SCOUNTRY,
826 LOCALE_SLIST };
827 static const LCTYPE lc_monetary_values[] = {
828 LOCALE_SCURRENCY,
829 LOCALE_ICURRENCY,
830 LOCALE_INEGCURR,
831 LOCALE_ICURRDIGITS,
832 LOCALE_ILZERO,
833 LOCALE_SMONDECIMALSEP,
834 LOCALE_SMONGROUPING,
835 LOCALE_SMONTHOUSANDSEP };
836 static const LCTYPE lc_numeric_values[] = {
837 LOCALE_SDECIMAL,
838 LOCALE_STHOUSAND,
839 LOCALE_IDIGITS,
840 LOCALE_IDIGITSUBSTITUTION,
841 LOCALE_SNATIVEDIGITS,
842 LOCALE_INEGNUMBER,
843 LOCALE_SNEGATIVESIGN,
844 LOCALE_SPOSITIVESIGN,
845 LOCALE_SGROUPING };
846 static const LCTYPE lc_time_values[] = {
847 LOCALE_S1159,
848 LOCALE_S2359,
849 LOCALE_STIME,
850 LOCALE_ITIME,
851 LOCALE_ITLZERO,
852 LOCALE_SSHORTDATE,
853 LOCALE_SLONGDATE,
854 LOCALE_SDATE,
855 LOCALE_ITIMEMARKPOSN,
856 LOCALE_ICALENDARTYPE,
857 LOCALE_IFIRSTDAYOFWEEK,
858 LOCALE_IFIRSTWEEKOFYEAR,
859 LOCALE_STIMEFORMAT,
860 LOCALE_SYEARMONTH,
861 LOCALE_IDATE };
862 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
863 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
864 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
866 UNICODE_STRING nameW;
867 WCHAR bufferW[80];
868 DWORD count, i;
869 HANDLE hkey;
870 LCID lcid = GetUserDefaultLCID();
872 if (!(hkey = create_registry_key()))
873 return; /* don't do anything if we can't create the registry key */
875 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
876 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
877 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
878 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
879 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
880 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
881 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
882 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
883 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
884 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
885 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
886 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
887 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
888 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
890 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
892 static const WCHAR codepageW[] =
893 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
894 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
895 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
897 OBJECT_ATTRIBUTES attr;
898 HANDLE nls_key;
899 DWORD len = 14;
901 RtlInitUnicodeString( &nameW, codepageW );
902 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
903 while (codepageW[len])
905 nameW.Length = len * sizeof(WCHAR);
906 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
907 NtClose( nls_key );
908 len++;
909 while (codepageW[len] && codepageW[len] != '\\') len++;
911 nameW.Length = len * sizeof(WCHAR);
912 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
914 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
916 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
917 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
918 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
919 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
921 NtClose( nls_key );
925 NtClose( hkey );
929 #ifdef __APPLE__
930 /***********************************************************************
931 * get_mac_locale
933 * Return a locale identifier string reflecting the Mac locale, in a form
934 * that parse_locale_name() will understand. So, strip out unusual
935 * things like script, variant, etc. Or, rather, just construct it as
936 * <lang>[_<country>].UTF-8.
938 static const char* get_mac_locale(void)
940 static char mac_locale[50];
942 if (!mac_locale[0])
944 CFLocaleRef locale = CFLocaleCopyCurrent();
945 CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
946 CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
947 CFStringRef locale_string;
949 if (country)
950 locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
951 else
952 locale_string = CFStringCreateCopy(NULL, lang);
954 CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
955 strcat(mac_locale, ".UTF-8");
957 CFRelease(locale);
958 CFRelease(locale_string);
961 return mac_locale;
965 /***********************************************************************
966 * has_env
968 static BOOL has_env(const char* name)
970 const char* value = getenv( name );
971 return value && value[0];
973 #endif
976 /***********************************************************************
977 * get_locale
979 * Get the locale identifier for a given category. On most platforms,
980 * this is just a thin wrapper around setlocale(). On OS X, though, it
981 * is common for the Mac locale settings to not be supported by the C
982 * library. So, we sometimes override the result with the Mac locale.
984 static const char* get_locale(int category, const char* category_name)
986 const char* ret = setlocale(category, NULL);
988 #ifdef __ANDROID__
989 if (!strcmp(ret, "C"))
991 ret = getenv( category_name );
992 if (!ret || !ret[0]) ret = getenv( "LC_ALL" );
993 if (!ret || !ret[0]) ret = "C";
995 #endif
997 #ifdef __APPLE__
998 /* If LC_ALL is set, respect it as a user override.
999 If LC_* is set, respect it as a user override, except if it's LC_CTYPE
1000 and equal to UTF-8. That's because, when the Mac locale isn't supported
1001 by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
1002 parse_locale_name() doesn't handle that properly, so we override that
1003 with the Mac locale (which uses UTF-8 for the charset, anyway).
1004 Otherwise:
1005 For LC_MESSAGES, we override the C library because the user language
1006 setting is separate from the locale setting on which LANG was based.
1007 If the C library didn't get anything better from LANG than C or POSIX,
1008 override that. That probably means the Mac locale isn't supported by
1009 the C library. */
1010 if (!has_env( "LC_ALL" ) &&
1011 ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
1012 (!has_env( category_name ) &&
1013 (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
1015 const char* override = get_mac_locale();
1017 if (category == LC_MESSAGES)
1019 /* Retrieve the preferred language as chosen in System Preferences. */
1020 static char messages_locale[50];
1022 if (!messages_locale[0])
1024 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1025 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1027 CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1028 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1029 if (components)
1031 CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1032 CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1033 CFLocaleRef locale = NULL;
1034 CFStringRef locale_string;
1036 if (!country)
1038 locale = CFLocaleCopyCurrent();
1039 country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1042 if (country)
1043 locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1044 else
1045 locale_string = CFStringCreateCopy( NULL, lang );
1046 CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1047 strcat( messages_locale, ".UTF-8" );
1049 CFRelease( locale_string );
1050 if (locale) CFRelease( locale );
1051 CFRelease( components );
1054 if (preferred_langs)
1055 CFRelease( preferred_langs );
1058 if (messages_locale[0])
1059 override = messages_locale;
1062 TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1063 ret = override;
1065 #endif
1067 return ret;
1071 /***********************************************************************
1072 * setup_unix_locales
1074 static UINT setup_unix_locales(void)
1076 struct locale_name locale_name;
1077 WCHAR buffer[128], ctype_buff[128];
1078 const char *locale;
1079 UINT unix_cp = 0;
1081 if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1083 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
1084 parse_locale_name( ctype_buff, &locale_name );
1085 lcid_LC_CTYPE = locale_name.lcid;
1086 unix_cp = locale_name.codepage;
1088 if (!lcid_LC_CTYPE) /* this one needs a default value */
1089 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
1091 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1092 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
1094 #define GET_UNIX_LOCALE(cat) do \
1095 if ((locale = get_locale( cat, #cat ))) \
1097 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
1098 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1099 else { \
1100 parse_locale_name( buffer, &locale_name ); \
1101 lcid_##cat = locale_name.lcid; \
1102 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
1103 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1105 } while (0)
1107 GET_UNIX_LOCALE( LC_COLLATE );
1108 GET_UNIX_LOCALE( LC_MESSAGES );
1109 GET_UNIX_LOCALE( LC_MONETARY );
1110 GET_UNIX_LOCALE( LC_NUMERIC );
1111 GET_UNIX_LOCALE( LC_TIME );
1112 #ifdef LC_PAPER
1113 GET_UNIX_LOCALE( LC_PAPER );
1114 #endif
1115 #ifdef LC_MEASUREMENT
1116 GET_UNIX_LOCALE( LC_MEASUREMENT );
1117 #endif
1118 #ifdef LC_TELEPHONE
1119 GET_UNIX_LOCALE( LC_TELEPHONE );
1120 #endif
1122 #undef GET_UNIX_LOCALE
1124 return unix_cp;
1128 /***********************************************************************
1129 * GetUserDefaultLangID (KERNEL32.@)
1131 * Get the default language Id for the current user.
1133 * PARAMS
1134 * None.
1136 * RETURNS
1137 * The current LANGID of the default language for the current user.
1139 LANGID WINAPI GetUserDefaultLangID(void)
1141 return LANGIDFROMLCID(GetUserDefaultLCID());
1145 /***********************************************************************
1146 * GetSystemDefaultLangID (KERNEL32.@)
1148 * Get the default language Id for the system.
1150 * PARAMS
1151 * None.
1153 * RETURNS
1154 * The current LANGID of the default language for the system.
1156 LANGID WINAPI GetSystemDefaultLangID(void)
1158 return LANGIDFROMLCID(GetSystemDefaultLCID());
1162 /***********************************************************************
1163 * GetUserDefaultLCID (KERNEL32.@)
1165 * Get the default locale Id for the current user.
1167 * PARAMS
1168 * None.
1170 * RETURNS
1171 * The current LCID of the default locale for the current user.
1173 LCID WINAPI GetUserDefaultLCID(void)
1175 LCID lcid;
1176 NtQueryDefaultLocale( TRUE, &lcid );
1177 return lcid;
1181 /***********************************************************************
1182 * GetSystemDefaultLCID (KERNEL32.@)
1184 * Get the default locale Id for the system.
1186 * PARAMS
1187 * None.
1189 * RETURNS
1190 * The current LCID of the default locale for the system.
1192 LCID WINAPI GetSystemDefaultLCID(void)
1194 LCID lcid;
1195 NtQueryDefaultLocale( FALSE, &lcid );
1196 return lcid;
1199 /***********************************************************************
1200 * GetSystemDefaultLocaleName (KERNEL32.@)
1202 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1204 LCID lcid = GetSystemDefaultLCID();
1205 return LCIDToLocaleName(lcid, localename, len, 0);
1208 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1210 LCTYPE type;
1211 int lsize;
1213 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1215 if (flags & MUI_LANGUAGE_ID)
1216 type = LOCALE_ILANGUAGE;
1217 else
1218 type = LOCALE_SNAME;
1220 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1221 if (!lsize)
1223 /* keep last error from callee */
1224 return FALSE;
1226 lsize++;
1227 if (!*size)
1229 *size = lsize;
1230 *count = 1;
1231 return TRUE;
1234 if (lsize > *size)
1236 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1237 return FALSE;
1240 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1242 /* keep last error from callee */
1243 return FALSE;
1246 buffer[lsize-1] = 0;
1247 *size = lsize;
1248 *count = 1;
1249 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1250 return TRUE;
1254 /***********************************************************************
1255 * GetSystemPreferredUILanguages (KERNEL32.@)
1257 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1259 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1261 SetLastError(ERROR_INVALID_PARAMETER);
1262 return FALSE;
1264 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1266 SetLastError(ERROR_INVALID_PARAMETER);
1267 return FALSE;
1269 if (*size && !buffer)
1271 SetLastError(ERROR_INVALID_PARAMETER);
1272 return FALSE;
1275 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1278 /***********************************************************************
1279 * SetThreadPreferredUILanguages (KERNEL32.@)
1281 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1283 FIXME( "%u, %p, %p\n", flags, buffer, count );
1284 return TRUE;
1287 /***********************************************************************
1288 * GetThreadPreferredUILanguages (KERNEL32.@)
1290 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1292 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1293 return get_dummy_preferred_ui_language( flags, count, buf, size );
1296 /******************************************************************************
1297 * GetUserPreferredUILanguages (KERNEL32.@)
1299 BOOL WINAPI GetUserPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1301 TRACE( "%u %p %p %p\n", flags, count, buffer, size );
1303 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID))
1305 SetLastError(ERROR_INVALID_PARAMETER);
1306 return FALSE;
1308 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1310 SetLastError(ERROR_INVALID_PARAMETER);
1311 return FALSE;
1313 if (*size && !buffer)
1315 SetLastError(ERROR_INVALID_PARAMETER);
1316 return FALSE;
1319 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1322 /***********************************************************************
1323 * GetUserDefaultUILanguage (KERNEL32.@)
1325 * Get the default user interface language Id for the current user.
1327 * PARAMS
1328 * None.
1330 * RETURNS
1331 * The current LANGID of the default UI language for the current user.
1333 LANGID WINAPI GetUserDefaultUILanguage(void)
1335 LANGID lang;
1336 NtQueryDefaultUILanguage( &lang );
1337 return lang;
1341 /***********************************************************************
1342 * GetSystemDefaultUILanguage (KERNEL32.@)
1344 * Get the default user interface language Id for the system.
1346 * PARAMS
1347 * None.
1349 * RETURNS
1350 * The current LANGID of the default UI language for the system. This is
1351 * typically the same language used during the installation process.
1353 LANGID WINAPI GetSystemDefaultUILanguage(void)
1355 LANGID lang;
1356 NtQueryInstallUILanguage( &lang );
1357 return lang;
1361 /***********************************************************************
1362 * LocaleNameToLCID (KERNEL32.@)
1364 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1366 struct locale_name locale_name;
1368 if (flags) FIXME( "unsupported flags %x\n", flags );
1370 if (name == LOCALE_NAME_USER_DEFAULT)
1371 return GetUserDefaultLCID();
1373 /* string parsing */
1374 parse_locale_name( name, &locale_name );
1376 TRACE( "found lcid %x for %s, matches %d\n",
1377 locale_name.lcid, debugstr_w(name), locale_name.matches );
1379 if (!locale_name.matches)
1381 SetLastError(ERROR_INVALID_PARAMETER);
1382 return 0;
1385 if (locale_name.matches == 1)
1386 WARN( "locale %s not recognized, defaulting to %s\n",
1387 debugstr_w(name), debugstr_w(locale_name.lang) );
1389 return locale_name.lcid;
1393 /***********************************************************************
1394 * LCIDToLocaleName (KERNEL32.@)
1396 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1398 if (flags) FIXME( "unsupported flags %x\n", flags );
1400 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1404 /******************************************************************************
1405 * get_locale_registry_value
1407 * Gets the registry value name and cache for a given lctype.
1409 static struct registry_value *get_locale_registry_value( DWORD lctype )
1411 int i;
1412 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1413 if (registry_values[i].lctype == lctype)
1414 return &registry_values[i];
1415 return NULL;
1419 /******************************************************************************
1420 * get_registry_locale_info
1422 * Retrieve user-modified locale info from the registry.
1423 * Return length, 0 on error, -1 if not found.
1425 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1427 DWORD size;
1428 INT ret;
1429 HANDLE hkey;
1430 NTSTATUS status;
1431 UNICODE_STRING nameW;
1432 KEY_VALUE_PARTIAL_INFORMATION *info;
1433 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1435 RtlEnterCriticalSection( &cache_section );
1437 if (!registry_value->cached_value)
1439 if (!(hkey = create_registry_key()))
1441 RtlLeaveCriticalSection( &cache_section );
1442 return -1;
1445 RtlInitUnicodeString( &nameW, registry_value->name );
1446 size = info_size + len * sizeof(WCHAR);
1448 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1450 NtClose( hkey );
1451 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1452 RtlLeaveCriticalSection( &cache_section );
1453 return 0;
1456 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1458 /* try again with a bigger buffer when we have to return the correct size */
1459 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1461 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1462 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1464 info = new_info;
1465 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1469 NtClose( hkey );
1471 if (!status)
1473 INT length = (size - info_size) / sizeof(WCHAR);
1474 LPWSTR cached_value;
1476 if (!length || ((WCHAR *)&info->Data)[length-1])
1477 length++;
1479 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1481 if (!cached_value)
1483 HeapFree( GetProcessHeap(), 0, info );
1484 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1485 RtlLeaveCriticalSection( &cache_section );
1486 return 0;
1489 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1490 cached_value[length-1] = 0;
1491 HeapFree( GetProcessHeap(), 0, info );
1492 registry_value->cached_value = cached_value;
1494 else
1496 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1498 ret = (size - info_size) / sizeof(WCHAR);
1500 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1502 ret = -1;
1504 else
1506 SetLastError( RtlNtStatusToDosError(status) );
1507 ret = 0;
1509 HeapFree( GetProcessHeap(), 0, info );
1510 RtlLeaveCriticalSection( &cache_section );
1511 return ret;
1515 ret = lstrlenW( registry_value->cached_value ) + 1;
1517 if (buffer)
1519 if (ret > len)
1521 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1522 ret = 0;
1524 else
1526 lstrcpyW( buffer, registry_value->cached_value );
1530 RtlLeaveCriticalSection( &cache_section );
1532 return ret;
1536 /******************************************************************************
1537 * GetLocaleInfoA (KERNEL32.@)
1539 * Get information about an aspect of a locale.
1541 * PARAMS
1542 * lcid [I] LCID of the locale
1543 * lctype [I] LCTYPE_ flags from "winnls.h"
1544 * buffer [O] Destination for the information
1545 * len [I] Length of buffer in characters
1547 * RETURNS
1548 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1549 * with the information.
1550 * Failure: 0. Use GetLastError() to determine the cause.
1552 * NOTES
1553 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1554 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1555 * which is a bit string.
1557 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1559 WCHAR *bufferW;
1560 INT lenW, ret;
1562 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1564 if (len < 0 || (len && !buffer))
1566 SetLastError( ERROR_INVALID_PARAMETER );
1567 return 0;
1569 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1570 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1572 SetLastError( ERROR_INVALID_FLAGS );
1573 return 0;
1576 if (!len) buffer = NULL;
1578 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1580 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1582 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1583 return 0;
1585 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1587 if ((lctype & LOCALE_RETURN_NUMBER) ||
1588 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1590 /* it's not an ASCII string, just bytes */
1591 ret *= sizeof(WCHAR);
1592 if (buffer)
1594 if (ret <= len) memcpy( buffer, bufferW, ret );
1595 else
1597 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1598 ret = 0;
1602 else
1604 UINT codepage = CP_ACP;
1605 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1606 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1609 HeapFree( GetProcessHeap(), 0, bufferW );
1610 return ret;
1613 static int get_value_base_by_lctype( LCTYPE lctype )
1615 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1618 /******************************************************************************
1619 * GetLocaleInfoW (KERNEL32.@)
1621 * See GetLocaleInfoA.
1623 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1625 LANGID lang_id;
1626 HRSRC hrsrc;
1627 HGLOBAL hmem;
1628 INT ret;
1629 UINT lcflags;
1630 const WCHAR *p;
1631 unsigned int i;
1633 if (len < 0 || (len && !buffer))
1635 SetLastError( ERROR_INVALID_PARAMETER );
1636 return 0;
1638 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1639 !is_genitive_name_supported( lctype ))
1641 SetLastError( ERROR_INVALID_FLAGS );
1642 return 0;
1645 if (!len) buffer = NULL;
1647 lcid = convert_default_lcid( lcid, lctype );
1649 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1650 lctype &= 0xffff;
1652 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1654 /* first check for overrides in the registry */
1656 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1657 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1659 struct registry_value *value = get_locale_registry_value(lctype);
1661 if (value)
1663 if (lcflags & LOCALE_RETURN_NUMBER)
1665 WCHAR tmp[16];
1666 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1667 if (ret > 0)
1669 WCHAR *end;
1670 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1671 if (*end) /* invalid number */
1673 SetLastError( ERROR_INVALID_FLAGS );
1674 return 0;
1676 ret = sizeof(UINT)/sizeof(WCHAR);
1677 if (!buffer) return ret;
1678 if (ret > len)
1680 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1681 return 0;
1683 memcpy( buffer, &number, sizeof(number) );
1686 else ret = get_registry_locale_info( value, buffer, len );
1688 if (ret != -1) return ret;
1692 /* now load it from kernel resources */
1694 lang_id = LANGIDFROMLCID( lcid );
1696 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1697 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1698 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), get_default_sublang( lang_id ));
1700 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1701 ULongToPtr((lctype >> 4) + 1), lang_id )))
1703 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1704 return 0;
1706 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1707 return 0;
1709 p = LockResource( hmem );
1710 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1712 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1713 else if (is_genitive_name_supported( lctype ) && *p)
1715 /* genitive form's stored after a null separator from a nominative */
1716 for (i = 1; i <= *p; i++) if (!p[i]) break;
1718 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1720 ret = *p - i + 1;
1721 p += i;
1723 else ret = i;
1725 else
1726 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1728 if (!buffer) return ret;
1730 if (ret > len)
1732 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1733 return 0;
1736 if (lcflags & LOCALE_RETURN_NUMBER)
1738 UINT number;
1739 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1740 if (!tmp) return 0;
1741 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1742 tmp[*p] = 0;
1743 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1744 if (!*end)
1745 memcpy( buffer, &number, sizeof(number) );
1746 else /* invalid number */
1748 SetLastError( ERROR_INVALID_FLAGS );
1749 ret = 0;
1751 HeapFree( GetProcessHeap(), 0, tmp );
1753 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1754 lcid, lctype, buffer, len, number );
1756 else
1758 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1759 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1761 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1762 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1764 return ret;
1767 /******************************************************************************
1768 * GetLocaleInfoEx (KERNEL32.@)
1770 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1772 LCID lcid = LocaleNameToLCID(locale, 0);
1774 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1776 if (!lcid) return 0;
1778 /* special handling for neutral locale names */
1779 if (info == LOCALE_SNAME && locale && strlenW(locale) == 2)
1781 if (len && len < 3)
1783 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1784 return 0;
1787 if (len) strcpyW(buffer, locale);
1788 return 3;
1791 return GetLocaleInfoW(lcid, info, buffer, len);
1794 /******************************************************************************
1795 * SetLocaleInfoA [KERNEL32.@]
1797 * Set information about an aspect of a locale.
1799 * PARAMS
1800 * lcid [I] LCID of the locale
1801 * lctype [I] LCTYPE_ flags from "winnls.h"
1802 * data [I] Information to set
1804 * RETURNS
1805 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1806 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1807 * Failure: FALSE. Use GetLastError() to determine the cause.
1809 * NOTES
1810 * - Values are only be set for the current user locale; the system locale
1811 * settings cannot be changed.
1812 * - Any settings changed by this call are lost when the locale is changed by
1813 * the control panel (in Wine, this happens every time you change LANG).
1814 * - The native implementation of this function does not check that lcid matches
1815 * the current user locale, and simply sets the new values. Wine warns you in
1816 * this case, but behaves the same.
1818 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1820 UINT codepage = CP_ACP;
1821 WCHAR *strW;
1822 DWORD len;
1823 BOOL ret;
1825 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1827 if (!data)
1829 SetLastError( ERROR_INVALID_PARAMETER );
1830 return FALSE;
1832 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1833 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1835 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1836 return FALSE;
1838 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1839 ret = SetLocaleInfoW( lcid, lctype, strW );
1840 HeapFree( GetProcessHeap(), 0, strW );
1841 return ret;
1845 /******************************************************************************
1846 * SetLocaleInfoW (KERNEL32.@)
1848 * See SetLocaleInfoA.
1850 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1852 struct registry_value *value;
1853 static const WCHAR intlW[] = {'i','n','t','l',0 };
1854 UNICODE_STRING valueW;
1855 NTSTATUS status;
1856 HANDLE hkey;
1858 lctype &= 0xffff;
1859 value = get_locale_registry_value( lctype );
1861 if (!data || !value)
1863 SetLastError( ERROR_INVALID_PARAMETER );
1864 return FALSE;
1867 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1869 SetLastError( ERROR_INVALID_FLAGS );
1870 return FALSE;
1873 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1875 /* FIXME: should check that data to set is sane */
1877 /* FIXME: profile functions should map to registry */
1878 WriteProfileStringW( intlW, value->name, data );
1880 if (!(hkey = create_registry_key())) return FALSE;
1881 RtlInitUnicodeString( &valueW, value->name );
1882 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1884 RtlEnterCriticalSection( &cache_section );
1885 HeapFree( GetProcessHeap(), 0, value->cached_value );
1886 value->cached_value = NULL;
1887 RtlLeaveCriticalSection( &cache_section );
1889 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1891 /* Set I-value from S value */
1892 WCHAR *lpD, *lpM, *lpY;
1893 WCHAR szBuff[2];
1895 lpD = strrchrW(data, 'd');
1896 lpM = strrchrW(data, 'M');
1897 lpY = strrchrW(data, 'y');
1899 if (lpD <= lpM)
1901 szBuff[0] = '1'; /* D-M-Y */
1903 else
1905 if (lpY <= lpM)
1906 szBuff[0] = '2'; /* Y-M-D */
1907 else
1908 szBuff[0] = '0'; /* M-D-Y */
1911 szBuff[1] = '\0';
1913 if (lctype == LOCALE_SSHORTDATE)
1914 lctype = LOCALE_IDATE;
1915 else
1916 lctype = LOCALE_ILDATE;
1918 value = get_locale_registry_value( lctype );
1920 WriteProfileStringW( intlW, value->name, szBuff );
1922 RtlInitUnicodeString( &valueW, value->name );
1923 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1925 RtlEnterCriticalSection( &cache_section );
1926 HeapFree( GetProcessHeap(), 0, value->cached_value );
1927 value->cached_value = NULL;
1928 RtlLeaveCriticalSection( &cache_section );
1931 NtClose( hkey );
1933 if (status) SetLastError( RtlNtStatusToDosError(status) );
1934 return !status;
1938 /******************************************************************************
1939 * GetACP (KERNEL32.@)
1941 * Get the current Ansi code page Id for the system.
1943 * PARAMS
1944 * None.
1946 * RETURNS
1947 * The current Ansi code page identifier for the system.
1949 UINT WINAPI GetACP(void)
1951 assert( ansi_cptable );
1952 return ansi_cptable->info.codepage;
1956 /******************************************************************************
1957 * SetCPGlobal (KERNEL32.@)
1959 * Set the current Ansi code page Id for the system.
1961 * PARAMS
1962 * acp [I] code page ID to be the new ACP.
1964 * RETURNS
1965 * The previous ACP.
1967 UINT WINAPI SetCPGlobal( UINT acp )
1969 UINT ret = GetACP();
1970 const union cptable *new_cptable = wine_cp_get_table( acp );
1972 if (new_cptable) ansi_cptable = new_cptable;
1973 return ret;
1977 /***********************************************************************
1978 * GetOEMCP (KERNEL32.@)
1980 * Get the current OEM code page Id for the system.
1982 * PARAMS
1983 * None.
1985 * RETURNS
1986 * The current OEM code page identifier for the system.
1988 UINT WINAPI GetOEMCP(void)
1990 assert( oem_cptable );
1991 return oem_cptable->info.codepage;
1995 /***********************************************************************
1996 * IsValidCodePage (KERNEL32.@)
1998 * Determine if a given code page identifier is valid.
2000 * PARAMS
2001 * codepage [I] Code page Id to verify.
2003 * RETURNS
2004 * TRUE, If codepage is valid and available on the system,
2005 * FALSE otherwise.
2007 BOOL WINAPI IsValidCodePage( UINT codepage )
2009 switch(codepage) {
2010 case CP_UTF7:
2011 case CP_UTF8:
2012 return TRUE;
2013 default:
2014 return wine_cp_get_table( codepage ) != NULL;
2019 /***********************************************************************
2020 * IsDBCSLeadByteEx (KERNEL32.@)
2022 * Determine if a character is a lead byte in a given code page.
2024 * PARAMS
2025 * codepage [I] Code page for the test.
2026 * testchar [I] Character to test
2028 * RETURNS
2029 * TRUE, if testchar is a lead byte in codepage,
2030 * FALSE otherwise.
2032 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
2034 const union cptable *table = get_codepage_table( codepage );
2035 return table && wine_is_dbcs_leadbyte( table, testchar );
2039 /***********************************************************************
2040 * IsDBCSLeadByte (KERNEL32.@)
2041 * IsDBCSLeadByte (KERNEL.207)
2043 * Determine if a character is a lead byte.
2045 * PARAMS
2046 * testchar [I] Character to test
2048 * RETURNS
2049 * TRUE, if testchar is a lead byte in the ANSI code page,
2050 * FALSE otherwise.
2052 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2054 if (!ansi_cptable) return FALSE;
2055 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2059 /***********************************************************************
2060 * GetCPInfo (KERNEL32.@)
2062 * Get information about a code page.
2064 * PARAMS
2065 * codepage [I] Code page number
2066 * cpinfo [O] Destination for code page information
2068 * RETURNS
2069 * Success: TRUE. cpinfo is updated with the information about codepage.
2070 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2072 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2074 const union cptable *table;
2076 if (!cpinfo)
2078 SetLastError( ERROR_INVALID_PARAMETER );
2079 return FALSE;
2082 if (!(table = get_codepage_table( codepage )))
2084 switch(codepage)
2086 case CP_UTF7:
2087 case CP_UTF8:
2088 cpinfo->DefaultChar[0] = 0x3f;
2089 cpinfo->DefaultChar[1] = 0;
2090 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2091 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2092 return TRUE;
2095 SetLastError( ERROR_INVALID_PARAMETER );
2096 return FALSE;
2098 if (table->info.def_char & 0xff00)
2100 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2101 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2103 else
2105 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2106 cpinfo->DefaultChar[1] = 0;
2108 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2109 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2110 else
2111 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2113 return TRUE;
2116 /***********************************************************************
2117 * GetCPInfoExA (KERNEL32.@)
2119 * Get extended information about a code page.
2121 * PARAMS
2122 * codepage [I] Code page number
2123 * dwFlags [I] Reserved, must to 0.
2124 * cpinfo [O] Destination for code page information
2126 * RETURNS
2127 * Success: TRUE. cpinfo is updated with the information about codepage.
2128 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2130 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2132 CPINFOEXW cpinfoW;
2134 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2135 return FALSE;
2137 /* the layout is the same except for CodePageName */
2138 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2139 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2140 return TRUE;
2143 /***********************************************************************
2144 * GetCPInfoExW (KERNEL32.@)
2146 * Unicode version of GetCPInfoExA.
2148 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2150 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2151 return FALSE;
2153 switch(codepage)
2155 case CP_UTF7:
2157 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2159 cpinfo->CodePage = CP_UTF7;
2160 cpinfo->UnicodeDefaultChar = 0x3f;
2161 strcpyW(cpinfo->CodePageName, utf7);
2162 break;
2165 case CP_UTF8:
2167 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2169 cpinfo->CodePage = CP_UTF8;
2170 cpinfo->UnicodeDefaultChar = 0x3f;
2171 strcpyW(cpinfo->CodePageName, utf8);
2172 break;
2175 default:
2177 const union cptable *table = get_codepage_table( codepage );
2179 cpinfo->CodePage = table->info.codepage;
2180 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2181 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2182 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2183 break;
2186 return TRUE;
2189 /***********************************************************************
2190 * EnumSystemCodePagesA (KERNEL32.@)
2192 * Call a user defined function for every code page installed on the system.
2194 * PARAMS
2195 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2196 * flags [I] Reserved, set to 0.
2198 * RETURNS
2199 * TRUE, If all code pages have been enumerated, or
2200 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2202 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2204 const union cptable *table;
2205 char buffer[10];
2206 int index = 0;
2208 for (;;)
2210 if (!(table = wine_cp_enum_table( index++ ))) break;
2211 sprintf( buffer, "%d", table->info.codepage );
2212 if (!lpfnCodePageEnum( buffer )) break;
2214 return TRUE;
2218 /***********************************************************************
2219 * EnumSystemCodePagesW (KERNEL32.@)
2221 * See EnumSystemCodePagesA.
2223 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2225 const union cptable *table;
2226 WCHAR buffer[10], *p;
2227 int page, index = 0;
2229 for (;;)
2231 if (!(table = wine_cp_enum_table( index++ ))) break;
2232 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2233 *--p = 0;
2234 page = table->info.codepage;
2237 *--p = '0' + (page % 10);
2238 page /= 10;
2239 } while( page );
2240 if (!lpfnCodePageEnum( p )) break;
2242 return TRUE;
2246 /***********************************************************************
2247 * utf7_write_w
2249 * Helper for utf7_mbstowcs
2251 * RETURNS
2252 * TRUE on success, FALSE on error
2254 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2256 if (dstlen > 0)
2258 if (*index >= dstlen)
2259 return FALSE;
2261 dst[*index] = character;
2264 (*index)++;
2266 return TRUE;
2269 /***********************************************************************
2270 * utf7_mbstowcs
2272 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2274 * RETURNS
2275 * On success, the number of characters written
2276 * On dst buffer overflow, -1
2278 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2280 static const signed char base64_decoding_table[] =
2282 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2283 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2284 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2285 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2286 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2287 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2288 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2289 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2292 const char *source_end = src + srclen;
2293 int dest_index = 0;
2295 DWORD byte_pair = 0;
2296 short offset = 0;
2298 while (src < source_end)
2300 if (*src == '+')
2302 src++;
2303 if (src >= source_end)
2304 break;
2306 if (*src == '-')
2308 /* just a plus sign escaped as +- */
2309 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2310 return -1;
2311 src++;
2312 continue;
2317 signed char sextet = *src;
2318 if (sextet == '-')
2320 /* skip over the dash and end base64 decoding
2321 * the current, unfinished byte pair is discarded */
2322 src++;
2323 offset = 0;
2324 break;
2326 if (sextet < 0)
2328 /* the next character of src is < 0 and therefore not part of a base64 sequence
2329 * the current, unfinished byte pair is NOT discarded in this case
2330 * this is probably a bug in Windows */
2331 break;
2334 sextet = base64_decoding_table[sextet];
2335 if (sextet == -1)
2337 /* -1 means that the next character of src is not part of a base64 sequence
2338 * in other words, all sextets in this base64 sequence have been processed
2339 * the current, unfinished byte pair is discarded */
2340 offset = 0;
2341 break;
2344 byte_pair = (byte_pair << 6) | sextet;
2345 offset += 6;
2347 if (offset >= 16)
2349 /* this byte pair is done */
2350 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2351 return -1;
2352 offset -= 16;
2355 src++;
2357 while (src < source_end);
2359 else
2361 /* we have to convert to unsigned char in case *src < 0 */
2362 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2363 return -1;
2364 src++;
2368 return dest_index;
2371 /***********************************************************************
2372 * MultiByteToWideChar (KERNEL32.@)
2374 * Convert a multibyte character string into a Unicode string.
2376 * PARAMS
2377 * page [I] Codepage character set to convert from
2378 * flags [I] Character mapping flags
2379 * src [I] Source string buffer
2380 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2381 * dst [O] Destination buffer
2382 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2384 * RETURNS
2385 * Success: If dstlen > 0, the number of characters written to dst.
2386 * If dstlen == 0, the number of characters needed to perform the
2387 * conversion. In both cases the count includes the terminating NUL.
2388 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2389 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2390 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2391 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2392 * possible for src.
2394 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2395 LPWSTR dst, INT dstlen )
2397 const union cptable *table;
2398 int ret;
2400 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2402 SetLastError( ERROR_INVALID_PARAMETER );
2403 return 0;
2406 if (srclen < 0) srclen = strlen(src) + 1;
2408 switch(page)
2410 case CP_SYMBOL:
2411 if (flags)
2413 SetLastError( ERROR_INVALID_FLAGS );
2414 return 0;
2416 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2417 break;
2418 case CP_UTF7:
2419 if (flags)
2421 SetLastError( ERROR_INVALID_FLAGS );
2422 return 0;
2424 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2425 break;
2426 case CP_UNIXCP:
2427 if (unix_cptable)
2429 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2430 break;
2432 #ifdef __APPLE__
2433 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2434 #endif
2435 /* fall through */
2436 case CP_UTF8:
2437 if (flags & ~MB_FLAGSMASK)
2439 SetLastError( ERROR_INVALID_FLAGS );
2440 return 0;
2442 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2443 break;
2444 default:
2445 if (!(table = get_codepage_table( page )))
2447 SetLastError( ERROR_INVALID_PARAMETER );
2448 return 0;
2450 if (flags & ~MB_FLAGSMASK)
2452 SetLastError( ERROR_INVALID_FLAGS );
2453 return 0;
2455 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2456 break;
2459 if (ret < 0)
2461 switch(ret)
2463 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2464 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2466 ret = 0;
2468 TRACE("cp %d %s -> %s, ret = %d\n",
2469 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2470 return ret;
2474 /***********************************************************************
2475 * utf7_can_directly_encode
2477 * Helper for utf7_wcstombs
2479 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2481 static const BOOL directly_encodable_table[] =
2483 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2484 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2485 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2486 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2487 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2488 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2489 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2490 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2493 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2496 /***********************************************************************
2497 * utf7_write_c
2499 * Helper for utf7_wcstombs
2501 * RETURNS
2502 * TRUE on success, FALSE on error
2504 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2506 if (dstlen > 0)
2508 if (*index >= dstlen)
2509 return FALSE;
2511 dst[*index] = character;
2514 (*index)++;
2516 return TRUE;
2519 /***********************************************************************
2520 * utf7_wcstombs
2522 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2524 * RETURNS
2525 * On success, the number of characters written
2526 * On dst buffer overflow, -1
2528 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2530 static const char base64_encoding_table[] =
2531 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2533 const WCHAR *source_end = src + srclen;
2534 int dest_index = 0;
2536 while (src < source_end)
2538 if (*src == '+')
2540 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2541 return -1;
2542 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2543 return -1;
2544 src++;
2546 else if (utf7_can_directly_encode(*src))
2548 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2549 return -1;
2550 src++;
2552 else
2554 unsigned int offset = 0;
2555 DWORD byte_pair = 0;
2557 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2558 return -1;
2560 while (src < source_end && !utf7_can_directly_encode(*src))
2562 byte_pair = (byte_pair << 16) | *src;
2563 offset += 16;
2564 while (offset >= 6)
2566 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2567 return -1;
2568 offset -= 6;
2570 src++;
2573 if (offset)
2575 /* Windows won't create a padded base64 character if there's no room for the - sign
2576 * as well ; this is probably a bug in Windows */
2577 if (dstlen > 0 && dest_index + 1 >= dstlen)
2578 return -1;
2580 byte_pair <<= (6 - offset);
2581 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2582 return -1;
2585 /* Windows always explicitly terminates the base64 sequence
2586 even though RFC 2152 (page 3, rule 2) does not require this */
2587 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2588 return -1;
2592 return dest_index;
2595 /***********************************************************************
2596 * WideCharToMultiByte (KERNEL32.@)
2598 * Convert a Unicode character string into a multibyte string.
2600 * PARAMS
2601 * page [I] Code page character set to convert to
2602 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2603 * src [I] Source string buffer
2604 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2605 * dst [O] Destination buffer
2606 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2607 * defchar [I] Default character to use for conversion if no exact
2608 * conversion can be made
2609 * used [O] Set if default character was used in the conversion
2611 * RETURNS
2612 * Success: If dstlen > 0, the number of characters written to dst.
2613 * If dstlen == 0, number of characters needed to perform the
2614 * conversion. In both cases the count includes the terminating NUL.
2615 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2616 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2617 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2618 * parameter was given.
2620 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2621 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2623 const union cptable *table;
2624 int ret, used_tmp;
2626 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2628 SetLastError( ERROR_INVALID_PARAMETER );
2629 return 0;
2632 if (srclen < 0) srclen = strlenW(src) + 1;
2634 switch(page)
2636 case CP_SYMBOL:
2637 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2638 if (flags)
2640 SetLastError( ERROR_INVALID_FLAGS );
2641 return 0;
2643 if (defchar || used)
2645 SetLastError( ERROR_INVALID_PARAMETER );
2646 return 0;
2648 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2649 break;
2650 case CP_UTF7:
2651 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2652 if (defchar || used)
2654 SetLastError( ERROR_INVALID_PARAMETER );
2655 return 0;
2657 if (flags)
2659 SetLastError( ERROR_INVALID_FLAGS );
2660 return 0;
2662 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2663 break;
2664 case CP_UNIXCP:
2665 if (unix_cptable)
2667 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2668 defchar, used ? &used_tmp : NULL );
2669 break;
2671 /* fall through */
2672 case CP_UTF8:
2673 if (defchar || used)
2675 SetLastError( ERROR_INVALID_PARAMETER );
2676 return 0;
2678 if (flags & ~WC_FLAGSMASK)
2680 SetLastError( ERROR_INVALID_FLAGS );
2681 return 0;
2683 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2684 break;
2685 default:
2686 if (!(table = get_codepage_table( page )))
2688 SetLastError( ERROR_INVALID_PARAMETER );
2689 return 0;
2691 if (flags & ~WC_FLAGSMASK)
2693 SetLastError( ERROR_INVALID_FLAGS );
2694 return 0;
2696 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2697 defchar, used ? &used_tmp : NULL );
2698 if (used) *used = used_tmp;
2699 break;
2702 if (ret < 0)
2704 switch(ret)
2706 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2707 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2709 ret = 0;
2711 TRACE("cp %d %s -> %s, ret = %d\n",
2712 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2713 return ret;
2717 /***********************************************************************
2718 * GetThreadLocale (KERNEL32.@)
2720 * Get the current threads locale.
2722 * PARAMS
2723 * None.
2725 * RETURNS
2726 * The LCID currently associated with the calling thread.
2728 LCID WINAPI GetThreadLocale(void)
2730 LCID ret = NtCurrentTeb()->CurrentLocale;
2731 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2732 return ret;
2735 /**********************************************************************
2736 * SetThreadLocale (KERNEL32.@)
2738 * Set the current threads locale.
2740 * PARAMS
2741 * lcid [I] LCID of the locale to set
2743 * RETURNS
2744 * Success: TRUE. The threads locale is set to lcid.
2745 * Failure: FALSE. Use GetLastError() to determine the cause.
2747 BOOL WINAPI SetThreadLocale( LCID lcid )
2749 TRACE("(0x%04X)\n", lcid);
2751 lcid = ConvertDefaultLocale(lcid);
2753 if (lcid != GetThreadLocale())
2755 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2757 SetLastError(ERROR_INVALID_PARAMETER);
2758 return FALSE;
2761 NtCurrentTeb()->CurrentLocale = lcid;
2763 return TRUE;
2766 /**********************************************************************
2767 * SetThreadUILanguage (KERNEL32.@)
2769 * Set the current threads UI language.
2771 * PARAMS
2772 * langid [I] LANGID of the language to set, or 0 to use
2773 * the available language which is best supported
2774 * for console applications
2776 * RETURNS
2777 * Success: The return value is the same as the input value.
2778 * Failure: The return value differs from the input value.
2779 * Use GetLastError() to determine the cause.
2781 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2783 TRACE("(0x%04x) stub - returning success\n", langid);
2784 return langid;
2787 /******************************************************************************
2788 * ConvertDefaultLocale (KERNEL32.@)
2790 * Convert a default locale identifier into a real identifier.
2792 * PARAMS
2793 * lcid [I] LCID identifier of the locale to convert
2795 * RETURNS
2796 * lcid unchanged, if not a default locale or its sublanguage is
2797 * not SUBLANG_NEUTRAL.
2798 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2799 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2800 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2802 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2804 LANGID langid;
2806 switch (lcid)
2808 case LOCALE_INVARIANT:
2809 /* keep as-is */
2810 break;
2811 case LOCALE_SYSTEM_DEFAULT:
2812 lcid = GetSystemDefaultLCID();
2813 break;
2814 case LOCALE_USER_DEFAULT:
2815 case LOCALE_NEUTRAL:
2816 lcid = GetUserDefaultLCID();
2817 break;
2818 default:
2819 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2820 langid = LANGIDFROMLCID(lcid);
2821 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2823 langid = MAKELANGID(PRIMARYLANGID(langid), get_default_sublang( langid ));
2824 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2827 return lcid;
2831 /******************************************************************************
2832 * IsValidLocale (KERNEL32.@)
2834 * Determine if a locale is valid.
2836 * PARAMS
2837 * lcid [I] LCID of the locale to check
2838 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2840 * RETURNS
2841 * TRUE, if lcid is valid,
2842 * FALSE, otherwise.
2844 * NOTES
2845 * Wine does not currently make the distinction between supported and installed. All
2846 * languages supported are installed by default.
2848 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2850 /* check if language is registered in the kernel32 resources */
2851 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2852 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2855 /******************************************************************************
2856 * IsValidLocaleName (KERNEL32.@)
2858 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2860 struct locale_name locale_name;
2862 if (!locale)
2863 return FALSE;
2865 /* string parsing */
2866 parse_locale_name( locale, &locale_name );
2868 TRACE( "found lcid %x for %s, matches %d\n",
2869 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2871 return locale_name.matches > 0;
2874 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2875 LPCSTR name, WORD LangID, LONG_PTR lParam )
2877 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2878 char buf[20];
2880 sprintf(buf, "%08x", (UINT)LangID);
2881 return lpfnLocaleEnum( buf );
2884 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2885 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2887 static const WCHAR formatW[] = {'%','0','8','x',0};
2888 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2889 WCHAR buf[20];
2890 sprintfW( buf, formatW, (UINT)LangID );
2891 return lpfnLocaleEnum( buf );
2894 /******************************************************************************
2895 * EnumSystemLocalesA (KERNEL32.@)
2897 * Call a users function for each locale available on the system.
2899 * PARAMS
2900 * lpfnLocaleEnum [I] Callback function to call for each locale
2901 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2903 * RETURNS
2904 * Success: TRUE.
2905 * Failure: FALSE. Use GetLastError() to determine the cause.
2907 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2909 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2910 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2911 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2912 (LONG_PTR)lpfnLocaleEnum);
2913 return TRUE;
2917 /******************************************************************************
2918 * EnumSystemLocalesW (KERNEL32.@)
2920 * See EnumSystemLocalesA.
2922 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2924 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2925 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2926 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2927 (LONG_PTR)lpfnLocaleEnum);
2928 return TRUE;
2932 struct enum_locale_ex_data
2934 LOCALE_ENUMPROCEX proc;
2935 DWORD flags;
2936 LPARAM lparam;
2939 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2940 LPCWSTR name, WORD lang, LONG_PTR lparam )
2942 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2943 WCHAR buffer[256];
2944 DWORD neutral;
2945 unsigned int flags;
2947 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2948 buffer, sizeof(buffer) / sizeof(WCHAR) );
2949 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2950 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2951 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2952 neutral = 0;
2953 flags = LOCALE_WINDOWS;
2954 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2955 if (data->flags && !(data->flags & flags)) return TRUE;
2956 return data->proc( buffer, flags, data->lparam );
2959 /******************************************************************************
2960 * EnumSystemLocalesEx (KERNEL32.@)
2962 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2964 struct enum_locale_ex_data data;
2966 if (reserved)
2968 SetLastError( ERROR_INVALID_PARAMETER );
2969 return FALSE;
2971 data.proc = proc;
2972 data.flags = flags;
2973 data.lparam = lparam;
2974 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2975 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2976 enum_locale_ex_proc, (LONG_PTR)&data );
2977 return TRUE;
2981 /***********************************************************************
2982 * VerLanguageNameA (KERNEL32.@)
2984 * Get the name of a language.
2986 * PARAMS
2987 * wLang [I] LANGID of the language
2988 * szLang [O] Destination for the language name
2990 * RETURNS
2991 * Success: The size of the language name. If szLang is non-NULL, it is filled
2992 * with the name.
2993 * Failure: 0. Use GetLastError() to determine the cause.
2996 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2998 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3002 /***********************************************************************
3003 * VerLanguageNameW (KERNEL32.@)
3005 * See VerLanguageNameA.
3007 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
3009 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3013 /******************************************************************************
3014 * GetStringTypeW (KERNEL32.@)
3016 * See GetStringTypeA.
3018 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3020 static const unsigned char type2_map[16] =
3022 C2_NOTAPPLICABLE, /* unassigned */
3023 C2_LEFTTORIGHT, /* L */
3024 C2_RIGHTTOLEFT, /* R */
3025 C2_EUROPENUMBER, /* EN */
3026 C2_EUROPESEPARATOR, /* ES */
3027 C2_EUROPETERMINATOR, /* ET */
3028 C2_ARABICNUMBER, /* AN */
3029 C2_COMMONSEPARATOR, /* CS */
3030 C2_BLOCKSEPARATOR, /* B */
3031 C2_SEGMENTSEPARATOR, /* S */
3032 C2_WHITESPACE, /* WS */
3033 C2_OTHERNEUTRAL, /* ON */
3034 C2_RIGHTTOLEFT, /* AL */
3035 C2_NOTAPPLICABLE, /* NSM */
3036 C2_NOTAPPLICABLE, /* BN */
3037 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
3040 if (!src)
3042 SetLastError( ERROR_INVALID_PARAMETER );
3043 return FALSE;
3046 if (count == -1) count = strlenW(src) + 1;
3047 switch(type)
3049 case CT_CTYPE1:
3050 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
3051 break;
3052 case CT_CTYPE2:
3053 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
3054 break;
3055 case CT_CTYPE3:
3057 WARN("CT_CTYPE3: semi-stub.\n");
3058 while (count--)
3060 int c = *src;
3061 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
3063 type1 = get_char_typeW( *src++ ) & 0xfff;
3064 /* try to construct type3 from type1 */
3065 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
3066 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
3067 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
3068 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
3069 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3070 if (c == 0x0640) type3 |= C3_KASHIDA;
3071 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3073 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3074 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3076 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3077 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3078 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3079 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3080 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3081 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3082 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3083 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3085 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3086 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3087 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3088 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3089 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3090 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3091 *chartype++ = type3;
3093 break;
3095 default:
3096 SetLastError( ERROR_INVALID_PARAMETER );
3097 return FALSE;
3099 return TRUE;
3103 /******************************************************************************
3104 * GetStringTypeExW (KERNEL32.@)
3106 * See GetStringTypeExA.
3108 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3110 /* locale is ignored for Unicode */
3111 return GetStringTypeW( type, src, count, chartype );
3115 /******************************************************************************
3116 * GetStringTypeA (KERNEL32.@)
3118 * Get characteristics of the characters making up a string.
3120 * PARAMS
3121 * locale [I] Locale Id for the string
3122 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3123 * src [I] String to analyse
3124 * count [I] Length of src in chars, or -1 if src is NUL terminated
3125 * chartype [O] Destination for the calculated characteristics
3127 * RETURNS
3128 * Success: TRUE. chartype is filled with the requested characteristics of each char
3129 * in src.
3130 * Failure: FALSE. Use GetLastError() to determine the cause.
3132 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3134 UINT cp;
3135 INT countW;
3136 LPWSTR srcW;
3137 BOOL ret = FALSE;
3139 if(count == -1) count = strlen(src) + 1;
3141 if (!(cp = get_lcid_codepage( locale )))
3143 FIXME("For locale %04x using current ANSI code page\n", locale);
3144 cp = GetACP();
3147 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3148 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3150 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3152 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3153 * string, with multibyte characters there maybe be more bytes in count
3154 * than character space in the buffer!
3156 ret = GetStringTypeW(type, srcW, countW, chartype);
3157 HeapFree(GetProcessHeap(), 0, srcW);
3159 return ret;
3162 /******************************************************************************
3163 * GetStringTypeExA (KERNEL32.@)
3165 * Get characteristics of the characters making up a string.
3167 * PARAMS
3168 * locale [I] Locale Id for the string
3169 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3170 * src [I] String to analyse
3171 * count [I] Length of src in chars, or -1 if src is NUL terminated
3172 * chartype [O] Destination for the calculated characteristics
3174 * RETURNS
3175 * Success: TRUE. chartype is filled with the requested characteristics of each char
3176 * in src.
3177 * Failure: FALSE. Use GetLastError() to determine the cause.
3179 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3181 return GetStringTypeA(locale, type, src, count, chartype);
3184 /* compose a full-width katakana. return consumed source characters. */
3185 static INT compose_katakana( LPCWSTR src, INT srclen, LPWSTR dst )
3187 const static BYTE katakana_map[] = {
3188 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
3189 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
3190 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
3191 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
3192 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
3193 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
3194 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
3195 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
3197 WCHAR before, dummy;
3199 if (!dst)
3200 dst = &dummy;
3202 switch (*src)
3204 case 0x309b: case 0x309c:
3205 *dst = *src - 2;
3206 return 1;
3207 case 0x30f0: case 0x30f1: case 0x30fd:
3208 *dst = *src;
3209 break;
3210 default:
3212 int shift = *src - 0xff61;
3213 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]) )
3214 return 0;
3215 else
3216 *dst = katakana_map[shift] | 0x3000;
3220 if (srclen <= 1)
3221 return 1;
3223 before = *dst;
3225 /* datakuten (voiced sound) */
3226 if (*(src + 1) == 0xff9e)
3228 if ((*src >= 0xff76 && *src <= 0xff84) ||
3229 (*src >= 0xff8a && *src <= 0xff8e) ||
3230 *src == 0x30fd)
3231 *dst += 1;
3232 else if (*src == 0xff73)
3233 *dst = 0x30f4; /* KATAKANA LETTER VU */
3234 else if (*src == 0xff9c)
3235 *dst = 0x30f7; /* KATAKANA LETTER VA */
3236 else if (*src == 0x30f0)
3237 *dst = 0x30f8; /* KATAKANA LETTER VI */
3238 else if (*src == 0x30f1)
3239 *dst = 0x30f9; /* KATAKANA LETTER VE */
3240 else if (*src == 0xff66)
3241 *dst = 0x30fa; /* KATAKANA LETTER VO */
3244 /* handakuten (semi-voiced sound) */
3245 if (*(src + 1) == 0xff9f)
3246 if (*src >= 0xff8a && *src <= 0xff8e)
3247 *dst += 2;
3249 return (*dst != before) ? 2 : 1;
3252 /* map one or two half-width characters to one full-width character */
3253 static INT map_to_fullwidth( LPCWSTR src, INT srclen, LPWSTR dst )
3255 INT n;
3257 if (*src <= '~' && *src > ' ' && *src != '\\')
3258 *dst = *src - 0x20 + 0xff00;
3259 else if (*src == ' ')
3260 *dst = 0x3000;
3261 else if (*src <= 0x00af && *src >= 0x00a2)
3263 const static BYTE misc_symbols_table[] = {
3264 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3265 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3267 if (misc_symbols_table[*src - 0x00a2])
3268 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3269 else
3270 *dst = *src;
3272 else if (*src == 0x20a9) /* WON SIGN */
3273 *dst = 0xffe6;
3274 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3275 return n;
3276 else if (*src >= 0xffa0 && *src <= 0xffdc)
3278 const static BYTE hangul_mapping_table[] = {
3279 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3280 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3281 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3282 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3283 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3284 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3285 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3286 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3289 if (hangul_mapping_table[*src - 0xffa0])
3290 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3291 else
3292 *dst = *src;
3294 else
3295 *dst = *src;
3297 return 1;
3300 /* decompose a full-width katakana character into one or two half-width characters. */
3301 static INT decompose_katakana( WCHAR c, LPWSTR dst, INT dstlen )
3303 const static BYTE katakana_map[] = {
3304 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3305 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3306 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3307 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3308 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3309 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3310 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3311 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3312 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3313 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3314 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3315 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3316 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3318 INT len = 0, shift = c - 0x3099;
3319 BYTE k;
3321 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]))
3322 return 0;
3324 k = katakana_map[shift];
3326 if (!k)
3328 if (dstlen > 0)
3329 *dst = c;
3330 len++;
3332 else if (k > 0x60)
3334 if (dstlen > 0)
3335 *dst = k | 0xff00;
3336 len++;
3338 else
3340 if (dstlen >= 2)
3342 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3343 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3345 len += 2;
3347 return len;
3350 /* map single full-width character to single or double half-width characters. */
3351 static INT map_to_halfwidth(WCHAR c, LPWSTR dst, INT dstlen)
3353 INT n = decompose_katakana(c, dst, dstlen);
3354 if (n > 0)
3355 return n;
3357 if (c == 0x3000)
3358 *dst = ' ';
3359 else if (c == 0x3001)
3360 *dst = 0xff64;
3361 else if (c == 0x3002)
3362 *dst = 0xff61;
3363 else if (c == 0x300c || c == 0x300d)
3364 *dst = (c - 0x300c) + 0xff62;
3365 else if (c >= 0x3131 && c <= 0x3163)
3367 *dst = c - 0x3131 + 0xffa1;
3368 if (*dst >= 0xffbf) *dst += 3;
3369 if (*dst >= 0xffc8) *dst += 2;
3370 if (*dst >= 0xffd0) *dst += 2;
3371 if (*dst >= 0xffd8) *dst += 2;
3373 else if (c == 0x3164)
3374 *dst = 0xffa0;
3375 else if (c == 0x2019)
3376 *dst = '\'';
3377 else if (c == 0x201d)
3378 *dst = '"';
3379 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3380 *dst = c - 0xff00 + 0x20;
3381 else if (c >= 0xffe0 && c <= 0xffe6)
3383 const static WCHAR misc_symbol_map[] = {
3384 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9
3386 *dst = misc_symbol_map[c - 0xffe0];
3388 else
3389 *dst = c;
3391 return 1;
3394 /*************************************************************************
3395 * LCMapStringEx (KERNEL32.@)
3397 * Map characters in a locale sensitive string.
3399 * PARAMS
3400 * name [I] Locale name for the conversion.
3401 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3402 * src [I] String to map
3403 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3404 * dst [O] Destination for mapped string
3405 * dstlen [I] Length of dst in characters
3406 * version [I] reserved, must be NULL
3407 * reserved [I] reserved, must be NULL
3408 * lparam [I] reserved, must be 0
3410 * RETURNS
3411 * Success: The length of the mapped string in dst, including the NUL terminator.
3412 * Failure: 0. Use GetLastError() to determine the cause.
3414 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3415 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3417 LPWSTR dst_ptr;
3418 INT len;
3420 if (version) FIXME("unsupported version structure %p\n", version);
3421 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3422 if (lparam)
3424 static int once;
3425 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3428 if (!src || !srclen || dstlen < 0)
3430 SetLastError(ERROR_INVALID_PARAMETER);
3431 return 0;
3434 /* mutually exclusive flags */
3435 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3436 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3437 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3438 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
3439 !flags)
3441 SetLastError(ERROR_INVALID_FLAGS);
3442 return 0;
3445 if (!dstlen) dst = NULL;
3447 if (flags & LCMAP_SORTKEY)
3449 INT ret;
3450 if (src == dst)
3452 SetLastError(ERROR_INVALID_FLAGS);
3453 return 0;
3456 if (srclen < 0) srclen = strlenW(src);
3458 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3459 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3461 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3462 if (ret == 0)
3463 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3464 else
3465 ret++;
3466 return ret;
3469 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3470 if (flags & SORT_STRINGSORT)
3472 SetLastError(ERROR_INVALID_FLAGS);
3473 return 0;
3475 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
3476 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
3477 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
3478 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
3480 SetLastError(ERROR_INVALID_FLAGS);
3481 return 0;
3484 if (srclen < 0) srclen = strlenW(src) + 1;
3486 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3487 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3489 if (!dst) /* return required string length */
3491 if (flags & NORM_IGNORESYMBOLS)
3493 for (len = 0; srclen; src++, srclen--)
3495 WCHAR wch = *src;
3496 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3497 * and skips white space and punctuation characters for
3498 * NORM_IGNORESYMBOLS.
3500 if (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE))
3501 continue;
3502 len++;
3505 else if (flags & LCMAP_FULLWIDTH)
3507 for (len = 0; srclen; src++, srclen--, len++)
3509 if (compose_katakana(src, srclen, NULL) == 2)
3511 src++;
3512 srclen--;
3516 else if (flags & LCMAP_HALFWIDTH)
3518 for (len = 0; srclen; src++, srclen--, len++)
3519 if (decompose_katakana(*src, NULL, 0) == 2)
3520 len++;
3522 else
3523 len = srclen;
3524 return len;
3527 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3529 SetLastError(ERROR_INVALID_FLAGS);
3530 return 0;
3533 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
3535 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3537 WCHAR wch = *src;
3538 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3539 continue;
3540 *dst_ptr++ = wch;
3541 len--;
3543 goto done;
3546 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
3548 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
3550 WCHAR wch;
3551 if (flags & LCMAP_FULLWIDTH)
3553 /* map half-width character to full-width one,
3554 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
3555 if (map_to_fullwidth(src, srclen, &wch) == 2)
3557 src++;
3558 srclen--;
3561 else
3562 wch = *src;
3564 if (flags & LCMAP_KATAKANA)
3566 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
3567 we can't use C3_HIRAGANA as some characters can't map to katakana */
3568 if ((wch >= 0x3041 && wch <= 0x3096) ||
3569 wch == 0x309D || wch == 0x309E)
3570 wch += 0x60;
3572 else if (flags & LCMAP_HIRAGANA)
3574 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
3575 we can't use C3_KATAKANA as some characters can't map to hiragana */
3576 if ((wch >= 0x30A1 && wch <= 0x30F6) ||
3577 wch == 0x30FD || wch == 0x30FE)
3578 wch -= 0x60;
3581 if (flags & LCMAP_HALFWIDTH)
3583 /* map full-width character to half-width one,
3584 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
3585 if (map_to_halfwidth(wch, dst_ptr, dstlen) == 2)
3587 dstlen--;
3588 dst_ptr++;
3589 if (!dstlen)
3591 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3592 return 0;
3596 else
3597 *dst_ptr = wch;
3599 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)))
3600 goto done;
3602 srclen = dst_ptr - dst;
3603 src = dst;
3606 if (flags & LCMAP_UPPERCASE)
3608 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3610 *dst_ptr++ = toupperW(*src);
3611 len--;
3614 else if (flags & LCMAP_LOWERCASE)
3616 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3618 *dst_ptr++ = tolowerW(*src);
3619 len--;
3622 else
3624 len = min(srclen, dstlen);
3625 memcpy(dst, src, len * sizeof(WCHAR));
3626 dst_ptr = dst + len;
3627 srclen -= len;
3630 done:
3631 if (srclen)
3633 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3634 return 0;
3637 return dst_ptr - dst;
3640 /*************************************************************************
3641 * LCMapStringW (KERNEL32.@)
3643 * See LCMapStringA.
3645 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3646 LPWSTR dst, INT dstlen)
3648 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3649 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3651 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3654 /*************************************************************************
3655 * LCMapStringA (KERNEL32.@)
3657 * Map characters in a locale sensitive string.
3659 * PARAMS
3660 * lcid [I] LCID for the conversion.
3661 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3662 * src [I] String to map
3663 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3664 * dst [O] Destination for mapped string
3665 * dstlen [I] Length of dst in characters
3667 * RETURNS
3668 * Success: The length of the mapped string in dst, including the NUL terminator.
3669 * Failure: 0. Use GetLastError() to determine the cause.
3671 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3672 LPSTR dst, INT dstlen)
3674 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3675 LPWSTR srcW, dstW;
3676 INT ret = 0, srclenW, dstlenW;
3677 UINT locale_cp = CP_ACP;
3679 if (!src || !srclen || dstlen < 0)
3681 SetLastError(ERROR_INVALID_PARAMETER);
3682 return 0;
3685 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3687 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3688 if (srclenW)
3689 srcW = bufW;
3690 else
3692 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3693 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3694 if (!srcW)
3696 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3697 return 0;
3699 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3702 if (flags & LCMAP_SORTKEY)
3704 if (src == dst)
3706 SetLastError(ERROR_INVALID_FLAGS);
3707 goto map_string_exit;
3709 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3710 if (ret == 0)
3711 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3712 else
3713 ret++;
3714 goto map_string_exit;
3717 if (flags & SORT_STRINGSORT)
3719 SetLastError(ERROR_INVALID_FLAGS);
3720 goto map_string_exit;
3723 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3724 if (!dstlenW)
3725 goto map_string_exit;
3727 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3728 if (!dstW)
3730 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3731 goto map_string_exit;
3734 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3735 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3736 HeapFree(GetProcessHeap(), 0, dstW);
3738 map_string_exit:
3739 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3740 return ret;
3743 /*************************************************************************
3744 * FoldStringA (KERNEL32.@)
3746 * Map characters in a string.
3748 * PARAMS
3749 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3750 * src [I] String to map
3751 * srclen [I] Length of src, or -1 if src is NUL terminated
3752 * dst [O] Destination for mapped string
3753 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3755 * RETURNS
3756 * Success: The length of the string written to dst, including the terminating NUL. If
3757 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3758 * and dst may be NULL.
3759 * Failure: 0. Use GetLastError() to determine the cause.
3761 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3762 LPSTR dst, INT dstlen)
3764 INT ret = 0, srclenW = 0;
3765 WCHAR *srcW = NULL, *dstW = NULL;
3767 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3769 SetLastError(ERROR_INVALID_PARAMETER);
3770 return 0;
3773 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3774 src, srclen, NULL, 0);
3775 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3777 if (!srcW)
3779 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3780 goto FoldStringA_exit;
3783 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3784 src, srclen, srcW, srclenW);
3786 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3788 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3789 if (ret && dstlen)
3791 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3793 if (!dstW)
3795 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3796 goto FoldStringA_exit;
3799 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3800 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3802 ret = 0;
3803 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3807 HeapFree(GetProcessHeap(), 0, dstW);
3809 FoldStringA_exit:
3810 HeapFree(GetProcessHeap(), 0, srcW);
3811 return ret;
3814 /*************************************************************************
3815 * FoldStringW (KERNEL32.@)
3817 * See FoldStringA.
3819 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3820 LPWSTR dst, INT dstlen)
3822 int ret;
3824 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3826 case 0:
3827 if (dwFlags)
3828 break;
3829 /* Fall through for dwFlags == 0 */
3830 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3831 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3832 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3833 SetLastError(ERROR_INVALID_FLAGS);
3834 return 0;
3837 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3839 SetLastError(ERROR_INVALID_PARAMETER);
3840 return 0;
3843 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3844 if (!ret)
3845 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3846 return ret;
3849 /******************************************************************************
3850 * CompareStringW (KERNEL32.@)
3852 * See CompareStringA.
3854 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3855 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3857 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3860 /******************************************************************************
3861 * CompareStringEx (KERNEL32.@)
3863 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3864 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3866 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3867 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3868 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3869 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3870 INT ret;
3871 static int once;
3873 if (version) FIXME("unexpected version parameter\n");
3874 if (reserved) FIXME("unexpected reserved value\n");
3875 if (lParam) FIXME("unexpected lParam\n");
3877 if (!str1 || !str2)
3879 SetLastError(ERROR_INVALID_PARAMETER);
3880 return 0;
3883 if (flags & ~(supported_flags|semistub_flags))
3885 SetLastError(ERROR_INVALID_FLAGS);
3886 return 0;
3889 if (flags & semistub_flags)
3891 if (!once++)
3892 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3895 if (len1 < 0) len1 = strlenW(str1);
3896 if (len2 < 0) len2 = strlenW(str2);
3898 ret = wine_compare_string(flags, str1, len1, str2, len2);
3900 if (ret) /* need to translate result */
3901 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3902 return CSTR_EQUAL;
3905 /******************************************************************************
3906 * CompareStringA (KERNEL32.@)
3908 * Compare two locale sensitive strings.
3910 * PARAMS
3911 * lcid [I] LCID for the comparison
3912 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3913 * str1 [I] First string to compare
3914 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3915 * str2 [I] Second string to compare
3916 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3918 * RETURNS
3919 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3920 * str1 is less than, equal to or greater than str2 respectively.
3921 * Failure: FALSE. Use GetLastError() to determine the cause.
3923 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3924 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3926 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3927 WCHAR *buf2W = buf1W + 130;
3928 LPWSTR str1W, str2W;
3929 INT len1W = 0, len2W = 0, ret;
3930 UINT locale_cp = CP_ACP;
3932 if (!str1 || !str2)
3934 SetLastError(ERROR_INVALID_PARAMETER);
3935 return 0;
3937 if (len1 < 0) len1 = strlen(str1);
3938 if (len2 < 0) len2 = strlen(str2);
3940 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3942 if (len1)
3944 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3945 if (len1W)
3946 str1W = buf1W;
3947 else
3949 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3950 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3951 if (!str1W)
3953 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3954 return 0;
3956 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3959 else
3961 len1W = 0;
3962 str1W = buf1W;
3965 if (len2)
3967 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3968 if (len2W)
3969 str2W = buf2W;
3970 else
3972 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3973 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3974 if (!str2W)
3976 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3977 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3978 return 0;
3980 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3983 else
3985 len2W = 0;
3986 str2W = buf2W;
3989 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3991 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3992 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3993 return ret;
3996 /******************************************************************************
3997 * CompareStringOrdinal (KERNEL32.@)
3999 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
4001 int ret;
4003 if (!str1 || !str2)
4005 SetLastError(ERROR_INVALID_PARAMETER);
4006 return 0;
4008 if (len1 < 0) len1 = strlenW(str1);
4009 if (len2 < 0) len2 = strlenW(str2);
4011 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4012 if (ret < 0) return CSTR_LESS_THAN;
4013 if (ret > 0) return CSTR_GREATER_THAN;
4014 return CSTR_EQUAL;
4017 /*************************************************************************
4018 * lstrcmp (KERNEL32.@)
4019 * lstrcmpA (KERNEL32.@)
4021 * Compare two strings using the current thread locale.
4023 * PARAMS
4024 * str1 [I] First string to compare
4025 * str2 [I] Second string to compare
4027 * RETURNS
4028 * Success: A number less than, equal to or greater than 0 depending on whether
4029 * str1 is less than, equal to or greater than str2 respectively.
4030 * Failure: FALSE. Use GetLastError() to determine the cause.
4032 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
4034 int ret;
4036 if ((str1 == NULL) && (str2 == NULL)) return 0;
4037 if (str1 == NULL) return -1;
4038 if (str2 == NULL) return 1;
4040 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4041 if (ret) ret -= 2;
4043 return ret;
4046 /*************************************************************************
4047 * lstrcmpi (KERNEL32.@)
4048 * lstrcmpiA (KERNEL32.@)
4050 * Compare two strings using the current thread locale, ignoring case.
4052 * PARAMS
4053 * str1 [I] First string to compare
4054 * str2 [I] Second string to compare
4056 * RETURNS
4057 * Success: A number less than, equal to or greater than 0 depending on whether
4058 * str2 is less than, equal to or greater than str1 respectively.
4059 * Failure: FALSE. Use GetLastError() to determine the cause.
4061 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
4063 int ret;
4065 if ((str1 == NULL) && (str2 == NULL)) return 0;
4066 if (str1 == NULL) return -1;
4067 if (str2 == NULL) return 1;
4069 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4070 if (ret) ret -= 2;
4072 return ret;
4075 /*************************************************************************
4076 * lstrcmpW (KERNEL32.@)
4078 * See lstrcmpA.
4080 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
4082 int ret;
4084 if ((str1 == NULL) && (str2 == NULL)) return 0;
4085 if (str1 == NULL) return -1;
4086 if (str2 == NULL) return 1;
4088 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
4089 if (ret) ret -= 2;
4091 return ret;
4094 /*************************************************************************
4095 * lstrcmpiW (KERNEL32.@)
4097 * See lstrcmpiA.
4099 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
4101 int ret;
4103 if ((str1 == NULL) && (str2 == NULL)) return 0;
4104 if (str1 == NULL) return -1;
4105 if (str2 == NULL) return 1;
4107 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
4108 if (ret) ret -= 2;
4110 return ret;
4113 /******************************************************************************
4114 * LOCALE_Init
4116 void LOCALE_Init(void)
4118 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
4119 const union cptable *unix_cp );
4121 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
4123 setlocale( LC_ALL, "" );
4125 #ifdef __APPLE__
4126 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
4127 if (!has_env("LANG"))
4129 const char* mac_locale = get_mac_locale();
4131 setenv( "LANG", mac_locale, 1 );
4132 if (setlocale( LC_ALL, "" ))
4133 TRACE( "setting LANG to '%s'\n", mac_locale );
4134 else
4136 /* no C library locale matching Mac locale; don't pass garbage to children */
4137 unsetenv("LANG");
4138 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
4141 #endif /* __APPLE__ */
4143 unix_cp = setup_unix_locales();
4144 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
4146 #ifdef __APPLE__
4147 if (!unix_cp)
4148 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
4149 #endif
4151 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
4152 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
4153 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
4155 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
4156 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
4157 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
4158 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
4159 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
4160 if (!unix_cp)
4161 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
4162 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
4164 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
4165 ansi_cptable = wine_cp_get_table( 1252 );
4166 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
4167 oem_cptable = wine_cp_get_table( 437 );
4168 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
4169 mac_cptable = wine_cp_get_table( 10000 );
4170 if (unix_cp != CP_UTF8)
4172 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
4173 unix_cptable = wine_cp_get_table( 28591 );
4176 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
4178 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
4179 ansi_cptable->info.codepage, oem_cptable->info.codepage,
4180 mac_cptable->info.codepage, unix_cp );
4182 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
4185 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4187 UNICODE_STRING keyName;
4188 OBJECT_ATTRIBUTES attr;
4189 HANDLE hkey;
4191 RtlInitUnicodeString( &keyName, szKeyName );
4192 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
4194 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
4195 hkey = 0;
4197 return hkey;
4200 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4201 LPWSTR szValueName, ULONG valueNameSize,
4202 LPWSTR szValueData, ULONG valueDataSize)
4204 BYTE buffer[80];
4205 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
4206 DWORD dwLen;
4208 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
4209 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
4210 info->NameLength > valueNameSize ||
4211 info->DataLength > valueDataSize)
4213 return FALSE;
4216 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
4218 memcpy( szValueName, info->Name, info->NameLength);
4219 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
4220 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
4221 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
4223 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
4224 return TRUE;
4227 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
4229 BYTE buffer[128];
4230 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
4231 DWORD dwSize = sizeof(buffer);
4232 UNICODE_STRING valueName;
4234 RtlInitUnicodeString( &valueName, szValueName );
4236 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
4237 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
4238 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
4239 info->DataLength == sizeof(DWORD))
4241 memcpy(lpVal, info->Data, sizeof(DWORD));
4242 return TRUE;
4245 return FALSE;
4248 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
4250 LANGID langId;
4251 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
4252 HRSRC hResource;
4253 BOOL bRet = FALSE;
4255 /* FIXME: Is it correct to use the system default langid? */
4256 langId = GetSystemDefaultLangID();
4258 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
4259 langId = MAKELANGID(PRIMARYLANGID(langId), get_default_sublang( langId ));
4261 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
4263 if (hResource)
4265 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
4267 if (hResDir)
4269 ULONG iResourceIndex = lgrpid & 0xf;
4270 LPCWSTR lpResEntry = LockResource( hResDir );
4271 ULONG i;
4273 for (i = 0; i < iResourceIndex; i++)
4274 lpResEntry += *lpResEntry + 1;
4276 if (*lpResEntry < nameSize)
4278 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
4279 szName[*lpResEntry] = '\0';
4280 bRet = TRUE;
4284 FreeResource( hResource );
4286 return bRet;
4289 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
4290 typedef struct
4292 LANGUAGEGROUP_ENUMPROCA procA;
4293 LANGUAGEGROUP_ENUMPROCW procW;
4294 DWORD dwFlags;
4295 LONG_PTR lParam;
4296 } ENUMLANGUAGEGROUP_CALLBACKS;
4298 /* Internal implementation of EnumSystemLanguageGroupsA/W */
4299 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
4301 WCHAR szNumber[10], szValue[4];
4302 HANDLE hKey;
4303 BOOL bContinue = TRUE;
4304 ULONG ulIndex = 0;
4306 if (!lpProcs)
4308 SetLastError(ERROR_INVALID_PARAMETER);
4309 return FALSE;
4312 switch (lpProcs->dwFlags)
4314 case 0:
4315 /* Default to LGRPID_INSTALLED */
4316 lpProcs->dwFlags = LGRPID_INSTALLED;
4317 /* Fall through... */
4318 case LGRPID_INSTALLED:
4319 case LGRPID_SUPPORTED:
4320 break;
4321 default:
4322 SetLastError(ERROR_INVALID_FLAGS);
4323 return FALSE;
4326 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4328 if (!hKey)
4329 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4331 while (bContinue)
4333 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4334 szValue, sizeof(szValue) ))
4336 BOOL bInstalled = szValue[0] == '1';
4337 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4339 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4340 bInstalled ? "" : "not ");
4342 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4344 WCHAR szGrpName[48];
4346 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
4347 szGrpName[0] = '\0';
4349 if (lpProcs->procW)
4350 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4351 lpProcs->lParam );
4352 else
4354 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4355 char szGrpNameA[48];
4357 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4358 * or whether the language names are ever localised. Assume CP_ACP.
4361 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4362 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4364 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4365 lpProcs->lParam );
4369 ulIndex++;
4371 else
4372 bContinue = FALSE;
4374 if (!bContinue)
4375 break;
4378 if (hKey)
4379 NtClose( hKey );
4381 return TRUE;
4384 /******************************************************************************
4385 * EnumSystemLanguageGroupsA (KERNEL32.@)
4387 * Call a users function for each language group available on the system.
4389 * PARAMS
4390 * pLangGrpEnumProc [I] Callback function to call for each language group
4391 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4392 * lParam [I] User parameter to pass to pLangGrpEnumProc
4394 * RETURNS
4395 * Success: TRUE.
4396 * Failure: FALSE. Use GetLastError() to determine the cause.
4398 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4399 DWORD dwFlags, LONG_PTR lParam)
4401 ENUMLANGUAGEGROUP_CALLBACKS procs;
4403 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4405 procs.procA = pLangGrpEnumProc;
4406 procs.procW = NULL;
4407 procs.dwFlags = dwFlags;
4408 procs.lParam = lParam;
4410 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4413 /******************************************************************************
4414 * EnumSystemLanguageGroupsW (KERNEL32.@)
4416 * See EnumSystemLanguageGroupsA.
4418 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4419 DWORD dwFlags, LONG_PTR lParam)
4421 ENUMLANGUAGEGROUP_CALLBACKS procs;
4423 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4425 procs.procA = NULL;
4426 procs.procW = pLangGrpEnumProc;
4427 procs.dwFlags = dwFlags;
4428 procs.lParam = lParam;
4430 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4433 /******************************************************************************
4434 * IsValidLanguageGroup (KERNEL32.@)
4436 * Determine if a language group is supported and/or installed.
4438 * PARAMS
4439 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4440 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4442 * RETURNS
4443 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4444 * FALSE otherwise.
4446 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4448 static const WCHAR szFormat[] = { '%','x','\0' };
4449 WCHAR szValueName[16], szValue[2];
4450 BOOL bSupported = FALSE, bInstalled = FALSE;
4451 HANDLE hKey;
4454 switch (dwFlags)
4456 case LGRPID_INSTALLED:
4457 case LGRPID_SUPPORTED:
4459 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4461 sprintfW( szValueName, szFormat, lgrpid );
4463 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4465 bSupported = TRUE;
4467 if (szValue[0] == '1')
4468 bInstalled = TRUE;
4471 if (hKey)
4472 NtClose( hKey );
4474 break;
4477 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4478 (dwFlags == LGRPID_INSTALLED && bInstalled))
4479 return TRUE;
4481 return FALSE;
4484 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4485 typedef struct
4487 LANGGROUPLOCALE_ENUMPROCA procA;
4488 LANGGROUPLOCALE_ENUMPROCW procW;
4489 DWORD dwFlags;
4490 LGRPID lgrpid;
4491 LONG_PTR lParam;
4492 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4494 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4495 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4497 static const WCHAR szAlternateSortsKeyName[] = {
4498 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4500 WCHAR szNumber[10], szValue[4];
4501 HANDLE hKey;
4502 BOOL bContinue = TRUE, bAlternate = FALSE;
4503 LGRPID lgrpid;
4504 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4506 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4508 SetLastError(ERROR_INVALID_PARAMETER);
4509 return FALSE;
4512 if (lpProcs->dwFlags)
4514 SetLastError(ERROR_INVALID_FLAGS);
4515 return FALSE;
4518 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4520 if (!hKey)
4521 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4523 while (bContinue)
4525 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4526 szValue, sizeof(szValue) ))
4528 lgrpid = strtoulW( szValue, NULL, 16 );
4530 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4531 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4533 if (lgrpid == lpProcs->lgrpid)
4535 LCID lcid;
4537 lcid = strtoulW( szNumber, NULL, 16 );
4539 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4540 * '00000437 ;Georgian'
4541 * At present we only pass the LCID string.
4544 if (lpProcs->procW)
4545 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4546 else
4548 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4550 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4552 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4556 ulIndex++;
4558 else
4560 /* Finished enumerating this key */
4561 if (!bAlternate)
4563 /* Enumerate alternate sorts also */
4564 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4565 bAlternate = TRUE;
4566 ulIndex = 0;
4568 else
4569 bContinue = FALSE; /* Finished both keys */
4572 if (!bContinue)
4573 break;
4576 if (hKey)
4577 NtClose( hKey );
4579 return TRUE;
4582 /******************************************************************************
4583 * EnumLanguageGroupLocalesA (KERNEL32.@)
4585 * Call a users function for every locale in a language group available on the system.
4587 * PARAMS
4588 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4589 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4590 * dwFlags [I] Reserved, set to 0
4591 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4593 * RETURNS
4594 * Success: TRUE.
4595 * Failure: FALSE. Use GetLastError() to determine the cause.
4597 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4598 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4600 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4602 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4604 callbacks.procA = pLangGrpLcEnumProc;
4605 callbacks.procW = NULL;
4606 callbacks.dwFlags = dwFlags;
4607 callbacks.lgrpid = lgrpid;
4608 callbacks.lParam = lParam;
4610 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4613 /******************************************************************************
4614 * EnumLanguageGroupLocalesW (KERNEL32.@)
4616 * See EnumLanguageGroupLocalesA.
4618 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4619 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4621 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4623 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4625 callbacks.procA = NULL;
4626 callbacks.procW = pLangGrpLcEnumProc;
4627 callbacks.dwFlags = dwFlags;
4628 callbacks.lgrpid = lgrpid;
4629 callbacks.lParam = lParam;
4631 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4634 /******************************************************************************
4635 * InvalidateNLSCache (KERNEL32.@)
4637 * Invalidate the cache of NLS values.
4639 * PARAMS
4640 * None.
4642 * RETURNS
4643 * Success: TRUE.
4644 * Failure: FALSE.
4646 BOOL WINAPI InvalidateNLSCache(void)
4648 FIXME("() stub\n");
4649 return FALSE;
4652 /******************************************************************************
4653 * GetUserGeoID (KERNEL32.@)
4655 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4657 GEOID ret = GEOID_NOT_AVAILABLE;
4658 static const WCHAR geoW[] = {'G','e','o',0};
4659 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4660 WCHAR bufferW[40], *end;
4661 DWORD count;
4662 HANDLE hkey, hSubkey = 0;
4663 UNICODE_STRING keyW;
4664 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4665 RtlInitUnicodeString( &keyW, nationW );
4666 count = sizeof(bufferW);
4668 if(!(hkey = create_registry_key())) return ret;
4670 switch( GeoClass ){
4671 case GEOCLASS_NATION:
4672 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4674 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4675 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4676 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4678 break;
4679 case GEOCLASS_REGION:
4680 FIXME("GEOCLASS_REGION not handled yet\n");
4681 break;
4684 NtClose(hkey);
4685 if (hSubkey) NtClose(hSubkey);
4686 return ret;
4689 /******************************************************************************
4690 * SetUserGeoID (KERNEL32.@)
4692 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4694 static const WCHAR geoW[] = {'G','e','o',0};
4695 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4696 static const WCHAR formatW[] = {'%','i',0};
4697 UNICODE_STRING nameW,keyW;
4698 WCHAR bufferW[10];
4699 OBJECT_ATTRIBUTES attr;
4700 HANDLE hkey;
4702 if(!(hkey = create_registry_key())) return FALSE;
4704 attr.Length = sizeof(attr);
4705 attr.RootDirectory = hkey;
4706 attr.ObjectName = &nameW;
4707 attr.Attributes = 0;
4708 attr.SecurityDescriptor = NULL;
4709 attr.SecurityQualityOfService = NULL;
4710 RtlInitUnicodeString( &nameW, geoW );
4711 RtlInitUnicodeString( &keyW, nationW );
4713 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4716 NtClose(attr.RootDirectory);
4717 return FALSE;
4720 sprintfW(bufferW, formatW, GeoID);
4721 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4722 NtClose(attr.RootDirectory);
4723 NtClose(hkey);
4724 return TRUE;
4727 typedef struct
4729 union
4731 UILANGUAGE_ENUMPROCA procA;
4732 UILANGUAGE_ENUMPROCW procW;
4733 } u;
4734 DWORD flags;
4735 LONG_PTR param;
4736 } ENUM_UILANG_CALLBACK;
4738 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4739 LPCSTR name, WORD LangID, LONG_PTR lParam )
4741 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4742 char buf[20];
4744 sprintf(buf, "%08x", (UINT)LangID);
4745 return enum_uilang->u.procA( buf, enum_uilang->param );
4748 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4749 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4751 static const WCHAR formatW[] = {'%','0','8','x',0};
4752 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4753 WCHAR buf[20];
4755 sprintfW( buf, formatW, (UINT)LangID );
4756 return enum_uilang->u.procW( buf, enum_uilang->param );
4759 /******************************************************************************
4760 * EnumUILanguagesA (KERNEL32.@)
4762 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4764 ENUM_UILANG_CALLBACK enum_uilang;
4766 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4768 if(!pUILangEnumProc) {
4769 SetLastError(ERROR_INVALID_PARAMETER);
4770 return FALSE;
4772 if(dwFlags) {
4773 SetLastError(ERROR_INVALID_FLAGS);
4774 return FALSE;
4777 enum_uilang.u.procA = pUILangEnumProc;
4778 enum_uilang.flags = dwFlags;
4779 enum_uilang.param = lParam;
4781 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4782 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4783 (LONG_PTR)&enum_uilang);
4784 return TRUE;
4787 /******************************************************************************
4788 * EnumUILanguagesW (KERNEL32.@)
4790 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4792 ENUM_UILANG_CALLBACK enum_uilang;
4794 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4797 if(!pUILangEnumProc) {
4798 SetLastError(ERROR_INVALID_PARAMETER);
4799 return FALSE;
4801 if(dwFlags) {
4802 SetLastError(ERROR_INVALID_FLAGS);
4803 return FALSE;
4806 enum_uilang.u.procW = pUILangEnumProc;
4807 enum_uilang.flags = dwFlags;
4808 enum_uilang.param = lParam;
4810 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4811 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4812 (LONG_PTR)&enum_uilang);
4813 return TRUE;
4816 enum locationkind {
4817 LOCATION_NATION = 0,
4818 LOCATION_REGION,
4819 LOCATION_BOTH
4822 struct geoinfo_t {
4823 GEOID id;
4824 WCHAR iso2W[3];
4825 WCHAR iso3W[4];
4826 GEOID parent;
4827 INT uncode;
4828 enum locationkind kind;
4831 static const struct geoinfo_t geoinfodata[] = {
4832 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4833 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4834 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4835 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4836 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4837 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4838 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4839 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4840 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4841 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4842 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4843 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4844 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4845 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4846 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4847 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4848 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4849 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4850 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4851 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4852 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4853 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4854 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4855 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4856 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4857 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4858 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4859 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4860 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4861 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4862 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4863 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4864 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4865 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4866 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4867 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4868 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4869 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4870 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4871 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4872 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4873 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4874 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4875 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4876 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4877 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4878 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4879 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4880 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4881 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4882 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4883 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4884 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4885 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4886 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4887 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4888 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4889 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4890 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4891 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4892 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4893 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4894 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4895 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4896 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4897 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4898 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4899 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4900 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4901 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4902 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4903 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4904 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4905 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4906 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4907 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4908 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4909 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4910 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4911 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4912 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4913 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4914 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4915 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4916 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4917 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4918 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4919 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4920 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4921 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4922 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4923 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4924 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4925 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4926 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4927 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4928 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4929 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4930 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4931 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4932 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4933 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4934 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4935 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4936 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4937 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4938 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4939 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4940 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4941 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4942 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4943 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4944 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4945 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4946 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4947 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4948 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4949 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4950 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4951 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4952 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4953 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4954 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4955 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4956 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4957 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4958 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4959 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4960 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4961 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4962 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4963 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4964 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4965 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4966 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4967 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4968 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4969 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4970 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4971 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4972 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4973 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4974 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4975 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4976 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4977 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4978 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4979 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4980 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4981 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4982 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4983 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4984 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4985 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4986 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4987 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4988 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4989 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4990 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4991 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4992 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4993 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4994 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4995 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4996 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4997 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4998 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4999 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
5000 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
5001 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
5002 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
5003 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
5004 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
5005 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
5006 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
5007 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
5008 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
5009 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
5010 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
5011 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
5012 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
5013 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
5014 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
5015 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
5016 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
5017 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
5018 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
5019 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
5020 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
5021 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
5022 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
5023 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
5024 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
5025 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
5026 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
5027 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
5028 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
5029 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
5030 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
5031 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
5032 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
5033 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
5034 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
5035 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
5036 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
5037 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
5038 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
5039 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
5040 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
5041 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
5042 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
5043 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
5044 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
5045 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
5046 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
5047 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
5048 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
5049 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
5050 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
5051 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
5052 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
5053 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
5054 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
5055 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
5056 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
5057 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
5058 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
5059 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
5060 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
5061 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
5062 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
5063 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
5064 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
5065 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
5066 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
5067 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
5068 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
5069 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
5070 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
5071 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
5072 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
5073 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
5074 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
5075 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
5076 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
5077 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
5078 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
5079 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
5080 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
5081 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
5082 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
5083 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
5084 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
5085 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
5086 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
5087 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
5088 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
5089 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
5090 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
5091 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
5092 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
5093 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
5094 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
5095 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
5096 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
5097 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
5098 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
5099 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
5100 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
5101 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
5102 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
5103 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
5104 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
5105 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
5106 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
5107 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
5108 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
5109 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
5110 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
5111 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
5112 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
5113 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
5114 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
5115 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
5116 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
5117 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
5118 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
5119 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
5120 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
5121 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
5122 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
5123 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
5124 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
5125 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
5126 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
5127 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
5128 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
5129 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
5130 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
5133 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
5135 int min, max;
5137 min = 0;
5138 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
5140 while (min <= max) {
5141 const struct geoinfo_t *ptr;
5142 int n = (min+max)/2;
5144 ptr = &geoinfodata[n];
5145 if (geoid == ptr->id)
5146 /* we don't need empty entries */
5147 return *ptr->iso2W ? ptr : NULL;
5149 if (ptr->id > geoid)
5150 max = n-1;
5151 else
5152 min = n+1;
5155 return NULL;
5158 /******************************************************************************
5159 * GetGeoInfoW (KERNEL32.@)
5161 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
5163 const struct geoinfo_t *ptr;
5164 const WCHAR *str = NULL;
5165 WCHAR buffW[12];
5166 LONG val = 0;
5167 INT len;
5169 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5171 if (!(ptr = get_geoinfo_dataptr(geoid))) {
5172 SetLastError(ERROR_INVALID_PARAMETER);
5173 return 0;
5176 switch (geotype) {
5177 case GEO_NATION:
5178 val = geoid;
5179 break;
5180 case GEO_ISO_UN_NUMBER:
5181 val = ptr->uncode;
5182 break;
5183 case GEO_PARENT:
5184 val = ptr->parent;
5185 break;
5186 case GEO_ISO2:
5187 case GEO_ISO3:
5189 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
5190 break;
5192 case GEO_RFC1766:
5193 case GEO_LCID:
5194 case GEO_FRIENDLYNAME:
5195 case GEO_OFFICIALNAME:
5196 case GEO_TIMEZONES:
5197 case GEO_OFFICIALLANGUAGES:
5198 case GEO_LATITUDE:
5199 case GEO_LONGITUDE:
5200 FIXME("type %d is not supported\n", geotype);
5201 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5202 return 0;
5203 default:
5204 WARN("unrecognized type %d\n", geotype);
5205 SetLastError(ERROR_INVALID_FLAGS);
5206 return 0;
5209 if (val) {
5210 static const WCHAR fmtW[] = {'%','d',0};
5211 sprintfW(buffW, fmtW, val);
5212 str = buffW;
5215 len = strlenW(str) + 1;
5216 if (!data || !data_len)
5217 return len;
5219 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
5220 if (data_len < len)
5221 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5222 return data_len < len ? 0 : len;
5225 /******************************************************************************
5226 * GetGeoInfoA (KERNEL32.@)
5228 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
5230 WCHAR *buffW;
5231 INT len;
5233 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5235 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
5236 if (!len)
5237 return 0;
5239 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
5240 if (!buffW)
5241 return 0;
5243 GetGeoInfoW(geoid, geotype, buffW, len, lang);
5244 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
5245 if (!data || !data_len) {
5246 HeapFree(GetProcessHeap(), 0, buffW);
5247 return len;
5250 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
5251 HeapFree(GetProcessHeap(), 0, buffW);
5253 if (data_len < len)
5254 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5255 return data_len < len ? 0 : len;
5258 /******************************************************************************
5259 * EnumSystemGeoID (KERNEL32.@)
5261 * Call a users function for every location available on the system.
5263 * PARAMS
5264 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
5265 * parent [I] GEOID for the parent
5266 * enumproc [I] Callback function to call for each location
5268 * RETURNS
5269 * Success: TRUE.
5270 * Failure: FALSE. Use GetLastError() to determine the cause.
5272 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
5274 INT i;
5276 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
5278 if (!enumproc) {
5279 SetLastError(ERROR_INVALID_PARAMETER);
5280 return FALSE;
5283 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
5284 SetLastError(ERROR_INVALID_FLAGS);
5285 return FALSE;
5288 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
5289 const struct geoinfo_t *ptr = &geoinfodata[i];
5291 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
5292 continue;
5294 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
5295 continue;
5297 if (parent && ptr->parent != parent)
5298 continue;
5300 if (!enumproc(ptr->id))
5301 return TRUE;
5304 return TRUE;
5307 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
5309 LCID userlcid;
5311 TRACE("%p, %d\n", localename, buffersize);
5313 userlcid = GetUserDefaultLCID();
5314 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
5317 /******************************************************************************
5318 * NormalizeString (KERNEL32.@)
5320 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
5321 LPWSTR lpDstString, INT cwDstLength)
5323 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
5324 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5325 return 0;
5328 /******************************************************************************
5329 * IsNormalizedString (KERNEL32.@)
5331 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
5333 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5334 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5335 return FALSE;
5338 enum {
5339 BASE = 36,
5340 TMIN = 1,
5341 TMAX = 26,
5342 SKEW = 38,
5343 DAMP = 700,
5344 INIT_BIAS = 72,
5345 INIT_N = 128
5348 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5350 INT k;
5352 delta /= (firsttime ? DAMP : 2);
5353 delta += delta/numpoints;
5355 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5356 delta /= BASE-TMIN;
5357 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5360 /******************************************************************************
5361 * IdnToAscii (KERNEL32.@)
5362 * Implementation of Punycode based on RFC 3492.
5364 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5365 LPWSTR lpASCIICharStr, INT cchASCIIChar)
5367 static const WCHAR prefixW[] = {'x','n','-','-'};
5369 WCHAR *norm_str;
5370 INT i, label_start, label_end, norm_len, out_label, out = 0;
5372 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5373 lpASCIICharStr, cchASCIIChar);
5375 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5376 if(!norm_len)
5377 return 0;
5378 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5379 if(!norm_str) {
5380 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5381 return 0;
5383 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5384 cchUnicodeChar, norm_str, norm_len);
5385 if(!norm_len) {
5386 HeapFree(GetProcessHeap(), 0, norm_str);
5387 return 0;
5390 for(label_start=0; label_start<norm_len;) {
5391 INT n = INIT_N, bias = INIT_BIAS;
5392 INT delta = 0, b = 0, h;
5394 out_label = out;
5395 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5396 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5397 if(norm_str[i] < 0x80)
5398 b++;
5399 label_end = i;
5401 if(b == label_end-label_start) {
5402 if(label_end < norm_len)
5403 b++;
5404 if(!lpASCIICharStr) {
5405 out += b;
5406 }else if(out+b <= cchASCIIChar) {
5407 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5408 out += b;
5409 }else {
5410 HeapFree(GetProcessHeap(), 0, norm_str);
5411 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5412 return 0;
5414 label_start = label_end+1;
5415 continue;
5418 if(!lpASCIICharStr) {
5419 out += 5+b; /* strlen(xn--...-) */
5420 }else if(out+5+b <= cchASCIIChar) {
5421 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5422 out += 4;
5423 for(i=label_start; i<label_end; i++)
5424 if(norm_str[i] < 0x80)
5425 lpASCIICharStr[out++] = norm_str[i];
5426 lpASCIICharStr[out++] = '-';
5427 }else {
5428 HeapFree(GetProcessHeap(), 0, norm_str);
5429 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5430 return 0;
5432 if(!b)
5433 out--;
5435 for(h=b; h<label_end-label_start;) {
5436 INT m = 0xffff, q, k;
5438 for(i=label_start; i<label_end; i++) {
5439 if(norm_str[i]>=n && m>norm_str[i])
5440 m = norm_str[i];
5442 delta += (m-n)*(h+1);
5443 n = m;
5445 for(i=label_start; i<label_end; i++) {
5446 if(norm_str[i] < n) {
5447 delta++;
5448 }else if(norm_str[i] == n) {
5449 for(q=delta, k=BASE; ; k+=BASE) {
5450 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5451 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5452 if(!lpASCIICharStr) {
5453 out++;
5454 }else if(out+1 <= cchASCIIChar) {
5455 lpASCIICharStr[out++] = disp<='z'-'a' ?
5456 'a'+disp : '0'+disp-'z'+'a'-1;
5457 }else {
5458 HeapFree(GetProcessHeap(), 0, norm_str);
5459 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5460 return 0;
5462 if(q < t)
5463 break;
5464 q = (q-t)/(BASE-t);
5466 bias = adapt(delta, h+1, h==b);
5467 delta = 0;
5468 h++;
5471 delta++;
5472 n++;
5475 if(out-out_label > 63) {
5476 HeapFree(GetProcessHeap(), 0, norm_str);
5477 SetLastError(ERROR_INVALID_NAME);
5478 return 0;
5481 if(label_end < norm_len) {
5482 if(!lpASCIICharStr) {
5483 out++;
5484 }else if(out+1 <= cchASCIIChar) {
5485 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5486 }else {
5487 HeapFree(GetProcessHeap(), 0, norm_str);
5488 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5489 return 0;
5492 label_start = label_end+1;
5495 HeapFree(GetProcessHeap(), 0, norm_str);
5496 return out;
5499 /******************************************************************************
5500 * IdnToNameprepUnicode (KERNEL32.@)
5502 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5503 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5505 enum {
5506 UNASSIGNED = 0x1,
5507 PROHIBITED = 0x2,
5508 BIDI_RAL = 0x4,
5509 BIDI_L = 0x8
5512 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5513 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5514 const WCHAR *ptr;
5515 WORD flags;
5516 WCHAR buf[64], *map_str, norm_str[64], ch;
5517 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5518 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5520 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5521 lpNameprepCharStr, cchNameprepChar);
5523 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5524 SetLastError(ERROR_INVALID_FLAGS);
5525 return 0;
5528 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5529 SetLastError(ERROR_INVALID_PARAMETER);
5530 return 0;
5533 if(cchUnicodeChar == -1)
5534 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5535 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5536 SetLastError(ERROR_INVALID_NAME);
5537 return 0;
5540 for(label_start=0; label_start<cchUnicodeChar;) {
5541 ascii_only = TRUE;
5542 for(i=label_start; i<cchUnicodeChar; i++) {
5543 ch = lpUnicodeCharStr[i];
5545 if(i!=cchUnicodeChar-1 && !ch) {
5546 SetLastError(ERROR_INVALID_NAME);
5547 return 0;
5549 /* check if ch is one of label separators defined in RFC3490 */
5550 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5551 break;
5553 if(ch > 0x7f) {
5554 ascii_only = FALSE;
5555 continue;
5558 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5559 continue;
5560 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5561 || (ch>='0' && ch<='9') || ch=='-')
5562 continue;
5564 SetLastError(ERROR_INVALID_NAME);
5565 return 0;
5567 label_end = i;
5568 /* last label may be empty */
5569 if(label_start==label_end && ch) {
5570 SetLastError(ERROR_INVALID_NAME);
5571 return 0;
5574 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5575 lpUnicodeCharStr[label_end-1]=='-')) {
5576 SetLastError(ERROR_INVALID_NAME);
5577 return 0;
5580 if(ascii_only) {
5581 /* maximal label length is 63 characters */
5582 if(label_end-label_start > 63) {
5583 SetLastError(ERROR_INVALID_NAME);
5584 return 0;
5586 if(label_end < cchUnicodeChar)
5587 label_end++;
5589 if(!lpNameprepCharStr) {
5590 out += label_end-label_start;
5591 }else if(out+label_end-label_start <= cchNameprepChar) {
5592 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5593 (label_end-label_start)*sizeof(WCHAR));
5594 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5595 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5596 out += label_end-label_start;
5597 }else {
5598 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5599 return 0;
5602 label_start = label_end;
5603 continue;
5606 map_len = 0;
5607 for(i=label_start; i<label_end; i++) {
5608 ch = lpUnicodeCharStr[i];
5609 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5610 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5612 if(!ptr[0]) map_len++;
5613 else if(!ptr[1]) map_len++;
5614 else if(!ptr[2]) map_len += 2;
5615 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5617 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5618 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5619 if(!map_str) {
5620 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5621 return 0;
5623 }else {
5624 map_str = buf;
5626 map_len = 0;
5627 for(i=label_start; i<label_end; i++) {
5628 ch = lpUnicodeCharStr[i];
5629 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5630 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5632 if(!ptr[0]) {
5633 map_str[map_len++] = ch;
5634 }else if(!ptr[1]) {
5635 map_str[map_len++] = ptr[0];
5636 }else if(!ptr[2]) {
5637 map_str[map_len++] = ptr[0];
5638 map_str[map_len++] = ptr[1];
5639 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5640 map_str[map_len++] = ptr[0];
5641 map_str[map_len++] = ptr[1];
5642 map_str[map_len++] = ptr[2];
5646 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5647 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5648 if(map_str != buf)
5649 HeapFree(GetProcessHeap(), 0, map_str);
5650 if(!norm_len) {
5651 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5652 SetLastError(ERROR_INVALID_NAME);
5653 return 0;
5656 if(label_end < cchUnicodeChar) {
5657 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5658 label_end++;
5661 if(!lpNameprepCharStr) {
5662 out += norm_len;
5663 }else if(out+norm_len <= cchNameprepChar) {
5664 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5665 out += norm_len;
5666 }else {
5667 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5668 return 0;
5671 have_bidi_ral = prohibit_bidi_ral = FALSE;
5672 mask = PROHIBITED;
5673 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5674 mask |= UNASSIGNED;
5675 for(i=0; i<norm_len; i++) {
5676 ch = norm_str[i];
5677 flags = get_table_entry( nameprep_char_type, ch );
5679 if(flags & mask) {
5680 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5681 : ERROR_NO_UNICODE_TRANSLATION);
5682 return 0;
5685 if(flags & BIDI_RAL)
5686 have_bidi_ral = TRUE;
5687 if(flags & BIDI_L)
5688 prohibit_bidi_ral = TRUE;
5691 if(have_bidi_ral) {
5692 ch = norm_str[0];
5693 flags = get_table_entry( nameprep_char_type, ch );
5694 if((flags & BIDI_RAL) == 0)
5695 prohibit_bidi_ral = TRUE;
5697 ch = norm_str[norm_len-1];
5698 flags = get_table_entry( nameprep_char_type, ch );
5699 if((flags & BIDI_RAL) == 0)
5700 prohibit_bidi_ral = TRUE;
5703 if(have_bidi_ral && prohibit_bidi_ral) {
5704 SetLastError(ERROR_INVALID_NAME);
5705 return 0;
5708 label_start = label_end;
5711 return out;
5714 /******************************************************************************
5715 * IdnToUnicode (KERNEL32.@)
5717 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5718 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5720 extern const unsigned short nameprep_char_type[];
5722 INT i, label_start, label_end, out_label, out = 0;
5723 WCHAR ch;
5725 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5726 lpUnicodeCharStr, cchUnicodeChar);
5728 for(label_start=0; label_start<cchASCIIChar;) {
5729 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5731 out_label = out;
5732 for(i=label_start; i<cchASCIIChar; i++) {
5733 ch = lpASCIICharStr[i];
5735 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5736 SetLastError(ERROR_INVALID_NAME);
5737 return 0;
5740 if(!ch || ch=='.')
5741 break;
5742 if(ch == '-')
5743 delim = i;
5745 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5746 continue;
5747 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5748 || (ch>='0' && ch<='9') || ch=='-')
5749 continue;
5751 SetLastError(ERROR_INVALID_NAME);
5752 return 0;
5754 label_end = i;
5755 /* last label may be empty */
5756 if(label_start==label_end && ch) {
5757 SetLastError(ERROR_INVALID_NAME);
5758 return 0;
5761 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5762 lpASCIICharStr[label_end-1]=='-')) {
5763 SetLastError(ERROR_INVALID_NAME);
5764 return 0;
5766 if(label_end-label_start > 63) {
5767 SetLastError(ERROR_INVALID_NAME);
5768 return 0;
5771 if(label_end-label_start<4 ||
5772 tolowerW(lpASCIICharStr[label_start])!='x' ||
5773 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5774 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5775 if(label_end < cchASCIIChar)
5776 label_end++;
5778 if(!lpUnicodeCharStr) {
5779 out += label_end-label_start;
5780 }else if(out+label_end-label_start <= cchUnicodeChar) {
5781 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5782 (label_end-label_start)*sizeof(WCHAR));
5783 out += label_end-label_start;
5784 }else {
5785 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5786 return 0;
5789 label_start = label_end;
5790 continue;
5793 if(delim == label_start+3)
5794 delim++;
5795 if(!lpUnicodeCharStr) {
5796 out += delim-label_start-4;
5797 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5798 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5799 (delim-label_start-4)*sizeof(WCHAR));
5800 out += delim-label_start-4;
5801 }else {
5802 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5803 return 0;
5805 if(out != out_label)
5806 delim++;
5808 for(i=delim; i<label_end;) {
5809 old_pos = pos;
5810 w = 1;
5811 for(k=BASE; ; k+=BASE) {
5812 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5813 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5814 SetLastError(ERROR_INVALID_NAME);
5815 return 0;
5817 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5818 pos += digit*w;
5819 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5820 if(digit < t)
5821 break;
5822 w *= BASE-t;
5824 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5825 n += pos/(out-out_label+1);
5826 pos %= out-out_label+1;
5828 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5829 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5830 SetLastError(ERROR_INVALID_NAME);
5831 return 0;
5833 if(!lpUnicodeCharStr) {
5834 out++;
5835 }else if(out+1 <= cchASCIIChar) {
5836 memmove(lpUnicodeCharStr+out_label+pos+1,
5837 lpUnicodeCharStr+out_label+pos,
5838 (out-out_label-pos)*sizeof(WCHAR));
5839 lpUnicodeCharStr[out_label+pos] = n;
5840 out++;
5841 }else {
5842 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5843 return 0;
5845 pos++;
5848 if(out-out_label > 63) {
5849 SetLastError(ERROR_INVALID_NAME);
5850 return 0;
5853 if(label_end < cchASCIIChar) {
5854 if(!lpUnicodeCharStr) {
5855 out++;
5856 }else if(out+1 <= cchUnicodeChar) {
5857 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5858 }else {
5859 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5860 return 0;
5863 label_start = label_end+1;
5866 return out;
5870 /******************************************************************************
5871 * GetFileMUIPath (KERNEL32.@)
5874 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5875 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5877 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5878 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5880 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5882 return FALSE;
5885 /******************************************************************************
5886 * GetFileMUIInfo (KERNEL32.@)
5889 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5891 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5893 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5894 return FALSE;
5897 /******************************************************************************
5898 * ResolveLocaleName (KERNEL32.@)
5901 INT WINAPI ResolveLocaleName(LPCWSTR name, LPWSTR localename, INT len)
5903 FIXME("stub: %s, %p, %d\n", wine_dbgstr_w(name), localename, len);
5905 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5906 return 0;