wined3d: Drop support for WINED3DFMT_D32_UNORM.
[wine.git] / dlls / kernel32 / locale.c
blob489e7191fba591bdf3ec5c588ff43a5c48f3478e
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/heap.h"
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(nls);
56 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
57 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
58 #define MB_FLAGSMASK (MB_PRECOMPOSED|MB_COMPOSITE|MB_USEGLYPHCHARS|MB_ERR_INVALID_CHARS)
59 #define WC_FLAGSMASK (WC_DISCARDNS|WC_SEPCHARS|WC_DEFAULTCHAR|WC_ERR_INVALID_CHARS|\
60 WC_COMPOSITECHECK|WC_NO_BEST_FIT_CHARS)
62 /* current code pages */
63 static const union cptable *ansi_cptable;
64 static const union cptable *oem_cptable;
65 static const union cptable *mac_cptable;
66 static const union cptable *unix_cptable; /* NULL if UTF8 */
68 static const WCHAR szLocaleKeyName[] = {
69 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
70 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
71 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
74 static const WCHAR szLangGroupsKeyName[] = {
75 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
76 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
77 'C','o','n','t','r','o','l','\\','N','l','s','\\',
78 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
81 /* Charset to codepage map, sorted by name. */
82 static const struct charset_entry
84 const char *charset_name;
85 UINT codepage;
86 } charset_names[] =
88 { "BIG5", 950 },
89 { "CP1250", 1250 },
90 { "CP1251", 1251 },
91 { "CP1252", 1252 },
92 { "CP1253", 1253 },
93 { "CP1254", 1254 },
94 { "CP1255", 1255 },
95 { "CP1256", 1256 },
96 { "CP1257", 1257 },
97 { "CP1258", 1258 },
98 { "CP932", 932 },
99 { "CP936", 936 },
100 { "CP949", 949 },
101 { "CP950", 950 },
102 { "EUCJP", 20932 },
103 { "GB2312", 936 },
104 { "IBM037", 37 },
105 { "IBM1026", 1026 },
106 { "IBM424", 424 },
107 { "IBM437", 437 },
108 { "IBM500", 500 },
109 { "IBM850", 850 },
110 { "IBM852", 852 },
111 { "IBM855", 855 },
112 { "IBM857", 857 },
113 { "IBM860", 860 },
114 { "IBM861", 861 },
115 { "IBM862", 862 },
116 { "IBM863", 863 },
117 { "IBM864", 864 },
118 { "IBM865", 865 },
119 { "IBM866", 866 },
120 { "IBM869", 869 },
121 { "IBM874", 874 },
122 { "IBM875", 875 },
123 { "ISO88591", 28591 },
124 { "ISO885910", 28600 },
125 { "ISO885913", 28603 },
126 { "ISO885914", 28604 },
127 { "ISO885915", 28605 },
128 { "ISO885916", 28606 },
129 { "ISO88592", 28592 },
130 { "ISO88593", 28593 },
131 { "ISO88594", 28594 },
132 { "ISO88595", 28595 },
133 { "ISO88596", 28596 },
134 { "ISO88597", 28597 },
135 { "ISO88598", 28598 },
136 { "ISO88599", 28599 },
137 { "KOI8R", 20866 },
138 { "KOI8U", 21866 },
139 { "UTF8", CP_UTF8 }
143 struct locale_name
145 WCHAR win_name[128]; /* Windows name ("en-US") */
146 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
147 WCHAR *country; /* country ("US") */
148 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
149 WCHAR *script; /* script ("Latn") for Windows format only */
150 WCHAR *modifier; /* modifier or sort order */
151 LCID lcid; /* corresponding LCID */
152 int matches; /* number of elements matching LCID (0..4) */
153 UINT codepage; /* codepage corresponding to charset */
156 /* locale ids corresponding to the various Unix locale parameters */
157 static LCID lcid_LC_COLLATE;
158 static LCID lcid_LC_CTYPE;
159 static LCID lcid_LC_MESSAGES;
160 static LCID lcid_LC_MONETARY;
161 static LCID lcid_LC_NUMERIC;
162 static LCID lcid_LC_TIME;
163 static LCID lcid_LC_PAPER;
164 static LCID lcid_LC_MEASUREMENT;
165 static LCID lcid_LC_TELEPHONE;
167 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
168 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
169 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
170 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
171 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
172 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
173 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
174 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
175 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
176 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
177 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
178 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
179 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
180 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
181 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
182 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
183 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
184 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
185 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
186 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
187 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
188 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
189 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
190 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
191 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
192 static const WCHAR sListW[] = {'s','L','i','s','t',0};
193 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
194 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
195 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
196 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
197 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
198 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
199 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
200 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
201 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
202 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
203 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
204 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
205 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
207 static struct registry_value
209 DWORD lctype;
210 const WCHAR *name;
211 WCHAR *cached_value;
212 } registry_values[] =
214 { LOCALE_ICALENDARTYPE, iCalendarTypeW },
215 { LOCALE_ICURRDIGITS, iCurrDigitsW },
216 { LOCALE_ICURRENCY, iCurrencyW },
217 { LOCALE_IDIGITS, iDigitsW },
218 { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
219 { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
220 { LOCALE_ILZERO, iLZeroW },
221 { LOCALE_IMEASURE, iMeasureW },
222 { LOCALE_INEGCURR, iNegCurrW },
223 { LOCALE_INEGNUMBER, iNegNumberW },
224 { LOCALE_IPAPERSIZE, iPaperSizeW },
225 { LOCALE_ITIME, iTimeW },
226 { LOCALE_S1159, s1159W },
227 { LOCALE_S2359, s2359W },
228 { LOCALE_SCURRENCY, sCurrencyW },
229 { LOCALE_SDATE, sDateW },
230 { LOCALE_SDECIMAL, sDecimalW },
231 { LOCALE_SGROUPING, sGroupingW },
232 { LOCALE_SLIST, sListW },
233 { LOCALE_SLONGDATE, sLongDateW },
234 { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
235 { LOCALE_SMONGROUPING, sMonGroupingW },
236 { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
237 { LOCALE_SNEGATIVESIGN, sNegativeSignW },
238 { LOCALE_SPOSITIVESIGN, sPositiveSignW },
239 { LOCALE_SSHORTDATE, sShortDateW },
240 { LOCALE_STHOUSAND, sThousandW },
241 { LOCALE_STIME, sTimeW },
242 { LOCALE_STIMEFORMAT, sTimeFormatW },
243 { LOCALE_SYEARMONTH, sYearMonthW },
244 /* The following are not listed under MSDN as supported,
245 * but seem to be used and also stored in the registry.
247 { LOCALE_ICOUNTRY, iCountryW },
248 { LOCALE_IDATE, iDateW },
249 { LOCALE_ILDATE, iLDateW },
250 { LOCALE_ITLZERO, iTLZeroW },
251 { LOCALE_SCOUNTRY, sCountryW },
252 { LOCALE_SABBREVLANGNAME, sLanguageW },
253 /* The following are used in XP and later */
254 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
255 { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
256 { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
259 static CRITICAL_SECTION cache_section;
260 static CRITICAL_SECTION_DEBUG critsect_debug =
262 0, 0, &cache_section,
263 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
264 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
266 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
268 /* Copy Ascii string to Unicode without using codepages */
269 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
271 while (n > 1 && *src)
273 *dst++ = (unsigned char)*src++;
274 n--;
276 if (n) *dst = 0;
279 extern const unsigned short wctype_table[] DECLSPEC_HIDDEN;
280 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
281 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
283 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
285 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
288 /***********************************************************************
289 * get_lcid_codepage
291 * Retrieve the ANSI codepage for a given locale.
293 static inline UINT get_lcid_codepage( LCID lcid )
295 UINT ret;
296 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
297 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
298 return ret;
302 /***********************************************************************
303 * get_codepage_table
305 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
307 static const union cptable *get_codepage_table( unsigned int codepage )
309 const union cptable *ret = NULL;
311 assert( ansi_cptable ); /* init must have been done already */
313 switch(codepage)
315 case CP_ACP:
316 return ansi_cptable;
317 case CP_OEMCP:
318 return oem_cptable;
319 case CP_MACCP:
320 return mac_cptable;
321 case CP_UTF7:
322 case CP_UTF8:
323 break;
324 case CP_THREAD_ACP:
325 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
326 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
327 if (!codepage) return ansi_cptable;
328 /* fall through */
329 default:
330 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
331 if (codepage == oem_cptable->info.codepage) return oem_cptable;
332 if (codepage == mac_cptable->info.codepage) return mac_cptable;
333 ret = wine_cp_get_table( codepage );
334 break;
336 return ret;
340 /***********************************************************************
341 * charset_cmp (internal)
343 static int charset_cmp( const void *name, const void *entry )
345 const struct charset_entry *charset = entry;
346 return _strnicmp( name, charset->charset_name, -1 );
349 /***********************************************************************
350 * find_charset
352 static UINT find_charset( const WCHAR *name )
354 const struct charset_entry *entry;
355 char charset_name[16];
356 size_t i, j;
358 /* remove punctuation characters from charset name */
359 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
360 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
361 charset_name[j] = 0;
363 entry = bsearch( charset_name, charset_names, ARRAY_SIZE( charset_names ),
364 sizeof(charset_names[0]), charset_cmp );
365 if (entry) return entry->codepage;
366 return 0;
369 static LANGID get_default_sublang( LANGID lang )
371 switch (lang)
373 case MAKELANGID( LANG_SPANISH, SUBLANG_NEUTRAL ):
374 return MAKELANGID( LANG_SPANISH, SUBLANG_SPANISH_MODERN );
375 case MAKELANGID( LANG_CHINESE, SUBLANG_NEUTRAL ):
376 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
377 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE ):
378 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
379 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ):
380 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_MACAU ):
381 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG );
383 if (SUBLANGID( lang ) == SUBLANG_NEUTRAL) lang = MAKELANGID( PRIMARYLANGID(lang), SUBLANG_DEFAULT );
384 return lang;
387 /***********************************************************************
388 * find_locale_id_callback
390 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
391 LPCWSTR name, LANGID lang, LPARAM lParam )
393 struct locale_name *data = (struct locale_name *)lParam;
394 WCHAR buffer[128];
395 int matches = 0;
396 LCID lcid = MAKELCID( lang, SORT_DEFAULT ); /* FIXME: handle sort order */
398 if (PRIMARYLANGID(lang) == LANG_NEUTRAL) return TRUE; /* continue search */
400 /* first check exact name */
401 if (data->win_name[0] &&
402 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, buffer, ARRAY_SIZE( buffer )))
404 if (!strcmpiW( data->win_name, buffer ))
406 matches = 4; /* everything matches */
407 goto done;
411 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
412 buffer, ARRAY_SIZE( buffer )))
413 return TRUE;
414 if (strcmpiW( buffer, data->lang )) return TRUE;
415 matches++; /* language name matched */
417 if (data->script)
419 if (GetLocaleInfoW( lcid, LOCALE_SSCRIPTS | LOCALE_NOUSEROVERRIDE,
420 buffer, ARRAY_SIZE( buffer )))
422 const WCHAR *p = buffer;
423 unsigned int len = strlenW( data->script );
424 while (*p)
426 if (!strncmpiW( p, data->script, len ) && (!p[len] || p[len] == ';')) break;
427 if (!(p = strchrW( p, ';'))) goto done;
428 p++;
430 if (!*p) goto done;
431 matches++; /* script matched */
435 if (data->country)
437 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
438 buffer, ARRAY_SIZE( buffer )))
440 if (strcmpiW( buffer, data->country )) goto done;
441 matches++; /* country name matched */
444 else /* match default language */
446 LANGID def_lang = data->script ? lang : MAKELANGID( PRIMARYLANGID(lang), LANG_NEUTRAL );
447 if (lang == get_default_sublang( def_lang )) matches++;
450 if (data->codepage)
452 UINT unix_cp;
453 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
454 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
456 if (unix_cp == data->codepage) matches++;
460 /* FIXME: check sort order */
462 done:
463 if (matches > data->matches)
465 data->lcid = lcid;
466 data->matches = matches;
468 return (data->matches < 4); /* no need to continue for perfect match */
472 /***********************************************************************
473 * parse_locale_name
475 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
476 * Unix format is: lang[_country][.charset][@modifier]
477 * Windows format is: lang[-script][-country][_modifier]
479 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
481 static const WCHAR sepW[] = {'-','_','.','@',0};
482 static const WCHAR winsepW[] = {'-','_',0};
483 static const WCHAR posixW[] = {'P','O','S','I','X',0};
484 static const WCHAR cW[] = {'C',0};
485 static const WCHAR latinW[] = {'l','a','t','i','n',0};
486 static const WCHAR latnW[] = {'-','L','a','t','n',0};
487 WCHAR *p;
489 TRACE("%s\n", debugstr_w(str));
491 name->country = name->charset = name->script = name->modifier = NULL;
492 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
493 name->matches = 0;
494 name->codepage = 0;
495 name->win_name[0] = 0;
496 lstrcpynW( name->lang, str, ARRAY_SIZE( name->lang ));
498 if (!*name->lang)
500 name->lcid = LOCALE_INVARIANT;
501 name->matches = 4;
502 return;
505 if (!(p = strpbrkW( name->lang, sepW )))
507 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
509 name->matches = 4; /* perfect match for default English lcid */
510 return;
512 strcpyW( name->win_name, name->lang );
514 else if (*p == '-') /* Windows format */
516 strcpyW( name->win_name, name->lang );
517 *p++ = 0;
518 name->country = p;
519 if ((p = strpbrkW( p, winsepW )) && *p == '-')
521 *p++ = 0;
522 name->script = name->country;
523 name->country = p;
524 p = strpbrkW( p, winsepW );
526 if (p)
528 *p++ = 0;
529 name->modifier = p;
531 /* second value can be script or country, check length to resolve the ambiguity */
532 if (!name->script && strlenW( name->country ) == 4)
534 name->script = name->country;
535 name->country = NULL;
538 else /* Unix format */
540 if (*p == '_')
542 *p++ = 0;
543 name->country = p;
544 p = strpbrkW( p, sepW + 2 );
546 if (p && *p == '.')
548 *p++ = 0;
549 name->charset = p;
550 p = strchrW( p, '@' );
552 if (p)
554 *p++ = 0;
555 name->modifier = p;
558 if (name->charset)
559 name->codepage = find_charset( name->charset );
561 /* rebuild a Windows name if possible */
563 if (name->charset) goto done; /* can't specify charset in Windows format */
564 if (name->modifier && strcmpW( name->modifier, latinW ))
565 goto done; /* only Latn script supported for now */
566 strcpyW( name->win_name, name->lang );
567 if (name->modifier) strcatW( name->win_name, latnW );
568 if (name->country)
570 p = name->win_name + strlenW(name->win_name);
571 *p++ = '-';
572 strcpyW( p, name->country );
575 done:
576 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
577 find_locale_id_callback, (LPARAM)name );
581 /***********************************************************************
582 * convert_default_lcid
584 * Get the default LCID to use for a given lctype in GetLocaleInfo.
586 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
588 if (lcid == LOCALE_SYSTEM_DEFAULT ||
589 lcid == LOCALE_USER_DEFAULT ||
590 lcid == LOCALE_NEUTRAL)
592 LCID default_id = 0;
594 switch(lctype & 0xffff)
596 case LOCALE_SSORTNAME:
597 default_id = lcid_LC_COLLATE;
598 break;
600 case LOCALE_FONTSIGNATURE:
601 case LOCALE_IDEFAULTANSICODEPAGE:
602 case LOCALE_IDEFAULTCODEPAGE:
603 case LOCALE_IDEFAULTEBCDICCODEPAGE:
604 case LOCALE_IDEFAULTMACCODEPAGE:
605 case LOCALE_IDEFAULTUNIXCODEPAGE:
606 default_id = lcid_LC_CTYPE;
607 break;
609 case LOCALE_ICURRDIGITS:
610 case LOCALE_ICURRENCY:
611 case LOCALE_IINTLCURRDIGITS:
612 case LOCALE_INEGCURR:
613 case LOCALE_INEGSEPBYSPACE:
614 case LOCALE_INEGSIGNPOSN:
615 case LOCALE_INEGSYMPRECEDES:
616 case LOCALE_IPOSSEPBYSPACE:
617 case LOCALE_IPOSSIGNPOSN:
618 case LOCALE_IPOSSYMPRECEDES:
619 case LOCALE_SCURRENCY:
620 case LOCALE_SINTLSYMBOL:
621 case LOCALE_SMONDECIMALSEP:
622 case LOCALE_SMONGROUPING:
623 case LOCALE_SMONTHOUSANDSEP:
624 case LOCALE_SNATIVECURRNAME:
625 default_id = lcid_LC_MONETARY;
626 break;
628 case LOCALE_IDIGITS:
629 case LOCALE_IDIGITSUBSTITUTION:
630 case LOCALE_ILZERO:
631 case LOCALE_INEGNUMBER:
632 case LOCALE_SDECIMAL:
633 case LOCALE_SGROUPING:
634 case LOCALE_SNAN:
635 case LOCALE_SNATIVEDIGITS:
636 case LOCALE_SNEGATIVESIGN:
637 case LOCALE_SNEGINFINITY:
638 case LOCALE_SPOSINFINITY:
639 case LOCALE_SPOSITIVESIGN:
640 case LOCALE_STHOUSAND:
641 default_id = lcid_LC_NUMERIC;
642 break;
644 case LOCALE_ICALENDARTYPE:
645 case LOCALE_ICENTURY:
646 case LOCALE_IDATE:
647 case LOCALE_IDAYLZERO:
648 case LOCALE_IFIRSTDAYOFWEEK:
649 case LOCALE_IFIRSTWEEKOFYEAR:
650 case LOCALE_ILDATE:
651 case LOCALE_IMONLZERO:
652 case LOCALE_IOPTIONALCALENDAR:
653 case LOCALE_ITIME:
654 case LOCALE_ITIMEMARKPOSN:
655 case LOCALE_ITLZERO:
656 case LOCALE_S1159:
657 case LOCALE_S2359:
658 case LOCALE_SABBREVDAYNAME1:
659 case LOCALE_SABBREVDAYNAME2:
660 case LOCALE_SABBREVDAYNAME3:
661 case LOCALE_SABBREVDAYNAME4:
662 case LOCALE_SABBREVDAYNAME5:
663 case LOCALE_SABBREVDAYNAME6:
664 case LOCALE_SABBREVDAYNAME7:
665 case LOCALE_SABBREVMONTHNAME1:
666 case LOCALE_SABBREVMONTHNAME2:
667 case LOCALE_SABBREVMONTHNAME3:
668 case LOCALE_SABBREVMONTHNAME4:
669 case LOCALE_SABBREVMONTHNAME5:
670 case LOCALE_SABBREVMONTHNAME6:
671 case LOCALE_SABBREVMONTHNAME7:
672 case LOCALE_SABBREVMONTHNAME8:
673 case LOCALE_SABBREVMONTHNAME9:
674 case LOCALE_SABBREVMONTHNAME10:
675 case LOCALE_SABBREVMONTHNAME11:
676 case LOCALE_SABBREVMONTHNAME12:
677 case LOCALE_SABBREVMONTHNAME13:
678 case LOCALE_SDATE:
679 case LOCALE_SDAYNAME1:
680 case LOCALE_SDAYNAME2:
681 case LOCALE_SDAYNAME3:
682 case LOCALE_SDAYNAME4:
683 case LOCALE_SDAYNAME5:
684 case LOCALE_SDAYNAME6:
685 case LOCALE_SDAYNAME7:
686 case LOCALE_SDURATION:
687 case LOCALE_SLONGDATE:
688 case LOCALE_SMONTHNAME1:
689 case LOCALE_SMONTHNAME2:
690 case LOCALE_SMONTHNAME3:
691 case LOCALE_SMONTHNAME4:
692 case LOCALE_SMONTHNAME5:
693 case LOCALE_SMONTHNAME6:
694 case LOCALE_SMONTHNAME7:
695 case LOCALE_SMONTHNAME8:
696 case LOCALE_SMONTHNAME9:
697 case LOCALE_SMONTHNAME10:
698 case LOCALE_SMONTHNAME11:
699 case LOCALE_SMONTHNAME12:
700 case LOCALE_SMONTHNAME13:
701 case LOCALE_SSHORTDATE:
702 case LOCALE_SSHORTESTDAYNAME1:
703 case LOCALE_SSHORTESTDAYNAME2:
704 case LOCALE_SSHORTESTDAYNAME3:
705 case LOCALE_SSHORTESTDAYNAME4:
706 case LOCALE_SSHORTESTDAYNAME5:
707 case LOCALE_SSHORTESTDAYNAME6:
708 case LOCALE_SSHORTESTDAYNAME7:
709 case LOCALE_STIME:
710 case LOCALE_STIMEFORMAT:
711 case LOCALE_SYEARMONTH:
712 default_id = lcid_LC_TIME;
713 break;
715 case LOCALE_IPAPERSIZE:
716 default_id = lcid_LC_PAPER;
717 break;
719 case LOCALE_IMEASURE:
720 default_id = lcid_LC_MEASUREMENT;
721 break;
723 case LOCALE_ICOUNTRY:
724 default_id = lcid_LC_TELEPHONE;
725 break;
727 if (default_id) lcid = default_id;
729 return ConvertDefaultLocale( lcid );
732 /***********************************************************************
733 * is_genitive_name_supported
735 * Determine could LCTYPE basically support genitive name form or not.
737 static BOOL is_genitive_name_supported( LCTYPE lctype )
739 switch(lctype & 0xffff)
741 case LOCALE_SMONTHNAME1:
742 case LOCALE_SMONTHNAME2:
743 case LOCALE_SMONTHNAME3:
744 case LOCALE_SMONTHNAME4:
745 case LOCALE_SMONTHNAME5:
746 case LOCALE_SMONTHNAME6:
747 case LOCALE_SMONTHNAME7:
748 case LOCALE_SMONTHNAME8:
749 case LOCALE_SMONTHNAME9:
750 case LOCALE_SMONTHNAME10:
751 case LOCALE_SMONTHNAME11:
752 case LOCALE_SMONTHNAME12:
753 case LOCALE_SMONTHNAME13:
754 return TRUE;
755 default:
756 return FALSE;
760 /***********************************************************************
761 * create_registry_key
763 * Create the Control Panel\\International registry key.
765 static inline HANDLE create_registry_key(void)
767 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
768 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
769 OBJECT_ATTRIBUTES attr;
770 UNICODE_STRING nameW;
771 HANDLE cpl_key, hkey = 0;
773 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
775 attr.Length = sizeof(attr);
776 attr.RootDirectory = hkey;
777 attr.ObjectName = &nameW;
778 attr.Attributes = 0;
779 attr.SecurityDescriptor = NULL;
780 attr.SecurityQualityOfService = NULL;
781 RtlInitUnicodeString( &nameW, cplW );
783 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
785 NtClose( attr.RootDirectory );
786 attr.RootDirectory = cpl_key;
787 RtlInitUnicodeString( &nameW, intlW );
788 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
790 NtClose( attr.RootDirectory );
791 return hkey;
795 /* update the registry settings for a given locale parameter */
796 /* return TRUE if an update was needed */
797 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
798 const LCTYPE *values, UINT nb_values )
800 static const WCHAR formatW[] = { '%','0','8','x',0 };
801 WCHAR bufferW[40];
802 UNICODE_STRING nameW;
803 DWORD count, i;
805 RtlInitUnicodeString( &nameW, name );
806 count = sizeof(bufferW);
807 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
809 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
810 LPCWSTR text = (LPCWSTR)info->Data;
812 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
813 TRACE( "updating registry, locale %s changed %s -> %08x\n",
814 debugstr_w(name), debugstr_w(text), lcid );
816 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
817 sprintfW( bufferW, formatW, lcid );
818 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
820 for (i = 0; i < nb_values; i++)
822 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW, ARRAY_SIZE( bufferW ));
823 SetLocaleInfoW( lcid, values[i], bufferW );
825 return TRUE;
829 /***********************************************************************
830 * LOCALE_InitRegistry
832 * Update registry contents on startup if the user locale has changed.
833 * This simulates the action of the Windows control panel.
835 void LOCALE_InitRegistry(void)
837 static const WCHAR acpW[] = {'A','C','P',0};
838 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
839 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
840 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
841 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
842 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
843 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
844 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
845 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
846 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
847 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
848 static const struct
850 LPCWSTR name;
851 USHORT value;
852 } update_cp_values[] = {
853 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
854 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
855 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
857 static const LCTYPE lc_messages_values[] = {
858 LOCALE_SABBREVLANGNAME,
859 LOCALE_SCOUNTRY,
860 LOCALE_SLIST };
861 static const LCTYPE lc_monetary_values[] = {
862 LOCALE_SCURRENCY,
863 LOCALE_ICURRENCY,
864 LOCALE_INEGCURR,
865 LOCALE_ICURRDIGITS,
866 LOCALE_ILZERO,
867 LOCALE_SMONDECIMALSEP,
868 LOCALE_SMONGROUPING,
869 LOCALE_SMONTHOUSANDSEP };
870 static const LCTYPE lc_numeric_values[] = {
871 LOCALE_SDECIMAL,
872 LOCALE_STHOUSAND,
873 LOCALE_IDIGITS,
874 LOCALE_IDIGITSUBSTITUTION,
875 LOCALE_SNATIVEDIGITS,
876 LOCALE_INEGNUMBER,
877 LOCALE_SNEGATIVESIGN,
878 LOCALE_SPOSITIVESIGN,
879 LOCALE_SGROUPING };
880 static const LCTYPE lc_time_values[] = {
881 LOCALE_S1159,
882 LOCALE_S2359,
883 LOCALE_STIME,
884 LOCALE_ITIME,
885 LOCALE_ITLZERO,
886 LOCALE_SSHORTDATE,
887 LOCALE_SLONGDATE,
888 LOCALE_SDATE,
889 LOCALE_ITIMEMARKPOSN,
890 LOCALE_ICALENDARTYPE,
891 LOCALE_IFIRSTDAYOFWEEK,
892 LOCALE_IFIRSTWEEKOFYEAR,
893 LOCALE_STIMEFORMAT,
894 LOCALE_SYEARMONTH,
895 LOCALE_IDATE };
896 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
897 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
898 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
900 UNICODE_STRING nameW;
901 WCHAR bufferW[80];
902 DWORD count, i;
903 HANDLE hkey;
904 LCID lcid = GetUserDefaultLCID();
906 if (!(hkey = create_registry_key()))
907 return; /* don't do anything if we can't create the registry key */
909 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
910 ARRAY_SIZE( lc_messages_values ));
911 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
912 ARRAY_SIZE( lc_monetary_values ));
913 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
914 ARRAY_SIZE( lc_numeric_values ));
915 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
916 ARRAY_SIZE( lc_time_values ));
917 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
918 ARRAY_SIZE( lc_measurement_values ));
919 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
920 ARRAY_SIZE( lc_telephone_values ));
921 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
922 ARRAY_SIZE( lc_paper_values ));
924 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
926 static const WCHAR codepageW[] =
927 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
928 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
929 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
931 OBJECT_ATTRIBUTES attr;
932 HANDLE nls_key;
933 DWORD len = 14;
935 RtlInitUnicodeString( &nameW, codepageW );
936 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
937 while (codepageW[len])
939 nameW.Length = len * sizeof(WCHAR);
940 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
941 NtClose( nls_key );
942 len++;
943 while (codepageW[len] && codepageW[len] != '\\') len++;
945 nameW.Length = len * sizeof(WCHAR);
946 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
948 for (i = 0; i < ARRAY_SIZE( update_cp_values ); i++)
950 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
951 bufferW, ARRAY_SIZE( bufferW ));
952 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
953 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
955 NtClose( nls_key );
959 NtClose( hkey );
963 #ifdef __APPLE__
964 /***********************************************************************
965 * get_mac_locale
967 * Return a locale identifier string reflecting the Mac locale, in a form
968 * that parse_locale_name() will understand. So, strip out unusual
969 * things like script, variant, etc. Or, rather, just construct it as
970 * <lang>[_<country>].UTF-8.
972 static const char* get_mac_locale(void)
974 static char mac_locale[50];
976 if (!mac_locale[0])
978 CFLocaleRef locale = CFLocaleCopyCurrent();
979 CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
980 CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
981 CFStringRef locale_string;
983 if (country)
984 locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
985 else
986 locale_string = CFStringCreateCopy(NULL, lang);
988 CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
989 strcat(mac_locale, ".UTF-8");
991 CFRelease(locale);
992 CFRelease(locale_string);
995 return mac_locale;
999 /***********************************************************************
1000 * has_env
1002 static BOOL has_env(const char* name)
1004 const char* value = getenv( name );
1005 return value && value[0];
1007 #endif
1010 /***********************************************************************
1011 * get_locale
1013 * Get the locale identifier for a given category. On most platforms,
1014 * this is just a thin wrapper around setlocale(). On OS X, though, it
1015 * is common for the Mac locale settings to not be supported by the C
1016 * library. So, we sometimes override the result with the Mac locale.
1018 static const char* get_locale(int category, const char* category_name)
1020 const char* ret = setlocale(category, NULL);
1022 #ifdef __ANDROID__
1023 if (!strcmp(ret, "C"))
1025 ret = getenv( category_name );
1026 if (!ret || !ret[0]) ret = getenv( "LC_ALL" );
1027 if (!ret || !ret[0]) ret = "C";
1029 #endif
1031 #ifdef __APPLE__
1032 /* If LC_ALL is set, respect it as a user override.
1033 If LC_* is set, respect it as a user override, except if it's LC_CTYPE
1034 and equal to UTF-8. That's because, when the Mac locale isn't supported
1035 by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
1036 parse_locale_name() doesn't handle that properly, so we override that
1037 with the Mac locale (which uses UTF-8 for the charset, anyway).
1038 Otherwise:
1039 For LC_MESSAGES, we override the C library because the user language
1040 setting is separate from the locale setting on which LANG was based.
1041 If the C library didn't get anything better from LANG than C or POSIX,
1042 override that. That probably means the Mac locale isn't supported by
1043 the C library. */
1044 if (!has_env( "LC_ALL" ) &&
1045 ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
1046 (!has_env( category_name ) &&
1047 (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
1049 const char* override = get_mac_locale();
1051 if (category == LC_MESSAGES)
1053 /* Retrieve the preferred language as chosen in System Preferences. */
1054 static char messages_locale[50];
1056 if (!messages_locale[0])
1058 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1059 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1061 CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1062 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1063 if (components)
1065 CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1066 CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1067 CFLocaleRef locale = NULL;
1068 CFStringRef locale_string;
1070 if (!country)
1072 locale = CFLocaleCopyCurrent();
1073 country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1076 if (country)
1077 locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1078 else
1079 locale_string = CFStringCreateCopy( NULL, lang );
1080 CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1081 strcat( messages_locale, ".UTF-8" );
1083 CFRelease( locale_string );
1084 if (locale) CFRelease( locale );
1085 CFRelease( components );
1088 if (preferred_langs)
1089 CFRelease( preferred_langs );
1092 if (messages_locale[0])
1093 override = messages_locale;
1096 TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1097 ret = override;
1099 #endif
1101 return ret;
1105 /***********************************************************************
1106 * setup_unix_locales
1108 static UINT setup_unix_locales(void)
1110 struct locale_name locale_name;
1111 WCHAR buffer[128], ctype_buff[128];
1112 const char *locale;
1113 UINT unix_cp = 0;
1115 if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1117 strcpynAtoW( ctype_buff, locale, ARRAY_SIZE( ctype_buff ));
1118 parse_locale_name( ctype_buff, &locale_name );
1119 lcid_LC_CTYPE = locale_name.lcid;
1120 unix_cp = locale_name.codepage;
1122 if (!lcid_LC_CTYPE) /* this one needs a default value */
1123 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
1125 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1126 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
1128 #define GET_UNIX_LOCALE(cat) do \
1129 if ((locale = get_locale( cat, #cat ))) \
1131 strcpynAtoW( buffer, locale, ARRAY_SIZE(buffer) ); \
1132 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1133 else { \
1134 parse_locale_name( buffer, &locale_name ); \
1135 lcid_##cat = locale_name.lcid; \
1136 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
1137 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1139 } while (0)
1141 GET_UNIX_LOCALE( LC_COLLATE );
1142 GET_UNIX_LOCALE( LC_MESSAGES );
1143 GET_UNIX_LOCALE( LC_MONETARY );
1144 GET_UNIX_LOCALE( LC_NUMERIC );
1145 GET_UNIX_LOCALE( LC_TIME );
1146 #ifdef LC_PAPER
1147 GET_UNIX_LOCALE( LC_PAPER );
1148 #endif
1149 #ifdef LC_MEASUREMENT
1150 GET_UNIX_LOCALE( LC_MEASUREMENT );
1151 #endif
1152 #ifdef LC_TELEPHONE
1153 GET_UNIX_LOCALE( LC_TELEPHONE );
1154 #endif
1156 #undef GET_UNIX_LOCALE
1158 return unix_cp;
1162 /***********************************************************************
1163 * GetUserDefaultLangID (KERNEL32.@)
1165 * Get the default language Id for the current user.
1167 * PARAMS
1168 * None.
1170 * RETURNS
1171 * The current LANGID of the default language for the current user.
1173 LANGID WINAPI GetUserDefaultLangID(void)
1175 return LANGIDFROMLCID(GetUserDefaultLCID());
1179 /***********************************************************************
1180 * GetSystemDefaultLangID (KERNEL32.@)
1182 * Get the default language Id for the system.
1184 * PARAMS
1185 * None.
1187 * RETURNS
1188 * The current LANGID of the default language for the system.
1190 LANGID WINAPI GetSystemDefaultLangID(void)
1192 return LANGIDFROMLCID(GetSystemDefaultLCID());
1196 /***********************************************************************
1197 * GetUserDefaultLCID (KERNEL32.@)
1199 * Get the default locale Id for the current user.
1201 * PARAMS
1202 * None.
1204 * RETURNS
1205 * The current LCID of the default locale for the current user.
1207 LCID WINAPI GetUserDefaultLCID(void)
1209 LCID lcid;
1210 NtQueryDefaultLocale( TRUE, &lcid );
1211 return lcid;
1215 /***********************************************************************
1216 * GetSystemDefaultLCID (KERNEL32.@)
1218 * Get the default locale Id for the system.
1220 * PARAMS
1221 * None.
1223 * RETURNS
1224 * The current LCID of the default locale for the system.
1226 LCID WINAPI GetSystemDefaultLCID(void)
1228 LCID lcid;
1229 NtQueryDefaultLocale( FALSE, &lcid );
1230 return lcid;
1233 /***********************************************************************
1234 * GetSystemDefaultLocaleName (KERNEL32.@)
1236 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1238 LCID lcid = GetSystemDefaultLCID();
1239 return LCIDToLocaleName(lcid, localename, len, 0);
1242 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1244 LCTYPE type;
1245 int lsize;
1247 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1249 if (flags & MUI_LANGUAGE_ID)
1250 type = LOCALE_ILANGUAGE;
1251 else
1252 type = LOCALE_SNAME;
1254 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1255 if (!lsize)
1257 /* keep last error from callee */
1258 return FALSE;
1260 lsize++;
1261 if (!*size)
1263 *size = lsize;
1264 *count = 1;
1265 return TRUE;
1268 if (lsize > *size)
1270 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1271 return FALSE;
1274 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1276 /* keep last error from callee */
1277 return FALSE;
1280 buffer[lsize-1] = 0;
1281 *size = lsize;
1282 *count = 1;
1283 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1284 return TRUE;
1288 /***********************************************************************
1289 * GetProcessPreferredUILanguages (KERNEL32.@)
1291 BOOL WINAPI GetProcessPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1293 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1294 return get_dummy_preferred_ui_language( flags, count, buf, size );
1297 /***********************************************************************
1298 * GetSystemPreferredUILanguages (KERNEL32.@)
1300 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1302 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1304 SetLastError(ERROR_INVALID_PARAMETER);
1305 return FALSE;
1307 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1309 SetLastError(ERROR_INVALID_PARAMETER);
1310 return FALSE;
1312 if (*size && !buffer)
1314 SetLastError(ERROR_INVALID_PARAMETER);
1315 return FALSE;
1318 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1321 /***********************************************************************
1322 * SetProcessPreferredUILanguages (KERNEL32.@)
1324 BOOL WINAPI SetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1326 FIXME("%u, %p, %p\n", flags, buffer, count );
1327 return TRUE;
1330 /***********************************************************************
1331 * SetThreadPreferredUILanguages (KERNEL32.@)
1333 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1335 FIXME( "%u, %p, %p\n", flags, buffer, count );
1336 return TRUE;
1339 /***********************************************************************
1340 * GetThreadPreferredUILanguages (KERNEL32.@)
1342 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1344 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1345 return get_dummy_preferred_ui_language( flags, count, buf, size );
1348 /******************************************************************************
1349 * GetUserPreferredUILanguages (KERNEL32.@)
1351 BOOL WINAPI GetUserPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1353 TRACE( "%u %p %p %p\n", flags, count, buffer, size );
1355 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID))
1357 SetLastError(ERROR_INVALID_PARAMETER);
1358 return FALSE;
1360 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1362 SetLastError(ERROR_INVALID_PARAMETER);
1363 return FALSE;
1365 if (*size && !buffer)
1367 SetLastError(ERROR_INVALID_PARAMETER);
1368 return FALSE;
1371 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1374 /***********************************************************************
1375 * GetUserDefaultUILanguage (KERNEL32.@)
1377 * Get the default user interface language Id for the current user.
1379 * PARAMS
1380 * None.
1382 * RETURNS
1383 * The current LANGID of the default UI language for the current user.
1385 LANGID WINAPI GetUserDefaultUILanguage(void)
1387 LANGID lang;
1388 NtQueryDefaultUILanguage( &lang );
1389 return lang;
1393 /***********************************************************************
1394 * GetSystemDefaultUILanguage (KERNEL32.@)
1396 * Get the default user interface language Id for the system.
1398 * PARAMS
1399 * None.
1401 * RETURNS
1402 * The current LANGID of the default UI language for the system. This is
1403 * typically the same language used during the installation process.
1405 LANGID WINAPI GetSystemDefaultUILanguage(void)
1407 LANGID lang;
1408 NtQueryInstallUILanguage( &lang );
1409 return lang;
1413 /***********************************************************************
1414 * LocaleNameToLCID (KERNEL32.@)
1416 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1418 struct locale_name locale_name;
1419 static int once;
1421 if (flags && !once++)
1422 FIXME( "unsupported flags %x\n", flags );
1424 if (name == LOCALE_NAME_USER_DEFAULT)
1425 return GetUserDefaultLCID();
1427 /* string parsing */
1428 parse_locale_name( name, &locale_name );
1430 TRACE( "found lcid %x for %s, matches %d\n",
1431 locale_name.lcid, debugstr_w(name), locale_name.matches );
1433 if (!locale_name.matches)
1435 SetLastError(ERROR_INVALID_PARAMETER);
1436 return 0;
1439 if (locale_name.matches == 1)
1440 WARN( "locale %s not recognized, defaulting to %s\n",
1441 debugstr_w(name), debugstr_w(locale_name.lang) );
1443 return locale_name.lcid;
1447 /***********************************************************************
1448 * LCIDToLocaleName (KERNEL32.@)
1450 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1452 static int once;
1453 if (flags && !once++) FIXME( "unsupported flags %x\n", flags );
1455 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1459 /******************************************************************************
1460 * get_locale_registry_value
1462 * Gets the registry value name and cache for a given lctype.
1464 static struct registry_value *get_locale_registry_value( DWORD lctype )
1466 int i;
1467 for (i = 0; i < ARRAY_SIZE( registry_values ); i++)
1468 if (registry_values[i].lctype == lctype)
1469 return &registry_values[i];
1470 return NULL;
1474 /******************************************************************************
1475 * get_registry_locale_info
1477 * Retrieve user-modified locale info from the registry.
1478 * Return length, 0 on error, -1 if not found.
1480 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1482 DWORD size;
1483 INT ret;
1484 HANDLE hkey;
1485 NTSTATUS status;
1486 UNICODE_STRING nameW;
1487 KEY_VALUE_PARTIAL_INFORMATION *info;
1488 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1490 RtlEnterCriticalSection( &cache_section );
1492 if (!registry_value->cached_value)
1494 if (!(hkey = create_registry_key()))
1496 RtlLeaveCriticalSection( &cache_section );
1497 return -1;
1500 RtlInitUnicodeString( &nameW, registry_value->name );
1501 size = info_size + len * sizeof(WCHAR);
1503 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1505 NtClose( hkey );
1506 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1507 RtlLeaveCriticalSection( &cache_section );
1508 return 0;
1511 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1513 /* try again with a bigger buffer when we have to return the correct size */
1514 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1516 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1517 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1519 info = new_info;
1520 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1524 NtClose( hkey );
1526 if (!status)
1528 INT length = (size - info_size) / sizeof(WCHAR);
1529 LPWSTR cached_value;
1531 if (!length || ((WCHAR *)&info->Data)[length-1])
1532 length++;
1534 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1536 if (!cached_value)
1538 HeapFree( GetProcessHeap(), 0, info );
1539 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1540 RtlLeaveCriticalSection( &cache_section );
1541 return 0;
1544 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1545 cached_value[length-1] = 0;
1546 HeapFree( GetProcessHeap(), 0, info );
1547 registry_value->cached_value = cached_value;
1549 else
1551 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1553 ret = (size - info_size) / sizeof(WCHAR);
1555 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1557 ret = -1;
1559 else
1561 SetLastError( RtlNtStatusToDosError(status) );
1562 ret = 0;
1564 HeapFree( GetProcessHeap(), 0, info );
1565 RtlLeaveCriticalSection( &cache_section );
1566 return ret;
1570 ret = lstrlenW( registry_value->cached_value ) + 1;
1572 if (buffer)
1574 if (ret > len)
1576 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1577 ret = 0;
1579 else
1581 lstrcpyW( buffer, registry_value->cached_value );
1585 RtlLeaveCriticalSection( &cache_section );
1587 return ret;
1591 /******************************************************************************
1592 * GetLocaleInfoA (KERNEL32.@)
1594 * Get information about an aspect of a locale.
1596 * PARAMS
1597 * lcid [I] LCID of the locale
1598 * lctype [I] LCTYPE_ flags from "winnls.h"
1599 * buffer [O] Destination for the information
1600 * len [I] Length of buffer in characters
1602 * RETURNS
1603 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1604 * with the information.
1605 * Failure: 0. Use GetLastError() to determine the cause.
1607 * NOTES
1608 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1609 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1610 * which is a bit string.
1612 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1614 WCHAR *bufferW;
1615 INT lenW, ret;
1617 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1619 if (len < 0 || (len && !buffer))
1621 SetLastError( ERROR_INVALID_PARAMETER );
1622 return 0;
1624 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1625 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1627 SetLastError( ERROR_INVALID_FLAGS );
1628 return 0;
1631 if (!len) buffer = NULL;
1633 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1635 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1637 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1638 return 0;
1640 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1642 if ((lctype & LOCALE_RETURN_NUMBER) ||
1643 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1645 /* it's not an ASCII string, just bytes */
1646 ret *= sizeof(WCHAR);
1647 if (buffer)
1649 if (ret <= len) memcpy( buffer, bufferW, ret );
1650 else
1652 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1653 ret = 0;
1657 else
1659 UINT codepage = CP_ACP;
1660 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1661 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1664 HeapFree( GetProcessHeap(), 0, bufferW );
1665 return ret;
1668 static int get_value_base_by_lctype( LCTYPE lctype )
1670 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1673 /******************************************************************************
1674 * GetLocaleInfoW (KERNEL32.@)
1676 * See GetLocaleInfoA.
1678 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1680 LANGID lang_id;
1681 HRSRC hrsrc;
1682 HGLOBAL hmem;
1683 INT ret;
1684 UINT lcflags;
1685 const WCHAR *p;
1686 unsigned int i;
1688 if (len < 0 || (len && !buffer))
1690 SetLastError( ERROR_INVALID_PARAMETER );
1691 return 0;
1693 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1694 !is_genitive_name_supported( lctype ))
1696 SetLastError( ERROR_INVALID_FLAGS );
1697 return 0;
1700 if (!len) buffer = NULL;
1702 lcid = convert_default_lcid( lcid, lctype );
1704 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1705 lctype &= 0xffff;
1707 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1709 /* first check for overrides in the registry */
1711 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1712 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1714 struct registry_value *value = get_locale_registry_value(lctype);
1716 if (value)
1718 if (lcflags & LOCALE_RETURN_NUMBER)
1720 WCHAR tmp[16];
1721 ret = get_registry_locale_info( value, tmp, ARRAY_SIZE( tmp ));
1722 if (ret > 0)
1724 WCHAR *end;
1725 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1726 if (*end) /* invalid number */
1728 SetLastError( ERROR_INVALID_FLAGS );
1729 return 0;
1731 ret = sizeof(UINT)/sizeof(WCHAR);
1732 if (!buffer) return ret;
1733 if (ret > len)
1735 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1736 return 0;
1738 memcpy( buffer, &number, sizeof(number) );
1741 else ret = get_registry_locale_info( value, buffer, len );
1743 if (ret != -1) return ret;
1747 /* now load it from kernel resources */
1749 lang_id = LANGIDFROMLCID( lcid );
1751 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1752 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL) lang_id = get_default_sublang( lang_id );
1754 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1755 ULongToPtr((lctype >> 4) + 1), lang_id )))
1757 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1758 return 0;
1760 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1761 return 0;
1763 p = LockResource( hmem );
1764 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1766 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1767 else if (is_genitive_name_supported( lctype ) && *p)
1769 /* genitive form's stored after a null separator from a nominative */
1770 for (i = 1; i <= *p; i++) if (!p[i]) break;
1772 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1774 ret = *p - i + 1;
1775 p += i;
1777 else ret = i;
1779 else
1780 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1782 if (!buffer) return ret;
1784 if (ret > len)
1786 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1787 return 0;
1790 if (lcflags & LOCALE_RETURN_NUMBER)
1792 UINT number;
1793 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1794 if (!tmp) return 0;
1795 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1796 tmp[*p] = 0;
1797 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1798 if (!*end)
1799 memcpy( buffer, &number, sizeof(number) );
1800 else /* invalid number */
1802 SetLastError( ERROR_INVALID_FLAGS );
1803 ret = 0;
1805 HeapFree( GetProcessHeap(), 0, tmp );
1807 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1808 lcid, lctype, buffer, len, number );
1810 else
1812 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1813 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1815 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1816 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1818 return ret;
1821 /******************************************************************************
1822 * GetLocaleInfoEx (KERNEL32.@)
1824 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1826 LCID lcid = LocaleNameToLCID(locale, 0);
1828 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1830 if (!lcid) return 0;
1832 /* special handling for neutral locale names */
1833 if (locale && strlenW(locale) == 2)
1835 switch (info & ~LOCALE_LOCALEINFOFLAGSMASK)
1837 case LOCALE_SNAME:
1838 if (len && len < 3)
1840 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1841 return 0;
1843 if (len) strcpyW(buffer, locale);
1844 return 3;
1845 case LOCALE_SPARENT:
1846 if (len) buffer[0] = 0;
1847 return 1;
1851 return GetLocaleInfoW(lcid, info, buffer, len);
1854 /******************************************************************************
1855 * SetLocaleInfoA [KERNEL32.@]
1857 * Set information about an aspect of a locale.
1859 * PARAMS
1860 * lcid [I] LCID of the locale
1861 * lctype [I] LCTYPE_ flags from "winnls.h"
1862 * data [I] Information to set
1864 * RETURNS
1865 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1866 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1867 * Failure: FALSE. Use GetLastError() to determine the cause.
1869 * NOTES
1870 * - Values are only be set for the current user locale; the system locale
1871 * settings cannot be changed.
1872 * - Any settings changed by this call are lost when the locale is changed by
1873 * the control panel (in Wine, this happens every time you change LANG).
1874 * - The native implementation of this function does not check that lcid matches
1875 * the current user locale, and simply sets the new values. Wine warns you in
1876 * this case, but behaves the same.
1878 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1880 UINT codepage = CP_ACP;
1881 WCHAR *strW;
1882 DWORD len;
1883 BOOL ret;
1885 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1887 if (!data)
1889 SetLastError( ERROR_INVALID_PARAMETER );
1890 return FALSE;
1892 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1893 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1895 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1896 return FALSE;
1898 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1899 ret = SetLocaleInfoW( lcid, lctype, strW );
1900 HeapFree( GetProcessHeap(), 0, strW );
1901 return ret;
1905 /******************************************************************************
1906 * SetLocaleInfoW (KERNEL32.@)
1908 * See SetLocaleInfoA.
1910 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1912 struct registry_value *value;
1913 static const WCHAR intlW[] = {'i','n','t','l',0 };
1914 UNICODE_STRING valueW;
1915 NTSTATUS status;
1916 HANDLE hkey;
1918 lctype &= 0xffff;
1919 value = get_locale_registry_value( lctype );
1921 if (!data || !value)
1923 SetLastError( ERROR_INVALID_PARAMETER );
1924 return FALSE;
1927 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1929 SetLastError( ERROR_INVALID_FLAGS );
1930 return FALSE;
1933 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1935 /* FIXME: should check that data to set is sane */
1937 /* FIXME: profile functions should map to registry */
1938 WriteProfileStringW( intlW, value->name, data );
1940 if (!(hkey = create_registry_key())) return FALSE;
1941 RtlInitUnicodeString( &valueW, value->name );
1942 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1944 RtlEnterCriticalSection( &cache_section );
1945 HeapFree( GetProcessHeap(), 0, value->cached_value );
1946 value->cached_value = NULL;
1947 RtlLeaveCriticalSection( &cache_section );
1949 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1951 /* Set I-value from S value */
1952 WCHAR *lpD, *lpM, *lpY;
1953 WCHAR szBuff[2];
1955 lpD = strrchrW(data, 'd');
1956 lpM = strrchrW(data, 'M');
1957 lpY = strrchrW(data, 'y');
1959 if (lpD <= lpM)
1961 szBuff[0] = '1'; /* D-M-Y */
1963 else
1965 if (lpY <= lpM)
1966 szBuff[0] = '2'; /* Y-M-D */
1967 else
1968 szBuff[0] = '0'; /* M-D-Y */
1971 szBuff[1] = '\0';
1973 if (lctype == LOCALE_SSHORTDATE)
1974 lctype = LOCALE_IDATE;
1975 else
1976 lctype = LOCALE_ILDATE;
1978 value = get_locale_registry_value( lctype );
1980 WriteProfileStringW( intlW, value->name, szBuff );
1982 RtlInitUnicodeString( &valueW, value->name );
1983 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1985 RtlEnterCriticalSection( &cache_section );
1986 HeapFree( GetProcessHeap(), 0, value->cached_value );
1987 value->cached_value = NULL;
1988 RtlLeaveCriticalSection( &cache_section );
1991 NtClose( hkey );
1993 if (status) SetLastError( RtlNtStatusToDosError(status) );
1994 return !status;
1998 /******************************************************************************
1999 * GetACP (KERNEL32.@)
2001 * Get the current Ansi code page Id for the system.
2003 * PARAMS
2004 * None.
2006 * RETURNS
2007 * The current Ansi code page identifier for the system.
2009 UINT WINAPI GetACP(void)
2011 assert( ansi_cptable );
2012 return ansi_cptable->info.codepage;
2016 /******************************************************************************
2017 * SetCPGlobal (KERNEL32.@)
2019 * Set the current Ansi code page Id for the system.
2021 * PARAMS
2022 * acp [I] code page ID to be the new ACP.
2024 * RETURNS
2025 * The previous ACP.
2027 UINT WINAPI SetCPGlobal( UINT acp )
2029 UINT ret = GetACP();
2030 const union cptable *new_cptable = wine_cp_get_table( acp );
2032 if (new_cptable) ansi_cptable = new_cptable;
2033 return ret;
2037 /***********************************************************************
2038 * GetOEMCP (KERNEL32.@)
2040 * Get the current OEM code page Id for the system.
2042 * PARAMS
2043 * None.
2045 * RETURNS
2046 * The current OEM code page identifier for the system.
2048 UINT WINAPI GetOEMCP(void)
2050 assert( oem_cptable );
2051 return oem_cptable->info.codepage;
2055 /***********************************************************************
2056 * IsValidCodePage (KERNEL32.@)
2058 * Determine if a given code page identifier is valid.
2060 * PARAMS
2061 * codepage [I] Code page Id to verify.
2063 * RETURNS
2064 * TRUE, If codepage is valid and available on the system,
2065 * FALSE otherwise.
2067 BOOL WINAPI IsValidCodePage( UINT codepage )
2069 switch(codepage) {
2070 case CP_UTF7:
2071 case CP_UTF8:
2072 return TRUE;
2073 default:
2074 return wine_cp_get_table( codepage ) != NULL;
2079 /***********************************************************************
2080 * IsDBCSLeadByteEx (KERNEL32.@)
2082 * Determine if a character is a lead byte in a given code page.
2084 * PARAMS
2085 * codepage [I] Code page for the test.
2086 * testchar [I] Character to test
2088 * RETURNS
2089 * TRUE, if testchar is a lead byte in codepage,
2090 * FALSE otherwise.
2092 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
2094 const union cptable *table = get_codepage_table( codepage );
2095 return table && wine_is_dbcs_leadbyte( table, testchar );
2099 /***********************************************************************
2100 * IsDBCSLeadByte (KERNEL32.@)
2101 * IsDBCSLeadByte (KERNEL.207)
2103 * Determine if a character is a lead byte.
2105 * PARAMS
2106 * testchar [I] Character to test
2108 * RETURNS
2109 * TRUE, if testchar is a lead byte in the ANSI code page,
2110 * FALSE otherwise.
2112 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2114 if (!ansi_cptable) return FALSE;
2115 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2119 /***********************************************************************
2120 * GetCPInfo (KERNEL32.@)
2122 * Get information about a code page.
2124 * PARAMS
2125 * codepage [I] Code page number
2126 * cpinfo [O] Destination for code page information
2128 * RETURNS
2129 * Success: TRUE. cpinfo is updated with the information about codepage.
2130 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2132 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2134 const union cptable *table;
2136 if (!cpinfo)
2138 SetLastError( ERROR_INVALID_PARAMETER );
2139 return FALSE;
2142 if (!(table = get_codepage_table( codepage )))
2144 switch(codepage)
2146 case CP_UTF7:
2147 case CP_UTF8:
2148 cpinfo->DefaultChar[0] = 0x3f;
2149 cpinfo->DefaultChar[1] = 0;
2150 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2151 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2152 return TRUE;
2155 SetLastError( ERROR_INVALID_PARAMETER );
2156 return FALSE;
2158 if (table->info.def_char & 0xff00)
2160 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2161 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2163 else
2165 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2166 cpinfo->DefaultChar[1] = 0;
2168 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2169 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2170 else
2171 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2173 return TRUE;
2176 /***********************************************************************
2177 * GetCPInfoExA (KERNEL32.@)
2179 * Get extended information about a code page.
2181 * PARAMS
2182 * codepage [I] Code page number
2183 * dwFlags [I] Reserved, must to 0.
2184 * cpinfo [O] Destination for code page information
2186 * RETURNS
2187 * Success: TRUE. cpinfo is updated with the information about codepage.
2188 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2190 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2192 CPINFOEXW cpinfoW;
2194 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2195 return FALSE;
2197 /* the layout is the same except for CodePageName */
2198 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2199 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2200 return TRUE;
2203 /***********************************************************************
2204 * GetCPInfoExW (KERNEL32.@)
2206 * Unicode version of GetCPInfoExA.
2208 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2210 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2211 return FALSE;
2213 switch(codepage)
2215 case CP_UTF7:
2217 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2219 cpinfo->CodePage = CP_UTF7;
2220 cpinfo->UnicodeDefaultChar = 0x3f;
2221 strcpyW(cpinfo->CodePageName, utf7);
2222 break;
2225 case CP_UTF8:
2227 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2229 cpinfo->CodePage = CP_UTF8;
2230 cpinfo->UnicodeDefaultChar = 0x3f;
2231 strcpyW(cpinfo->CodePageName, utf8);
2232 break;
2235 default:
2237 const union cptable *table = get_codepage_table( codepage );
2239 cpinfo->CodePage = table->info.codepage;
2240 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2241 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2242 ARRAY_SIZE( cpinfo->CodePageName ));
2243 break;
2246 return TRUE;
2249 /***********************************************************************
2250 * EnumSystemCodePagesA (KERNEL32.@)
2252 * Call a user defined function for every code page installed on the system.
2254 * PARAMS
2255 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2256 * flags [I] Reserved, set to 0.
2258 * RETURNS
2259 * TRUE, If all code pages have been enumerated, or
2260 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2262 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2264 const union cptable *table;
2265 char buffer[10];
2266 int index = 0;
2268 for (;;)
2270 if (!(table = wine_cp_enum_table( index++ ))) break;
2271 sprintf( buffer, "%d", table->info.codepage );
2272 if (!lpfnCodePageEnum( buffer )) break;
2274 return TRUE;
2278 /***********************************************************************
2279 * EnumSystemCodePagesW (KERNEL32.@)
2281 * See EnumSystemCodePagesA.
2283 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2285 const union cptable *table;
2286 WCHAR buffer[10], *p;
2287 int page, index = 0;
2289 for (;;)
2291 if (!(table = wine_cp_enum_table( index++ ))) break;
2292 p = buffer + ARRAY_SIZE( buffer );
2293 *--p = 0;
2294 page = table->info.codepage;
2297 *--p = '0' + (page % 10);
2298 page /= 10;
2299 } while( page );
2300 if (!lpfnCodePageEnum( p )) break;
2302 return TRUE;
2306 /***********************************************************************
2307 * utf7_write_w
2309 * Helper for utf7_mbstowcs
2311 * RETURNS
2312 * TRUE on success, FALSE on error
2314 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2316 if (dstlen > 0)
2318 if (*index >= dstlen)
2319 return FALSE;
2321 dst[*index] = character;
2324 (*index)++;
2326 return TRUE;
2329 /***********************************************************************
2330 * utf7_mbstowcs
2332 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2334 * RETURNS
2335 * On success, the number of characters written
2336 * On dst buffer overflow, -1
2338 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2340 static const signed char base64_decoding_table[] =
2342 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2343 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2344 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2345 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2346 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2347 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2348 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2349 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2352 const char *source_end = src + srclen;
2353 int dest_index = 0;
2355 DWORD byte_pair = 0;
2356 short offset = 0;
2358 while (src < source_end)
2360 if (*src == '+')
2362 src++;
2363 if (src >= source_end)
2364 break;
2366 if (*src == '-')
2368 /* just a plus sign escaped as +- */
2369 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2370 return -1;
2371 src++;
2372 continue;
2377 signed char sextet = *src;
2378 if (sextet == '-')
2380 /* skip over the dash and end base64 decoding
2381 * the current, unfinished byte pair is discarded */
2382 src++;
2383 offset = 0;
2384 break;
2386 if (sextet < 0)
2388 /* the next character of src is < 0 and therefore not part of a base64 sequence
2389 * the current, unfinished byte pair is NOT discarded in this case
2390 * this is probably a bug in Windows */
2391 break;
2394 sextet = base64_decoding_table[sextet];
2395 if (sextet == -1)
2397 /* -1 means that the next character of src is not part of a base64 sequence
2398 * in other words, all sextets in this base64 sequence have been processed
2399 * the current, unfinished byte pair is discarded */
2400 offset = 0;
2401 break;
2404 byte_pair = (byte_pair << 6) | sextet;
2405 offset += 6;
2407 if (offset >= 16)
2409 /* this byte pair is done */
2410 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2411 return -1;
2412 offset -= 16;
2415 src++;
2417 while (src < source_end);
2419 else
2421 /* we have to convert to unsigned char in case *src < 0 */
2422 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2423 return -1;
2424 src++;
2428 return dest_index;
2431 /***********************************************************************
2432 * MultiByteToWideChar (KERNEL32.@)
2434 * Convert a multibyte character string into a Unicode string.
2436 * PARAMS
2437 * page [I] Codepage character set to convert from
2438 * flags [I] Character mapping flags
2439 * src [I] Source string buffer
2440 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2441 * dst [O] Destination buffer
2442 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2444 * RETURNS
2445 * Success: If dstlen > 0, the number of characters written to dst.
2446 * If dstlen == 0, the number of characters needed to perform the
2447 * conversion. In both cases the count includes the terminating NUL.
2448 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2449 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2450 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2451 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2452 * possible for src.
2454 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2455 LPWSTR dst, INT dstlen )
2457 const union cptable *table;
2458 int ret;
2460 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2462 SetLastError( ERROR_INVALID_PARAMETER );
2463 return 0;
2466 if (srclen < 0) srclen = strlen(src) + 1;
2468 switch(page)
2470 case CP_SYMBOL:
2471 if (flags)
2473 SetLastError( ERROR_INVALID_FLAGS );
2474 return 0;
2476 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2477 break;
2478 case CP_UTF7:
2479 if (flags)
2481 SetLastError( ERROR_INVALID_FLAGS );
2482 return 0;
2484 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2485 break;
2486 case CP_UNIXCP:
2487 if (unix_cptable)
2489 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2490 break;
2492 #ifdef __APPLE__
2493 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2494 #endif
2495 /* fall through */
2496 case CP_UTF8:
2497 if (flags & ~MB_FLAGSMASK)
2499 SetLastError( ERROR_INVALID_FLAGS );
2500 return 0;
2502 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2503 break;
2504 default:
2505 if (!(table = get_codepage_table( page )))
2507 SetLastError( ERROR_INVALID_PARAMETER );
2508 return 0;
2510 if (flags & ~MB_FLAGSMASK)
2512 SetLastError( ERROR_INVALID_FLAGS );
2513 return 0;
2515 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2516 break;
2519 if (ret < 0)
2521 switch(ret)
2523 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2524 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2526 ret = 0;
2528 TRACE("cp %d %s -> %s, ret = %d\n",
2529 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2530 return ret;
2534 /***********************************************************************
2535 * utf7_can_directly_encode
2537 * Helper for utf7_wcstombs
2539 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2541 static const BOOL directly_encodable_table[] =
2543 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2544 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2545 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2546 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2547 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2548 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2549 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2550 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2553 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2556 /***********************************************************************
2557 * utf7_write_c
2559 * Helper for utf7_wcstombs
2561 * RETURNS
2562 * TRUE on success, FALSE on error
2564 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2566 if (dstlen > 0)
2568 if (*index >= dstlen)
2569 return FALSE;
2571 dst[*index] = character;
2574 (*index)++;
2576 return TRUE;
2579 /***********************************************************************
2580 * utf7_wcstombs
2582 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2584 * RETURNS
2585 * On success, the number of characters written
2586 * On dst buffer overflow, -1
2588 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2590 static const char base64_encoding_table[] =
2591 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2593 const WCHAR *source_end = src + srclen;
2594 int dest_index = 0;
2596 while (src < source_end)
2598 if (*src == '+')
2600 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2601 return -1;
2602 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2603 return -1;
2604 src++;
2606 else if (utf7_can_directly_encode(*src))
2608 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2609 return -1;
2610 src++;
2612 else
2614 unsigned int offset = 0;
2615 DWORD byte_pair = 0;
2617 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2618 return -1;
2620 while (src < source_end && !utf7_can_directly_encode(*src))
2622 byte_pair = (byte_pair << 16) | *src;
2623 offset += 16;
2624 while (offset >= 6)
2626 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2627 return -1;
2628 offset -= 6;
2630 src++;
2633 if (offset)
2635 /* Windows won't create a padded base64 character if there's no room for the - sign
2636 * as well ; this is probably a bug in Windows */
2637 if (dstlen > 0 && dest_index + 1 >= dstlen)
2638 return -1;
2640 byte_pair <<= (6 - offset);
2641 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2642 return -1;
2645 /* Windows always explicitly terminates the base64 sequence
2646 even though RFC 2152 (page 3, rule 2) does not require this */
2647 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2648 return -1;
2652 return dest_index;
2655 /***********************************************************************
2656 * WideCharToMultiByte (KERNEL32.@)
2658 * Convert a Unicode character string into a multibyte string.
2660 * PARAMS
2661 * page [I] Code page character set to convert to
2662 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2663 * src [I] Source string buffer
2664 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2665 * dst [O] Destination buffer
2666 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2667 * defchar [I] Default character to use for conversion if no exact
2668 * conversion can be made
2669 * used [O] Set if default character was used in the conversion
2671 * RETURNS
2672 * Success: If dstlen > 0, the number of characters written to dst.
2673 * If dstlen == 0, number of characters needed to perform the
2674 * conversion. In both cases the count includes the terminating NUL.
2675 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2676 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2677 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2678 * parameter was given.
2680 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2681 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2683 const union cptable *table;
2684 int ret, used_tmp;
2686 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2688 SetLastError( ERROR_INVALID_PARAMETER );
2689 return 0;
2692 if (srclen < 0) srclen = strlenW(src) + 1;
2694 switch(page)
2696 case CP_SYMBOL:
2697 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2698 if (flags)
2700 SetLastError( ERROR_INVALID_FLAGS );
2701 return 0;
2703 if (defchar || used)
2705 SetLastError( ERROR_INVALID_PARAMETER );
2706 return 0;
2708 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2709 break;
2710 case CP_UTF7:
2711 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2712 if (defchar || used)
2714 SetLastError( ERROR_INVALID_PARAMETER );
2715 return 0;
2717 if (flags)
2719 SetLastError( ERROR_INVALID_FLAGS );
2720 return 0;
2722 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2723 break;
2724 case CP_UNIXCP:
2725 if (unix_cptable)
2727 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2728 defchar, used ? &used_tmp : NULL );
2729 break;
2731 /* fall through */
2732 case CP_UTF8:
2733 if (defchar || used)
2735 SetLastError( ERROR_INVALID_PARAMETER );
2736 return 0;
2738 if (flags & ~WC_FLAGSMASK)
2740 SetLastError( ERROR_INVALID_FLAGS );
2741 return 0;
2743 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2744 break;
2745 default:
2746 if (!(table = get_codepage_table( page )))
2748 SetLastError( ERROR_INVALID_PARAMETER );
2749 return 0;
2751 if (flags & ~WC_FLAGSMASK)
2753 SetLastError( ERROR_INVALID_FLAGS );
2754 return 0;
2756 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2757 defchar, used ? &used_tmp : NULL );
2758 if (used) *used = used_tmp;
2759 break;
2762 if (ret < 0)
2764 switch(ret)
2766 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2767 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2769 ret = 0;
2771 TRACE("cp %d %s -> %s, ret = %d\n",
2772 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2773 return ret;
2777 /***********************************************************************
2778 * GetThreadLocale (KERNEL32.@)
2780 * Get the current threads locale.
2782 * PARAMS
2783 * None.
2785 * RETURNS
2786 * The LCID currently associated with the calling thread.
2788 LCID WINAPI GetThreadLocale(void)
2790 LCID ret = NtCurrentTeb()->CurrentLocale;
2791 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2792 return ret;
2795 /**********************************************************************
2796 * SetThreadLocale (KERNEL32.@)
2798 * Set the current threads locale.
2800 * PARAMS
2801 * lcid [I] LCID of the locale to set
2803 * RETURNS
2804 * Success: TRUE. The threads locale is set to lcid.
2805 * Failure: FALSE. Use GetLastError() to determine the cause.
2807 BOOL WINAPI SetThreadLocale( LCID lcid )
2809 TRACE("(0x%04X)\n", lcid);
2811 lcid = ConvertDefaultLocale(lcid);
2813 if (lcid != GetThreadLocale())
2815 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2817 SetLastError(ERROR_INVALID_PARAMETER);
2818 return FALSE;
2821 NtCurrentTeb()->CurrentLocale = lcid;
2823 return TRUE;
2826 /**********************************************************************
2827 * SetThreadUILanguage (KERNEL32.@)
2829 * Set the current threads UI language.
2831 * PARAMS
2832 * langid [I] LANGID of the language to set, or 0 to use
2833 * the available language which is best supported
2834 * for console applications
2836 * RETURNS
2837 * Success: The return value is the same as the input value.
2838 * Failure: The return value differs from the input value.
2839 * Use GetLastError() to determine the cause.
2841 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2843 TRACE("(0x%04x) stub - returning success\n", langid);
2845 if (!langid)
2846 return GetThreadUILanguage();
2847 else
2848 return langid;
2851 /******************************************************************************
2852 * ConvertDefaultLocale (KERNEL32.@)
2854 * Convert a default locale identifier into a real identifier.
2856 * PARAMS
2857 * lcid [I] LCID identifier of the locale to convert
2859 * RETURNS
2860 * lcid unchanged, if not a default locale or its sublanguage is
2861 * not SUBLANG_NEUTRAL.
2862 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2863 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2864 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2866 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2868 LANGID langid;
2870 switch (lcid)
2872 case LOCALE_INVARIANT:
2873 /* keep as-is */
2874 break;
2875 case LOCALE_SYSTEM_DEFAULT:
2876 lcid = GetSystemDefaultLCID();
2877 break;
2878 case LOCALE_USER_DEFAULT:
2879 case LOCALE_NEUTRAL:
2880 lcid = GetUserDefaultLCID();
2881 break;
2882 default:
2883 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2884 langid = LANGIDFROMLCID(lcid);
2885 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2887 langid = get_default_sublang( langid );
2888 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2891 return lcid;
2895 /******************************************************************************
2896 * IsValidLocale (KERNEL32.@)
2898 * Determine if a locale is valid.
2900 * PARAMS
2901 * lcid [I] LCID of the locale to check
2902 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2904 * RETURNS
2905 * TRUE, if lcid is valid,
2906 * FALSE, otherwise.
2908 * NOTES
2909 * Wine does not currently make the distinction between supported and installed. All
2910 * languages supported are installed by default.
2912 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2914 /* check if language is registered in the kernel32 resources */
2915 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2916 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2919 /******************************************************************************
2920 * IsValidLocaleName (KERNEL32.@)
2922 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2924 struct locale_name locale_name;
2926 if (!locale)
2927 return FALSE;
2929 /* string parsing */
2930 parse_locale_name( locale, &locale_name );
2932 TRACE( "found lcid %x for %s, matches %d\n",
2933 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2935 return locale_name.matches > 0;
2938 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2939 LPCSTR name, WORD LangID, LONG_PTR lParam )
2941 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2942 char buf[20];
2944 sprintf(buf, "%08x", (UINT)LangID);
2945 return lpfnLocaleEnum( buf );
2948 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2949 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2951 static const WCHAR formatW[] = {'%','0','8','x',0};
2952 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2953 WCHAR buf[20];
2954 sprintfW( buf, formatW, (UINT)LangID );
2955 return lpfnLocaleEnum( buf );
2958 /******************************************************************************
2959 * EnumSystemLocalesA (KERNEL32.@)
2961 * Call a users function for each locale available on the system.
2963 * PARAMS
2964 * lpfnLocaleEnum [I] Callback function to call for each locale
2965 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2967 * RETURNS
2968 * Success: TRUE.
2969 * Failure: FALSE. Use GetLastError() to determine the cause.
2971 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2973 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2974 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2975 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2976 (LONG_PTR)lpfnLocaleEnum);
2977 return TRUE;
2981 /******************************************************************************
2982 * EnumSystemLocalesW (KERNEL32.@)
2984 * See EnumSystemLocalesA.
2986 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2988 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2989 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2990 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2991 (LONG_PTR)lpfnLocaleEnum);
2992 return TRUE;
2996 struct enum_locale_ex_data
2998 LOCALE_ENUMPROCEX proc;
2999 DWORD flags;
3000 LPARAM lparam;
3003 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
3004 LPCWSTR name, WORD lang, LONG_PTR lparam )
3006 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
3007 WCHAR buffer[256];
3008 DWORD neutral;
3009 unsigned int flags;
3011 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
3012 buffer, ARRAY_SIZE( buffer ));
3013 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
3014 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
3015 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
3016 neutral = 0;
3017 flags = LOCALE_WINDOWS;
3018 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
3019 if (data->flags && !(data->flags & flags)) return TRUE;
3020 return data->proc( buffer, flags, data->lparam );
3023 /******************************************************************************
3024 * EnumSystemLocalesEx (KERNEL32.@)
3026 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
3028 struct enum_locale_ex_data data;
3030 if (reserved)
3032 SetLastError( ERROR_INVALID_PARAMETER );
3033 return FALSE;
3035 data.proc = proc;
3036 data.flags = flags;
3037 data.lparam = lparam;
3038 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3039 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
3040 enum_locale_ex_proc, (LONG_PTR)&data );
3041 return TRUE;
3045 /***********************************************************************
3046 * VerLanguageNameA (KERNEL32.@)
3048 * Get the name of a language.
3050 * PARAMS
3051 * wLang [I] LANGID of the language
3052 * szLang [O] Destination for the language name
3054 * RETURNS
3055 * Success: The size of the language name. If szLang is non-NULL, it is filled
3056 * with the name.
3057 * Failure: 0. Use GetLastError() to determine the cause.
3060 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
3062 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3066 /***********************************************************************
3067 * VerLanguageNameW (KERNEL32.@)
3069 * See VerLanguageNameA.
3071 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
3073 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3077 /******************************************************************************
3078 * GetStringTypeW (KERNEL32.@)
3080 * See GetStringTypeA.
3082 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3084 if (!src)
3086 SetLastError( ERROR_INVALID_PARAMETER );
3087 return FALSE;
3090 if (count == -1) count = strlenW(src) + 1;
3091 switch(type)
3093 case CT_CTYPE1:
3094 while (count--) *chartype++ = get_table_entry( wctype_table, *src++ ) & 0xfff;
3095 break;
3096 case CT_CTYPE2:
3097 while (count--) *chartype++ = get_table_entry( wctype_table, *src++ ) >> 12;
3098 break;
3099 case CT_CTYPE3:
3101 WARN("CT_CTYPE3: semi-stub.\n");
3102 while (count--)
3104 int c = *src;
3105 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
3107 type1 = get_table_entry( wctype_table, *src++ ) & 0xfff;
3108 /* try to construct type3 from type1 */
3109 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
3110 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
3111 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
3112 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
3113 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3114 if (c == 0x0640) type3 |= C3_KASHIDA;
3115 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3117 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3118 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3120 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3121 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3122 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3123 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3124 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3125 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3126 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3127 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3129 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3130 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3131 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3132 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3133 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3134 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3135 *chartype++ = type3;
3137 break;
3139 default:
3140 SetLastError( ERROR_INVALID_PARAMETER );
3141 return FALSE;
3143 return TRUE;
3147 /******************************************************************************
3148 * GetStringTypeExW (KERNEL32.@)
3150 * See GetStringTypeExA.
3152 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3154 /* locale is ignored for Unicode */
3155 return GetStringTypeW( type, src, count, chartype );
3159 /******************************************************************************
3160 * GetStringTypeA (KERNEL32.@)
3162 * Get characteristics of the characters making up a string.
3164 * PARAMS
3165 * locale [I] Locale Id for the string
3166 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3167 * src [I] String to analyse
3168 * count [I] Length of src in chars, or -1 if src is NUL terminated
3169 * chartype [O] Destination for the calculated characteristics
3171 * RETURNS
3172 * Success: TRUE. chartype is filled with the requested characteristics of each char
3173 * in src.
3174 * Failure: FALSE. Use GetLastError() to determine the cause.
3176 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3178 UINT cp;
3179 INT countW;
3180 LPWSTR srcW;
3181 BOOL ret = FALSE;
3183 if(count == -1) count = strlen(src) + 1;
3185 if (!(cp = get_lcid_codepage( locale )))
3187 FIXME("For locale %04x using current ANSI code page\n", locale);
3188 cp = GetACP();
3191 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3192 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3194 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3196 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3197 * string, with multibyte characters there maybe be more bytes in count
3198 * than character space in the buffer!
3200 ret = GetStringTypeW(type, srcW, countW, chartype);
3201 HeapFree(GetProcessHeap(), 0, srcW);
3203 return ret;
3206 /******************************************************************************
3207 * GetStringTypeExA (KERNEL32.@)
3209 * Get characteristics of the characters making up a string.
3211 * PARAMS
3212 * locale [I] Locale Id for the string
3213 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3214 * src [I] String to analyse
3215 * count [I] Length of src in chars, or -1 if src is NUL terminated
3216 * chartype [O] Destination for the calculated characteristics
3218 * RETURNS
3219 * Success: TRUE. chartype is filled with the requested characteristics of each char
3220 * in src.
3221 * Failure: FALSE. Use GetLastError() to determine the cause.
3223 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3225 return GetStringTypeA(locale, type, src, count, chartype);
3228 /* compose a full-width katakana. return consumed source characters. */
3229 static INT compose_katakana( LPCWSTR src, INT srclen, LPWSTR dst )
3231 const static BYTE katakana_map[] = {
3232 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
3233 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
3234 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
3235 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
3236 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
3237 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
3238 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
3239 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
3241 WCHAR before, dummy;
3243 if (!dst)
3244 dst = &dummy;
3246 switch (*src)
3248 case 0x309b: case 0x309c:
3249 *dst = *src - 2;
3250 return 1;
3251 case 0x30f0: case 0x30f1: case 0x30fd:
3252 *dst = *src;
3253 break;
3254 default:
3256 int shift = *src - 0xff61;
3257 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map ))
3258 return 0;
3259 else
3260 *dst = katakana_map[shift] | 0x3000;
3264 if (srclen <= 1)
3265 return 1;
3267 before = *dst;
3269 /* datakuten (voiced sound) */
3270 if (*(src + 1) == 0xff9e)
3272 if ((*src >= 0xff76 && *src <= 0xff84) ||
3273 (*src >= 0xff8a && *src <= 0xff8e) ||
3274 *src == 0x30fd)
3275 *dst += 1;
3276 else if (*src == 0xff73)
3277 *dst = 0x30f4; /* KATAKANA LETTER VU */
3278 else if (*src == 0xff9c)
3279 *dst = 0x30f7; /* KATAKANA LETTER VA */
3280 else if (*src == 0x30f0)
3281 *dst = 0x30f8; /* KATAKANA LETTER VI */
3282 else if (*src == 0x30f1)
3283 *dst = 0x30f9; /* KATAKANA LETTER VE */
3284 else if (*src == 0xff66)
3285 *dst = 0x30fa; /* KATAKANA LETTER VO */
3288 /* handakuten (semi-voiced sound) */
3289 if (*(src + 1) == 0xff9f)
3290 if (*src >= 0xff8a && *src <= 0xff8e)
3291 *dst += 2;
3293 return (*dst != before) ? 2 : 1;
3296 /* map one or two half-width characters to one full-width character */
3297 static INT map_to_fullwidth( LPCWSTR src, INT srclen, LPWSTR dst )
3299 INT n;
3301 if (*src <= '~' && *src > ' ' && *src != '\\')
3302 *dst = *src - 0x20 + 0xff00;
3303 else if (*src == ' ')
3304 *dst = 0x3000;
3305 else if (*src <= 0x00af && *src >= 0x00a2)
3307 const static BYTE misc_symbols_table[] = {
3308 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3309 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3311 if (misc_symbols_table[*src - 0x00a2])
3312 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3313 else
3314 *dst = *src;
3316 else if (*src == 0x20a9) /* WON SIGN */
3317 *dst = 0xffe6;
3318 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3319 return n;
3320 else if (*src >= 0xffa0 && *src <= 0xffdc)
3322 const static BYTE hangul_mapping_table[] = {
3323 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3324 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3325 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3326 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3327 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3328 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3329 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3330 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3333 if (hangul_mapping_table[*src - 0xffa0])
3334 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3335 else
3336 *dst = *src;
3338 else
3339 *dst = *src;
3341 return 1;
3344 /* decompose a full-width katakana character into one or two half-width characters. */
3345 static INT decompose_katakana( WCHAR c, LPWSTR dst, INT dstlen )
3347 const static BYTE katakana_map[] = {
3348 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3349 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3350 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3351 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3352 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3353 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3354 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3355 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3356 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3357 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3358 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3359 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3360 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3362 INT len = 0, shift = c - 0x3099;
3363 BYTE k;
3365 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map ))
3366 return 0;
3368 k = katakana_map[shift];
3370 if (!k)
3372 if (dstlen > 0)
3373 *dst = c;
3374 len++;
3376 else if (k > 0x60)
3378 if (dstlen > 0)
3379 *dst = k | 0xff00;
3380 len++;
3382 else
3384 if (dstlen >= 2)
3386 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3387 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3389 len += 2;
3391 return len;
3394 /* map single full-width character to single or double half-width characters. */
3395 static INT map_to_halfwidth(WCHAR c, LPWSTR dst, INT dstlen)
3397 INT n = decompose_katakana(c, dst, dstlen);
3398 if (n > 0)
3399 return n;
3401 if (c == 0x3000)
3402 *dst = ' ';
3403 else if (c == 0x3001)
3404 *dst = 0xff64;
3405 else if (c == 0x3002)
3406 *dst = 0xff61;
3407 else if (c == 0x300c || c == 0x300d)
3408 *dst = (c - 0x300c) + 0xff62;
3409 else if (c >= 0x3131 && c <= 0x3163)
3411 *dst = c - 0x3131 + 0xffa1;
3412 if (*dst >= 0xffbf) *dst += 3;
3413 if (*dst >= 0xffc8) *dst += 2;
3414 if (*dst >= 0xffd0) *dst += 2;
3415 if (*dst >= 0xffd8) *dst += 2;
3417 else if (c == 0x3164)
3418 *dst = 0xffa0;
3419 else if (c == 0x2019)
3420 *dst = '\'';
3421 else if (c == 0x201d)
3422 *dst = '"';
3423 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3424 *dst = c - 0xff00 + 0x20;
3425 else if (c >= 0xffe0 && c <= 0xffe6)
3427 const static WCHAR misc_symbol_map[] = {
3428 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9
3430 *dst = misc_symbol_map[c - 0xffe0];
3432 else
3433 *dst = c;
3435 return 1;
3438 /*************************************************************************
3439 * LCMapStringEx (KERNEL32.@)
3441 * Map characters in a locale sensitive string.
3443 * PARAMS
3444 * name [I] Locale name for the conversion.
3445 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3446 * src [I] String to map
3447 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3448 * dst [O] Destination for mapped string
3449 * dstlen [I] Length of dst in characters
3450 * version [I] reserved, must be NULL
3451 * reserved [I] reserved, must be NULL
3452 * lparam [I] reserved, must be 0
3454 * RETURNS
3455 * Success: The length of the mapped string in dst, including the NUL terminator.
3456 * Failure: 0. Use GetLastError() to determine the cause.
3458 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3459 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3461 LPWSTR dst_ptr;
3462 INT len;
3464 if (version) FIXME("unsupported version structure %p\n", version);
3465 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3466 if (lparam)
3468 static int once;
3469 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3472 if (!src || !srclen || dstlen < 0)
3474 SetLastError(ERROR_INVALID_PARAMETER);
3475 return 0;
3478 /* mutually exclusive flags */
3479 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3480 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3481 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3482 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
3483 !flags)
3485 SetLastError(ERROR_INVALID_FLAGS);
3486 return 0;
3489 if (!dstlen) dst = NULL;
3491 if (flags & LCMAP_SORTKEY)
3493 INT ret;
3494 if (src == dst)
3496 SetLastError(ERROR_INVALID_FLAGS);
3497 return 0;
3500 if (srclen < 0) srclen = strlenW(src);
3502 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3503 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3505 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3506 if (ret == 0)
3507 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3508 else
3509 ret++;
3510 return ret;
3513 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3514 if (flags & SORT_STRINGSORT)
3516 SetLastError(ERROR_INVALID_FLAGS);
3517 return 0;
3519 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
3520 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
3521 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
3522 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
3524 SetLastError(ERROR_INVALID_FLAGS);
3525 return 0;
3528 if (srclen < 0) srclen = strlenW(src) + 1;
3530 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3531 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3533 if (!dst) /* return required string length */
3535 if (flags & NORM_IGNORESYMBOLS)
3537 for (len = 0; srclen; src++, srclen--)
3539 WCHAR wch = *src;
3540 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3541 * and skips white space and punctuation characters for
3542 * NORM_IGNORESYMBOLS.
3544 if (get_table_entry( wctype_table, wch ) & (C1_PUNCT | C1_SPACE))
3545 continue;
3546 len++;
3549 else if (flags & LCMAP_FULLWIDTH)
3551 for (len = 0; srclen; src++, srclen--, len++)
3553 if (compose_katakana(src, srclen, NULL) == 2)
3555 src++;
3556 srclen--;
3560 else if (flags & LCMAP_HALFWIDTH)
3562 for (len = 0; srclen; src++, srclen--, len++)
3564 WCHAR wch = *src;
3565 /* map Hiragana to Katakana before decomposition if needed */
3566 if ((flags & LCMAP_KATAKANA) &&
3567 ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E))
3568 wch += 0x60;
3570 if (decompose_katakana(wch, NULL, 0) == 2)
3571 len++;
3574 else
3575 len = srclen;
3576 return len;
3579 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3581 SetLastError(ERROR_INVALID_FLAGS);
3582 return 0;
3585 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
3587 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3589 WCHAR wch = *src;
3590 if ((flags & NORM_IGNORESYMBOLS) && (get_table_entry( wctype_table, wch ) & (C1_PUNCT | C1_SPACE)))
3591 continue;
3592 *dst_ptr++ = wch;
3593 len--;
3595 goto done;
3598 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
3600 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
3602 WCHAR wch;
3603 if (flags & LCMAP_FULLWIDTH)
3605 /* map half-width character to full-width one,
3606 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
3607 if (map_to_fullwidth(src, srclen, &wch) == 2)
3609 src++;
3610 srclen--;
3613 else
3614 wch = *src;
3616 if (flags & LCMAP_KATAKANA)
3618 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
3619 we can't use C3_HIRAGANA as some characters can't map to katakana */
3620 if ((wch >= 0x3041 && wch <= 0x3096) ||
3621 wch == 0x309D || wch == 0x309E)
3622 wch += 0x60;
3624 else if (flags & LCMAP_HIRAGANA)
3626 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
3627 we can't use C3_KATAKANA as some characters can't map to hiragana */
3628 if ((wch >= 0x30A1 && wch <= 0x30F6) ||
3629 wch == 0x30FD || wch == 0x30FE)
3630 wch -= 0x60;
3633 if (flags & LCMAP_HALFWIDTH)
3635 /* map full-width character to half-width one,
3636 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
3637 if (map_to_halfwidth(wch, dst_ptr, len) == 2)
3639 len--;
3640 dst_ptr++;
3641 if (!len) break;
3644 else
3645 *dst_ptr = wch;
3647 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) || srclen)
3648 goto done;
3650 srclen = dst_ptr - dst;
3651 src = dst;
3654 if (flags & LCMAP_UPPERCASE)
3656 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3658 *dst_ptr++ = toupperW(*src);
3659 len--;
3662 else if (flags & LCMAP_LOWERCASE)
3664 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3666 *dst_ptr++ = tolowerW(*src);
3667 len--;
3670 else
3672 len = min(srclen, dstlen);
3673 memcpy(dst, src, len * sizeof(WCHAR));
3674 dst_ptr = dst + len;
3675 srclen -= len;
3678 done:
3679 if (srclen)
3681 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3682 return 0;
3685 return dst_ptr - dst;
3688 /*************************************************************************
3689 * LCMapStringW (KERNEL32.@)
3691 * See LCMapStringA.
3693 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3694 LPWSTR dst, INT dstlen)
3696 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3697 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3699 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3702 /*************************************************************************
3703 * LCMapStringA (KERNEL32.@)
3705 * Map characters in a locale sensitive string.
3707 * PARAMS
3708 * lcid [I] LCID for the conversion.
3709 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3710 * src [I] String to map
3711 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3712 * dst [O] Destination for mapped string
3713 * dstlen [I] Length of dst in characters
3715 * RETURNS
3716 * Success: The length of the mapped string in dst, including the NUL terminator.
3717 * Failure: 0. Use GetLastError() to determine the cause.
3719 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3720 LPSTR dst, INT dstlen)
3722 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3723 LPWSTR srcW, dstW;
3724 INT ret = 0, srclenW, dstlenW;
3725 UINT locale_cp = CP_ACP;
3727 if (!src || !srclen || dstlen < 0)
3729 SetLastError(ERROR_INVALID_PARAMETER);
3730 return 0;
3733 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3735 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3736 if (srclenW)
3737 srcW = bufW;
3738 else
3740 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3741 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3742 if (!srcW)
3744 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3745 return 0;
3747 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3750 if (flags & LCMAP_SORTKEY)
3752 if (src == dst)
3754 SetLastError(ERROR_INVALID_FLAGS);
3755 goto map_string_exit;
3757 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3758 if (ret == 0)
3759 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3760 else
3761 ret++;
3762 goto map_string_exit;
3765 if (flags & SORT_STRINGSORT)
3767 SetLastError(ERROR_INVALID_FLAGS);
3768 goto map_string_exit;
3771 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3772 if (!dstlenW)
3773 goto map_string_exit;
3775 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3776 if (!dstW)
3778 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3779 goto map_string_exit;
3782 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3783 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3784 HeapFree(GetProcessHeap(), 0, dstW);
3786 map_string_exit:
3787 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3788 return ret;
3791 /*************************************************************************
3792 * FoldStringA (KERNEL32.@)
3794 * Map characters in a string.
3796 * PARAMS
3797 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3798 * src [I] String to map
3799 * srclen [I] Length of src, or -1 if src is NUL terminated
3800 * dst [O] Destination for mapped string
3801 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3803 * RETURNS
3804 * Success: The length of the string written to dst, including the terminating NUL. If
3805 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3806 * and dst may be NULL.
3807 * Failure: 0. Use GetLastError() to determine the cause.
3809 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3810 LPSTR dst, INT dstlen)
3812 INT ret = 0, srclenW = 0;
3813 WCHAR *srcW = NULL, *dstW = NULL;
3815 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3817 SetLastError(ERROR_INVALID_PARAMETER);
3818 return 0;
3821 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3822 src, srclen, NULL, 0);
3823 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3825 if (!srcW)
3827 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3828 goto FoldStringA_exit;
3831 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3832 src, srclen, srcW, srclenW);
3834 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3836 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3837 if (ret && dstlen)
3839 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3841 if (!dstW)
3843 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3844 goto FoldStringA_exit;
3847 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3848 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3850 ret = 0;
3851 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3855 HeapFree(GetProcessHeap(), 0, dstW);
3857 FoldStringA_exit:
3858 HeapFree(GetProcessHeap(), 0, srcW);
3859 return ret;
3862 /*************************************************************************
3863 * FoldStringW (KERNEL32.@)
3865 * See FoldStringA.
3867 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3868 LPWSTR dst, INT dstlen)
3870 int ret;
3872 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3874 case 0:
3875 if (dwFlags)
3876 break;
3877 /* Fall through for dwFlags == 0 */
3878 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3879 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3880 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3881 SetLastError(ERROR_INVALID_FLAGS);
3882 return 0;
3885 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3887 SetLastError(ERROR_INVALID_PARAMETER);
3888 return 0;
3891 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3892 if (!ret)
3893 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3894 return ret;
3897 /******************************************************************************
3898 * CompareStringW (KERNEL32.@)
3900 * See CompareStringA.
3902 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3903 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3905 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3908 /******************************************************************************
3909 * CompareStringEx (KERNEL32.@)
3911 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3912 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3914 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3915 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3916 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3917 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3918 INT ret;
3919 static int once;
3921 if (version) FIXME("unexpected version parameter\n");
3922 if (reserved) FIXME("unexpected reserved value\n");
3923 if (lParam) FIXME("unexpected lParam\n");
3925 if (!str1 || !str2)
3927 SetLastError(ERROR_INVALID_PARAMETER);
3928 return 0;
3931 if (flags & ~(supported_flags|semistub_flags))
3933 SetLastError(ERROR_INVALID_FLAGS);
3934 return 0;
3937 if (flags & semistub_flags)
3939 if (!once++)
3940 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3943 if (len1 < 0) len1 = strlenW(str1);
3944 if (len2 < 0) len2 = strlenW(str2);
3946 ret = wine_compare_string(flags, str1, len1, str2, len2);
3948 if (ret) /* need to translate result */
3949 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3950 return CSTR_EQUAL;
3953 /******************************************************************************
3954 * CompareStringA (KERNEL32.@)
3956 * Compare two locale sensitive strings.
3958 * PARAMS
3959 * lcid [I] LCID for the comparison
3960 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3961 * str1 [I] First string to compare
3962 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3963 * str2 [I] Second string to compare
3964 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3966 * RETURNS
3967 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3968 * str1 is less than, equal to or greater than str2 respectively.
3969 * Failure: FALSE. Use GetLastError() to determine the cause.
3971 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3972 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3974 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3975 WCHAR *buf2W = buf1W + 130;
3976 LPWSTR str1W, str2W;
3977 INT len1W = 0, len2W = 0, ret;
3978 UINT locale_cp = CP_ACP;
3980 if (!str1 || !str2)
3982 SetLastError(ERROR_INVALID_PARAMETER);
3983 return 0;
3985 if (len1 < 0) len1 = strlen(str1);
3986 if (len2 < 0) len2 = strlen(str2);
3988 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3990 if (len1)
3992 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3993 if (len1W)
3994 str1W = buf1W;
3995 else
3997 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3998 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3999 if (!str1W)
4001 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4002 return 0;
4004 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
4007 else
4009 len1W = 0;
4010 str1W = buf1W;
4013 if (len2)
4015 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
4016 if (len2W)
4017 str2W = buf2W;
4018 else
4020 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
4021 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
4022 if (!str2W)
4024 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4025 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4026 return 0;
4028 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
4031 else
4033 len2W = 0;
4034 str2W = buf2W;
4037 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
4039 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4040 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
4041 return ret;
4044 /******************************************************************************
4045 * CompareStringOrdinal (KERNEL32.@)
4047 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
4049 int ret;
4051 if (!str1 || !str2)
4053 SetLastError(ERROR_INVALID_PARAMETER);
4054 return 0;
4056 if (len1 < 0) len1 = strlenW(str1);
4057 if (len2 < 0) len2 = strlenW(str2);
4059 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4060 if (ret < 0) return CSTR_LESS_THAN;
4061 if (ret > 0) return CSTR_GREATER_THAN;
4062 return CSTR_EQUAL;
4065 /*************************************************************************
4066 * lstrcmp (KERNEL32.@)
4067 * lstrcmpA (KERNEL32.@)
4069 * Compare two strings using the current thread locale.
4071 * PARAMS
4072 * str1 [I] First string to compare
4073 * str2 [I] Second string to compare
4075 * RETURNS
4076 * Success: A number less than, equal to or greater than 0 depending on whether
4077 * str1 is less than, equal to or greater than str2 respectively.
4078 * Failure: FALSE. Use GetLastError() to determine the cause.
4080 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR 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 = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4089 if (ret) ret -= 2;
4091 return ret;
4094 /*************************************************************************
4095 * lstrcmpi (KERNEL32.@)
4096 * lstrcmpiA (KERNEL32.@)
4098 * Compare two strings using the current thread locale, ignoring case.
4100 * PARAMS
4101 * str1 [I] First string to compare
4102 * str2 [I] Second string to compare
4104 * RETURNS
4105 * Success: A number less than, equal to or greater than 0 depending on whether
4106 * str2 is less than, equal to or greater than str1 respectively.
4107 * Failure: FALSE. Use GetLastError() to determine the cause.
4109 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
4111 int ret;
4113 if ((str1 == NULL) && (str2 == NULL)) return 0;
4114 if (str1 == NULL) return -1;
4115 if (str2 == NULL) return 1;
4117 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4118 if (ret) ret -= 2;
4120 return ret;
4123 /*************************************************************************
4124 * lstrcmpW (KERNEL32.@)
4126 * See lstrcmpA.
4128 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
4130 int ret;
4132 if ((str1 == NULL) && (str2 == NULL)) return 0;
4133 if (str1 == NULL) return -1;
4134 if (str2 == NULL) return 1;
4136 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
4137 if (ret) ret -= 2;
4139 return ret;
4142 /*************************************************************************
4143 * lstrcmpiW (KERNEL32.@)
4145 * See lstrcmpiA.
4147 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
4149 int ret;
4151 if ((str1 == NULL) && (str2 == NULL)) return 0;
4152 if (str1 == NULL) return -1;
4153 if (str2 == NULL) return 1;
4155 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
4156 if (ret) ret -= 2;
4158 return ret;
4161 /******************************************************************************
4162 * LOCALE_Init
4164 void LOCALE_Init(void)
4166 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
4167 const union cptable *unix_cp );
4169 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
4171 setlocale( LC_ALL, "" );
4173 #ifdef __APPLE__
4174 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
4175 if (!has_env("LANG"))
4177 const char* mac_locale = get_mac_locale();
4179 setenv( "LANG", mac_locale, 1 );
4180 if (setlocale( LC_ALL, "" ))
4181 TRACE( "setting LANG to '%s'\n", mac_locale );
4182 else
4184 /* no C library locale matching Mac locale; don't pass garbage to children */
4185 unsetenv("LANG");
4186 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
4189 #endif /* __APPLE__ */
4191 unix_cp = setup_unix_locales();
4192 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
4194 #ifdef __APPLE__
4195 if (!unix_cp)
4196 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
4197 #endif
4199 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
4200 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
4201 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
4203 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
4204 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
4205 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
4206 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
4207 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
4208 if (!unix_cp)
4209 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
4210 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
4212 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
4213 ansi_cptable = wine_cp_get_table( 1252 );
4214 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
4215 oem_cptable = wine_cp_get_table( 437 );
4216 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
4217 mac_cptable = wine_cp_get_table( 10000 );
4218 if (unix_cp != CP_UTF8)
4220 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
4221 unix_cptable = wine_cp_get_table( 28591 );
4224 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
4226 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
4227 ansi_cptable->info.codepage, oem_cptable->info.codepage,
4228 mac_cptable->info.codepage, unix_cp );
4230 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
4233 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4235 UNICODE_STRING keyName;
4236 OBJECT_ATTRIBUTES attr;
4237 HANDLE hkey;
4239 RtlInitUnicodeString( &keyName, szKeyName );
4240 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
4242 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
4243 hkey = 0;
4245 return hkey;
4248 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4249 LPWSTR szValueName, ULONG valueNameSize,
4250 LPWSTR szValueData, ULONG valueDataSize)
4252 BYTE buffer[80];
4253 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
4254 DWORD dwLen;
4256 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
4257 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
4258 info->NameLength > valueNameSize ||
4259 info->DataLength > valueDataSize)
4261 return FALSE;
4264 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
4266 memcpy( szValueName, info->Name, info->NameLength);
4267 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
4268 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
4269 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
4271 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
4272 return TRUE;
4275 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
4277 BYTE buffer[128];
4278 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
4279 DWORD dwSize = sizeof(buffer);
4280 UNICODE_STRING valueName;
4282 RtlInitUnicodeString( &valueName, szValueName );
4284 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
4285 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
4286 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
4287 info->DataLength == sizeof(DWORD))
4289 memcpy(lpVal, info->Data, sizeof(DWORD));
4290 return TRUE;
4293 return FALSE;
4296 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
4298 LANGID langId;
4299 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
4300 HRSRC hResource;
4301 BOOL bRet = FALSE;
4303 /* FIXME: Is it correct to use the system default langid? */
4304 langId = GetSystemDefaultLangID();
4306 if (SUBLANGID(langId) == SUBLANG_NEUTRAL) langId = get_default_sublang( langId );
4308 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
4310 if (hResource)
4312 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
4314 if (hResDir)
4316 ULONG iResourceIndex = lgrpid & 0xf;
4317 LPCWSTR lpResEntry = LockResource( hResDir );
4318 ULONG i;
4320 for (i = 0; i < iResourceIndex; i++)
4321 lpResEntry += *lpResEntry + 1;
4323 if (*lpResEntry < nameSize)
4325 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
4326 szName[*lpResEntry] = '\0';
4327 bRet = TRUE;
4331 FreeResource( hResource );
4333 return bRet;
4336 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
4337 typedef struct
4339 LANGUAGEGROUP_ENUMPROCA procA;
4340 LANGUAGEGROUP_ENUMPROCW procW;
4341 DWORD dwFlags;
4342 LONG_PTR lParam;
4343 } ENUMLANGUAGEGROUP_CALLBACKS;
4345 /* Internal implementation of EnumSystemLanguageGroupsA/W */
4346 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
4348 WCHAR szNumber[10], szValue[4];
4349 HANDLE hKey;
4350 BOOL bContinue = TRUE;
4351 ULONG ulIndex = 0;
4353 if (!lpProcs)
4355 SetLastError(ERROR_INVALID_PARAMETER);
4356 return FALSE;
4359 switch (lpProcs->dwFlags)
4361 case 0:
4362 /* Default to LGRPID_INSTALLED */
4363 lpProcs->dwFlags = LGRPID_INSTALLED;
4364 /* Fall through... */
4365 case LGRPID_INSTALLED:
4366 case LGRPID_SUPPORTED:
4367 break;
4368 default:
4369 SetLastError(ERROR_INVALID_FLAGS);
4370 return FALSE;
4373 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4375 if (!hKey)
4376 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4378 while (bContinue)
4380 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4381 szValue, sizeof(szValue) ))
4383 BOOL bInstalled = szValue[0] == '1';
4384 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4386 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4387 bInstalled ? "" : "not ");
4389 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4391 WCHAR szGrpName[48];
4393 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, ARRAY_SIZE( szGrpName )))
4394 szGrpName[0] = '\0';
4396 if (lpProcs->procW)
4397 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4398 lpProcs->lParam );
4399 else
4401 char szNumberA[ARRAY_SIZE( szNumber )];
4402 char szGrpNameA[48];
4404 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4405 * or whether the language names are ever localised. Assume CP_ACP.
4408 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4409 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4411 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4412 lpProcs->lParam );
4416 ulIndex++;
4418 else
4419 bContinue = FALSE;
4421 if (!bContinue)
4422 break;
4425 if (hKey)
4426 NtClose( hKey );
4428 return TRUE;
4431 /******************************************************************************
4432 * EnumSystemLanguageGroupsA (KERNEL32.@)
4434 * Call a users function for each language group available on the system.
4436 * PARAMS
4437 * pLangGrpEnumProc [I] Callback function to call for each language group
4438 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4439 * lParam [I] User parameter to pass to pLangGrpEnumProc
4441 * RETURNS
4442 * Success: TRUE.
4443 * Failure: FALSE. Use GetLastError() to determine the cause.
4445 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4446 DWORD dwFlags, LONG_PTR lParam)
4448 ENUMLANGUAGEGROUP_CALLBACKS procs;
4450 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4452 procs.procA = pLangGrpEnumProc;
4453 procs.procW = NULL;
4454 procs.dwFlags = dwFlags;
4455 procs.lParam = lParam;
4457 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4460 /******************************************************************************
4461 * EnumSystemLanguageGroupsW (KERNEL32.@)
4463 * See EnumSystemLanguageGroupsA.
4465 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4466 DWORD dwFlags, LONG_PTR lParam)
4468 ENUMLANGUAGEGROUP_CALLBACKS procs;
4470 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4472 procs.procA = NULL;
4473 procs.procW = pLangGrpEnumProc;
4474 procs.dwFlags = dwFlags;
4475 procs.lParam = lParam;
4477 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4480 /******************************************************************************
4481 * IsValidLanguageGroup (KERNEL32.@)
4483 * Determine if a language group is supported and/or installed.
4485 * PARAMS
4486 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4487 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4489 * RETURNS
4490 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4491 * FALSE otherwise.
4493 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4495 static const WCHAR szFormat[] = { '%','x','\0' };
4496 WCHAR szValueName[16], szValue[2];
4497 BOOL bSupported = FALSE, bInstalled = FALSE;
4498 HANDLE hKey;
4501 switch (dwFlags)
4503 case LGRPID_INSTALLED:
4504 case LGRPID_SUPPORTED:
4506 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4508 sprintfW( szValueName, szFormat, lgrpid );
4510 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4512 bSupported = TRUE;
4514 if (szValue[0] == '1')
4515 bInstalled = TRUE;
4518 if (hKey)
4519 NtClose( hKey );
4521 break;
4524 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4525 (dwFlags == LGRPID_INSTALLED && bInstalled))
4526 return TRUE;
4528 return FALSE;
4531 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4532 typedef struct
4534 LANGGROUPLOCALE_ENUMPROCA procA;
4535 LANGGROUPLOCALE_ENUMPROCW procW;
4536 DWORD dwFlags;
4537 LGRPID lgrpid;
4538 LONG_PTR lParam;
4539 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4541 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4542 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4544 static const WCHAR szAlternateSortsKeyName[] = {
4545 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4547 WCHAR szNumber[10], szValue[4];
4548 HANDLE hKey;
4549 BOOL bContinue = TRUE, bAlternate = FALSE;
4550 LGRPID lgrpid;
4551 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4553 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4555 SetLastError(ERROR_INVALID_PARAMETER);
4556 return FALSE;
4559 if (lpProcs->dwFlags)
4561 SetLastError(ERROR_INVALID_FLAGS);
4562 return FALSE;
4565 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4567 if (!hKey)
4568 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4570 while (bContinue)
4572 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4573 szValue, sizeof(szValue) ))
4575 lgrpid = strtoulW( szValue, NULL, 16 );
4577 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4578 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4580 if (lgrpid == lpProcs->lgrpid)
4582 LCID lcid;
4584 lcid = strtoulW( szNumber, NULL, 16 );
4586 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4587 * '00000437 ;Georgian'
4588 * At present we only pass the LCID string.
4591 if (lpProcs->procW)
4592 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4593 else
4595 char szNumberA[ARRAY_SIZE( szNumber )];
4597 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4599 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4603 ulIndex++;
4605 else
4607 /* Finished enumerating this key */
4608 if (!bAlternate)
4610 /* Enumerate alternate sorts also */
4611 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4612 bAlternate = TRUE;
4613 ulIndex = 0;
4615 else
4616 bContinue = FALSE; /* Finished both keys */
4619 if (!bContinue)
4620 break;
4623 if (hKey)
4624 NtClose( hKey );
4626 return TRUE;
4629 /******************************************************************************
4630 * EnumLanguageGroupLocalesA (KERNEL32.@)
4632 * Call a users function for every locale in a language group available on the system.
4634 * PARAMS
4635 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4636 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4637 * dwFlags [I] Reserved, set to 0
4638 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4640 * RETURNS
4641 * Success: TRUE.
4642 * Failure: FALSE. Use GetLastError() to determine the cause.
4644 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4645 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4647 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4649 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4651 callbacks.procA = pLangGrpLcEnumProc;
4652 callbacks.procW = NULL;
4653 callbacks.dwFlags = dwFlags;
4654 callbacks.lgrpid = lgrpid;
4655 callbacks.lParam = lParam;
4657 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4660 /******************************************************************************
4661 * EnumLanguageGroupLocalesW (KERNEL32.@)
4663 * See EnumLanguageGroupLocalesA.
4665 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4666 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4668 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4670 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4672 callbacks.procA = NULL;
4673 callbacks.procW = pLangGrpLcEnumProc;
4674 callbacks.dwFlags = dwFlags;
4675 callbacks.lgrpid = lgrpid;
4676 callbacks.lParam = lParam;
4678 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4681 /******************************************************************************
4682 * InvalidateNLSCache (KERNEL32.@)
4684 * Invalidate the cache of NLS values.
4686 * PARAMS
4687 * None.
4689 * RETURNS
4690 * Success: TRUE.
4691 * Failure: FALSE.
4693 BOOL WINAPI InvalidateNLSCache(void)
4695 FIXME("() stub\n");
4696 return FALSE;
4699 /******************************************************************************
4700 * GetUserGeoID (KERNEL32.@)
4702 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4704 GEOID ret = GEOID_NOT_AVAILABLE;
4705 static const WCHAR geoW[] = {'G','e','o',0};
4706 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4707 WCHAR bufferW[40], *end;
4708 DWORD count;
4709 HANDLE hkey, hSubkey = 0;
4710 UNICODE_STRING keyW;
4711 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4712 RtlInitUnicodeString( &keyW, nationW );
4713 count = sizeof(bufferW);
4715 if(!(hkey = create_registry_key())) return ret;
4717 switch( GeoClass ){
4718 case GEOCLASS_NATION:
4719 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4721 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4722 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4723 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4725 break;
4726 case GEOCLASS_REGION:
4727 FIXME("GEOCLASS_REGION not handled yet\n");
4728 break;
4731 NtClose(hkey);
4732 if (hSubkey) NtClose(hSubkey);
4733 return ret;
4736 /******************************************************************************
4737 * SetUserGeoID (KERNEL32.@)
4739 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4741 static const WCHAR geoW[] = {'G','e','o',0};
4742 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4743 static const WCHAR formatW[] = {'%','i',0};
4744 UNICODE_STRING nameW,keyW;
4745 WCHAR bufferW[10];
4746 OBJECT_ATTRIBUTES attr;
4747 HANDLE hkey;
4749 if(!(hkey = create_registry_key())) return FALSE;
4751 attr.Length = sizeof(attr);
4752 attr.RootDirectory = hkey;
4753 attr.ObjectName = &nameW;
4754 attr.Attributes = 0;
4755 attr.SecurityDescriptor = NULL;
4756 attr.SecurityQualityOfService = NULL;
4757 RtlInitUnicodeString( &nameW, geoW );
4758 RtlInitUnicodeString( &keyW, nationW );
4760 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4763 NtClose(attr.RootDirectory);
4764 return FALSE;
4767 sprintfW(bufferW, formatW, GeoID);
4768 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4769 NtClose(attr.RootDirectory);
4770 NtClose(hkey);
4771 return TRUE;
4774 typedef struct
4776 union
4778 UILANGUAGE_ENUMPROCA procA;
4779 UILANGUAGE_ENUMPROCW procW;
4780 } u;
4781 DWORD flags;
4782 LONG_PTR param;
4783 } ENUM_UILANG_CALLBACK;
4785 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4786 LPCSTR name, WORD LangID, LONG_PTR lParam )
4788 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4789 char buf[20];
4791 sprintf(buf, "%08x", (UINT)LangID);
4792 return enum_uilang->u.procA( buf, enum_uilang->param );
4795 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4796 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4798 static const WCHAR formatW[] = {'%','0','8','x',0};
4799 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4800 WCHAR buf[20];
4802 sprintfW( buf, formatW, (UINT)LangID );
4803 return enum_uilang->u.procW( buf, enum_uilang->param );
4806 /******************************************************************************
4807 * EnumUILanguagesA (KERNEL32.@)
4809 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4811 ENUM_UILANG_CALLBACK enum_uilang;
4813 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4815 if(!pUILangEnumProc) {
4816 SetLastError(ERROR_INVALID_PARAMETER);
4817 return FALSE;
4819 if(dwFlags & ~MUI_LANGUAGE_ID) {
4820 SetLastError(ERROR_INVALID_FLAGS);
4821 return FALSE;
4824 enum_uilang.u.procA = pUILangEnumProc;
4825 enum_uilang.flags = dwFlags;
4826 enum_uilang.param = lParam;
4828 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4829 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4830 (LONG_PTR)&enum_uilang);
4831 return TRUE;
4834 /******************************************************************************
4835 * EnumUILanguagesW (KERNEL32.@)
4837 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4839 ENUM_UILANG_CALLBACK enum_uilang;
4841 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4844 if(!pUILangEnumProc) {
4845 SetLastError(ERROR_INVALID_PARAMETER);
4846 return FALSE;
4848 if(dwFlags & ~MUI_LANGUAGE_ID) {
4849 SetLastError(ERROR_INVALID_FLAGS);
4850 return FALSE;
4853 enum_uilang.u.procW = pUILangEnumProc;
4854 enum_uilang.flags = dwFlags;
4855 enum_uilang.param = lParam;
4857 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4858 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4859 (LONG_PTR)&enum_uilang);
4860 return TRUE;
4863 enum locationkind {
4864 LOCATION_NATION = 0,
4865 LOCATION_REGION,
4866 LOCATION_BOTH
4869 struct geoinfo_t {
4870 GEOID id;
4871 WCHAR iso2W[3];
4872 WCHAR iso3W[4];
4873 GEOID parent;
4874 INT uncode;
4875 enum locationkind kind;
4878 static const struct geoinfo_t geoinfodata[] = {
4879 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4880 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4881 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4882 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4883 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4884 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4885 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4886 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4887 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4888 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4889 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4890 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4891 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4892 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4893 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4894 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4895 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4896 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4897 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4898 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4899 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4900 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4901 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4902 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4903 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4904 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4905 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4906 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4907 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4908 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4909 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4910 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4911 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4912 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4913 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4914 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4915 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4916 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4917 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4918 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4919 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4920 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4921 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4922 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4923 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4924 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4925 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4926 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4927 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4928 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4929 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4930 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4931 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4932 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4933 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4934 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4935 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4936 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4937 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4938 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4939 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4940 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4941 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4942 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4943 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4944 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4945 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4946 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4947 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4948 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4949 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4950 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4951 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4952 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4953 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4954 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4955 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4956 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4957 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4958 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4959 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4960 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4961 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4962 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4963 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4964 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4965 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4966 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4967 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4968 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4969 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4970 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4971 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4972 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4973 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4974 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4975 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4976 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4977 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4978 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4979 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4980 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4981 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4982 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4983 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4984 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4985 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4986 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4987 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4988 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4989 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4990 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4991 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4992 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4993 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4994 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4995 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4996 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4997 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4998 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4999 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
5000 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
5001 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
5002 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
5003 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
5004 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
5005 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
5006 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
5007 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
5008 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
5009 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
5010 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
5011 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
5012 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
5013 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
5014 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
5015 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
5016 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
5017 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
5018 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
5019 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
5020 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
5021 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
5022 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
5023 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
5024 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
5025 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
5026 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
5027 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
5028 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
5029 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
5030 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
5031 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
5032 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
5033 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
5034 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
5035 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
5036 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
5037 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
5038 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
5039 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
5040 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
5041 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
5042 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
5043 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
5044 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
5045 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
5046 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
5047 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
5048 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
5049 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
5050 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
5051 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
5052 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
5053 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
5054 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
5055 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
5056 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
5057 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
5058 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
5059 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
5060 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
5061 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
5062 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
5063 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
5064 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
5065 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
5066 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
5067 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
5068 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
5069 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
5070 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
5071 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
5072 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
5073 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
5074 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
5075 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
5076 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
5077 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
5078 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
5079 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
5080 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
5081 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
5082 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
5083 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
5084 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
5085 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
5086 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
5087 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
5088 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
5089 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
5090 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
5091 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
5092 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
5093 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
5094 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
5095 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
5096 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
5097 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
5098 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
5099 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
5100 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
5101 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
5102 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
5103 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
5104 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
5105 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
5106 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
5107 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
5108 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
5109 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
5110 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
5111 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
5112 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
5113 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
5114 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
5115 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
5116 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
5117 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
5118 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
5119 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
5120 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
5121 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
5122 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
5123 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
5124 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
5125 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
5126 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
5127 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
5128 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
5129 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
5130 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
5131 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
5132 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
5133 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
5134 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
5135 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
5136 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
5137 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
5138 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
5139 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
5140 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
5141 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
5142 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
5143 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
5144 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
5145 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
5146 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
5147 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
5148 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
5149 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
5150 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
5151 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
5152 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
5153 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
5154 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
5155 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
5156 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
5157 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
5158 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
5159 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
5160 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
5161 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
5162 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
5163 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
5164 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
5165 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
5166 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
5167 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
5168 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
5169 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
5170 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
5171 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
5172 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
5173 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
5174 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
5175 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
5176 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
5177 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
5180 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
5182 int min, max;
5184 min = 0;
5185 max = ARRAY_SIZE(geoinfodata)-1;
5187 while (min <= max) {
5188 const struct geoinfo_t *ptr;
5189 int n = (min+max)/2;
5191 ptr = &geoinfodata[n];
5192 if (geoid == ptr->id)
5193 /* we don't need empty entries */
5194 return *ptr->iso2W ? ptr : NULL;
5196 if (ptr->id > geoid)
5197 max = n-1;
5198 else
5199 min = n+1;
5202 return NULL;
5205 /******************************************************************************
5206 * GetGeoInfoW (KERNEL32.@)
5208 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
5210 const struct geoinfo_t *ptr;
5211 const WCHAR *str = NULL;
5212 WCHAR buffW[12];
5213 LONG val = 0;
5214 INT len;
5216 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5218 if (!(ptr = get_geoinfo_dataptr(geoid))) {
5219 SetLastError(ERROR_INVALID_PARAMETER);
5220 return 0;
5223 switch (geotype) {
5224 case GEO_NATION:
5225 val = geoid;
5226 break;
5227 case GEO_ISO_UN_NUMBER:
5228 val = ptr->uncode;
5229 break;
5230 case GEO_PARENT:
5231 val = ptr->parent;
5232 break;
5233 case GEO_ISO2:
5234 case GEO_ISO3:
5236 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
5237 break;
5239 case GEO_RFC1766:
5240 case GEO_LCID:
5241 case GEO_FRIENDLYNAME:
5242 case GEO_OFFICIALNAME:
5243 case GEO_TIMEZONES:
5244 case GEO_OFFICIALLANGUAGES:
5245 case GEO_LATITUDE:
5246 case GEO_LONGITUDE:
5247 FIXME("type %d is not supported\n", geotype);
5248 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5249 return 0;
5250 default:
5251 WARN("unrecognized type %d\n", geotype);
5252 SetLastError(ERROR_INVALID_FLAGS);
5253 return 0;
5256 if (val) {
5257 static const WCHAR fmtW[] = {'%','d',0};
5258 sprintfW(buffW, fmtW, val);
5259 str = buffW;
5262 len = strlenW(str) + 1;
5263 if (!data || !data_len)
5264 return len;
5266 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
5267 if (data_len < len)
5268 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5269 return data_len < len ? 0 : len;
5272 /******************************************************************************
5273 * GetGeoInfoA (KERNEL32.@)
5275 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
5277 WCHAR *buffW;
5278 INT len;
5280 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5282 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
5283 if (!len)
5284 return 0;
5286 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
5287 if (!buffW)
5288 return 0;
5290 GetGeoInfoW(geoid, geotype, buffW, len, lang);
5291 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
5292 if (!data || !data_len) {
5293 HeapFree(GetProcessHeap(), 0, buffW);
5294 return len;
5297 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
5298 HeapFree(GetProcessHeap(), 0, buffW);
5300 if (data_len < len)
5301 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5302 return data_len < len ? 0 : len;
5305 /******************************************************************************
5306 * EnumSystemGeoID (KERNEL32.@)
5308 * Call a users function for every location available on the system.
5310 * PARAMS
5311 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
5312 * parent [I] GEOID for the parent
5313 * enumproc [I] Callback function to call for each location
5315 * RETURNS
5316 * Success: TRUE.
5317 * Failure: FALSE. Use GetLastError() to determine the cause.
5319 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
5321 INT i;
5323 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
5325 if (!enumproc) {
5326 SetLastError(ERROR_INVALID_PARAMETER);
5327 return FALSE;
5330 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
5331 SetLastError(ERROR_INVALID_FLAGS);
5332 return FALSE;
5335 for (i = 0; i < ARRAY_SIZE(geoinfodata); i++) {
5336 const struct geoinfo_t *ptr = &geoinfodata[i];
5338 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
5339 continue;
5341 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
5342 continue;
5344 if (parent && ptr->parent != parent)
5345 continue;
5347 if (!enumproc(ptr->id))
5348 return TRUE;
5351 return TRUE;
5354 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
5356 LCID userlcid;
5358 TRACE("%p, %d\n", localename, buffersize);
5360 userlcid = GetUserDefaultLCID();
5361 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
5364 /******************************************************************************
5365 * NormalizeString (KERNEL32.@)
5367 INT WINAPI NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len, WCHAR *dst, INT dst_len)
5369 int flags = 0, compose = 0;
5370 unsigned int res, buf_len;
5371 WCHAR *buf = NULL;
5373 TRACE("%x %s %d %p %d\n", form, debugstr_wn(src, src_len), src_len, dst, dst_len);
5375 if (src_len == -1) src_len = strlenW(src) + 1;
5377 if (form == NormalizationKC || form == NormalizationKD) flags |= WINE_DECOMPOSE_COMPAT;
5378 if (form == NormalizationC || form == NormalizationKC) compose = 1;
5379 if (compose || dst_len) flags |= WINE_DECOMPOSE_REORDER;
5381 if (!compose && dst_len)
5383 res = wine_decompose_string( flags, src, src_len, dst, dst_len );
5384 if (!res)
5386 SetLastError( ERROR_INSUFFICIENT_BUFFER );
5387 goto done;
5389 buf = dst;
5391 else
5393 buf_len = src_len * 4;
5396 WCHAR *old_buf = buf;
5398 buf = heap_realloc( buf, buf_len );
5399 if (!buf)
5401 heap_free( old_buf );
5402 SetLastError( ERROR_OUTOFMEMORY );
5403 return 0;
5405 res = wine_decompose_string( flags, src, src_len, buf, buf_len );
5406 buf_len *= 2;
5407 } while (!res);
5410 if (compose)
5412 res = wine_compose_string( buf, res );
5413 if (dst_len >= res) memcpy( dst, buf, res * sizeof(WCHAR) );
5416 done:
5417 if (buf != dst) heap_free( buf );
5418 return res;
5421 /******************************************************************************
5422 * IsNormalizedString (KERNEL32.@)
5424 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
5426 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5427 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5428 return FALSE;
5431 enum {
5432 BASE = 36,
5433 TMIN = 1,
5434 TMAX = 26,
5435 SKEW = 38,
5436 DAMP = 700,
5437 INIT_BIAS = 72,
5438 INIT_N = 128
5441 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5443 INT k;
5445 delta /= (firsttime ? DAMP : 2);
5446 delta += delta/numpoints;
5448 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5449 delta /= BASE-TMIN;
5450 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5453 /******************************************************************************
5454 * IdnToAscii (KERNEL32.@)
5455 * Implementation of Punycode based on RFC 3492.
5457 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5458 LPWSTR lpASCIICharStr, INT cchASCIIChar)
5460 static const WCHAR prefixW[] = {'x','n','-','-'};
5462 WCHAR *norm_str;
5463 INT i, label_start, label_end, norm_len, out_label, out = 0;
5465 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5466 lpASCIICharStr, cchASCIIChar);
5468 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5469 if(!norm_len)
5470 return 0;
5471 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5472 if(!norm_str) {
5473 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5474 return 0;
5476 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5477 cchUnicodeChar, norm_str, norm_len);
5478 if(!norm_len) {
5479 HeapFree(GetProcessHeap(), 0, norm_str);
5480 return 0;
5483 for(label_start=0; label_start<norm_len;) {
5484 INT n = INIT_N, bias = INIT_BIAS;
5485 INT delta = 0, b = 0, h;
5487 out_label = out;
5488 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5489 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5490 if(norm_str[i] < 0x80)
5491 b++;
5492 label_end = i;
5494 if(b == label_end-label_start) {
5495 if(label_end < norm_len)
5496 b++;
5497 if(!lpASCIICharStr) {
5498 out += b;
5499 }else if(out+b <= cchASCIIChar) {
5500 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5501 out += b;
5502 }else {
5503 HeapFree(GetProcessHeap(), 0, norm_str);
5504 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5505 return 0;
5507 label_start = label_end+1;
5508 continue;
5511 if(!lpASCIICharStr) {
5512 out += 5+b; /* strlen(xn--...-) */
5513 }else if(out+5+b <= cchASCIIChar) {
5514 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5515 out += 4;
5516 for(i=label_start; i<label_end; i++)
5517 if(norm_str[i] < 0x80)
5518 lpASCIICharStr[out++] = norm_str[i];
5519 lpASCIICharStr[out++] = '-';
5520 }else {
5521 HeapFree(GetProcessHeap(), 0, norm_str);
5522 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5523 return 0;
5525 if(!b)
5526 out--;
5528 for(h=b; h<label_end-label_start;) {
5529 INT m = 0xffff, q, k;
5531 for(i=label_start; i<label_end; i++) {
5532 if(norm_str[i]>=n && m>norm_str[i])
5533 m = norm_str[i];
5535 delta += (m-n)*(h+1);
5536 n = m;
5538 for(i=label_start; i<label_end; i++) {
5539 if(norm_str[i] < n) {
5540 delta++;
5541 }else if(norm_str[i] == n) {
5542 for(q=delta, k=BASE; ; k+=BASE) {
5543 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5544 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5545 if(!lpASCIICharStr) {
5546 out++;
5547 }else if(out+1 <= cchASCIIChar) {
5548 lpASCIICharStr[out++] = disp<='z'-'a' ?
5549 'a'+disp : '0'+disp-'z'+'a'-1;
5550 }else {
5551 HeapFree(GetProcessHeap(), 0, norm_str);
5552 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5553 return 0;
5555 if(q < t)
5556 break;
5557 q = (q-t)/(BASE-t);
5559 bias = adapt(delta, h+1, h==b);
5560 delta = 0;
5561 h++;
5564 delta++;
5565 n++;
5568 if(out-out_label > 63) {
5569 HeapFree(GetProcessHeap(), 0, norm_str);
5570 SetLastError(ERROR_INVALID_NAME);
5571 return 0;
5574 if(label_end < norm_len) {
5575 if(!lpASCIICharStr) {
5576 out++;
5577 }else if(out+1 <= cchASCIIChar) {
5578 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5579 }else {
5580 HeapFree(GetProcessHeap(), 0, norm_str);
5581 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5582 return 0;
5585 label_start = label_end+1;
5588 HeapFree(GetProcessHeap(), 0, norm_str);
5589 return out;
5592 /******************************************************************************
5593 * IdnToNameprepUnicode (KERNEL32.@)
5595 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5596 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5598 enum {
5599 UNASSIGNED = 0x1,
5600 PROHIBITED = 0x2,
5601 BIDI_RAL = 0x4,
5602 BIDI_L = 0x8
5605 const WCHAR *ptr;
5606 WORD flags;
5607 WCHAR buf[64], *map_str, norm_str[64], ch;
5608 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5609 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5611 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5612 lpNameprepCharStr, cchNameprepChar);
5614 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5615 SetLastError(ERROR_INVALID_FLAGS);
5616 return 0;
5619 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5620 SetLastError(ERROR_INVALID_PARAMETER);
5621 return 0;
5624 if(cchUnicodeChar == -1)
5625 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5626 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5627 SetLastError(ERROR_INVALID_NAME);
5628 return 0;
5631 for(label_start=0; label_start<cchUnicodeChar;) {
5632 ascii_only = TRUE;
5633 for(i=label_start; i<cchUnicodeChar; i++) {
5634 ch = lpUnicodeCharStr[i];
5636 if(i!=cchUnicodeChar-1 && !ch) {
5637 SetLastError(ERROR_INVALID_NAME);
5638 return 0;
5640 /* check if ch is one of label separators defined in RFC3490 */
5641 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5642 break;
5644 if(ch > 0x7f) {
5645 ascii_only = FALSE;
5646 continue;
5649 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5650 continue;
5651 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5652 || (ch>='0' && ch<='9') || ch=='-')
5653 continue;
5655 SetLastError(ERROR_INVALID_NAME);
5656 return 0;
5658 label_end = i;
5659 /* last label may be empty */
5660 if(label_start==label_end && ch) {
5661 SetLastError(ERROR_INVALID_NAME);
5662 return 0;
5665 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5666 lpUnicodeCharStr[label_end-1]=='-')) {
5667 SetLastError(ERROR_INVALID_NAME);
5668 return 0;
5671 if(ascii_only) {
5672 /* maximal label length is 63 characters */
5673 if(label_end-label_start > 63) {
5674 SetLastError(ERROR_INVALID_NAME);
5675 return 0;
5677 if(label_end < cchUnicodeChar)
5678 label_end++;
5680 if(!lpNameprepCharStr) {
5681 out += label_end-label_start;
5682 }else if(out+label_end-label_start <= cchNameprepChar) {
5683 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5684 (label_end-label_start)*sizeof(WCHAR));
5685 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5686 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5687 out += label_end-label_start;
5688 }else {
5689 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5690 return 0;
5693 label_start = label_end;
5694 continue;
5697 map_len = 0;
5698 for(i=label_start; i<label_end; i++) {
5699 ch = lpUnicodeCharStr[i];
5700 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5701 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5703 if(!ptr[0]) map_len++;
5704 else if(!ptr[1]) map_len++;
5705 else if(!ptr[2]) map_len += 2;
5706 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5708 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5709 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5710 if(!map_str) {
5711 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5712 return 0;
5714 }else {
5715 map_str = buf;
5717 map_len = 0;
5718 for(i=label_start; i<label_end; i++) {
5719 ch = lpUnicodeCharStr[i];
5720 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5721 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5723 if(!ptr[0]) {
5724 map_str[map_len++] = ch;
5725 }else if(!ptr[1]) {
5726 map_str[map_len++] = ptr[0];
5727 }else if(!ptr[2]) {
5728 map_str[map_len++] = ptr[0];
5729 map_str[map_len++] = ptr[1];
5730 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5731 map_str[map_len++] = ptr[0];
5732 map_str[map_len++] = ptr[1];
5733 map_str[map_len++] = ptr[2];
5737 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len, norm_str, ARRAY_SIZE(norm_str)-1);
5738 if(map_str != buf)
5739 HeapFree(GetProcessHeap(), 0, map_str);
5740 if(!norm_len) {
5741 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5742 SetLastError(ERROR_INVALID_NAME);
5743 return 0;
5746 if(label_end < cchUnicodeChar) {
5747 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5748 label_end++;
5751 if(!lpNameprepCharStr) {
5752 out += norm_len;
5753 }else if(out+norm_len <= cchNameprepChar) {
5754 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5755 out += norm_len;
5756 }else {
5757 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5758 return 0;
5761 have_bidi_ral = prohibit_bidi_ral = FALSE;
5762 mask = PROHIBITED;
5763 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5764 mask |= UNASSIGNED;
5765 for(i=0; i<norm_len; i++) {
5766 ch = norm_str[i];
5767 flags = get_table_entry( nameprep_char_type, ch );
5769 if(flags & mask) {
5770 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5771 : ERROR_NO_UNICODE_TRANSLATION);
5772 return 0;
5775 if(flags & BIDI_RAL)
5776 have_bidi_ral = TRUE;
5777 if(flags & BIDI_L)
5778 prohibit_bidi_ral = TRUE;
5781 if(have_bidi_ral) {
5782 ch = norm_str[0];
5783 flags = get_table_entry( nameprep_char_type, ch );
5784 if((flags & BIDI_RAL) == 0)
5785 prohibit_bidi_ral = TRUE;
5787 ch = norm_str[norm_len-1];
5788 flags = get_table_entry( nameprep_char_type, ch );
5789 if((flags & BIDI_RAL) == 0)
5790 prohibit_bidi_ral = TRUE;
5793 if(have_bidi_ral && prohibit_bidi_ral) {
5794 SetLastError(ERROR_INVALID_NAME);
5795 return 0;
5798 label_start = label_end;
5801 return out;
5804 /******************************************************************************
5805 * IdnToUnicode (KERNEL32.@)
5807 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5808 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5810 INT i, label_start, label_end, out_label, out = 0;
5811 WCHAR ch;
5813 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5814 lpUnicodeCharStr, cchUnicodeChar);
5816 for(label_start=0; label_start<cchASCIIChar;) {
5817 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5819 out_label = out;
5820 for(i=label_start; i<cchASCIIChar; i++) {
5821 ch = lpASCIICharStr[i];
5823 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5824 SetLastError(ERROR_INVALID_NAME);
5825 return 0;
5828 if(!ch || ch=='.')
5829 break;
5830 if(ch == '-')
5831 delim = i;
5833 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5834 continue;
5835 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5836 || (ch>='0' && ch<='9') || ch=='-')
5837 continue;
5839 SetLastError(ERROR_INVALID_NAME);
5840 return 0;
5842 label_end = i;
5843 /* last label may be empty */
5844 if(label_start==label_end && ch) {
5845 SetLastError(ERROR_INVALID_NAME);
5846 return 0;
5849 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5850 lpASCIICharStr[label_end-1]=='-')) {
5851 SetLastError(ERROR_INVALID_NAME);
5852 return 0;
5854 if(label_end-label_start > 63) {
5855 SetLastError(ERROR_INVALID_NAME);
5856 return 0;
5859 if(label_end-label_start<4 ||
5860 tolowerW(lpASCIICharStr[label_start])!='x' ||
5861 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5862 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5863 if(label_end < cchASCIIChar)
5864 label_end++;
5866 if(!lpUnicodeCharStr) {
5867 out += label_end-label_start;
5868 }else if(out+label_end-label_start <= cchUnicodeChar) {
5869 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5870 (label_end-label_start)*sizeof(WCHAR));
5871 out += label_end-label_start;
5872 }else {
5873 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5874 return 0;
5877 label_start = label_end;
5878 continue;
5881 if(delim == label_start+3)
5882 delim++;
5883 if(!lpUnicodeCharStr) {
5884 out += delim-label_start-4;
5885 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5886 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5887 (delim-label_start-4)*sizeof(WCHAR));
5888 out += delim-label_start-4;
5889 }else {
5890 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5891 return 0;
5893 if(out != out_label)
5894 delim++;
5896 for(i=delim; i<label_end;) {
5897 old_pos = pos;
5898 w = 1;
5899 for(k=BASE; ; k+=BASE) {
5900 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5901 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5902 SetLastError(ERROR_INVALID_NAME);
5903 return 0;
5905 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5906 pos += digit*w;
5907 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5908 if(digit < t)
5909 break;
5910 w *= BASE-t;
5912 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5913 n += pos/(out-out_label+1);
5914 pos %= out-out_label+1;
5916 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5917 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5918 SetLastError(ERROR_INVALID_NAME);
5919 return 0;
5921 if(!lpUnicodeCharStr) {
5922 out++;
5923 }else if(out+1 <= cchASCIIChar) {
5924 memmove(lpUnicodeCharStr+out_label+pos+1,
5925 lpUnicodeCharStr+out_label+pos,
5926 (out-out_label-pos)*sizeof(WCHAR));
5927 lpUnicodeCharStr[out_label+pos] = n;
5928 out++;
5929 }else {
5930 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5931 return 0;
5933 pos++;
5936 if(out-out_label > 63) {
5937 SetLastError(ERROR_INVALID_NAME);
5938 return 0;
5941 if(label_end < cchASCIIChar) {
5942 if(!lpUnicodeCharStr) {
5943 out++;
5944 }else if(out+1 <= cchUnicodeChar) {
5945 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5946 }else {
5947 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5948 return 0;
5951 label_start = label_end+1;
5954 return out;
5958 /******************************************************************************
5959 * GetFileMUIPath (KERNEL32.@)
5962 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5963 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5965 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5966 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5968 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5970 return FALSE;
5973 /******************************************************************************
5974 * GetFileMUIInfo (KERNEL32.@)
5977 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5979 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5981 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5982 return FALSE;
5985 /******************************************************************************
5986 * ResolveLocaleName (KERNEL32.@)
5989 INT WINAPI ResolveLocaleName(LPCWSTR name, LPWSTR localename, INT len)
5991 FIXME("stub: %s, %p, %d\n", wine_dbgstr_w(name), localename, len);
5993 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5994 return 0;
5997 /******************************************************************************
5998 * FindNLSStringEx (KERNEL32.@)
6001 INT WINAPI FindNLSStringEx(const WCHAR *localename, DWORD flags, const WCHAR *src,
6002 INT src_size, const WCHAR *value, INT value_size,
6003 INT *found, NLSVERSIONINFO *version_info, void *reserved,
6004 LPARAM sort_handle)
6007 /* FIXME: this function should normalize strings before calling CompareStringEx() */
6008 DWORD mask = flags;
6009 int offset, inc, count;
6011 TRACE("%s %x %s %d %s %d %p %p %p %ld\n", wine_dbgstr_w(localename), flags,
6012 wine_dbgstr_w(src), src_size, wine_dbgstr_w(value), value_size, found,
6013 version_info, reserved, sort_handle);
6015 if (version_info != NULL || reserved != NULL || sort_handle != 0 ||
6016 !IsValidLocaleName(localename) || src == NULL || src_size == 0 ||
6017 src_size < -1 || value == NULL || value_size == 0 || value_size < -1)
6019 SetLastError(ERROR_INVALID_PARAMETER);
6020 return -1;
6022 if (src_size == -1)
6023 src_size = strlenW(src);
6024 if (value_size == -1)
6025 value_size = strlenW(value);
6027 src_size -= value_size;
6028 if (src_size < 0) return -1;
6030 mask = flags & ~(FIND_FROMSTART | FIND_FROMEND | FIND_STARTSWITH | FIND_ENDSWITH);
6031 count = flags & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
6032 offset = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
6033 inc = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
6034 while (count--)
6036 if (CompareStringEx(localename, mask, src + offset, value_size, value, value_size, NULL, NULL, 0) == CSTR_EQUAL)
6038 if (found)
6039 *found = value_size;
6040 return offset;
6042 offset += inc;
6045 return -1;
6048 /******************************************************************************
6049 * FindStringOrdinal (KERNEL32.@)
6052 INT WINAPI FindStringOrdinal(DWORD flag, const WCHAR *src, INT src_size, const WCHAR *val, INT val_size,
6053 BOOL ignore_case)
6055 INT offset, inc, count;
6056 TRACE("%#x %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size, wine_dbgstr_w(val), val_size, ignore_case);
6058 if (!src || !val)
6060 SetLastError(ERROR_INVALID_PARAMETER);
6061 return -1;
6064 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
6066 SetLastError(ERROR_INVALID_FLAGS);
6067 return -1;
6070 if (src_size == -1) src_size = strlenW(src);
6071 if (val_size == -1) val_size = strlenW(val);
6073 src_size -= val_size;
6074 if (src_size < 0)
6076 SetLastError(NO_ERROR);
6077 return -1;
6080 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
6081 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
6082 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
6083 while (count--)
6085 if (CompareStringOrdinal(src + offset, val_size, val, val_size, ignore_case) == CSTR_EQUAL)
6087 SetLastError(NO_ERROR);
6088 return offset;
6090 offset += inc;
6093 SetLastError(NO_ERROR);
6094 return -1;