ntdll: Add stub for RtlGetUnloadEventTraceEx.
[wine.git] / dlls / kernel32 / locale.c
blobe62b849d0917e8f975622d0bf80f4a6c14f3279a
1 /*
2 * Locale support
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2002 Alexandre Julliard for CodeWeavers
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFLocale.h>
37 # include <CoreFoundation/CFString.h>
38 #endif
40 #include "ntstatus.h"
41 #define WIN32_NO_STATUS
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h" /* for RT_STRINGW */
45 #include "winternl.h"
46 #include "wine/unicode.h"
47 #include "winnls.h"
48 #include "winerror.h"
49 #include "winver.h"
50 #include "kernel_private.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(nls);
55 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
56 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
57 #define MB_FLAGSMASK (MB_PRECOMPOSED|MB_COMPOSITE|MB_USEGLYPHCHARS|MB_ERR_INVALID_CHARS)
58 #define WC_FLAGSMASK (WC_DISCARDNS|WC_SEPCHARS|WC_DEFAULTCHAR|WC_ERR_INVALID_CHARS|\
59 WC_COMPOSITECHECK|WC_NO_BEST_FIT_CHARS)
61 /* current code pages */
62 static const union cptable *ansi_cptable;
63 static const union cptable *oem_cptable;
64 static const union cptable *mac_cptable;
65 static const union cptable *unix_cptable; /* NULL if UTF8 */
67 static const WCHAR szLocaleKeyName[] = {
68 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
69 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
70 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
73 static const WCHAR szLangGroupsKeyName[] = {
74 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
75 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
76 'C','o','n','t','r','o','l','\\','N','l','s','\\',
77 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
80 /* Charset to codepage map, sorted by name. */
81 static const struct charset_entry
83 const char *charset_name;
84 UINT codepage;
85 } charset_names[] =
87 { "BIG5", 950 },
88 { "CP1250", 1250 },
89 { "CP1251", 1251 },
90 { "CP1252", 1252 },
91 { "CP1253", 1253 },
92 { "CP1254", 1254 },
93 { "CP1255", 1255 },
94 { "CP1256", 1256 },
95 { "CP1257", 1257 },
96 { "CP1258", 1258 },
97 { "CP932", 932 },
98 { "CP936", 936 },
99 { "CP949", 949 },
100 { "CP950", 950 },
101 { "EUCJP", 20932 },
102 { "GB2312", 936 },
103 { "IBM037", 37 },
104 { "IBM1026", 1026 },
105 { "IBM424", 424 },
106 { "IBM437", 437 },
107 { "IBM500", 500 },
108 { "IBM850", 850 },
109 { "IBM852", 852 },
110 { "IBM855", 855 },
111 { "IBM857", 857 },
112 { "IBM860", 860 },
113 { "IBM861", 861 },
114 { "IBM862", 862 },
115 { "IBM863", 863 },
116 { "IBM864", 864 },
117 { "IBM865", 865 },
118 { "IBM866", 866 },
119 { "IBM869", 869 },
120 { "IBM874", 874 },
121 { "IBM875", 875 },
122 { "ISO88591", 28591 },
123 { "ISO885910", 28600 },
124 { "ISO885913", 28603 },
125 { "ISO885914", 28604 },
126 { "ISO885915", 28605 },
127 { "ISO885916", 28606 },
128 { "ISO88592", 28592 },
129 { "ISO88593", 28593 },
130 { "ISO88594", 28594 },
131 { "ISO88595", 28595 },
132 { "ISO88596", 28596 },
133 { "ISO88597", 28597 },
134 { "ISO88598", 28598 },
135 { "ISO88599", 28599 },
136 { "KOI8R", 20866 },
137 { "KOI8U", 21866 },
138 { "UTF8", CP_UTF8 }
142 struct locale_name
144 WCHAR win_name[128]; /* Windows name ("en-US") */
145 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
146 WCHAR *country; /* country ("US") */
147 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
148 WCHAR *script; /* script ("Latn") for Windows format only */
149 WCHAR *modifier; /* modifier or sort order */
150 LCID lcid; /* corresponding LCID */
151 int matches; /* number of elements matching LCID (0..4) */
152 UINT codepage; /* codepage corresponding to charset */
155 /* locale ids corresponding to the various Unix locale parameters */
156 static LCID lcid_LC_COLLATE;
157 static LCID lcid_LC_CTYPE;
158 static LCID lcid_LC_MESSAGES;
159 static LCID lcid_LC_MONETARY;
160 static LCID lcid_LC_NUMERIC;
161 static LCID lcid_LC_TIME;
162 static LCID lcid_LC_PAPER;
163 static LCID lcid_LC_MEASUREMENT;
164 static LCID lcid_LC_TELEPHONE;
166 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
167 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
168 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
169 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
170 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
171 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
172 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
173 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
174 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
175 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
176 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
177 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
178 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
179 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
180 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
181 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
182 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
183 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
184 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
185 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
186 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
187 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
188 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
189 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
190 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
191 static const WCHAR sListW[] = {'s','L','i','s','t',0};
192 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
193 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
194 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
195 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
196 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
197 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
198 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
199 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
200 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
201 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
202 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
203 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
204 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
206 static struct registry_value
208 DWORD lctype;
209 const WCHAR *name;
210 WCHAR *cached_value;
211 } registry_values[] =
213 { LOCALE_ICALENDARTYPE, iCalendarTypeW },
214 { LOCALE_ICURRDIGITS, iCurrDigitsW },
215 { LOCALE_ICURRENCY, iCurrencyW },
216 { LOCALE_IDIGITS, iDigitsW },
217 { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
218 { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
219 { LOCALE_ILZERO, iLZeroW },
220 { LOCALE_IMEASURE, iMeasureW },
221 { LOCALE_INEGCURR, iNegCurrW },
222 { LOCALE_INEGNUMBER, iNegNumberW },
223 { LOCALE_IPAPERSIZE, iPaperSizeW },
224 { LOCALE_ITIME, iTimeW },
225 { LOCALE_S1159, s1159W },
226 { LOCALE_S2359, s2359W },
227 { LOCALE_SCURRENCY, sCurrencyW },
228 { LOCALE_SDATE, sDateW },
229 { LOCALE_SDECIMAL, sDecimalW },
230 { LOCALE_SGROUPING, sGroupingW },
231 { LOCALE_SLIST, sListW },
232 { LOCALE_SLONGDATE, sLongDateW },
233 { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
234 { LOCALE_SMONGROUPING, sMonGroupingW },
235 { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
236 { LOCALE_SNEGATIVESIGN, sNegativeSignW },
237 { LOCALE_SPOSITIVESIGN, sPositiveSignW },
238 { LOCALE_SSHORTDATE, sShortDateW },
239 { LOCALE_STHOUSAND, sThousandW },
240 { LOCALE_STIME, sTimeW },
241 { LOCALE_STIMEFORMAT, sTimeFormatW },
242 { LOCALE_SYEARMONTH, sYearMonthW },
243 /* The following are not listed under MSDN as supported,
244 * but seem to be used and also stored in the registry.
246 { LOCALE_ICOUNTRY, iCountryW },
247 { LOCALE_IDATE, iDateW },
248 { LOCALE_ILDATE, iLDateW },
249 { LOCALE_ITLZERO, iTLZeroW },
250 { LOCALE_SCOUNTRY, sCountryW },
251 { LOCALE_SABBREVLANGNAME, sLanguageW },
252 /* The following are used in XP and later */
253 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
254 { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
255 { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
258 static CRITICAL_SECTION cache_section;
259 static CRITICAL_SECTION_DEBUG critsect_debug =
261 0, 0, &cache_section,
262 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
263 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
265 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
267 /* Copy Ascii string to Unicode without using codepages */
268 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
270 while (n > 1 && *src)
272 *dst++ = (unsigned char)*src++;
273 n--;
275 if (n) *dst = 0;
278 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
280 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
283 /***********************************************************************
284 * get_lcid_codepage
286 * Retrieve the ANSI codepage for a given locale.
288 static inline UINT get_lcid_codepage( LCID lcid )
290 UINT ret;
291 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
292 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
293 return ret;
297 /***********************************************************************
298 * get_codepage_table
300 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
302 static const union cptable *get_codepage_table( unsigned int codepage )
304 const union cptable *ret = NULL;
306 assert( ansi_cptable ); /* init must have been done already */
308 switch(codepage)
310 case CP_ACP:
311 return ansi_cptable;
312 case CP_OEMCP:
313 return oem_cptable;
314 case CP_MACCP:
315 return mac_cptable;
316 case CP_UTF7:
317 case CP_UTF8:
318 break;
319 case CP_THREAD_ACP:
320 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
321 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
322 if (!codepage) return ansi_cptable;
323 /* fall through */
324 default:
325 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
326 if (codepage == oem_cptable->info.codepage) return oem_cptable;
327 if (codepage == mac_cptable->info.codepage) return mac_cptable;
328 ret = wine_cp_get_table( codepage );
329 break;
331 return ret;
335 /***********************************************************************
336 * charset_cmp (internal)
338 static int charset_cmp( const void *name, const void *entry )
340 const struct charset_entry *charset = entry;
341 return strcasecmp( name, charset->charset_name );
344 /***********************************************************************
345 * find_charset
347 static UINT find_charset( const WCHAR *name )
349 const struct charset_entry *entry;
350 char charset_name[16];
351 size_t i, j;
353 /* remove punctuation characters from charset name */
354 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
355 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
356 charset_name[j] = 0;
358 entry = bsearch( charset_name, charset_names, ARRAY_SIZE( charset_names ),
359 sizeof(charset_names[0]), charset_cmp );
360 if (entry) return entry->codepage;
361 return 0;
364 static LANGID get_default_sublang( LANGID lang )
366 switch (lang)
368 case MAKELANGID( LANG_SPANISH, SUBLANG_NEUTRAL ):
369 return MAKELANGID( LANG_SPANISH, SUBLANG_SPANISH_MODERN );
370 case MAKELANGID( LANG_CHINESE, SUBLANG_NEUTRAL ):
371 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
372 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE ):
373 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
374 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ):
375 case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_MACAU ):
376 return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG );
378 if (SUBLANGID( lang ) == SUBLANG_NEUTRAL) lang = MAKELANGID( PRIMARYLANGID(lang), SUBLANG_DEFAULT );
379 return lang;
382 /***********************************************************************
383 * find_locale_id_callback
385 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
386 LPCWSTR name, LANGID lang, LPARAM lParam )
388 struct locale_name *data = (struct locale_name *)lParam;
389 WCHAR buffer[128];
390 int matches = 0;
391 LCID lcid = MAKELCID( lang, SORT_DEFAULT ); /* FIXME: handle sort order */
393 if (PRIMARYLANGID(lang) == LANG_NEUTRAL) return TRUE; /* continue search */
395 /* first check exact name */
396 if (data->win_name[0] &&
397 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, buffer, ARRAY_SIZE( buffer )))
399 if (!strcmpiW( data->win_name, buffer ))
401 matches = 4; /* everything matches */
402 goto done;
406 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
407 buffer, ARRAY_SIZE( buffer )))
408 return TRUE;
409 if (strcmpiW( buffer, data->lang )) return TRUE;
410 matches++; /* language name matched */
412 if (data->script)
414 if (GetLocaleInfoW( lcid, LOCALE_SSCRIPTS | LOCALE_NOUSEROVERRIDE,
415 buffer, ARRAY_SIZE( buffer )))
417 const WCHAR *p = buffer;
418 unsigned int len = strlenW( data->script );
419 while (*p)
421 if (!strncmpiW( p, data->script, len ) && (!p[len] || p[len] == ';')) break;
422 if (!(p = strchrW( p, ';'))) goto done;
423 p++;
425 if (!*p) goto done;
426 matches++; /* script matched */
430 if (data->country)
432 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
433 buffer, ARRAY_SIZE( buffer )))
435 if (strcmpiW( buffer, data->country )) goto done;
436 matches++; /* country name matched */
439 else /* match default language */
441 LANGID def_lang = data->script ? lang : MAKELANGID( PRIMARYLANGID(lang), LANG_NEUTRAL );
442 if (lang == get_default_sublang( def_lang )) matches++;
445 if (data->codepage)
447 UINT unix_cp;
448 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
449 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
451 if (unix_cp == data->codepage) matches++;
455 /* FIXME: check sort order */
457 done:
458 if (matches > data->matches)
460 data->lcid = lcid;
461 data->matches = matches;
463 return (data->matches < 4); /* no need to continue for perfect match */
467 /***********************************************************************
468 * parse_locale_name
470 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
471 * Unix format is: lang[_country][.charset][@modifier]
472 * Windows format is: lang[-script][-country][_modifier]
474 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
476 static const WCHAR sepW[] = {'-','_','.','@',0};
477 static const WCHAR winsepW[] = {'-','_',0};
478 static const WCHAR posixW[] = {'P','O','S','I','X',0};
479 static const WCHAR cW[] = {'C',0};
480 static const WCHAR latinW[] = {'l','a','t','i','n',0};
481 static const WCHAR latnW[] = {'-','L','a','t','n',0};
482 WCHAR *p;
484 TRACE("%s\n", debugstr_w(str));
486 name->country = name->charset = name->script = name->modifier = NULL;
487 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
488 name->matches = 0;
489 name->codepage = 0;
490 name->win_name[0] = 0;
491 lstrcpynW( name->lang, str, ARRAY_SIZE( name->lang ));
493 if (!*name->lang)
495 name->lcid = LOCALE_INVARIANT;
496 name->matches = 4;
497 return;
500 if (!(p = strpbrkW( name->lang, sepW )))
502 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
504 name->matches = 4; /* perfect match for default English lcid */
505 return;
507 strcpyW( name->win_name, name->lang );
509 else if (*p == '-') /* Windows format */
511 strcpyW( name->win_name, name->lang );
512 *p++ = 0;
513 name->country = p;
514 if ((p = strpbrkW( p, winsepW )) && *p == '-')
516 *p++ = 0;
517 name->script = name->country;
518 name->country = p;
519 p = strpbrkW( p, winsepW );
521 if (p)
523 *p++ = 0;
524 name->modifier = p;
526 /* second value can be script or country, check length to resolve the ambiguity */
527 if (!name->script && strlenW( name->country ) == 4)
529 name->script = name->country;
530 name->country = NULL;
533 else /* Unix format */
535 if (*p == '_')
537 *p++ = 0;
538 name->country = p;
539 p = strpbrkW( p, sepW + 2 );
541 if (p && *p == '.')
543 *p++ = 0;
544 name->charset = p;
545 p = strchrW( p, '@' );
547 if (p)
549 *p++ = 0;
550 name->modifier = p;
553 if (name->charset)
554 name->codepage = find_charset( name->charset );
556 /* rebuild a Windows name if possible */
558 if (name->charset) goto done; /* can't specify charset in Windows format */
559 if (name->modifier && strcmpW( name->modifier, latinW ))
560 goto done; /* only Latn script supported for now */
561 strcpyW( name->win_name, name->lang );
562 if (name->modifier) strcatW( name->win_name, latnW );
563 if (name->country)
565 p = name->win_name + strlenW(name->win_name);
566 *p++ = '-';
567 strcpyW( p, name->country );
570 done:
571 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
572 find_locale_id_callback, (LPARAM)name );
576 /***********************************************************************
577 * convert_default_lcid
579 * Get the default LCID to use for a given lctype in GetLocaleInfo.
581 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
583 if (lcid == LOCALE_SYSTEM_DEFAULT ||
584 lcid == LOCALE_USER_DEFAULT ||
585 lcid == LOCALE_NEUTRAL)
587 LCID default_id = 0;
589 switch(lctype & 0xffff)
591 case LOCALE_SSORTNAME:
592 default_id = lcid_LC_COLLATE;
593 break;
595 case LOCALE_FONTSIGNATURE:
596 case LOCALE_IDEFAULTANSICODEPAGE:
597 case LOCALE_IDEFAULTCODEPAGE:
598 case LOCALE_IDEFAULTEBCDICCODEPAGE:
599 case LOCALE_IDEFAULTMACCODEPAGE:
600 case LOCALE_IDEFAULTUNIXCODEPAGE:
601 default_id = lcid_LC_CTYPE;
602 break;
604 case LOCALE_ICURRDIGITS:
605 case LOCALE_ICURRENCY:
606 case LOCALE_IINTLCURRDIGITS:
607 case LOCALE_INEGCURR:
608 case LOCALE_INEGSEPBYSPACE:
609 case LOCALE_INEGSIGNPOSN:
610 case LOCALE_INEGSYMPRECEDES:
611 case LOCALE_IPOSSEPBYSPACE:
612 case LOCALE_IPOSSIGNPOSN:
613 case LOCALE_IPOSSYMPRECEDES:
614 case LOCALE_SCURRENCY:
615 case LOCALE_SINTLSYMBOL:
616 case LOCALE_SMONDECIMALSEP:
617 case LOCALE_SMONGROUPING:
618 case LOCALE_SMONTHOUSANDSEP:
619 case LOCALE_SNATIVECURRNAME:
620 default_id = lcid_LC_MONETARY;
621 break;
623 case LOCALE_IDIGITS:
624 case LOCALE_IDIGITSUBSTITUTION:
625 case LOCALE_ILZERO:
626 case LOCALE_INEGNUMBER:
627 case LOCALE_SDECIMAL:
628 case LOCALE_SGROUPING:
629 case LOCALE_SNAN:
630 case LOCALE_SNATIVEDIGITS:
631 case LOCALE_SNEGATIVESIGN:
632 case LOCALE_SNEGINFINITY:
633 case LOCALE_SPOSINFINITY:
634 case LOCALE_SPOSITIVESIGN:
635 case LOCALE_STHOUSAND:
636 default_id = lcid_LC_NUMERIC;
637 break;
639 case LOCALE_ICALENDARTYPE:
640 case LOCALE_ICENTURY:
641 case LOCALE_IDATE:
642 case LOCALE_IDAYLZERO:
643 case LOCALE_IFIRSTDAYOFWEEK:
644 case LOCALE_IFIRSTWEEKOFYEAR:
645 case LOCALE_ILDATE:
646 case LOCALE_IMONLZERO:
647 case LOCALE_IOPTIONALCALENDAR:
648 case LOCALE_ITIME:
649 case LOCALE_ITIMEMARKPOSN:
650 case LOCALE_ITLZERO:
651 case LOCALE_S1159:
652 case LOCALE_S2359:
653 case LOCALE_SABBREVDAYNAME1:
654 case LOCALE_SABBREVDAYNAME2:
655 case LOCALE_SABBREVDAYNAME3:
656 case LOCALE_SABBREVDAYNAME4:
657 case LOCALE_SABBREVDAYNAME5:
658 case LOCALE_SABBREVDAYNAME6:
659 case LOCALE_SABBREVDAYNAME7:
660 case LOCALE_SABBREVMONTHNAME1:
661 case LOCALE_SABBREVMONTHNAME2:
662 case LOCALE_SABBREVMONTHNAME3:
663 case LOCALE_SABBREVMONTHNAME4:
664 case LOCALE_SABBREVMONTHNAME5:
665 case LOCALE_SABBREVMONTHNAME6:
666 case LOCALE_SABBREVMONTHNAME7:
667 case LOCALE_SABBREVMONTHNAME8:
668 case LOCALE_SABBREVMONTHNAME9:
669 case LOCALE_SABBREVMONTHNAME10:
670 case LOCALE_SABBREVMONTHNAME11:
671 case LOCALE_SABBREVMONTHNAME12:
672 case LOCALE_SABBREVMONTHNAME13:
673 case LOCALE_SDATE:
674 case LOCALE_SDAYNAME1:
675 case LOCALE_SDAYNAME2:
676 case LOCALE_SDAYNAME3:
677 case LOCALE_SDAYNAME4:
678 case LOCALE_SDAYNAME5:
679 case LOCALE_SDAYNAME6:
680 case LOCALE_SDAYNAME7:
681 case LOCALE_SDURATION:
682 case LOCALE_SLONGDATE:
683 case LOCALE_SMONTHNAME1:
684 case LOCALE_SMONTHNAME2:
685 case LOCALE_SMONTHNAME3:
686 case LOCALE_SMONTHNAME4:
687 case LOCALE_SMONTHNAME5:
688 case LOCALE_SMONTHNAME6:
689 case LOCALE_SMONTHNAME7:
690 case LOCALE_SMONTHNAME8:
691 case LOCALE_SMONTHNAME9:
692 case LOCALE_SMONTHNAME10:
693 case LOCALE_SMONTHNAME11:
694 case LOCALE_SMONTHNAME12:
695 case LOCALE_SMONTHNAME13:
696 case LOCALE_SSHORTDATE:
697 case LOCALE_SSHORTESTDAYNAME1:
698 case LOCALE_SSHORTESTDAYNAME2:
699 case LOCALE_SSHORTESTDAYNAME3:
700 case LOCALE_SSHORTESTDAYNAME4:
701 case LOCALE_SSHORTESTDAYNAME5:
702 case LOCALE_SSHORTESTDAYNAME6:
703 case LOCALE_SSHORTESTDAYNAME7:
704 case LOCALE_STIME:
705 case LOCALE_STIMEFORMAT:
706 case LOCALE_SYEARMONTH:
707 default_id = lcid_LC_TIME;
708 break;
710 case LOCALE_IPAPERSIZE:
711 default_id = lcid_LC_PAPER;
712 break;
714 case LOCALE_IMEASURE:
715 default_id = lcid_LC_MEASUREMENT;
716 break;
718 case LOCALE_ICOUNTRY:
719 default_id = lcid_LC_TELEPHONE;
720 break;
722 if (default_id) lcid = default_id;
724 return ConvertDefaultLocale( lcid );
727 /***********************************************************************
728 * is_genitive_name_supported
730 * Determine could LCTYPE basically support genitive name form or not.
732 static BOOL is_genitive_name_supported( LCTYPE lctype )
734 switch(lctype & 0xffff)
736 case LOCALE_SMONTHNAME1:
737 case LOCALE_SMONTHNAME2:
738 case LOCALE_SMONTHNAME3:
739 case LOCALE_SMONTHNAME4:
740 case LOCALE_SMONTHNAME5:
741 case LOCALE_SMONTHNAME6:
742 case LOCALE_SMONTHNAME7:
743 case LOCALE_SMONTHNAME8:
744 case LOCALE_SMONTHNAME9:
745 case LOCALE_SMONTHNAME10:
746 case LOCALE_SMONTHNAME11:
747 case LOCALE_SMONTHNAME12:
748 case LOCALE_SMONTHNAME13:
749 return TRUE;
750 default:
751 return FALSE;
755 /***********************************************************************
756 * create_registry_key
758 * Create the Control Panel\\International registry key.
760 static inline HANDLE create_registry_key(void)
762 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
763 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
764 OBJECT_ATTRIBUTES attr;
765 UNICODE_STRING nameW;
766 HANDLE cpl_key, hkey = 0;
768 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
770 attr.Length = sizeof(attr);
771 attr.RootDirectory = hkey;
772 attr.ObjectName = &nameW;
773 attr.Attributes = 0;
774 attr.SecurityDescriptor = NULL;
775 attr.SecurityQualityOfService = NULL;
776 RtlInitUnicodeString( &nameW, cplW );
778 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
780 NtClose( attr.RootDirectory );
781 attr.RootDirectory = cpl_key;
782 RtlInitUnicodeString( &nameW, intlW );
783 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
785 NtClose( attr.RootDirectory );
786 return hkey;
790 /* update the registry settings for a given locale parameter */
791 /* return TRUE if an update was needed */
792 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
793 const LCTYPE *values, UINT nb_values )
795 static const WCHAR formatW[] = { '%','0','8','x',0 };
796 WCHAR bufferW[40];
797 UNICODE_STRING nameW;
798 DWORD count, i;
800 RtlInitUnicodeString( &nameW, name );
801 count = sizeof(bufferW);
802 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
804 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
805 LPCWSTR text = (LPCWSTR)info->Data;
807 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
808 TRACE( "updating registry, locale %s changed %s -> %08x\n",
809 debugstr_w(name), debugstr_w(text), lcid );
811 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
812 sprintfW( bufferW, formatW, lcid );
813 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
815 for (i = 0; i < nb_values; i++)
817 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW, ARRAY_SIZE( bufferW ));
818 SetLocaleInfoW( lcid, values[i], bufferW );
820 return TRUE;
824 /***********************************************************************
825 * LOCALE_InitRegistry
827 * Update registry contents on startup if the user locale has changed.
828 * This simulates the action of the Windows control panel.
830 void LOCALE_InitRegistry(void)
832 static const WCHAR acpW[] = {'A','C','P',0};
833 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
834 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
835 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
836 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
837 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
838 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
839 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
840 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
841 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
842 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
843 static const struct
845 LPCWSTR name;
846 USHORT value;
847 } update_cp_values[] = {
848 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
849 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
850 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
852 static const LCTYPE lc_messages_values[] = {
853 LOCALE_SABBREVLANGNAME,
854 LOCALE_SCOUNTRY,
855 LOCALE_SLIST };
856 static const LCTYPE lc_monetary_values[] = {
857 LOCALE_SCURRENCY,
858 LOCALE_ICURRENCY,
859 LOCALE_INEGCURR,
860 LOCALE_ICURRDIGITS,
861 LOCALE_ILZERO,
862 LOCALE_SMONDECIMALSEP,
863 LOCALE_SMONGROUPING,
864 LOCALE_SMONTHOUSANDSEP };
865 static const LCTYPE lc_numeric_values[] = {
866 LOCALE_SDECIMAL,
867 LOCALE_STHOUSAND,
868 LOCALE_IDIGITS,
869 LOCALE_IDIGITSUBSTITUTION,
870 LOCALE_SNATIVEDIGITS,
871 LOCALE_INEGNUMBER,
872 LOCALE_SNEGATIVESIGN,
873 LOCALE_SPOSITIVESIGN,
874 LOCALE_SGROUPING };
875 static const LCTYPE lc_time_values[] = {
876 LOCALE_S1159,
877 LOCALE_S2359,
878 LOCALE_STIME,
879 LOCALE_ITIME,
880 LOCALE_ITLZERO,
881 LOCALE_SSHORTDATE,
882 LOCALE_SLONGDATE,
883 LOCALE_SDATE,
884 LOCALE_ITIMEMARKPOSN,
885 LOCALE_ICALENDARTYPE,
886 LOCALE_IFIRSTDAYOFWEEK,
887 LOCALE_IFIRSTWEEKOFYEAR,
888 LOCALE_STIMEFORMAT,
889 LOCALE_SYEARMONTH,
890 LOCALE_IDATE };
891 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
892 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
893 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
895 UNICODE_STRING nameW;
896 WCHAR bufferW[80];
897 DWORD count, i;
898 HANDLE hkey;
899 LCID lcid = GetUserDefaultLCID();
901 if (!(hkey = create_registry_key()))
902 return; /* don't do anything if we can't create the registry key */
904 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
905 ARRAY_SIZE( lc_messages_values ));
906 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
907 ARRAY_SIZE( lc_monetary_values ));
908 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
909 ARRAY_SIZE( lc_numeric_values ));
910 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
911 ARRAY_SIZE( lc_time_values ));
912 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
913 ARRAY_SIZE( lc_measurement_values ));
914 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
915 ARRAY_SIZE( lc_telephone_values ));
916 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
917 ARRAY_SIZE( lc_paper_values ));
919 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
921 static const WCHAR codepageW[] =
922 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
923 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
924 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
926 OBJECT_ATTRIBUTES attr;
927 HANDLE nls_key;
928 DWORD len = 14;
930 RtlInitUnicodeString( &nameW, codepageW );
931 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
932 while (codepageW[len])
934 nameW.Length = len * sizeof(WCHAR);
935 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
936 NtClose( nls_key );
937 len++;
938 while (codepageW[len] && codepageW[len] != '\\') len++;
940 nameW.Length = len * sizeof(WCHAR);
941 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
943 for (i = 0; i < ARRAY_SIZE( update_cp_values ); i++)
945 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
946 bufferW, ARRAY_SIZE( bufferW ));
947 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
948 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
950 NtClose( nls_key );
954 NtClose( hkey );
958 #ifdef __APPLE__
959 /***********************************************************************
960 * get_mac_locale
962 * Return a locale identifier string reflecting the Mac locale, in a form
963 * that parse_locale_name() will understand. So, strip out unusual
964 * things like script, variant, etc. Or, rather, just construct it as
965 * <lang>[_<country>].UTF-8.
967 static const char* get_mac_locale(void)
969 static char mac_locale[50];
971 if (!mac_locale[0])
973 CFLocaleRef locale = CFLocaleCopyCurrent();
974 CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
975 CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
976 CFStringRef locale_string;
978 if (country)
979 locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
980 else
981 locale_string = CFStringCreateCopy(NULL, lang);
983 CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
984 strcat(mac_locale, ".UTF-8");
986 CFRelease(locale);
987 CFRelease(locale_string);
990 return mac_locale;
994 /***********************************************************************
995 * has_env
997 static BOOL has_env(const char* name)
999 const char* value = getenv( name );
1000 return value && value[0];
1002 #endif
1005 /***********************************************************************
1006 * get_locale
1008 * Get the locale identifier for a given category. On most platforms,
1009 * this is just a thin wrapper around setlocale(). On OS X, though, it
1010 * is common for the Mac locale settings to not be supported by the C
1011 * library. So, we sometimes override the result with the Mac locale.
1013 static const char* get_locale(int category, const char* category_name)
1015 const char* ret = setlocale(category, NULL);
1017 #ifdef __ANDROID__
1018 if (!strcmp(ret, "C"))
1020 ret = getenv( category_name );
1021 if (!ret || !ret[0]) ret = getenv( "LC_ALL" );
1022 if (!ret || !ret[0]) ret = "C";
1024 #endif
1026 #ifdef __APPLE__
1027 /* If LC_ALL is set, respect it as a user override.
1028 If LC_* is set, respect it as a user override, except if it's LC_CTYPE
1029 and equal to UTF-8. That's because, when the Mac locale isn't supported
1030 by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
1031 parse_locale_name() doesn't handle that properly, so we override that
1032 with the Mac locale (which uses UTF-8 for the charset, anyway).
1033 Otherwise:
1034 For LC_MESSAGES, we override the C library because the user language
1035 setting is separate from the locale setting on which LANG was based.
1036 If the C library didn't get anything better from LANG than C or POSIX,
1037 override that. That probably means the Mac locale isn't supported by
1038 the C library. */
1039 if (!has_env( "LC_ALL" ) &&
1040 ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
1041 (!has_env( category_name ) &&
1042 (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
1044 const char* override = get_mac_locale();
1046 if (category == LC_MESSAGES)
1048 /* Retrieve the preferred language as chosen in System Preferences. */
1049 static char messages_locale[50];
1051 if (!messages_locale[0])
1053 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1054 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1056 CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1057 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1058 if (components)
1060 CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1061 CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1062 CFLocaleRef locale = NULL;
1063 CFStringRef locale_string;
1065 if (!country)
1067 locale = CFLocaleCopyCurrent();
1068 country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1071 if (country)
1072 locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1073 else
1074 locale_string = CFStringCreateCopy( NULL, lang );
1075 CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1076 strcat( messages_locale, ".UTF-8" );
1078 CFRelease( locale_string );
1079 if (locale) CFRelease( locale );
1080 CFRelease( components );
1083 if (preferred_langs)
1084 CFRelease( preferred_langs );
1087 if (messages_locale[0])
1088 override = messages_locale;
1091 TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1092 ret = override;
1094 #endif
1096 return ret;
1100 /***********************************************************************
1101 * setup_unix_locales
1103 static UINT setup_unix_locales(void)
1105 struct locale_name locale_name;
1106 WCHAR buffer[128], ctype_buff[128];
1107 const char *locale;
1108 UINT unix_cp = 0;
1110 if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1112 strcpynAtoW( ctype_buff, locale, ARRAY_SIZE( ctype_buff ));
1113 parse_locale_name( ctype_buff, &locale_name );
1114 lcid_LC_CTYPE = locale_name.lcid;
1115 unix_cp = locale_name.codepage;
1117 if (!lcid_LC_CTYPE) /* this one needs a default value */
1118 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
1120 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1121 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
1123 #define GET_UNIX_LOCALE(cat) do \
1124 if ((locale = get_locale( cat, #cat ))) \
1126 strcpynAtoW( buffer, locale, ARRAY_SIZE(buffer) ); \
1127 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1128 else { \
1129 parse_locale_name( buffer, &locale_name ); \
1130 lcid_##cat = locale_name.lcid; \
1131 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
1132 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1134 } while (0)
1136 GET_UNIX_LOCALE( LC_COLLATE );
1137 GET_UNIX_LOCALE( LC_MESSAGES );
1138 GET_UNIX_LOCALE( LC_MONETARY );
1139 GET_UNIX_LOCALE( LC_NUMERIC );
1140 GET_UNIX_LOCALE( LC_TIME );
1141 #ifdef LC_PAPER
1142 GET_UNIX_LOCALE( LC_PAPER );
1143 #endif
1144 #ifdef LC_MEASUREMENT
1145 GET_UNIX_LOCALE( LC_MEASUREMENT );
1146 #endif
1147 #ifdef LC_TELEPHONE
1148 GET_UNIX_LOCALE( LC_TELEPHONE );
1149 #endif
1151 #undef GET_UNIX_LOCALE
1153 return unix_cp;
1157 /***********************************************************************
1158 * GetUserDefaultLangID (KERNEL32.@)
1160 * Get the default language Id for the current user.
1162 * PARAMS
1163 * None.
1165 * RETURNS
1166 * The current LANGID of the default language for the current user.
1168 LANGID WINAPI GetUserDefaultLangID(void)
1170 return LANGIDFROMLCID(GetUserDefaultLCID());
1174 /***********************************************************************
1175 * GetSystemDefaultLangID (KERNEL32.@)
1177 * Get the default language Id for the system.
1179 * PARAMS
1180 * None.
1182 * RETURNS
1183 * The current LANGID of the default language for the system.
1185 LANGID WINAPI GetSystemDefaultLangID(void)
1187 return LANGIDFROMLCID(GetSystemDefaultLCID());
1191 /***********************************************************************
1192 * GetUserDefaultLCID (KERNEL32.@)
1194 * Get the default locale Id for the current user.
1196 * PARAMS
1197 * None.
1199 * RETURNS
1200 * The current LCID of the default locale for the current user.
1202 LCID WINAPI GetUserDefaultLCID(void)
1204 LCID lcid;
1205 NtQueryDefaultLocale( TRUE, &lcid );
1206 return lcid;
1210 /***********************************************************************
1211 * GetSystemDefaultLCID (KERNEL32.@)
1213 * Get the default locale Id for the system.
1215 * PARAMS
1216 * None.
1218 * RETURNS
1219 * The current LCID of the default locale for the system.
1221 LCID WINAPI GetSystemDefaultLCID(void)
1223 LCID lcid;
1224 NtQueryDefaultLocale( FALSE, &lcid );
1225 return lcid;
1228 /***********************************************************************
1229 * GetSystemDefaultLocaleName (KERNEL32.@)
1231 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1233 LCID lcid = GetSystemDefaultLCID();
1234 return LCIDToLocaleName(lcid, localename, len, 0);
1237 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1239 LCTYPE type;
1240 int lsize;
1242 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1244 if (flags & MUI_LANGUAGE_ID)
1245 type = LOCALE_ILANGUAGE;
1246 else
1247 type = LOCALE_SNAME;
1249 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1250 if (!lsize)
1252 /* keep last error from callee */
1253 return FALSE;
1255 lsize++;
1256 if (!*size)
1258 *size = lsize;
1259 *count = 1;
1260 return TRUE;
1263 if (lsize > *size)
1265 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1266 return FALSE;
1269 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1271 /* keep last error from callee */
1272 return FALSE;
1275 buffer[lsize-1] = 0;
1276 *size = lsize;
1277 *count = 1;
1278 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1279 return TRUE;
1283 /***********************************************************************
1284 * GetSystemPreferredUILanguages (KERNEL32.@)
1286 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1288 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1290 SetLastError(ERROR_INVALID_PARAMETER);
1291 return FALSE;
1293 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1295 SetLastError(ERROR_INVALID_PARAMETER);
1296 return FALSE;
1298 if (*size && !buffer)
1300 SetLastError(ERROR_INVALID_PARAMETER);
1301 return FALSE;
1304 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1307 /***********************************************************************
1308 * SetThreadPreferredUILanguages (KERNEL32.@)
1310 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1312 FIXME( "%u, %p, %p\n", flags, buffer, count );
1313 return TRUE;
1316 /***********************************************************************
1317 * GetThreadPreferredUILanguages (KERNEL32.@)
1319 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1321 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1322 return get_dummy_preferred_ui_language( flags, count, buf, size );
1325 /******************************************************************************
1326 * GetUserPreferredUILanguages (KERNEL32.@)
1328 BOOL WINAPI GetUserPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1330 TRACE( "%u %p %p %p\n", flags, count, buffer, size );
1332 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID))
1334 SetLastError(ERROR_INVALID_PARAMETER);
1335 return FALSE;
1337 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1339 SetLastError(ERROR_INVALID_PARAMETER);
1340 return FALSE;
1342 if (*size && !buffer)
1344 SetLastError(ERROR_INVALID_PARAMETER);
1345 return FALSE;
1348 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1351 /***********************************************************************
1352 * GetUserDefaultUILanguage (KERNEL32.@)
1354 * Get the default user interface language Id for the current user.
1356 * PARAMS
1357 * None.
1359 * RETURNS
1360 * The current LANGID of the default UI language for the current user.
1362 LANGID WINAPI GetUserDefaultUILanguage(void)
1364 LANGID lang;
1365 NtQueryDefaultUILanguage( &lang );
1366 return lang;
1370 /***********************************************************************
1371 * GetSystemDefaultUILanguage (KERNEL32.@)
1373 * Get the default user interface language Id for the system.
1375 * PARAMS
1376 * None.
1378 * RETURNS
1379 * The current LANGID of the default UI language for the system. This is
1380 * typically the same language used during the installation process.
1382 LANGID WINAPI GetSystemDefaultUILanguage(void)
1384 LANGID lang;
1385 NtQueryInstallUILanguage( &lang );
1386 return lang;
1390 /***********************************************************************
1391 * LocaleNameToLCID (KERNEL32.@)
1393 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1395 struct locale_name locale_name;
1396 static int once;
1398 if (flags && !once++)
1399 FIXME( "unsupported flags %x\n", flags );
1401 if (name == LOCALE_NAME_USER_DEFAULT)
1402 return GetUserDefaultLCID();
1404 /* string parsing */
1405 parse_locale_name( name, &locale_name );
1407 TRACE( "found lcid %x for %s, matches %d\n",
1408 locale_name.lcid, debugstr_w(name), locale_name.matches );
1410 if (!locale_name.matches)
1412 SetLastError(ERROR_INVALID_PARAMETER);
1413 return 0;
1416 if (locale_name.matches == 1)
1417 WARN( "locale %s not recognized, defaulting to %s\n",
1418 debugstr_w(name), debugstr_w(locale_name.lang) );
1420 return locale_name.lcid;
1424 /***********************************************************************
1425 * LCIDToLocaleName (KERNEL32.@)
1427 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1429 static int once;
1430 if (flags && !once++) FIXME( "unsupported flags %x\n", flags );
1432 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1436 /******************************************************************************
1437 * get_locale_registry_value
1439 * Gets the registry value name and cache for a given lctype.
1441 static struct registry_value *get_locale_registry_value( DWORD lctype )
1443 int i;
1444 for (i = 0; i < ARRAY_SIZE( registry_values ); i++)
1445 if (registry_values[i].lctype == lctype)
1446 return &registry_values[i];
1447 return NULL;
1451 /******************************************************************************
1452 * get_registry_locale_info
1454 * Retrieve user-modified locale info from the registry.
1455 * Return length, 0 on error, -1 if not found.
1457 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1459 DWORD size;
1460 INT ret;
1461 HANDLE hkey;
1462 NTSTATUS status;
1463 UNICODE_STRING nameW;
1464 KEY_VALUE_PARTIAL_INFORMATION *info;
1465 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1467 RtlEnterCriticalSection( &cache_section );
1469 if (!registry_value->cached_value)
1471 if (!(hkey = create_registry_key()))
1473 RtlLeaveCriticalSection( &cache_section );
1474 return -1;
1477 RtlInitUnicodeString( &nameW, registry_value->name );
1478 size = info_size + len * sizeof(WCHAR);
1480 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1482 NtClose( hkey );
1483 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1484 RtlLeaveCriticalSection( &cache_section );
1485 return 0;
1488 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1490 /* try again with a bigger buffer when we have to return the correct size */
1491 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1493 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1494 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1496 info = new_info;
1497 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1501 NtClose( hkey );
1503 if (!status)
1505 INT length = (size - info_size) / sizeof(WCHAR);
1506 LPWSTR cached_value;
1508 if (!length || ((WCHAR *)&info->Data)[length-1])
1509 length++;
1511 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1513 if (!cached_value)
1515 HeapFree( GetProcessHeap(), 0, info );
1516 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1517 RtlLeaveCriticalSection( &cache_section );
1518 return 0;
1521 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1522 cached_value[length-1] = 0;
1523 HeapFree( GetProcessHeap(), 0, info );
1524 registry_value->cached_value = cached_value;
1526 else
1528 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1530 ret = (size - info_size) / sizeof(WCHAR);
1532 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1534 ret = -1;
1536 else
1538 SetLastError( RtlNtStatusToDosError(status) );
1539 ret = 0;
1541 HeapFree( GetProcessHeap(), 0, info );
1542 RtlLeaveCriticalSection( &cache_section );
1543 return ret;
1547 ret = lstrlenW( registry_value->cached_value ) + 1;
1549 if (buffer)
1551 if (ret > len)
1553 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1554 ret = 0;
1556 else
1558 lstrcpyW( buffer, registry_value->cached_value );
1562 RtlLeaveCriticalSection( &cache_section );
1564 return ret;
1568 /******************************************************************************
1569 * GetLocaleInfoA (KERNEL32.@)
1571 * Get information about an aspect of a locale.
1573 * PARAMS
1574 * lcid [I] LCID of the locale
1575 * lctype [I] LCTYPE_ flags from "winnls.h"
1576 * buffer [O] Destination for the information
1577 * len [I] Length of buffer in characters
1579 * RETURNS
1580 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1581 * with the information.
1582 * Failure: 0. Use GetLastError() to determine the cause.
1584 * NOTES
1585 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1586 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1587 * which is a bit string.
1589 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1591 WCHAR *bufferW;
1592 INT lenW, ret;
1594 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1596 if (len < 0 || (len && !buffer))
1598 SetLastError( ERROR_INVALID_PARAMETER );
1599 return 0;
1601 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1602 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1604 SetLastError( ERROR_INVALID_FLAGS );
1605 return 0;
1608 if (!len) buffer = NULL;
1610 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1612 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1614 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1615 return 0;
1617 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1619 if ((lctype & LOCALE_RETURN_NUMBER) ||
1620 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1622 /* it's not an ASCII string, just bytes */
1623 ret *= sizeof(WCHAR);
1624 if (buffer)
1626 if (ret <= len) memcpy( buffer, bufferW, ret );
1627 else
1629 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1630 ret = 0;
1634 else
1636 UINT codepage = CP_ACP;
1637 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1638 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1641 HeapFree( GetProcessHeap(), 0, bufferW );
1642 return ret;
1645 static int get_value_base_by_lctype( LCTYPE lctype )
1647 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1650 /******************************************************************************
1651 * GetLocaleInfoW (KERNEL32.@)
1653 * See GetLocaleInfoA.
1655 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1657 LANGID lang_id;
1658 HRSRC hrsrc;
1659 HGLOBAL hmem;
1660 INT ret;
1661 UINT lcflags;
1662 const WCHAR *p;
1663 unsigned int i;
1665 if (len < 0 || (len && !buffer))
1667 SetLastError( ERROR_INVALID_PARAMETER );
1668 return 0;
1670 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1671 !is_genitive_name_supported( lctype ))
1673 SetLastError( ERROR_INVALID_FLAGS );
1674 return 0;
1677 if (!len) buffer = NULL;
1679 lcid = convert_default_lcid( lcid, lctype );
1681 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1682 lctype &= 0xffff;
1684 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1686 /* first check for overrides in the registry */
1688 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1689 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1691 struct registry_value *value = get_locale_registry_value(lctype);
1693 if (value)
1695 if (lcflags & LOCALE_RETURN_NUMBER)
1697 WCHAR tmp[16];
1698 ret = get_registry_locale_info( value, tmp, ARRAY_SIZE( tmp ));
1699 if (ret > 0)
1701 WCHAR *end;
1702 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1703 if (*end) /* invalid number */
1705 SetLastError( ERROR_INVALID_FLAGS );
1706 return 0;
1708 ret = sizeof(UINT)/sizeof(WCHAR);
1709 if (!buffer) return ret;
1710 if (ret > len)
1712 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1713 return 0;
1715 memcpy( buffer, &number, sizeof(number) );
1718 else ret = get_registry_locale_info( value, buffer, len );
1720 if (ret != -1) return ret;
1724 /* now load it from kernel resources */
1726 lang_id = LANGIDFROMLCID( lcid );
1728 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1729 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL) lang_id = get_default_sublang( lang_id );
1731 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1732 ULongToPtr((lctype >> 4) + 1), lang_id )))
1734 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1735 return 0;
1737 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1738 return 0;
1740 p = LockResource( hmem );
1741 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1743 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1744 else if (is_genitive_name_supported( lctype ) && *p)
1746 /* genitive form's stored after a null separator from a nominative */
1747 for (i = 1; i <= *p; i++) if (!p[i]) break;
1749 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1751 ret = *p - i + 1;
1752 p += i;
1754 else ret = i;
1756 else
1757 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1759 if (!buffer) return ret;
1761 if (ret > len)
1763 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1764 return 0;
1767 if (lcflags & LOCALE_RETURN_NUMBER)
1769 UINT number;
1770 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1771 if (!tmp) return 0;
1772 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1773 tmp[*p] = 0;
1774 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1775 if (!*end)
1776 memcpy( buffer, &number, sizeof(number) );
1777 else /* invalid number */
1779 SetLastError( ERROR_INVALID_FLAGS );
1780 ret = 0;
1782 HeapFree( GetProcessHeap(), 0, tmp );
1784 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1785 lcid, lctype, buffer, len, number );
1787 else
1789 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1790 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1792 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1793 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1795 return ret;
1798 /******************************************************************************
1799 * GetLocaleInfoEx (KERNEL32.@)
1801 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1803 LCID lcid = LocaleNameToLCID(locale, 0);
1805 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1807 if (!lcid) return 0;
1809 /* special handling for neutral locale names */
1810 if (locale && strlenW(locale) == 2)
1812 switch (info & ~LOCALE_LOCALEINFOFLAGSMASK)
1814 case LOCALE_SNAME:
1815 if (len && len < 3)
1817 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1818 return 0;
1820 if (len) strcpyW(buffer, locale);
1821 return 3;
1822 case LOCALE_SPARENT:
1823 if (len) buffer[0] = 0;
1824 return 1;
1828 return GetLocaleInfoW(lcid, info, buffer, len);
1831 /******************************************************************************
1832 * SetLocaleInfoA [KERNEL32.@]
1834 * Set information about an aspect of a locale.
1836 * PARAMS
1837 * lcid [I] LCID of the locale
1838 * lctype [I] LCTYPE_ flags from "winnls.h"
1839 * data [I] Information to set
1841 * RETURNS
1842 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1843 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1844 * Failure: FALSE. Use GetLastError() to determine the cause.
1846 * NOTES
1847 * - Values are only be set for the current user locale; the system locale
1848 * settings cannot be changed.
1849 * - Any settings changed by this call are lost when the locale is changed by
1850 * the control panel (in Wine, this happens every time you change LANG).
1851 * - The native implementation of this function does not check that lcid matches
1852 * the current user locale, and simply sets the new values. Wine warns you in
1853 * this case, but behaves the same.
1855 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1857 UINT codepage = CP_ACP;
1858 WCHAR *strW;
1859 DWORD len;
1860 BOOL ret;
1862 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1864 if (!data)
1866 SetLastError( ERROR_INVALID_PARAMETER );
1867 return FALSE;
1869 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1870 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1872 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1873 return FALSE;
1875 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1876 ret = SetLocaleInfoW( lcid, lctype, strW );
1877 HeapFree( GetProcessHeap(), 0, strW );
1878 return ret;
1882 /******************************************************************************
1883 * SetLocaleInfoW (KERNEL32.@)
1885 * See SetLocaleInfoA.
1887 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1889 struct registry_value *value;
1890 static const WCHAR intlW[] = {'i','n','t','l',0 };
1891 UNICODE_STRING valueW;
1892 NTSTATUS status;
1893 HANDLE hkey;
1895 lctype &= 0xffff;
1896 value = get_locale_registry_value( lctype );
1898 if (!data || !value)
1900 SetLastError( ERROR_INVALID_PARAMETER );
1901 return FALSE;
1904 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1906 SetLastError( ERROR_INVALID_FLAGS );
1907 return FALSE;
1910 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1912 /* FIXME: should check that data to set is sane */
1914 /* FIXME: profile functions should map to registry */
1915 WriteProfileStringW( intlW, value->name, data );
1917 if (!(hkey = create_registry_key())) return FALSE;
1918 RtlInitUnicodeString( &valueW, value->name );
1919 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1921 RtlEnterCriticalSection( &cache_section );
1922 HeapFree( GetProcessHeap(), 0, value->cached_value );
1923 value->cached_value = NULL;
1924 RtlLeaveCriticalSection( &cache_section );
1926 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1928 /* Set I-value from S value */
1929 WCHAR *lpD, *lpM, *lpY;
1930 WCHAR szBuff[2];
1932 lpD = strrchrW(data, 'd');
1933 lpM = strrchrW(data, 'M');
1934 lpY = strrchrW(data, 'y');
1936 if (lpD <= lpM)
1938 szBuff[0] = '1'; /* D-M-Y */
1940 else
1942 if (lpY <= lpM)
1943 szBuff[0] = '2'; /* Y-M-D */
1944 else
1945 szBuff[0] = '0'; /* M-D-Y */
1948 szBuff[1] = '\0';
1950 if (lctype == LOCALE_SSHORTDATE)
1951 lctype = LOCALE_IDATE;
1952 else
1953 lctype = LOCALE_ILDATE;
1955 value = get_locale_registry_value( lctype );
1957 WriteProfileStringW( intlW, value->name, szBuff );
1959 RtlInitUnicodeString( &valueW, value->name );
1960 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1962 RtlEnterCriticalSection( &cache_section );
1963 HeapFree( GetProcessHeap(), 0, value->cached_value );
1964 value->cached_value = NULL;
1965 RtlLeaveCriticalSection( &cache_section );
1968 NtClose( hkey );
1970 if (status) SetLastError( RtlNtStatusToDosError(status) );
1971 return !status;
1975 /******************************************************************************
1976 * GetACP (KERNEL32.@)
1978 * Get the current Ansi code page Id for the system.
1980 * PARAMS
1981 * None.
1983 * RETURNS
1984 * The current Ansi code page identifier for the system.
1986 UINT WINAPI GetACP(void)
1988 assert( ansi_cptable );
1989 return ansi_cptable->info.codepage;
1993 /******************************************************************************
1994 * SetCPGlobal (KERNEL32.@)
1996 * Set the current Ansi code page Id for the system.
1998 * PARAMS
1999 * acp [I] code page ID to be the new ACP.
2001 * RETURNS
2002 * The previous ACP.
2004 UINT WINAPI SetCPGlobal( UINT acp )
2006 UINT ret = GetACP();
2007 const union cptable *new_cptable = wine_cp_get_table( acp );
2009 if (new_cptable) ansi_cptable = new_cptable;
2010 return ret;
2014 /***********************************************************************
2015 * GetOEMCP (KERNEL32.@)
2017 * Get the current OEM code page Id for the system.
2019 * PARAMS
2020 * None.
2022 * RETURNS
2023 * The current OEM code page identifier for the system.
2025 UINT WINAPI GetOEMCP(void)
2027 assert( oem_cptable );
2028 return oem_cptable->info.codepage;
2032 /***********************************************************************
2033 * IsValidCodePage (KERNEL32.@)
2035 * Determine if a given code page identifier is valid.
2037 * PARAMS
2038 * codepage [I] Code page Id to verify.
2040 * RETURNS
2041 * TRUE, If codepage is valid and available on the system,
2042 * FALSE otherwise.
2044 BOOL WINAPI IsValidCodePage( UINT codepage )
2046 switch(codepage) {
2047 case CP_UTF7:
2048 case CP_UTF8:
2049 return TRUE;
2050 default:
2051 return wine_cp_get_table( codepage ) != NULL;
2056 /***********************************************************************
2057 * IsDBCSLeadByteEx (KERNEL32.@)
2059 * Determine if a character is a lead byte in a given code page.
2061 * PARAMS
2062 * codepage [I] Code page for the test.
2063 * testchar [I] Character to test
2065 * RETURNS
2066 * TRUE, if testchar is a lead byte in codepage,
2067 * FALSE otherwise.
2069 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
2071 const union cptable *table = get_codepage_table( codepage );
2072 return table && wine_is_dbcs_leadbyte( table, testchar );
2076 /***********************************************************************
2077 * IsDBCSLeadByte (KERNEL32.@)
2078 * IsDBCSLeadByte (KERNEL.207)
2080 * Determine if a character is a lead byte.
2082 * PARAMS
2083 * testchar [I] Character to test
2085 * RETURNS
2086 * TRUE, if testchar is a lead byte in the ANSI code page,
2087 * FALSE otherwise.
2089 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2091 if (!ansi_cptable) return FALSE;
2092 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2096 /***********************************************************************
2097 * GetCPInfo (KERNEL32.@)
2099 * Get information about a code page.
2101 * PARAMS
2102 * codepage [I] Code page number
2103 * cpinfo [O] Destination for code page information
2105 * RETURNS
2106 * Success: TRUE. cpinfo is updated with the information about codepage.
2107 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2109 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2111 const union cptable *table;
2113 if (!cpinfo)
2115 SetLastError( ERROR_INVALID_PARAMETER );
2116 return FALSE;
2119 if (!(table = get_codepage_table( codepage )))
2121 switch(codepage)
2123 case CP_UTF7:
2124 case CP_UTF8:
2125 cpinfo->DefaultChar[0] = 0x3f;
2126 cpinfo->DefaultChar[1] = 0;
2127 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2128 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2129 return TRUE;
2132 SetLastError( ERROR_INVALID_PARAMETER );
2133 return FALSE;
2135 if (table->info.def_char & 0xff00)
2137 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2138 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2140 else
2142 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2143 cpinfo->DefaultChar[1] = 0;
2145 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2146 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2147 else
2148 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2150 return TRUE;
2153 /***********************************************************************
2154 * GetCPInfoExA (KERNEL32.@)
2156 * Get extended information about a code page.
2158 * PARAMS
2159 * codepage [I] Code page number
2160 * dwFlags [I] Reserved, must to 0.
2161 * cpinfo [O] Destination for code page information
2163 * RETURNS
2164 * Success: TRUE. cpinfo is updated with the information about codepage.
2165 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2167 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2169 CPINFOEXW cpinfoW;
2171 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2172 return FALSE;
2174 /* the layout is the same except for CodePageName */
2175 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2176 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2177 return TRUE;
2180 /***********************************************************************
2181 * GetCPInfoExW (KERNEL32.@)
2183 * Unicode version of GetCPInfoExA.
2185 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2187 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2188 return FALSE;
2190 switch(codepage)
2192 case CP_UTF7:
2194 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2196 cpinfo->CodePage = CP_UTF7;
2197 cpinfo->UnicodeDefaultChar = 0x3f;
2198 strcpyW(cpinfo->CodePageName, utf7);
2199 break;
2202 case CP_UTF8:
2204 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2206 cpinfo->CodePage = CP_UTF8;
2207 cpinfo->UnicodeDefaultChar = 0x3f;
2208 strcpyW(cpinfo->CodePageName, utf8);
2209 break;
2212 default:
2214 const union cptable *table = get_codepage_table( codepage );
2216 cpinfo->CodePage = table->info.codepage;
2217 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2218 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2219 ARRAY_SIZE( cpinfo->CodePageName ));
2220 break;
2223 return TRUE;
2226 /***********************************************************************
2227 * EnumSystemCodePagesA (KERNEL32.@)
2229 * Call a user defined function for every code page installed on the system.
2231 * PARAMS
2232 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2233 * flags [I] Reserved, set to 0.
2235 * RETURNS
2236 * TRUE, If all code pages have been enumerated, or
2237 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2239 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2241 const union cptable *table;
2242 char buffer[10];
2243 int index = 0;
2245 for (;;)
2247 if (!(table = wine_cp_enum_table( index++ ))) break;
2248 sprintf( buffer, "%d", table->info.codepage );
2249 if (!lpfnCodePageEnum( buffer )) break;
2251 return TRUE;
2255 /***********************************************************************
2256 * EnumSystemCodePagesW (KERNEL32.@)
2258 * See EnumSystemCodePagesA.
2260 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2262 const union cptable *table;
2263 WCHAR buffer[10], *p;
2264 int page, index = 0;
2266 for (;;)
2268 if (!(table = wine_cp_enum_table( index++ ))) break;
2269 p = buffer + ARRAY_SIZE( buffer );
2270 *--p = 0;
2271 page = table->info.codepage;
2274 *--p = '0' + (page % 10);
2275 page /= 10;
2276 } while( page );
2277 if (!lpfnCodePageEnum( p )) break;
2279 return TRUE;
2283 /***********************************************************************
2284 * utf7_write_w
2286 * Helper for utf7_mbstowcs
2288 * RETURNS
2289 * TRUE on success, FALSE on error
2291 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2293 if (dstlen > 0)
2295 if (*index >= dstlen)
2296 return FALSE;
2298 dst[*index] = character;
2301 (*index)++;
2303 return TRUE;
2306 /***********************************************************************
2307 * utf7_mbstowcs
2309 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2311 * RETURNS
2312 * On success, the number of characters written
2313 * On dst buffer overflow, -1
2315 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2317 static const signed char base64_decoding_table[] =
2319 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2320 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2321 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2322 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2323 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2324 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2325 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2326 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2329 const char *source_end = src + srclen;
2330 int dest_index = 0;
2332 DWORD byte_pair = 0;
2333 short offset = 0;
2335 while (src < source_end)
2337 if (*src == '+')
2339 src++;
2340 if (src >= source_end)
2341 break;
2343 if (*src == '-')
2345 /* just a plus sign escaped as +- */
2346 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2347 return -1;
2348 src++;
2349 continue;
2354 signed char sextet = *src;
2355 if (sextet == '-')
2357 /* skip over the dash and end base64 decoding
2358 * the current, unfinished byte pair is discarded */
2359 src++;
2360 offset = 0;
2361 break;
2363 if (sextet < 0)
2365 /* the next character of src is < 0 and therefore not part of a base64 sequence
2366 * the current, unfinished byte pair is NOT discarded in this case
2367 * this is probably a bug in Windows */
2368 break;
2371 sextet = base64_decoding_table[sextet];
2372 if (sextet == -1)
2374 /* -1 means that the next character of src is not part of a base64 sequence
2375 * in other words, all sextets in this base64 sequence have been processed
2376 * the current, unfinished byte pair is discarded */
2377 offset = 0;
2378 break;
2381 byte_pair = (byte_pair << 6) | sextet;
2382 offset += 6;
2384 if (offset >= 16)
2386 /* this byte pair is done */
2387 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2388 return -1;
2389 offset -= 16;
2392 src++;
2394 while (src < source_end);
2396 else
2398 /* we have to convert to unsigned char in case *src < 0 */
2399 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2400 return -1;
2401 src++;
2405 return dest_index;
2408 /***********************************************************************
2409 * MultiByteToWideChar (KERNEL32.@)
2411 * Convert a multibyte character string into a Unicode string.
2413 * PARAMS
2414 * page [I] Codepage character set to convert from
2415 * flags [I] Character mapping flags
2416 * src [I] Source string buffer
2417 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2418 * dst [O] Destination buffer
2419 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2421 * RETURNS
2422 * Success: If dstlen > 0, the number of characters written to dst.
2423 * If dstlen == 0, the number of characters needed to perform the
2424 * conversion. In both cases the count includes the terminating NUL.
2425 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2426 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2427 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2428 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2429 * possible for src.
2431 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2432 LPWSTR dst, INT dstlen )
2434 const union cptable *table;
2435 int ret;
2437 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2439 SetLastError( ERROR_INVALID_PARAMETER );
2440 return 0;
2443 if (srclen < 0) srclen = strlen(src) + 1;
2445 switch(page)
2447 case CP_SYMBOL:
2448 if (flags)
2450 SetLastError( ERROR_INVALID_FLAGS );
2451 return 0;
2453 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2454 break;
2455 case CP_UTF7:
2456 if (flags)
2458 SetLastError( ERROR_INVALID_FLAGS );
2459 return 0;
2461 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2462 break;
2463 case CP_UNIXCP:
2464 if (unix_cptable)
2466 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2467 break;
2469 #ifdef __APPLE__
2470 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2471 #endif
2472 /* fall through */
2473 case CP_UTF8:
2474 if (flags & ~MB_FLAGSMASK)
2476 SetLastError( ERROR_INVALID_FLAGS );
2477 return 0;
2479 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2480 break;
2481 default:
2482 if (!(table = get_codepage_table( page )))
2484 SetLastError( ERROR_INVALID_PARAMETER );
2485 return 0;
2487 if (flags & ~MB_FLAGSMASK)
2489 SetLastError( ERROR_INVALID_FLAGS );
2490 return 0;
2492 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2493 break;
2496 if (ret < 0)
2498 switch(ret)
2500 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2501 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2503 ret = 0;
2505 TRACE("cp %d %s -> %s, ret = %d\n",
2506 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2507 return ret;
2511 /***********************************************************************
2512 * utf7_can_directly_encode
2514 * Helper for utf7_wcstombs
2516 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2518 static const BOOL directly_encodable_table[] =
2520 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2521 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2522 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2523 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2524 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2525 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2526 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2527 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2530 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2533 /***********************************************************************
2534 * utf7_write_c
2536 * Helper for utf7_wcstombs
2538 * RETURNS
2539 * TRUE on success, FALSE on error
2541 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2543 if (dstlen > 0)
2545 if (*index >= dstlen)
2546 return FALSE;
2548 dst[*index] = character;
2551 (*index)++;
2553 return TRUE;
2556 /***********************************************************************
2557 * utf7_wcstombs
2559 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2561 * RETURNS
2562 * On success, the number of characters written
2563 * On dst buffer overflow, -1
2565 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2567 static const char base64_encoding_table[] =
2568 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2570 const WCHAR *source_end = src + srclen;
2571 int dest_index = 0;
2573 while (src < source_end)
2575 if (*src == '+')
2577 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2578 return -1;
2579 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2580 return -1;
2581 src++;
2583 else if (utf7_can_directly_encode(*src))
2585 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2586 return -1;
2587 src++;
2589 else
2591 unsigned int offset = 0;
2592 DWORD byte_pair = 0;
2594 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2595 return -1;
2597 while (src < source_end && !utf7_can_directly_encode(*src))
2599 byte_pair = (byte_pair << 16) | *src;
2600 offset += 16;
2601 while (offset >= 6)
2603 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2604 return -1;
2605 offset -= 6;
2607 src++;
2610 if (offset)
2612 /* Windows won't create a padded base64 character if there's no room for the - sign
2613 * as well ; this is probably a bug in Windows */
2614 if (dstlen > 0 && dest_index + 1 >= dstlen)
2615 return -1;
2617 byte_pair <<= (6 - offset);
2618 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2619 return -1;
2622 /* Windows always explicitly terminates the base64 sequence
2623 even though RFC 2152 (page 3, rule 2) does not require this */
2624 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2625 return -1;
2629 return dest_index;
2632 /***********************************************************************
2633 * WideCharToMultiByte (KERNEL32.@)
2635 * Convert a Unicode character string into a multibyte string.
2637 * PARAMS
2638 * page [I] Code page character set to convert to
2639 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2640 * src [I] Source string buffer
2641 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2642 * dst [O] Destination buffer
2643 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2644 * defchar [I] Default character to use for conversion if no exact
2645 * conversion can be made
2646 * used [O] Set if default character was used in the conversion
2648 * RETURNS
2649 * Success: If dstlen > 0, the number of characters written to dst.
2650 * If dstlen == 0, number of characters needed to perform the
2651 * conversion. In both cases the count includes the terminating NUL.
2652 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2653 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2654 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2655 * parameter was given.
2657 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2658 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2660 const union cptable *table;
2661 int ret, used_tmp;
2663 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2665 SetLastError( ERROR_INVALID_PARAMETER );
2666 return 0;
2669 if (srclen < 0) srclen = strlenW(src) + 1;
2671 switch(page)
2673 case CP_SYMBOL:
2674 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2675 if (flags)
2677 SetLastError( ERROR_INVALID_FLAGS );
2678 return 0;
2680 if (defchar || used)
2682 SetLastError( ERROR_INVALID_PARAMETER );
2683 return 0;
2685 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2686 break;
2687 case CP_UTF7:
2688 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2689 if (defchar || used)
2691 SetLastError( ERROR_INVALID_PARAMETER );
2692 return 0;
2694 if (flags)
2696 SetLastError( ERROR_INVALID_FLAGS );
2697 return 0;
2699 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2700 break;
2701 case CP_UNIXCP:
2702 if (unix_cptable)
2704 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2705 defchar, used ? &used_tmp : NULL );
2706 break;
2708 /* fall through */
2709 case CP_UTF8:
2710 if (defchar || used)
2712 SetLastError( ERROR_INVALID_PARAMETER );
2713 return 0;
2715 if (flags & ~WC_FLAGSMASK)
2717 SetLastError( ERROR_INVALID_FLAGS );
2718 return 0;
2720 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2721 break;
2722 default:
2723 if (!(table = get_codepage_table( page )))
2725 SetLastError( ERROR_INVALID_PARAMETER );
2726 return 0;
2728 if (flags & ~WC_FLAGSMASK)
2730 SetLastError( ERROR_INVALID_FLAGS );
2731 return 0;
2733 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2734 defchar, used ? &used_tmp : NULL );
2735 if (used) *used = used_tmp;
2736 break;
2739 if (ret < 0)
2741 switch(ret)
2743 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2744 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2746 ret = 0;
2748 TRACE("cp %d %s -> %s, ret = %d\n",
2749 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2750 return ret;
2754 /***********************************************************************
2755 * GetThreadLocale (KERNEL32.@)
2757 * Get the current threads locale.
2759 * PARAMS
2760 * None.
2762 * RETURNS
2763 * The LCID currently associated with the calling thread.
2765 LCID WINAPI GetThreadLocale(void)
2767 LCID ret = NtCurrentTeb()->CurrentLocale;
2768 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2769 return ret;
2772 /**********************************************************************
2773 * SetThreadLocale (KERNEL32.@)
2775 * Set the current threads locale.
2777 * PARAMS
2778 * lcid [I] LCID of the locale to set
2780 * RETURNS
2781 * Success: TRUE. The threads locale is set to lcid.
2782 * Failure: FALSE. Use GetLastError() to determine the cause.
2784 BOOL WINAPI SetThreadLocale( LCID lcid )
2786 TRACE("(0x%04X)\n", lcid);
2788 lcid = ConvertDefaultLocale(lcid);
2790 if (lcid != GetThreadLocale())
2792 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2794 SetLastError(ERROR_INVALID_PARAMETER);
2795 return FALSE;
2798 NtCurrentTeb()->CurrentLocale = lcid;
2800 return TRUE;
2803 /**********************************************************************
2804 * SetThreadUILanguage (KERNEL32.@)
2806 * Set the current threads UI language.
2808 * PARAMS
2809 * langid [I] LANGID of the language to set, or 0 to use
2810 * the available language which is best supported
2811 * for console applications
2813 * RETURNS
2814 * Success: The return value is the same as the input value.
2815 * Failure: The return value differs from the input value.
2816 * Use GetLastError() to determine the cause.
2818 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2820 TRACE("(0x%04x) stub - returning success\n", langid);
2822 if (!langid)
2823 return GetThreadUILanguage();
2824 else
2825 return langid;
2828 /******************************************************************************
2829 * ConvertDefaultLocale (KERNEL32.@)
2831 * Convert a default locale identifier into a real identifier.
2833 * PARAMS
2834 * lcid [I] LCID identifier of the locale to convert
2836 * RETURNS
2837 * lcid unchanged, if not a default locale or its sublanguage is
2838 * not SUBLANG_NEUTRAL.
2839 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2840 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2841 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2843 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2845 LANGID langid;
2847 switch (lcid)
2849 case LOCALE_INVARIANT:
2850 /* keep as-is */
2851 break;
2852 case LOCALE_SYSTEM_DEFAULT:
2853 lcid = GetSystemDefaultLCID();
2854 break;
2855 case LOCALE_USER_DEFAULT:
2856 case LOCALE_NEUTRAL:
2857 lcid = GetUserDefaultLCID();
2858 break;
2859 default:
2860 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2861 langid = LANGIDFROMLCID(lcid);
2862 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2864 langid = get_default_sublang( langid );
2865 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2868 return lcid;
2872 /******************************************************************************
2873 * IsValidLocale (KERNEL32.@)
2875 * Determine if a locale is valid.
2877 * PARAMS
2878 * lcid [I] LCID of the locale to check
2879 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2881 * RETURNS
2882 * TRUE, if lcid is valid,
2883 * FALSE, otherwise.
2885 * NOTES
2886 * Wine does not currently make the distinction between supported and installed. All
2887 * languages supported are installed by default.
2889 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2891 /* check if language is registered in the kernel32 resources */
2892 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2893 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2896 /******************************************************************************
2897 * IsValidLocaleName (KERNEL32.@)
2899 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2901 struct locale_name locale_name;
2903 if (!locale)
2904 return FALSE;
2906 /* string parsing */
2907 parse_locale_name( locale, &locale_name );
2909 TRACE( "found lcid %x for %s, matches %d\n",
2910 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2912 return locale_name.matches > 0;
2915 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2916 LPCSTR name, WORD LangID, LONG_PTR lParam )
2918 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2919 char buf[20];
2921 sprintf(buf, "%08x", (UINT)LangID);
2922 return lpfnLocaleEnum( buf );
2925 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2926 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2928 static const WCHAR formatW[] = {'%','0','8','x',0};
2929 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2930 WCHAR buf[20];
2931 sprintfW( buf, formatW, (UINT)LangID );
2932 return lpfnLocaleEnum( buf );
2935 /******************************************************************************
2936 * EnumSystemLocalesA (KERNEL32.@)
2938 * Call a users function for each locale available on the system.
2940 * PARAMS
2941 * lpfnLocaleEnum [I] Callback function to call for each locale
2942 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2944 * RETURNS
2945 * Success: TRUE.
2946 * Failure: FALSE. Use GetLastError() to determine the cause.
2948 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2950 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2951 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2952 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2953 (LONG_PTR)lpfnLocaleEnum);
2954 return TRUE;
2958 /******************************************************************************
2959 * EnumSystemLocalesW (KERNEL32.@)
2961 * See EnumSystemLocalesA.
2963 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2965 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2966 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2967 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2968 (LONG_PTR)lpfnLocaleEnum);
2969 return TRUE;
2973 struct enum_locale_ex_data
2975 LOCALE_ENUMPROCEX proc;
2976 DWORD flags;
2977 LPARAM lparam;
2980 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2981 LPCWSTR name, WORD lang, LONG_PTR lparam )
2983 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2984 WCHAR buffer[256];
2985 DWORD neutral;
2986 unsigned int flags;
2988 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2989 buffer, ARRAY_SIZE( buffer ));
2990 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2991 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2992 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2993 neutral = 0;
2994 flags = LOCALE_WINDOWS;
2995 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2996 if (data->flags && !(data->flags & flags)) return TRUE;
2997 return data->proc( buffer, flags, data->lparam );
3000 /******************************************************************************
3001 * EnumSystemLocalesEx (KERNEL32.@)
3003 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
3005 struct enum_locale_ex_data data;
3007 if (reserved)
3009 SetLastError( ERROR_INVALID_PARAMETER );
3010 return FALSE;
3012 data.proc = proc;
3013 data.flags = flags;
3014 data.lparam = lparam;
3015 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3016 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
3017 enum_locale_ex_proc, (LONG_PTR)&data );
3018 return TRUE;
3022 /***********************************************************************
3023 * VerLanguageNameA (KERNEL32.@)
3025 * Get the name of a language.
3027 * PARAMS
3028 * wLang [I] LANGID of the language
3029 * szLang [O] Destination for the language name
3031 * RETURNS
3032 * Success: The size of the language name. If szLang is non-NULL, it is filled
3033 * with the name.
3034 * Failure: 0. Use GetLastError() to determine the cause.
3037 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
3039 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3043 /***********************************************************************
3044 * VerLanguageNameW (KERNEL32.@)
3046 * See VerLanguageNameA.
3048 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
3050 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
3054 /******************************************************************************
3055 * GetStringTypeW (KERNEL32.@)
3057 * See GetStringTypeA.
3059 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3061 static const unsigned char type2_map[16] =
3063 C2_NOTAPPLICABLE, /* unassigned */
3064 C2_LEFTTORIGHT, /* L */
3065 C2_RIGHTTOLEFT, /* R */
3066 C2_EUROPENUMBER, /* EN */
3067 C2_EUROPESEPARATOR, /* ES */
3068 C2_EUROPETERMINATOR, /* ET */
3069 C2_ARABICNUMBER, /* AN */
3070 C2_COMMONSEPARATOR, /* CS */
3071 C2_BLOCKSEPARATOR, /* B */
3072 C2_SEGMENTSEPARATOR, /* S */
3073 C2_WHITESPACE, /* WS */
3074 C2_OTHERNEUTRAL, /* ON */
3075 C2_RIGHTTOLEFT, /* AL */
3076 C2_NOTAPPLICABLE, /* NSM */
3077 C2_NOTAPPLICABLE, /* BN */
3078 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
3081 if (!src)
3083 SetLastError( ERROR_INVALID_PARAMETER );
3084 return FALSE;
3087 if (count == -1) count = strlenW(src) + 1;
3088 switch(type)
3090 case CT_CTYPE1:
3091 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
3092 break;
3093 case CT_CTYPE2:
3094 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
3095 break;
3096 case CT_CTYPE3:
3098 WARN("CT_CTYPE3: semi-stub.\n");
3099 while (count--)
3101 int c = *src;
3102 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
3104 type1 = get_char_typeW( *src++ ) & 0xfff;
3105 /* try to construct type3 from type1 */
3106 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
3107 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
3108 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
3109 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
3110 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3111 if (c == 0x0640) type3 |= C3_KASHIDA;
3112 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3114 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3115 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3117 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3118 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3119 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3120 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3121 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3122 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3123 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3124 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3126 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3127 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3128 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3129 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3130 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3131 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3132 *chartype++ = type3;
3134 break;
3136 default:
3137 SetLastError( ERROR_INVALID_PARAMETER );
3138 return FALSE;
3140 return TRUE;
3144 /******************************************************************************
3145 * GetStringTypeExW (KERNEL32.@)
3147 * See GetStringTypeExA.
3149 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3151 /* locale is ignored for Unicode */
3152 return GetStringTypeW( type, src, count, chartype );
3156 /******************************************************************************
3157 * GetStringTypeA (KERNEL32.@)
3159 * Get characteristics of the characters making up a string.
3161 * PARAMS
3162 * locale [I] Locale Id for the string
3163 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3164 * src [I] String to analyse
3165 * count [I] Length of src in chars, or -1 if src is NUL terminated
3166 * chartype [O] Destination for the calculated characteristics
3168 * RETURNS
3169 * Success: TRUE. chartype is filled with the requested characteristics of each char
3170 * in src.
3171 * Failure: FALSE. Use GetLastError() to determine the cause.
3173 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3175 UINT cp;
3176 INT countW;
3177 LPWSTR srcW;
3178 BOOL ret = FALSE;
3180 if(count == -1) count = strlen(src) + 1;
3182 if (!(cp = get_lcid_codepage( locale )))
3184 FIXME("For locale %04x using current ANSI code page\n", locale);
3185 cp = GetACP();
3188 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3189 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3191 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3193 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3194 * string, with multibyte characters there maybe be more bytes in count
3195 * than character space in the buffer!
3197 ret = GetStringTypeW(type, srcW, countW, chartype);
3198 HeapFree(GetProcessHeap(), 0, srcW);
3200 return ret;
3203 /******************************************************************************
3204 * GetStringTypeExA (KERNEL32.@)
3206 * Get characteristics of the characters making up a string.
3208 * PARAMS
3209 * locale [I] Locale Id for the string
3210 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3211 * src [I] String to analyse
3212 * count [I] Length of src in chars, or -1 if src is NUL terminated
3213 * chartype [O] Destination for the calculated characteristics
3215 * RETURNS
3216 * Success: TRUE. chartype is filled with the requested characteristics of each char
3217 * in src.
3218 * Failure: FALSE. Use GetLastError() to determine the cause.
3220 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3222 return GetStringTypeA(locale, type, src, count, chartype);
3225 /* compose a full-width katakana. return consumed source characters. */
3226 static INT compose_katakana( LPCWSTR src, INT srclen, LPWSTR dst )
3228 const static BYTE katakana_map[] = {
3229 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
3230 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
3231 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
3232 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
3233 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
3234 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
3235 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
3236 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
3238 WCHAR before, dummy;
3240 if (!dst)
3241 dst = &dummy;
3243 switch (*src)
3245 case 0x309b: case 0x309c:
3246 *dst = *src - 2;
3247 return 1;
3248 case 0x30f0: case 0x30f1: case 0x30fd:
3249 *dst = *src;
3250 break;
3251 default:
3253 int shift = *src - 0xff61;
3254 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map ))
3255 return 0;
3256 else
3257 *dst = katakana_map[shift] | 0x3000;
3261 if (srclen <= 1)
3262 return 1;
3264 before = *dst;
3266 /* datakuten (voiced sound) */
3267 if (*(src + 1) == 0xff9e)
3269 if ((*src >= 0xff76 && *src <= 0xff84) ||
3270 (*src >= 0xff8a && *src <= 0xff8e) ||
3271 *src == 0x30fd)
3272 *dst += 1;
3273 else if (*src == 0xff73)
3274 *dst = 0x30f4; /* KATAKANA LETTER VU */
3275 else if (*src == 0xff9c)
3276 *dst = 0x30f7; /* KATAKANA LETTER VA */
3277 else if (*src == 0x30f0)
3278 *dst = 0x30f8; /* KATAKANA LETTER VI */
3279 else if (*src == 0x30f1)
3280 *dst = 0x30f9; /* KATAKANA LETTER VE */
3281 else if (*src == 0xff66)
3282 *dst = 0x30fa; /* KATAKANA LETTER VO */
3285 /* handakuten (semi-voiced sound) */
3286 if (*(src + 1) == 0xff9f)
3287 if (*src >= 0xff8a && *src <= 0xff8e)
3288 *dst += 2;
3290 return (*dst != before) ? 2 : 1;
3293 /* map one or two half-width characters to one full-width character */
3294 static INT map_to_fullwidth( LPCWSTR src, INT srclen, LPWSTR dst )
3296 INT n;
3298 if (*src <= '~' && *src > ' ' && *src != '\\')
3299 *dst = *src - 0x20 + 0xff00;
3300 else if (*src == ' ')
3301 *dst = 0x3000;
3302 else if (*src <= 0x00af && *src >= 0x00a2)
3304 const static BYTE misc_symbols_table[] = {
3305 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3306 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3308 if (misc_symbols_table[*src - 0x00a2])
3309 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3310 else
3311 *dst = *src;
3313 else if (*src == 0x20a9) /* WON SIGN */
3314 *dst = 0xffe6;
3315 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3316 return n;
3317 else if (*src >= 0xffa0 && *src <= 0xffdc)
3319 const static BYTE hangul_mapping_table[] = {
3320 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3321 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3322 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3323 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3324 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3325 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3326 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3327 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3330 if (hangul_mapping_table[*src - 0xffa0])
3331 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3332 else
3333 *dst = *src;
3335 else
3336 *dst = *src;
3338 return 1;
3341 /* decompose a full-width katakana character into one or two half-width characters. */
3342 static INT decompose_katakana( WCHAR c, LPWSTR dst, INT dstlen )
3344 const static BYTE katakana_map[] = {
3345 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3346 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3347 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3348 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3349 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3350 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3351 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3352 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3353 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3354 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3355 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3356 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3357 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3359 INT len = 0, shift = c - 0x3099;
3360 BYTE k;
3362 if (shift < 0 || shift >= ARRAY_SIZE( katakana_map ))
3363 return 0;
3365 k = katakana_map[shift];
3367 if (!k)
3369 if (dstlen > 0)
3370 *dst = c;
3371 len++;
3373 else if (k > 0x60)
3375 if (dstlen > 0)
3376 *dst = k | 0xff00;
3377 len++;
3379 else
3381 if (dstlen >= 2)
3383 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3384 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3386 len += 2;
3388 return len;
3391 /* map single full-width character to single or double half-width characters. */
3392 static INT map_to_halfwidth(WCHAR c, LPWSTR dst, INT dstlen)
3394 INT n = decompose_katakana(c, dst, dstlen);
3395 if (n > 0)
3396 return n;
3398 if (c == 0x3000)
3399 *dst = ' ';
3400 else if (c == 0x3001)
3401 *dst = 0xff64;
3402 else if (c == 0x3002)
3403 *dst = 0xff61;
3404 else if (c == 0x300c || c == 0x300d)
3405 *dst = (c - 0x300c) + 0xff62;
3406 else if (c >= 0x3131 && c <= 0x3163)
3408 *dst = c - 0x3131 + 0xffa1;
3409 if (*dst >= 0xffbf) *dst += 3;
3410 if (*dst >= 0xffc8) *dst += 2;
3411 if (*dst >= 0xffd0) *dst += 2;
3412 if (*dst >= 0xffd8) *dst += 2;
3414 else if (c == 0x3164)
3415 *dst = 0xffa0;
3416 else if (c == 0x2019)
3417 *dst = '\'';
3418 else if (c == 0x201d)
3419 *dst = '"';
3420 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3421 *dst = c - 0xff00 + 0x20;
3422 else if (c >= 0xffe0 && c <= 0xffe6)
3424 const static WCHAR misc_symbol_map[] = {
3425 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9
3427 *dst = misc_symbol_map[c - 0xffe0];
3429 else
3430 *dst = c;
3432 return 1;
3435 /*************************************************************************
3436 * LCMapStringEx (KERNEL32.@)
3438 * Map characters in a locale sensitive string.
3440 * PARAMS
3441 * name [I] Locale name for the conversion.
3442 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3443 * src [I] String to map
3444 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3445 * dst [O] Destination for mapped string
3446 * dstlen [I] Length of dst in characters
3447 * version [I] reserved, must be NULL
3448 * reserved [I] reserved, must be NULL
3449 * lparam [I] reserved, must be 0
3451 * RETURNS
3452 * Success: The length of the mapped string in dst, including the NUL terminator.
3453 * Failure: 0. Use GetLastError() to determine the cause.
3455 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3456 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3458 LPWSTR dst_ptr;
3459 INT len;
3461 if (version) FIXME("unsupported version structure %p\n", version);
3462 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3463 if (lparam)
3465 static int once;
3466 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3469 if (!src || !srclen || dstlen < 0)
3471 SetLastError(ERROR_INVALID_PARAMETER);
3472 return 0;
3475 /* mutually exclusive flags */
3476 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3477 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3478 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3479 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
3480 !flags)
3482 SetLastError(ERROR_INVALID_FLAGS);
3483 return 0;
3486 if (!dstlen) dst = NULL;
3488 if (flags & LCMAP_SORTKEY)
3490 INT ret;
3491 if (src == dst)
3493 SetLastError(ERROR_INVALID_FLAGS);
3494 return 0;
3497 if (srclen < 0) srclen = strlenW(src);
3499 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3500 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3502 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3503 if (ret == 0)
3504 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3505 else
3506 ret++;
3507 return ret;
3510 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3511 if (flags & SORT_STRINGSORT)
3513 SetLastError(ERROR_INVALID_FLAGS);
3514 return 0;
3516 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
3517 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
3518 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
3519 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
3521 SetLastError(ERROR_INVALID_FLAGS);
3522 return 0;
3525 if (srclen < 0) srclen = strlenW(src) + 1;
3527 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3528 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3530 if (!dst) /* return required string length */
3532 if (flags & NORM_IGNORESYMBOLS)
3534 for (len = 0; srclen; src++, srclen--)
3536 WCHAR wch = *src;
3537 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3538 * and skips white space and punctuation characters for
3539 * NORM_IGNORESYMBOLS.
3541 if (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE))
3542 continue;
3543 len++;
3546 else if (flags & LCMAP_FULLWIDTH)
3548 for (len = 0; srclen; src++, srclen--, len++)
3550 if (compose_katakana(src, srclen, NULL) == 2)
3552 src++;
3553 srclen--;
3557 else if (flags & LCMAP_HALFWIDTH)
3559 for (len = 0; srclen; src++, srclen--, len++)
3561 WCHAR wch = *src;
3562 /* map Hiragana to Katakana before decomposition if needed */
3563 if ((flags & LCMAP_KATAKANA) &&
3564 ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E))
3565 wch += 0x60;
3567 if (decompose_katakana(wch, NULL, 0) == 2)
3568 len++;
3571 else
3572 len = srclen;
3573 return len;
3576 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3578 SetLastError(ERROR_INVALID_FLAGS);
3579 return 0;
3582 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
3584 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3586 WCHAR wch = *src;
3587 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3588 continue;
3589 *dst_ptr++ = wch;
3590 len--;
3592 goto done;
3595 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
3597 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
3599 WCHAR wch;
3600 if (flags & LCMAP_FULLWIDTH)
3602 /* map half-width character to full-width one,
3603 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
3604 if (map_to_fullwidth(src, srclen, &wch) == 2)
3606 src++;
3607 srclen--;
3610 else
3611 wch = *src;
3613 if (flags & LCMAP_KATAKANA)
3615 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
3616 we can't use C3_HIRAGANA as some characters can't map to katakana */
3617 if ((wch >= 0x3041 && wch <= 0x3096) ||
3618 wch == 0x309D || wch == 0x309E)
3619 wch += 0x60;
3621 else if (flags & LCMAP_HIRAGANA)
3623 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
3624 we can't use C3_KATAKANA as some characters can't map to hiragana */
3625 if ((wch >= 0x30A1 && wch <= 0x30F6) ||
3626 wch == 0x30FD || wch == 0x30FE)
3627 wch -= 0x60;
3630 if (flags & LCMAP_HALFWIDTH)
3632 /* map full-width character to half-width one,
3633 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
3634 if (map_to_halfwidth(wch, dst_ptr, len) == 2)
3636 len--;
3637 dst_ptr++;
3638 if (!len) break;
3641 else
3642 *dst_ptr = wch;
3644 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) || srclen)
3645 goto done;
3647 srclen = dst_ptr - dst;
3648 src = dst;
3651 if (flags & LCMAP_UPPERCASE)
3653 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3655 *dst_ptr++ = toupperW(*src);
3656 len--;
3659 else if (flags & LCMAP_LOWERCASE)
3661 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3663 *dst_ptr++ = tolowerW(*src);
3664 len--;
3667 else
3669 len = min(srclen, dstlen);
3670 memcpy(dst, src, len * sizeof(WCHAR));
3671 dst_ptr = dst + len;
3672 srclen -= len;
3675 done:
3676 if (srclen)
3678 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3679 return 0;
3682 return dst_ptr - dst;
3685 /*************************************************************************
3686 * LCMapStringW (KERNEL32.@)
3688 * See LCMapStringA.
3690 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3691 LPWSTR dst, INT dstlen)
3693 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3694 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3696 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3699 /*************************************************************************
3700 * LCMapStringA (KERNEL32.@)
3702 * Map characters in a locale sensitive string.
3704 * PARAMS
3705 * lcid [I] LCID for the conversion.
3706 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3707 * src [I] String to map
3708 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3709 * dst [O] Destination for mapped string
3710 * dstlen [I] Length of dst in characters
3712 * RETURNS
3713 * Success: The length of the mapped string in dst, including the NUL terminator.
3714 * Failure: 0. Use GetLastError() to determine the cause.
3716 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3717 LPSTR dst, INT dstlen)
3719 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3720 LPWSTR srcW, dstW;
3721 INT ret = 0, srclenW, dstlenW;
3722 UINT locale_cp = CP_ACP;
3724 if (!src || !srclen || dstlen < 0)
3726 SetLastError(ERROR_INVALID_PARAMETER);
3727 return 0;
3730 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3732 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3733 if (srclenW)
3734 srcW = bufW;
3735 else
3737 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3738 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3739 if (!srcW)
3741 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3742 return 0;
3744 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3747 if (flags & LCMAP_SORTKEY)
3749 if (src == dst)
3751 SetLastError(ERROR_INVALID_FLAGS);
3752 goto map_string_exit;
3754 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3755 if (ret == 0)
3756 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3757 else
3758 ret++;
3759 goto map_string_exit;
3762 if (flags & SORT_STRINGSORT)
3764 SetLastError(ERROR_INVALID_FLAGS);
3765 goto map_string_exit;
3768 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3769 if (!dstlenW)
3770 goto map_string_exit;
3772 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3773 if (!dstW)
3775 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3776 goto map_string_exit;
3779 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3780 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3781 HeapFree(GetProcessHeap(), 0, dstW);
3783 map_string_exit:
3784 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3785 return ret;
3788 /*************************************************************************
3789 * FoldStringA (KERNEL32.@)
3791 * Map characters in a string.
3793 * PARAMS
3794 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3795 * src [I] String to map
3796 * srclen [I] Length of src, or -1 if src is NUL terminated
3797 * dst [O] Destination for mapped string
3798 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3800 * RETURNS
3801 * Success: The length of the string written to dst, including the terminating NUL. If
3802 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3803 * and dst may be NULL.
3804 * Failure: 0. Use GetLastError() to determine the cause.
3806 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3807 LPSTR dst, INT dstlen)
3809 INT ret = 0, srclenW = 0;
3810 WCHAR *srcW = NULL, *dstW = NULL;
3812 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3814 SetLastError(ERROR_INVALID_PARAMETER);
3815 return 0;
3818 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3819 src, srclen, NULL, 0);
3820 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3822 if (!srcW)
3824 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3825 goto FoldStringA_exit;
3828 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3829 src, srclen, srcW, srclenW);
3831 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3833 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3834 if (ret && dstlen)
3836 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3838 if (!dstW)
3840 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3841 goto FoldStringA_exit;
3844 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3845 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3847 ret = 0;
3848 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3852 HeapFree(GetProcessHeap(), 0, dstW);
3854 FoldStringA_exit:
3855 HeapFree(GetProcessHeap(), 0, srcW);
3856 return ret;
3859 /*************************************************************************
3860 * FoldStringW (KERNEL32.@)
3862 * See FoldStringA.
3864 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3865 LPWSTR dst, INT dstlen)
3867 int ret;
3869 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3871 case 0:
3872 if (dwFlags)
3873 break;
3874 /* Fall through for dwFlags == 0 */
3875 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3876 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3877 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3878 SetLastError(ERROR_INVALID_FLAGS);
3879 return 0;
3882 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3884 SetLastError(ERROR_INVALID_PARAMETER);
3885 return 0;
3888 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3889 if (!ret)
3890 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3891 return ret;
3894 /******************************************************************************
3895 * CompareStringW (KERNEL32.@)
3897 * See CompareStringA.
3899 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3900 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3902 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3905 /******************************************************************************
3906 * CompareStringEx (KERNEL32.@)
3908 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3909 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3911 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3912 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3913 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3914 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3915 INT ret;
3916 static int once;
3918 if (version) FIXME("unexpected version parameter\n");
3919 if (reserved) FIXME("unexpected reserved value\n");
3920 if (lParam) FIXME("unexpected lParam\n");
3922 if (!str1 || !str2)
3924 SetLastError(ERROR_INVALID_PARAMETER);
3925 return 0;
3928 if (flags & ~(supported_flags|semistub_flags))
3930 SetLastError(ERROR_INVALID_FLAGS);
3931 return 0;
3934 if (flags & semistub_flags)
3936 if (!once++)
3937 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3940 if (len1 < 0) len1 = strlenW(str1);
3941 if (len2 < 0) len2 = strlenW(str2);
3943 ret = wine_compare_string(flags, str1, len1, str2, len2);
3945 if (ret) /* need to translate result */
3946 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3947 return CSTR_EQUAL;
3950 /******************************************************************************
3951 * CompareStringA (KERNEL32.@)
3953 * Compare two locale sensitive strings.
3955 * PARAMS
3956 * lcid [I] LCID for the comparison
3957 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3958 * str1 [I] First string to compare
3959 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3960 * str2 [I] Second string to compare
3961 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3963 * RETURNS
3964 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3965 * str1 is less than, equal to or greater than str2 respectively.
3966 * Failure: FALSE. Use GetLastError() to determine the cause.
3968 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3969 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3971 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3972 WCHAR *buf2W = buf1W + 130;
3973 LPWSTR str1W, str2W;
3974 INT len1W = 0, len2W = 0, ret;
3975 UINT locale_cp = CP_ACP;
3977 if (!str1 || !str2)
3979 SetLastError(ERROR_INVALID_PARAMETER);
3980 return 0;
3982 if (len1 < 0) len1 = strlen(str1);
3983 if (len2 < 0) len2 = strlen(str2);
3985 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3987 if (len1)
3989 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3990 if (len1W)
3991 str1W = buf1W;
3992 else
3994 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3995 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3996 if (!str1W)
3998 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3999 return 0;
4001 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
4004 else
4006 len1W = 0;
4007 str1W = buf1W;
4010 if (len2)
4012 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
4013 if (len2W)
4014 str2W = buf2W;
4015 else
4017 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
4018 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
4019 if (!str2W)
4021 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4022 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4023 return 0;
4025 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
4028 else
4030 len2W = 0;
4031 str2W = buf2W;
4034 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
4036 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
4037 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
4038 return ret;
4041 /******************************************************************************
4042 * CompareStringOrdinal (KERNEL32.@)
4044 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
4046 int ret;
4048 if (!str1 || !str2)
4050 SetLastError(ERROR_INVALID_PARAMETER);
4051 return 0;
4053 if (len1 < 0) len1 = strlenW(str1);
4054 if (len2 < 0) len2 = strlenW(str2);
4056 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
4057 if (ret < 0) return CSTR_LESS_THAN;
4058 if (ret > 0) return CSTR_GREATER_THAN;
4059 return CSTR_EQUAL;
4062 /*************************************************************************
4063 * lstrcmp (KERNEL32.@)
4064 * lstrcmpA (KERNEL32.@)
4066 * Compare two strings using the current thread locale.
4068 * PARAMS
4069 * str1 [I] First string to compare
4070 * str2 [I] Second string to compare
4072 * RETURNS
4073 * Success: A number less than, equal to or greater than 0 depending on whether
4074 * str1 is less than, equal to or greater than str2 respectively.
4075 * Failure: FALSE. Use GetLastError() to determine the cause.
4077 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
4079 int ret;
4081 if ((str1 == NULL) && (str2 == NULL)) return 0;
4082 if (str1 == NULL) return -1;
4083 if (str2 == NULL) return 1;
4085 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4086 if (ret) ret -= 2;
4088 return ret;
4091 /*************************************************************************
4092 * lstrcmpi (KERNEL32.@)
4093 * lstrcmpiA (KERNEL32.@)
4095 * Compare two strings using the current thread locale, ignoring case.
4097 * PARAMS
4098 * str1 [I] First string to compare
4099 * str2 [I] Second string to compare
4101 * RETURNS
4102 * Success: A number less than, equal to or greater than 0 depending on whether
4103 * str2 is less than, equal to or greater than str1 respectively.
4104 * Failure: FALSE. Use GetLastError() to determine the cause.
4106 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
4108 int ret;
4110 if ((str1 == NULL) && (str2 == NULL)) return 0;
4111 if (str1 == NULL) return -1;
4112 if (str2 == NULL) return 1;
4114 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4115 if (ret) ret -= 2;
4117 return ret;
4120 /*************************************************************************
4121 * lstrcmpW (KERNEL32.@)
4123 * See lstrcmpA.
4125 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
4127 int ret;
4129 if ((str1 == NULL) && (str2 == NULL)) return 0;
4130 if (str1 == NULL) return -1;
4131 if (str2 == NULL) return 1;
4133 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
4134 if (ret) ret -= 2;
4136 return ret;
4139 /*************************************************************************
4140 * lstrcmpiW (KERNEL32.@)
4142 * See lstrcmpiA.
4144 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
4146 int ret;
4148 if ((str1 == NULL) && (str2 == NULL)) return 0;
4149 if (str1 == NULL) return -1;
4150 if (str2 == NULL) return 1;
4152 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
4153 if (ret) ret -= 2;
4155 return ret;
4158 /******************************************************************************
4159 * LOCALE_Init
4161 void LOCALE_Init(void)
4163 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
4164 const union cptable *unix_cp );
4166 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
4168 setlocale( LC_ALL, "" );
4170 #ifdef __APPLE__
4171 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
4172 if (!has_env("LANG"))
4174 const char* mac_locale = get_mac_locale();
4176 setenv( "LANG", mac_locale, 1 );
4177 if (setlocale( LC_ALL, "" ))
4178 TRACE( "setting LANG to '%s'\n", mac_locale );
4179 else
4181 /* no C library locale matching Mac locale; don't pass garbage to children */
4182 unsetenv("LANG");
4183 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
4186 #endif /* __APPLE__ */
4188 unix_cp = setup_unix_locales();
4189 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
4191 #ifdef __APPLE__
4192 if (!unix_cp)
4193 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
4194 #endif
4196 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
4197 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
4198 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
4200 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
4201 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
4202 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
4203 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
4204 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
4205 if (!unix_cp)
4206 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
4207 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
4209 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
4210 ansi_cptable = wine_cp_get_table( 1252 );
4211 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
4212 oem_cptable = wine_cp_get_table( 437 );
4213 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
4214 mac_cptable = wine_cp_get_table( 10000 );
4215 if (unix_cp != CP_UTF8)
4217 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
4218 unix_cptable = wine_cp_get_table( 28591 );
4221 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
4223 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
4224 ansi_cptable->info.codepage, oem_cptable->info.codepage,
4225 mac_cptable->info.codepage, unix_cp );
4227 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
4230 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4232 UNICODE_STRING keyName;
4233 OBJECT_ATTRIBUTES attr;
4234 HANDLE hkey;
4236 RtlInitUnicodeString( &keyName, szKeyName );
4237 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
4239 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
4240 hkey = 0;
4242 return hkey;
4245 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4246 LPWSTR szValueName, ULONG valueNameSize,
4247 LPWSTR szValueData, ULONG valueDataSize)
4249 BYTE buffer[80];
4250 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
4251 DWORD dwLen;
4253 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
4254 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
4255 info->NameLength > valueNameSize ||
4256 info->DataLength > valueDataSize)
4258 return FALSE;
4261 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
4263 memcpy( szValueName, info->Name, info->NameLength);
4264 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
4265 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
4266 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
4268 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
4269 return TRUE;
4272 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
4274 BYTE buffer[128];
4275 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
4276 DWORD dwSize = sizeof(buffer);
4277 UNICODE_STRING valueName;
4279 RtlInitUnicodeString( &valueName, szValueName );
4281 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
4282 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
4283 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
4284 info->DataLength == sizeof(DWORD))
4286 memcpy(lpVal, info->Data, sizeof(DWORD));
4287 return TRUE;
4290 return FALSE;
4293 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
4295 LANGID langId;
4296 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
4297 HRSRC hResource;
4298 BOOL bRet = FALSE;
4300 /* FIXME: Is it correct to use the system default langid? */
4301 langId = GetSystemDefaultLangID();
4303 if (SUBLANGID(langId) == SUBLANG_NEUTRAL) langId = get_default_sublang( langId );
4305 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
4307 if (hResource)
4309 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
4311 if (hResDir)
4313 ULONG iResourceIndex = lgrpid & 0xf;
4314 LPCWSTR lpResEntry = LockResource( hResDir );
4315 ULONG i;
4317 for (i = 0; i < iResourceIndex; i++)
4318 lpResEntry += *lpResEntry + 1;
4320 if (*lpResEntry < nameSize)
4322 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
4323 szName[*lpResEntry] = '\0';
4324 bRet = TRUE;
4328 FreeResource( hResource );
4330 return bRet;
4333 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
4334 typedef struct
4336 LANGUAGEGROUP_ENUMPROCA procA;
4337 LANGUAGEGROUP_ENUMPROCW procW;
4338 DWORD dwFlags;
4339 LONG_PTR lParam;
4340 } ENUMLANGUAGEGROUP_CALLBACKS;
4342 /* Internal implementation of EnumSystemLanguageGroupsA/W */
4343 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
4345 WCHAR szNumber[10], szValue[4];
4346 HANDLE hKey;
4347 BOOL bContinue = TRUE;
4348 ULONG ulIndex = 0;
4350 if (!lpProcs)
4352 SetLastError(ERROR_INVALID_PARAMETER);
4353 return FALSE;
4356 switch (lpProcs->dwFlags)
4358 case 0:
4359 /* Default to LGRPID_INSTALLED */
4360 lpProcs->dwFlags = LGRPID_INSTALLED;
4361 /* Fall through... */
4362 case LGRPID_INSTALLED:
4363 case LGRPID_SUPPORTED:
4364 break;
4365 default:
4366 SetLastError(ERROR_INVALID_FLAGS);
4367 return FALSE;
4370 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4372 if (!hKey)
4373 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4375 while (bContinue)
4377 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4378 szValue, sizeof(szValue) ))
4380 BOOL bInstalled = szValue[0] == '1';
4381 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4383 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4384 bInstalled ? "" : "not ");
4386 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4388 WCHAR szGrpName[48];
4390 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, ARRAY_SIZE( szGrpName )))
4391 szGrpName[0] = '\0';
4393 if (lpProcs->procW)
4394 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4395 lpProcs->lParam );
4396 else
4398 char szNumberA[ARRAY_SIZE( szNumber )];
4399 char szGrpNameA[48];
4401 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4402 * or whether the language names are ever localised. Assume CP_ACP.
4405 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4406 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4408 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4409 lpProcs->lParam );
4413 ulIndex++;
4415 else
4416 bContinue = FALSE;
4418 if (!bContinue)
4419 break;
4422 if (hKey)
4423 NtClose( hKey );
4425 return TRUE;
4428 /******************************************************************************
4429 * EnumSystemLanguageGroupsA (KERNEL32.@)
4431 * Call a users function for each language group available on the system.
4433 * PARAMS
4434 * pLangGrpEnumProc [I] Callback function to call for each language group
4435 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4436 * lParam [I] User parameter to pass to pLangGrpEnumProc
4438 * RETURNS
4439 * Success: TRUE.
4440 * Failure: FALSE. Use GetLastError() to determine the cause.
4442 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4443 DWORD dwFlags, LONG_PTR lParam)
4445 ENUMLANGUAGEGROUP_CALLBACKS procs;
4447 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4449 procs.procA = pLangGrpEnumProc;
4450 procs.procW = NULL;
4451 procs.dwFlags = dwFlags;
4452 procs.lParam = lParam;
4454 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4457 /******************************************************************************
4458 * EnumSystemLanguageGroupsW (KERNEL32.@)
4460 * See EnumSystemLanguageGroupsA.
4462 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4463 DWORD dwFlags, LONG_PTR lParam)
4465 ENUMLANGUAGEGROUP_CALLBACKS procs;
4467 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4469 procs.procA = NULL;
4470 procs.procW = pLangGrpEnumProc;
4471 procs.dwFlags = dwFlags;
4472 procs.lParam = lParam;
4474 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4477 /******************************************************************************
4478 * IsValidLanguageGroup (KERNEL32.@)
4480 * Determine if a language group is supported and/or installed.
4482 * PARAMS
4483 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4484 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4486 * RETURNS
4487 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4488 * FALSE otherwise.
4490 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4492 static const WCHAR szFormat[] = { '%','x','\0' };
4493 WCHAR szValueName[16], szValue[2];
4494 BOOL bSupported = FALSE, bInstalled = FALSE;
4495 HANDLE hKey;
4498 switch (dwFlags)
4500 case LGRPID_INSTALLED:
4501 case LGRPID_SUPPORTED:
4503 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4505 sprintfW( szValueName, szFormat, lgrpid );
4507 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4509 bSupported = TRUE;
4511 if (szValue[0] == '1')
4512 bInstalled = TRUE;
4515 if (hKey)
4516 NtClose( hKey );
4518 break;
4521 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4522 (dwFlags == LGRPID_INSTALLED && bInstalled))
4523 return TRUE;
4525 return FALSE;
4528 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4529 typedef struct
4531 LANGGROUPLOCALE_ENUMPROCA procA;
4532 LANGGROUPLOCALE_ENUMPROCW procW;
4533 DWORD dwFlags;
4534 LGRPID lgrpid;
4535 LONG_PTR lParam;
4536 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4538 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4539 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4541 static const WCHAR szAlternateSortsKeyName[] = {
4542 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4544 WCHAR szNumber[10], szValue[4];
4545 HANDLE hKey;
4546 BOOL bContinue = TRUE, bAlternate = FALSE;
4547 LGRPID lgrpid;
4548 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4550 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4552 SetLastError(ERROR_INVALID_PARAMETER);
4553 return FALSE;
4556 if (lpProcs->dwFlags)
4558 SetLastError(ERROR_INVALID_FLAGS);
4559 return FALSE;
4562 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4564 if (!hKey)
4565 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4567 while (bContinue)
4569 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4570 szValue, sizeof(szValue) ))
4572 lgrpid = strtoulW( szValue, NULL, 16 );
4574 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4575 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4577 if (lgrpid == lpProcs->lgrpid)
4579 LCID lcid;
4581 lcid = strtoulW( szNumber, NULL, 16 );
4583 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4584 * '00000437 ;Georgian'
4585 * At present we only pass the LCID string.
4588 if (lpProcs->procW)
4589 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4590 else
4592 char szNumberA[ARRAY_SIZE( szNumber )];
4594 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4596 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4600 ulIndex++;
4602 else
4604 /* Finished enumerating this key */
4605 if (!bAlternate)
4607 /* Enumerate alternate sorts also */
4608 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4609 bAlternate = TRUE;
4610 ulIndex = 0;
4612 else
4613 bContinue = FALSE; /* Finished both keys */
4616 if (!bContinue)
4617 break;
4620 if (hKey)
4621 NtClose( hKey );
4623 return TRUE;
4626 /******************************************************************************
4627 * EnumLanguageGroupLocalesA (KERNEL32.@)
4629 * Call a users function for every locale in a language group available on the system.
4631 * PARAMS
4632 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4633 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4634 * dwFlags [I] Reserved, set to 0
4635 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4637 * RETURNS
4638 * Success: TRUE.
4639 * Failure: FALSE. Use GetLastError() to determine the cause.
4641 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4642 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4644 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4646 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4648 callbacks.procA = pLangGrpLcEnumProc;
4649 callbacks.procW = NULL;
4650 callbacks.dwFlags = dwFlags;
4651 callbacks.lgrpid = lgrpid;
4652 callbacks.lParam = lParam;
4654 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4657 /******************************************************************************
4658 * EnumLanguageGroupLocalesW (KERNEL32.@)
4660 * See EnumLanguageGroupLocalesA.
4662 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4663 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4665 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4667 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4669 callbacks.procA = NULL;
4670 callbacks.procW = pLangGrpLcEnumProc;
4671 callbacks.dwFlags = dwFlags;
4672 callbacks.lgrpid = lgrpid;
4673 callbacks.lParam = lParam;
4675 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4678 /******************************************************************************
4679 * InvalidateNLSCache (KERNEL32.@)
4681 * Invalidate the cache of NLS values.
4683 * PARAMS
4684 * None.
4686 * RETURNS
4687 * Success: TRUE.
4688 * Failure: FALSE.
4690 BOOL WINAPI InvalidateNLSCache(void)
4692 FIXME("() stub\n");
4693 return FALSE;
4696 /******************************************************************************
4697 * GetUserGeoID (KERNEL32.@)
4699 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4701 GEOID ret = GEOID_NOT_AVAILABLE;
4702 static const WCHAR geoW[] = {'G','e','o',0};
4703 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4704 WCHAR bufferW[40], *end;
4705 DWORD count;
4706 HANDLE hkey, hSubkey = 0;
4707 UNICODE_STRING keyW;
4708 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4709 RtlInitUnicodeString( &keyW, nationW );
4710 count = sizeof(bufferW);
4712 if(!(hkey = create_registry_key())) return ret;
4714 switch( GeoClass ){
4715 case GEOCLASS_NATION:
4716 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4718 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4719 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4720 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4722 break;
4723 case GEOCLASS_REGION:
4724 FIXME("GEOCLASS_REGION not handled yet\n");
4725 break;
4728 NtClose(hkey);
4729 if (hSubkey) NtClose(hSubkey);
4730 return ret;
4733 /******************************************************************************
4734 * SetUserGeoID (KERNEL32.@)
4736 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4738 static const WCHAR geoW[] = {'G','e','o',0};
4739 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4740 static const WCHAR formatW[] = {'%','i',0};
4741 UNICODE_STRING nameW,keyW;
4742 WCHAR bufferW[10];
4743 OBJECT_ATTRIBUTES attr;
4744 HANDLE hkey;
4746 if(!(hkey = create_registry_key())) return FALSE;
4748 attr.Length = sizeof(attr);
4749 attr.RootDirectory = hkey;
4750 attr.ObjectName = &nameW;
4751 attr.Attributes = 0;
4752 attr.SecurityDescriptor = NULL;
4753 attr.SecurityQualityOfService = NULL;
4754 RtlInitUnicodeString( &nameW, geoW );
4755 RtlInitUnicodeString( &keyW, nationW );
4757 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4760 NtClose(attr.RootDirectory);
4761 return FALSE;
4764 sprintfW(bufferW, formatW, GeoID);
4765 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4766 NtClose(attr.RootDirectory);
4767 NtClose(hkey);
4768 return TRUE;
4771 typedef struct
4773 union
4775 UILANGUAGE_ENUMPROCA procA;
4776 UILANGUAGE_ENUMPROCW procW;
4777 } u;
4778 DWORD flags;
4779 LONG_PTR param;
4780 } ENUM_UILANG_CALLBACK;
4782 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4783 LPCSTR name, WORD LangID, LONG_PTR lParam )
4785 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4786 char buf[20];
4788 sprintf(buf, "%08x", (UINT)LangID);
4789 return enum_uilang->u.procA( buf, enum_uilang->param );
4792 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4793 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4795 static const WCHAR formatW[] = {'%','0','8','x',0};
4796 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4797 WCHAR buf[20];
4799 sprintfW( buf, formatW, (UINT)LangID );
4800 return enum_uilang->u.procW( buf, enum_uilang->param );
4803 /******************************************************************************
4804 * EnumUILanguagesA (KERNEL32.@)
4806 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4808 ENUM_UILANG_CALLBACK enum_uilang;
4810 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4812 if(!pUILangEnumProc) {
4813 SetLastError(ERROR_INVALID_PARAMETER);
4814 return FALSE;
4816 if(dwFlags) {
4817 SetLastError(ERROR_INVALID_FLAGS);
4818 return FALSE;
4821 enum_uilang.u.procA = pUILangEnumProc;
4822 enum_uilang.flags = dwFlags;
4823 enum_uilang.param = lParam;
4825 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4826 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4827 (LONG_PTR)&enum_uilang);
4828 return TRUE;
4831 /******************************************************************************
4832 * EnumUILanguagesW (KERNEL32.@)
4834 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4836 ENUM_UILANG_CALLBACK enum_uilang;
4838 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4841 if(!pUILangEnumProc) {
4842 SetLastError(ERROR_INVALID_PARAMETER);
4843 return FALSE;
4845 if(dwFlags) {
4846 SetLastError(ERROR_INVALID_FLAGS);
4847 return FALSE;
4850 enum_uilang.u.procW = pUILangEnumProc;
4851 enum_uilang.flags = dwFlags;
4852 enum_uilang.param = lParam;
4854 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4855 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4856 (LONG_PTR)&enum_uilang);
4857 return TRUE;
4860 enum locationkind {
4861 LOCATION_NATION = 0,
4862 LOCATION_REGION,
4863 LOCATION_BOTH
4866 struct geoinfo_t {
4867 GEOID id;
4868 WCHAR iso2W[3];
4869 WCHAR iso3W[4];
4870 GEOID parent;
4871 INT uncode;
4872 enum locationkind kind;
4875 static const struct geoinfo_t geoinfodata[] = {
4876 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4877 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4878 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4879 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4880 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4881 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4882 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4883 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4884 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4885 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4886 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4887 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4888 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4889 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4890 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4891 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4892 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4893 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4894 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4895 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4896 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4897 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4898 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4899 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4900 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4901 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4902 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4903 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4904 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4905 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4906 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4907 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4908 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4909 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4910 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4911 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4912 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4913 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4914 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4915 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4916 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4917 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4918 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4919 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4920 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4921 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4922 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4923 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4924 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4925 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4926 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4927 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4928 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4929 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4930 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4931 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4932 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4933 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4934 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4935 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4936 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4937 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4938 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4939 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4940 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4941 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4942 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4943 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4944 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4945 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4946 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4947 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4948 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4949 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4950 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4951 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4952 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4953 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4954 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4955 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4956 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4957 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4958 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4959 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4960 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4961 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4962 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4963 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4964 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4965 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4966 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4967 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4968 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4969 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4970 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4971 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4972 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4973 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4974 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4975 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4976 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4977 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4978 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4979 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4980 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4981 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4982 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4983 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4984 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4985 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4986 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4987 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4988 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4989 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4990 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4991 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4992 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4993 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4994 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4995 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4996 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4997 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4998 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4999 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
5000 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
5001 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
5002 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
5003 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
5004 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
5005 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
5006 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
5007 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
5008 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
5009 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
5010 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
5011 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
5012 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
5013 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
5014 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
5015 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
5016 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
5017 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
5018 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
5019 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
5020 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
5021 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
5022 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
5023 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
5024 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
5025 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
5026 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
5027 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
5028 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
5029 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
5030 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
5031 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
5032 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
5033 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
5034 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
5035 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
5036 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
5037 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
5038 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
5039 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
5040 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
5041 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
5042 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
5043 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
5044 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
5045 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
5046 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
5047 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
5048 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
5049 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
5050 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
5051 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
5052 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
5053 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
5054 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
5055 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
5056 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
5057 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
5058 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
5059 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
5060 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
5061 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
5062 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
5063 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
5064 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
5065 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
5066 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
5067 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
5068 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
5069 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
5070 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
5071 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
5072 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
5073 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
5074 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
5075 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
5076 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
5077 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
5078 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
5079 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
5080 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
5081 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
5082 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
5083 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
5084 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
5085 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
5086 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
5087 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
5088 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
5089 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
5090 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
5091 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
5092 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
5093 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
5094 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
5095 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
5096 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
5097 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
5098 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
5099 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
5100 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
5101 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
5102 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
5103 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
5104 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
5105 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
5106 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
5107 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
5108 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
5109 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
5110 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
5111 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
5112 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
5113 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
5114 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
5115 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
5116 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
5117 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
5118 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
5119 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
5120 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
5121 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
5122 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
5123 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
5124 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
5125 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
5126 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
5127 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
5128 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
5129 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
5130 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
5131 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
5132 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
5133 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
5134 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
5135 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
5136 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
5137 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
5138 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
5139 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
5140 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
5141 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
5142 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
5143 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
5144 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
5145 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
5146 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
5147 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
5148 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
5149 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
5150 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
5151 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
5152 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
5153 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
5154 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
5155 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
5156 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
5157 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
5158 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
5159 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
5160 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
5161 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
5162 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
5163 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
5164 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
5165 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
5166 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
5167 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
5168 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
5169 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
5170 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
5171 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
5172 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
5173 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
5174 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
5177 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
5179 int min, max;
5181 min = 0;
5182 max = ARRAY_SIZE(geoinfodata)-1;
5184 while (min <= max) {
5185 const struct geoinfo_t *ptr;
5186 int n = (min+max)/2;
5188 ptr = &geoinfodata[n];
5189 if (geoid == ptr->id)
5190 /* we don't need empty entries */
5191 return *ptr->iso2W ? ptr : NULL;
5193 if (ptr->id > geoid)
5194 max = n-1;
5195 else
5196 min = n+1;
5199 return NULL;
5202 /******************************************************************************
5203 * GetGeoInfoW (KERNEL32.@)
5205 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
5207 const struct geoinfo_t *ptr;
5208 const WCHAR *str = NULL;
5209 WCHAR buffW[12];
5210 LONG val = 0;
5211 INT len;
5213 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5215 if (!(ptr = get_geoinfo_dataptr(geoid))) {
5216 SetLastError(ERROR_INVALID_PARAMETER);
5217 return 0;
5220 switch (geotype) {
5221 case GEO_NATION:
5222 val = geoid;
5223 break;
5224 case GEO_ISO_UN_NUMBER:
5225 val = ptr->uncode;
5226 break;
5227 case GEO_PARENT:
5228 val = ptr->parent;
5229 break;
5230 case GEO_ISO2:
5231 case GEO_ISO3:
5233 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
5234 break;
5236 case GEO_RFC1766:
5237 case GEO_LCID:
5238 case GEO_FRIENDLYNAME:
5239 case GEO_OFFICIALNAME:
5240 case GEO_TIMEZONES:
5241 case GEO_OFFICIALLANGUAGES:
5242 case GEO_LATITUDE:
5243 case GEO_LONGITUDE:
5244 FIXME("type %d is not supported\n", geotype);
5245 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5246 return 0;
5247 default:
5248 WARN("unrecognized type %d\n", geotype);
5249 SetLastError(ERROR_INVALID_FLAGS);
5250 return 0;
5253 if (val) {
5254 static const WCHAR fmtW[] = {'%','d',0};
5255 sprintfW(buffW, fmtW, val);
5256 str = buffW;
5259 len = strlenW(str) + 1;
5260 if (!data || !data_len)
5261 return len;
5263 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
5264 if (data_len < len)
5265 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5266 return data_len < len ? 0 : len;
5269 /******************************************************************************
5270 * GetGeoInfoA (KERNEL32.@)
5272 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
5274 WCHAR *buffW;
5275 INT len;
5277 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5279 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
5280 if (!len)
5281 return 0;
5283 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
5284 if (!buffW)
5285 return 0;
5287 GetGeoInfoW(geoid, geotype, buffW, len, lang);
5288 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
5289 if (!data || !data_len) {
5290 HeapFree(GetProcessHeap(), 0, buffW);
5291 return len;
5294 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
5295 HeapFree(GetProcessHeap(), 0, buffW);
5297 if (data_len < len)
5298 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5299 return data_len < len ? 0 : len;
5302 /******************************************************************************
5303 * EnumSystemGeoID (KERNEL32.@)
5305 * Call a users function for every location available on the system.
5307 * PARAMS
5308 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
5309 * parent [I] GEOID for the parent
5310 * enumproc [I] Callback function to call for each location
5312 * RETURNS
5313 * Success: TRUE.
5314 * Failure: FALSE. Use GetLastError() to determine the cause.
5316 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
5318 INT i;
5320 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
5322 if (!enumproc) {
5323 SetLastError(ERROR_INVALID_PARAMETER);
5324 return FALSE;
5327 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
5328 SetLastError(ERROR_INVALID_FLAGS);
5329 return FALSE;
5332 for (i = 0; i < ARRAY_SIZE(geoinfodata); i++) {
5333 const struct geoinfo_t *ptr = &geoinfodata[i];
5335 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
5336 continue;
5338 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
5339 continue;
5341 if (parent && ptr->parent != parent)
5342 continue;
5344 if (!enumproc(ptr->id))
5345 return TRUE;
5348 return TRUE;
5351 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
5353 LCID userlcid;
5355 TRACE("%p, %d\n", localename, buffersize);
5357 userlcid = GetUserDefaultLCID();
5358 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
5361 /******************************************************************************
5362 * NormalizeString (KERNEL32.@)
5364 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
5365 LPWSTR lpDstString, INT cwDstLength)
5367 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
5368 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5369 return 0;
5372 /******************************************************************************
5373 * IsNormalizedString (KERNEL32.@)
5375 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
5377 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5378 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5379 return FALSE;
5382 enum {
5383 BASE = 36,
5384 TMIN = 1,
5385 TMAX = 26,
5386 SKEW = 38,
5387 DAMP = 700,
5388 INIT_BIAS = 72,
5389 INIT_N = 128
5392 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5394 INT k;
5396 delta /= (firsttime ? DAMP : 2);
5397 delta += delta/numpoints;
5399 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5400 delta /= BASE-TMIN;
5401 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5404 /******************************************************************************
5405 * IdnToAscii (KERNEL32.@)
5406 * Implementation of Punycode based on RFC 3492.
5408 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5409 LPWSTR lpASCIICharStr, INT cchASCIIChar)
5411 static const WCHAR prefixW[] = {'x','n','-','-'};
5413 WCHAR *norm_str;
5414 INT i, label_start, label_end, norm_len, out_label, out = 0;
5416 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5417 lpASCIICharStr, cchASCIIChar);
5419 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5420 if(!norm_len)
5421 return 0;
5422 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5423 if(!norm_str) {
5424 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5425 return 0;
5427 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5428 cchUnicodeChar, norm_str, norm_len);
5429 if(!norm_len) {
5430 HeapFree(GetProcessHeap(), 0, norm_str);
5431 return 0;
5434 for(label_start=0; label_start<norm_len;) {
5435 INT n = INIT_N, bias = INIT_BIAS;
5436 INT delta = 0, b = 0, h;
5438 out_label = out;
5439 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5440 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5441 if(norm_str[i] < 0x80)
5442 b++;
5443 label_end = i;
5445 if(b == label_end-label_start) {
5446 if(label_end < norm_len)
5447 b++;
5448 if(!lpASCIICharStr) {
5449 out += b;
5450 }else if(out+b <= cchASCIIChar) {
5451 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5452 out += b;
5453 }else {
5454 HeapFree(GetProcessHeap(), 0, norm_str);
5455 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5456 return 0;
5458 label_start = label_end+1;
5459 continue;
5462 if(!lpASCIICharStr) {
5463 out += 5+b; /* strlen(xn--...-) */
5464 }else if(out+5+b <= cchASCIIChar) {
5465 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5466 out += 4;
5467 for(i=label_start; i<label_end; i++)
5468 if(norm_str[i] < 0x80)
5469 lpASCIICharStr[out++] = norm_str[i];
5470 lpASCIICharStr[out++] = '-';
5471 }else {
5472 HeapFree(GetProcessHeap(), 0, norm_str);
5473 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5474 return 0;
5476 if(!b)
5477 out--;
5479 for(h=b; h<label_end-label_start;) {
5480 INT m = 0xffff, q, k;
5482 for(i=label_start; i<label_end; i++) {
5483 if(norm_str[i]>=n && m>norm_str[i])
5484 m = norm_str[i];
5486 delta += (m-n)*(h+1);
5487 n = m;
5489 for(i=label_start; i<label_end; i++) {
5490 if(norm_str[i] < n) {
5491 delta++;
5492 }else if(norm_str[i] == n) {
5493 for(q=delta, k=BASE; ; k+=BASE) {
5494 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5495 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5496 if(!lpASCIICharStr) {
5497 out++;
5498 }else if(out+1 <= cchASCIIChar) {
5499 lpASCIICharStr[out++] = disp<='z'-'a' ?
5500 'a'+disp : '0'+disp-'z'+'a'-1;
5501 }else {
5502 HeapFree(GetProcessHeap(), 0, norm_str);
5503 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5504 return 0;
5506 if(q < t)
5507 break;
5508 q = (q-t)/(BASE-t);
5510 bias = adapt(delta, h+1, h==b);
5511 delta = 0;
5512 h++;
5515 delta++;
5516 n++;
5519 if(out-out_label > 63) {
5520 HeapFree(GetProcessHeap(), 0, norm_str);
5521 SetLastError(ERROR_INVALID_NAME);
5522 return 0;
5525 if(label_end < norm_len) {
5526 if(!lpASCIICharStr) {
5527 out++;
5528 }else if(out+1 <= cchASCIIChar) {
5529 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5530 }else {
5531 HeapFree(GetProcessHeap(), 0, norm_str);
5532 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5533 return 0;
5536 label_start = label_end+1;
5539 HeapFree(GetProcessHeap(), 0, norm_str);
5540 return out;
5543 /******************************************************************************
5544 * IdnToNameprepUnicode (KERNEL32.@)
5546 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5547 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5549 enum {
5550 UNASSIGNED = 0x1,
5551 PROHIBITED = 0x2,
5552 BIDI_RAL = 0x4,
5553 BIDI_L = 0x8
5556 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5557 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5558 const WCHAR *ptr;
5559 WORD flags;
5560 WCHAR buf[64], *map_str, norm_str[64], ch;
5561 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5562 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5564 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5565 lpNameprepCharStr, cchNameprepChar);
5567 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5568 SetLastError(ERROR_INVALID_FLAGS);
5569 return 0;
5572 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5573 SetLastError(ERROR_INVALID_PARAMETER);
5574 return 0;
5577 if(cchUnicodeChar == -1)
5578 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5579 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5580 SetLastError(ERROR_INVALID_NAME);
5581 return 0;
5584 for(label_start=0; label_start<cchUnicodeChar;) {
5585 ascii_only = TRUE;
5586 for(i=label_start; i<cchUnicodeChar; i++) {
5587 ch = lpUnicodeCharStr[i];
5589 if(i!=cchUnicodeChar-1 && !ch) {
5590 SetLastError(ERROR_INVALID_NAME);
5591 return 0;
5593 /* check if ch is one of label separators defined in RFC3490 */
5594 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5595 break;
5597 if(ch > 0x7f) {
5598 ascii_only = FALSE;
5599 continue;
5602 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5603 continue;
5604 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5605 || (ch>='0' && ch<='9') || ch=='-')
5606 continue;
5608 SetLastError(ERROR_INVALID_NAME);
5609 return 0;
5611 label_end = i;
5612 /* last label may be empty */
5613 if(label_start==label_end && ch) {
5614 SetLastError(ERROR_INVALID_NAME);
5615 return 0;
5618 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5619 lpUnicodeCharStr[label_end-1]=='-')) {
5620 SetLastError(ERROR_INVALID_NAME);
5621 return 0;
5624 if(ascii_only) {
5625 /* maximal label length is 63 characters */
5626 if(label_end-label_start > 63) {
5627 SetLastError(ERROR_INVALID_NAME);
5628 return 0;
5630 if(label_end < cchUnicodeChar)
5631 label_end++;
5633 if(!lpNameprepCharStr) {
5634 out += label_end-label_start;
5635 }else if(out+label_end-label_start <= cchNameprepChar) {
5636 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5637 (label_end-label_start)*sizeof(WCHAR));
5638 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5639 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5640 out += label_end-label_start;
5641 }else {
5642 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5643 return 0;
5646 label_start = label_end;
5647 continue;
5650 map_len = 0;
5651 for(i=label_start; i<label_end; i++) {
5652 ch = lpUnicodeCharStr[i];
5653 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5654 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5656 if(!ptr[0]) map_len++;
5657 else if(!ptr[1]) map_len++;
5658 else if(!ptr[2]) map_len += 2;
5659 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5661 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5662 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5663 if(!map_str) {
5664 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5665 return 0;
5667 }else {
5668 map_str = buf;
5670 map_len = 0;
5671 for(i=label_start; i<label_end; i++) {
5672 ch = lpUnicodeCharStr[i];
5673 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5674 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5676 if(!ptr[0]) {
5677 map_str[map_len++] = ch;
5678 }else if(!ptr[1]) {
5679 map_str[map_len++] = ptr[0];
5680 }else if(!ptr[2]) {
5681 map_str[map_len++] = ptr[0];
5682 map_str[map_len++] = ptr[1];
5683 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5684 map_str[map_len++] = ptr[0];
5685 map_str[map_len++] = ptr[1];
5686 map_str[map_len++] = ptr[2];
5690 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len, norm_str, ARRAY_SIZE(norm_str)-1);
5691 if(map_str != buf)
5692 HeapFree(GetProcessHeap(), 0, map_str);
5693 if(!norm_len) {
5694 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5695 SetLastError(ERROR_INVALID_NAME);
5696 return 0;
5699 if(label_end < cchUnicodeChar) {
5700 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5701 label_end++;
5704 if(!lpNameprepCharStr) {
5705 out += norm_len;
5706 }else if(out+norm_len <= cchNameprepChar) {
5707 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5708 out += norm_len;
5709 }else {
5710 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5711 return 0;
5714 have_bidi_ral = prohibit_bidi_ral = FALSE;
5715 mask = PROHIBITED;
5716 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5717 mask |= UNASSIGNED;
5718 for(i=0; i<norm_len; i++) {
5719 ch = norm_str[i];
5720 flags = get_table_entry( nameprep_char_type, ch );
5722 if(flags & mask) {
5723 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5724 : ERROR_NO_UNICODE_TRANSLATION);
5725 return 0;
5728 if(flags & BIDI_RAL)
5729 have_bidi_ral = TRUE;
5730 if(flags & BIDI_L)
5731 prohibit_bidi_ral = TRUE;
5734 if(have_bidi_ral) {
5735 ch = norm_str[0];
5736 flags = get_table_entry( nameprep_char_type, ch );
5737 if((flags & BIDI_RAL) == 0)
5738 prohibit_bidi_ral = TRUE;
5740 ch = norm_str[norm_len-1];
5741 flags = get_table_entry( nameprep_char_type, ch );
5742 if((flags & BIDI_RAL) == 0)
5743 prohibit_bidi_ral = TRUE;
5746 if(have_bidi_ral && prohibit_bidi_ral) {
5747 SetLastError(ERROR_INVALID_NAME);
5748 return 0;
5751 label_start = label_end;
5754 return out;
5757 /******************************************************************************
5758 * IdnToUnicode (KERNEL32.@)
5760 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5761 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5763 extern const unsigned short nameprep_char_type[];
5765 INT i, label_start, label_end, out_label, out = 0;
5766 WCHAR ch;
5768 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5769 lpUnicodeCharStr, cchUnicodeChar);
5771 for(label_start=0; label_start<cchASCIIChar;) {
5772 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5774 out_label = out;
5775 for(i=label_start; i<cchASCIIChar; i++) {
5776 ch = lpASCIICharStr[i];
5778 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5779 SetLastError(ERROR_INVALID_NAME);
5780 return 0;
5783 if(!ch || ch=='.')
5784 break;
5785 if(ch == '-')
5786 delim = i;
5788 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5789 continue;
5790 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5791 || (ch>='0' && ch<='9') || ch=='-')
5792 continue;
5794 SetLastError(ERROR_INVALID_NAME);
5795 return 0;
5797 label_end = i;
5798 /* last label may be empty */
5799 if(label_start==label_end && ch) {
5800 SetLastError(ERROR_INVALID_NAME);
5801 return 0;
5804 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5805 lpASCIICharStr[label_end-1]=='-')) {
5806 SetLastError(ERROR_INVALID_NAME);
5807 return 0;
5809 if(label_end-label_start > 63) {
5810 SetLastError(ERROR_INVALID_NAME);
5811 return 0;
5814 if(label_end-label_start<4 ||
5815 tolowerW(lpASCIICharStr[label_start])!='x' ||
5816 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5817 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5818 if(label_end < cchASCIIChar)
5819 label_end++;
5821 if(!lpUnicodeCharStr) {
5822 out += label_end-label_start;
5823 }else if(out+label_end-label_start <= cchUnicodeChar) {
5824 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5825 (label_end-label_start)*sizeof(WCHAR));
5826 out += label_end-label_start;
5827 }else {
5828 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5829 return 0;
5832 label_start = label_end;
5833 continue;
5836 if(delim == label_start+3)
5837 delim++;
5838 if(!lpUnicodeCharStr) {
5839 out += delim-label_start-4;
5840 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5841 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5842 (delim-label_start-4)*sizeof(WCHAR));
5843 out += delim-label_start-4;
5844 }else {
5845 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5846 return 0;
5848 if(out != out_label)
5849 delim++;
5851 for(i=delim; i<label_end;) {
5852 old_pos = pos;
5853 w = 1;
5854 for(k=BASE; ; k+=BASE) {
5855 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5856 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5857 SetLastError(ERROR_INVALID_NAME);
5858 return 0;
5860 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5861 pos += digit*w;
5862 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5863 if(digit < t)
5864 break;
5865 w *= BASE-t;
5867 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5868 n += pos/(out-out_label+1);
5869 pos %= out-out_label+1;
5871 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5872 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5873 SetLastError(ERROR_INVALID_NAME);
5874 return 0;
5876 if(!lpUnicodeCharStr) {
5877 out++;
5878 }else if(out+1 <= cchASCIIChar) {
5879 memmove(lpUnicodeCharStr+out_label+pos+1,
5880 lpUnicodeCharStr+out_label+pos,
5881 (out-out_label-pos)*sizeof(WCHAR));
5882 lpUnicodeCharStr[out_label+pos] = n;
5883 out++;
5884 }else {
5885 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5886 return 0;
5888 pos++;
5891 if(out-out_label > 63) {
5892 SetLastError(ERROR_INVALID_NAME);
5893 return 0;
5896 if(label_end < cchASCIIChar) {
5897 if(!lpUnicodeCharStr) {
5898 out++;
5899 }else if(out+1 <= cchUnicodeChar) {
5900 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5901 }else {
5902 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5903 return 0;
5906 label_start = label_end+1;
5909 return out;
5913 /******************************************************************************
5914 * GetFileMUIPath (KERNEL32.@)
5917 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5918 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5920 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5921 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5923 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5925 return FALSE;
5928 /******************************************************************************
5929 * GetFileMUIInfo (KERNEL32.@)
5932 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5934 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5936 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5937 return FALSE;
5940 /******************************************************************************
5941 * ResolveLocaleName (KERNEL32.@)
5944 INT WINAPI ResolveLocaleName(LPCWSTR name, LPWSTR localename, INT len)
5946 FIXME("stub: %s, %p, %d\n", wine_dbgstr_w(name), localename, len);
5948 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5949 return 0;
5952 /******************************************************************************
5953 * FindNLSStringEx (KERNEL32.@)
5956 INT WINAPI FindNLSStringEx(const WCHAR *localename, DWORD flags, const WCHAR *src,
5957 INT src_size, const WCHAR *value, INT value_size,
5958 INT *found, NLSVERSIONINFO *version_info, void *reserved,
5959 LPARAM sort_handle)
5962 /* FIXME: this function should normalize strings before calling CompareStringEx() */
5963 DWORD mask = flags;
5964 int offset, inc, count;
5966 TRACE("%s %x %s %d %s %d %p %p %p %ld\n", wine_dbgstr_w(localename), flags,
5967 wine_dbgstr_w(src), src_size, wine_dbgstr_w(value), value_size, found,
5968 version_info, reserved, sort_handle);
5970 if (version_info != NULL || reserved != NULL || sort_handle != 0 ||
5971 !IsValidLocaleName(localename) || src == NULL || src_size == 0 ||
5972 src_size < -1 || value == NULL || value_size == 0 || value_size < -1)
5974 SetLastError(ERROR_INVALID_PARAMETER);
5975 return -1;
5977 if (src_size == -1)
5978 src_size = strlenW(src);
5979 if (value_size == -1)
5980 value_size = strlenW(value);
5982 src_size -= value_size;
5983 if (src_size < 0) return -1;
5985 mask = flags & ~(FIND_FROMSTART | FIND_FROMEND | FIND_STARTSWITH | FIND_ENDSWITH);
5986 count = flags & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
5987 offset = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
5988 inc = flags & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
5989 while (count--)
5991 if (CompareStringEx(localename, mask, src + offset, value_size, value, value_size, NULL, NULL, 0) == CSTR_EQUAL)
5993 if (found)
5994 *found = value_size;
5995 return offset;
5997 offset += inc;
6000 return -1;
6003 /******************************************************************************
6004 * FindStringOrdinal (KERNEL32.@)
6007 INT WINAPI FindStringOrdinal(DWORD flag, const WCHAR *src, INT src_size, const WCHAR *val, INT val_size,
6008 BOOL ignore_case)
6010 INT offset, inc, count;
6011 TRACE("%#x %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size, wine_dbgstr_w(val), val_size, ignore_case);
6013 if (!src || !val)
6015 SetLastError(ERROR_INVALID_PARAMETER);
6016 return -1;
6019 if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
6021 SetLastError(ERROR_INVALID_FLAGS);
6022 return -1;
6025 if (src_size == -1) src_size = strlenW(src);
6026 if (val_size == -1) val_size = strlenW(val);
6028 src_size -= val_size;
6029 if (src_size < 0)
6031 SetLastError(NO_ERROR);
6032 return -1;
6035 count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
6036 offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
6037 inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
6038 while (count--)
6040 if (CompareStringOrdinal(src + offset, val_size, val, val_size, ignore_case) == CSTR_EQUAL)
6042 SetLastError(NO_ERROR);
6043 return offset;
6045 offset += inc;
6048 SetLastError(NO_ERROR);
6049 return -1;