kernel32: Print a FIXME only once in LCMapStringEx.
[wine.git] / dlls / kernel32 / locale.c
blob528f945513f94f40b8b6233a7ee85e0f84e85476
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)
58 /* current code pages */
59 static const union cptable *ansi_cptable;
60 static const union cptable *oem_cptable;
61 static const union cptable *mac_cptable;
62 static const union cptable *unix_cptable; /* NULL if UTF8 */
64 static const WCHAR szLocaleKeyName[] = {
65 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
66 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
67 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
70 static const WCHAR szLangGroupsKeyName[] = {
71 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
72 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
73 'C','o','n','t','r','o','l','\\','N','l','s','\\',
74 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
77 /* Charset to codepage map, sorted by name. */
78 static const struct charset_entry
80 const char *charset_name;
81 UINT codepage;
82 } charset_names[] =
84 { "BIG5", 950 },
85 { "CP1250", 1250 },
86 { "CP1251", 1251 },
87 { "CP1252", 1252 },
88 { "CP1253", 1253 },
89 { "CP1254", 1254 },
90 { "CP1255", 1255 },
91 { "CP1256", 1256 },
92 { "CP1257", 1257 },
93 { "CP1258", 1258 },
94 { "CP932", 932 },
95 { "CP936", 936 },
96 { "CP949", 949 },
97 { "CP950", 950 },
98 { "EUCJP", 20932 },
99 { "GB2312", 936 },
100 { "IBM037", 37 },
101 { "IBM1026", 1026 },
102 { "IBM424", 424 },
103 { "IBM437", 437 },
104 { "IBM500", 500 },
105 { "IBM850", 850 },
106 { "IBM852", 852 },
107 { "IBM855", 855 },
108 { "IBM857", 857 },
109 { "IBM860", 860 },
110 { "IBM861", 861 },
111 { "IBM862", 862 },
112 { "IBM863", 863 },
113 { "IBM864", 864 },
114 { "IBM865", 865 },
115 { "IBM866", 866 },
116 { "IBM869", 869 },
117 { "IBM874", 874 },
118 { "IBM875", 875 },
119 { "ISO88591", 28591 },
120 { "ISO885910", 28600 },
121 { "ISO885913", 28603 },
122 { "ISO885914", 28604 },
123 { "ISO885915", 28605 },
124 { "ISO885916", 28606 },
125 { "ISO88592", 28592 },
126 { "ISO88593", 28593 },
127 { "ISO88594", 28594 },
128 { "ISO88595", 28595 },
129 { "ISO88596", 28596 },
130 { "ISO88597", 28597 },
131 { "ISO88598", 28598 },
132 { "ISO88599", 28599 },
133 { "KOI8R", 20866 },
134 { "KOI8U", 21866 },
135 { "UTF8", CP_UTF8 }
139 struct locale_name
141 WCHAR win_name[128]; /* Windows name ("en-US") */
142 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
143 WCHAR *country; /* country ("US") */
144 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
145 WCHAR *script; /* script ("Latn") for Windows format only */
146 WCHAR *modifier; /* modifier or sort order */
147 LCID lcid; /* corresponding LCID */
148 int matches; /* number of elements matching LCID (0..4) */
149 UINT codepage; /* codepage corresponding to charset */
152 /* locale ids corresponding to the various Unix locale parameters */
153 static LCID lcid_LC_COLLATE;
154 static LCID lcid_LC_CTYPE;
155 static LCID lcid_LC_MESSAGES;
156 static LCID lcid_LC_MONETARY;
157 static LCID lcid_LC_NUMERIC;
158 static LCID lcid_LC_TIME;
159 static LCID lcid_LC_PAPER;
160 static LCID lcid_LC_MEASUREMENT;
161 static LCID lcid_LC_TELEPHONE;
163 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
164 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
165 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
166 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
167 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
168 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
169 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
170 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
171 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
172 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
173 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
174 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
175 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
176 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
177 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
178 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
179 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
180 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
181 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
182 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
183 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
184 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
185 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
186 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
187 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
188 static const WCHAR sListW[] = {'s','L','i','s','t',0};
189 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
190 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
191 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
192 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
193 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
194 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
195 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
196 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
197 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
198 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
199 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
200 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
201 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
203 static struct registry_value
205 DWORD lctype;
206 const WCHAR *name;
207 WCHAR *cached_value;
208 } registry_values[] =
210 { LOCALE_ICALENDARTYPE, iCalendarTypeW },
211 { LOCALE_ICURRDIGITS, iCurrDigitsW },
212 { LOCALE_ICURRENCY, iCurrencyW },
213 { LOCALE_IDIGITS, iDigitsW },
214 { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
215 { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
216 { LOCALE_ILZERO, iLZeroW },
217 { LOCALE_IMEASURE, iMeasureW },
218 { LOCALE_INEGCURR, iNegCurrW },
219 { LOCALE_INEGNUMBER, iNegNumberW },
220 { LOCALE_IPAPERSIZE, iPaperSizeW },
221 { LOCALE_ITIME, iTimeW },
222 { LOCALE_S1159, s1159W },
223 { LOCALE_S2359, s2359W },
224 { LOCALE_SCURRENCY, sCurrencyW },
225 { LOCALE_SDATE, sDateW },
226 { LOCALE_SDECIMAL, sDecimalW },
227 { LOCALE_SGROUPING, sGroupingW },
228 { LOCALE_SLIST, sListW },
229 { LOCALE_SLONGDATE, sLongDateW },
230 { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
231 { LOCALE_SMONGROUPING, sMonGroupingW },
232 { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
233 { LOCALE_SNEGATIVESIGN, sNegativeSignW },
234 { LOCALE_SPOSITIVESIGN, sPositiveSignW },
235 { LOCALE_SSHORTDATE, sShortDateW },
236 { LOCALE_STHOUSAND, sThousandW },
237 { LOCALE_STIME, sTimeW },
238 { LOCALE_STIMEFORMAT, sTimeFormatW },
239 { LOCALE_SYEARMONTH, sYearMonthW },
240 /* The following are not listed under MSDN as supported,
241 * but seem to be used and also stored in the registry.
243 { LOCALE_ICOUNTRY, iCountryW },
244 { LOCALE_IDATE, iDateW },
245 { LOCALE_ILDATE, iLDateW },
246 { LOCALE_ITLZERO, iTLZeroW },
247 { LOCALE_SCOUNTRY, sCountryW },
248 { LOCALE_SABBREVLANGNAME, sLanguageW },
249 /* The following are used in XP and later */
250 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
251 { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
252 { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
255 static CRITICAL_SECTION cache_section;
256 static CRITICAL_SECTION_DEBUG critsect_debug =
258 0, 0, &cache_section,
259 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
260 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
262 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
264 /* Copy Ascii string to Unicode without using codepages */
265 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
267 while (n > 1 && *src)
269 *dst++ = (unsigned char)*src++;
270 n--;
272 if (n) *dst = 0;
275 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
277 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
280 /***********************************************************************
281 * get_lcid_codepage
283 * Retrieve the ANSI codepage for a given locale.
285 static inline UINT get_lcid_codepage( LCID lcid )
287 UINT ret;
288 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
289 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
290 return ret;
294 /***********************************************************************
295 * get_codepage_table
297 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
299 static const union cptable *get_codepage_table( unsigned int codepage )
301 const union cptable *ret = NULL;
303 assert( ansi_cptable ); /* init must have been done already */
305 switch(codepage)
307 case CP_ACP:
308 return ansi_cptable;
309 case CP_OEMCP:
310 return oem_cptable;
311 case CP_MACCP:
312 return mac_cptable;
313 case CP_UTF7:
314 case CP_UTF8:
315 break;
316 case CP_THREAD_ACP:
317 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
318 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
319 if (!codepage) return ansi_cptable;
320 /* fall through */
321 default:
322 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
323 if (codepage == oem_cptable->info.codepage) return oem_cptable;
324 if (codepage == mac_cptable->info.codepage) return mac_cptable;
325 ret = wine_cp_get_table( codepage );
326 break;
328 return ret;
332 /***********************************************************************
333 * charset_cmp (internal)
335 static int charset_cmp( const void *name, const void *entry )
337 const struct charset_entry *charset = entry;
338 return strcasecmp( name, charset->charset_name );
341 /***********************************************************************
342 * find_charset
344 static UINT find_charset( const WCHAR *name )
346 const struct charset_entry *entry;
347 char charset_name[16];
348 size_t i, j;
350 /* remove punctuation characters from charset name */
351 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
352 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
353 charset_name[j] = 0;
355 entry = bsearch( charset_name, charset_names,
356 sizeof(charset_names)/sizeof(charset_names[0]),
357 sizeof(charset_names[0]), charset_cmp );
358 if (entry) return entry->codepage;
359 return 0;
363 /***********************************************************************
364 * find_locale_id_callback
366 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
367 LPCWSTR name, WORD LangID, LPARAM lParam )
369 struct locale_name *data = (struct locale_name *)lParam;
370 WCHAR buffer[128];
371 int matches = 0;
372 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
374 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
376 /* first check exact name */
377 if (data->win_name[0] &&
378 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
379 buffer, sizeof(buffer)/sizeof(WCHAR) ))
381 if (!strcmpW( data->win_name, buffer ))
383 matches = 4; /* everything matches */
384 goto done;
388 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
389 buffer, sizeof(buffer)/sizeof(WCHAR) ))
390 return TRUE;
391 if (strcmpW( buffer, data->lang )) return TRUE;
392 matches++; /* language name matched */
394 if (data->country)
396 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
397 buffer, sizeof(buffer)/sizeof(WCHAR) ))
399 if (strcmpW( buffer, data->country )) goto done;
400 matches++; /* country name matched */
403 else /* match default language */
405 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
408 if (data->codepage)
410 UINT unix_cp;
411 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
412 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
414 if (unix_cp == data->codepage) matches++;
418 /* FIXME: check sort order */
420 done:
421 if (matches > data->matches)
423 data->lcid = lcid;
424 data->matches = matches;
426 return (data->matches < 4); /* no need to continue for perfect match */
430 /***********************************************************************
431 * parse_locale_name
433 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
434 * Unix format is: lang[_country][.charset][@modifier]
435 * Windows format is: lang[-script][-country][_modifier]
437 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
439 static const WCHAR sepW[] = {'-','_','.','@',0};
440 static const WCHAR winsepW[] = {'-','_',0};
441 static const WCHAR posixW[] = {'P','O','S','I','X',0};
442 static const WCHAR cW[] = {'C',0};
443 static const WCHAR latinW[] = {'l','a','t','i','n',0};
444 static const WCHAR latnW[] = {'-','L','a','t','n',0};
445 WCHAR *p;
447 TRACE("%s\n", debugstr_w(str));
449 name->country = name->charset = name->script = name->modifier = NULL;
450 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
451 name->matches = 0;
452 name->codepage = 0;
453 name->win_name[0] = 0;
454 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
456 if (!*name->lang)
458 name->lcid = LOCALE_INVARIANT;
459 name->matches = 4;
460 return;
463 if (!(p = strpbrkW( name->lang, sepW )))
465 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
467 name->matches = 4; /* perfect match for default English lcid */
468 return;
470 strcpyW( name->win_name, name->lang );
472 else if (*p == '-') /* Windows format */
474 strcpyW( name->win_name, name->lang );
475 *p++ = 0;
476 name->country = p;
477 if (!(p = strpbrkW( p, winsepW ))) goto done;
478 if (*p == '-')
480 *p++ = 0;
481 name->script = name->country;
482 name->country = p;
483 if (!(p = strpbrkW( p, winsepW ))) goto done;
485 *p++ = 0;
486 name->modifier = p;
488 else /* Unix format */
490 if (*p == '_')
492 *p++ = 0;
493 name->country = p;
494 p = strpbrkW( p, sepW + 2 );
496 if (p && *p == '.')
498 *p++ = 0;
499 name->charset = p;
500 p = strchrW( p, '@' );
502 if (p)
504 *p++ = 0;
505 name->modifier = p;
508 if (name->charset)
509 name->codepage = find_charset( name->charset );
511 /* rebuild a Windows name if possible */
513 if (name->charset) goto done; /* can't specify charset in Windows format */
514 if (name->modifier && strcmpW( name->modifier, latinW ))
515 goto done; /* only Latn script supported for now */
516 strcpyW( name->win_name, name->lang );
517 if (name->modifier) strcatW( name->win_name, latnW );
518 if (name->country)
520 p = name->win_name + strlenW(name->win_name);
521 *p++ = '-';
522 strcpyW( p, name->country );
525 done:
526 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
527 find_locale_id_callback, (LPARAM)name );
531 /***********************************************************************
532 * convert_default_lcid
534 * Get the default LCID to use for a given lctype in GetLocaleInfo.
536 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
538 if (lcid == LOCALE_SYSTEM_DEFAULT ||
539 lcid == LOCALE_USER_DEFAULT ||
540 lcid == LOCALE_NEUTRAL)
542 LCID default_id = 0;
544 switch(lctype & 0xffff)
546 case LOCALE_SSORTNAME:
547 default_id = lcid_LC_COLLATE;
548 break;
550 case LOCALE_FONTSIGNATURE:
551 case LOCALE_IDEFAULTANSICODEPAGE:
552 case LOCALE_IDEFAULTCODEPAGE:
553 case LOCALE_IDEFAULTEBCDICCODEPAGE:
554 case LOCALE_IDEFAULTMACCODEPAGE:
555 case LOCALE_IDEFAULTUNIXCODEPAGE:
556 default_id = lcid_LC_CTYPE;
557 break;
559 case LOCALE_ICURRDIGITS:
560 case LOCALE_ICURRENCY:
561 case LOCALE_IINTLCURRDIGITS:
562 case LOCALE_INEGCURR:
563 case LOCALE_INEGSEPBYSPACE:
564 case LOCALE_INEGSIGNPOSN:
565 case LOCALE_INEGSYMPRECEDES:
566 case LOCALE_IPOSSEPBYSPACE:
567 case LOCALE_IPOSSIGNPOSN:
568 case LOCALE_IPOSSYMPRECEDES:
569 case LOCALE_SCURRENCY:
570 case LOCALE_SINTLSYMBOL:
571 case LOCALE_SMONDECIMALSEP:
572 case LOCALE_SMONGROUPING:
573 case LOCALE_SMONTHOUSANDSEP:
574 case LOCALE_SNATIVECURRNAME:
575 default_id = lcid_LC_MONETARY;
576 break;
578 case LOCALE_IDIGITS:
579 case LOCALE_IDIGITSUBSTITUTION:
580 case LOCALE_ILZERO:
581 case LOCALE_INEGNUMBER:
582 case LOCALE_SDECIMAL:
583 case LOCALE_SGROUPING:
584 case LOCALE_SNAN:
585 case LOCALE_SNATIVEDIGITS:
586 case LOCALE_SNEGATIVESIGN:
587 case LOCALE_SNEGINFINITY:
588 case LOCALE_SPOSINFINITY:
589 case LOCALE_SPOSITIVESIGN:
590 case LOCALE_STHOUSAND:
591 default_id = lcid_LC_NUMERIC;
592 break;
594 case LOCALE_ICALENDARTYPE:
595 case LOCALE_ICENTURY:
596 case LOCALE_IDATE:
597 case LOCALE_IDAYLZERO:
598 case LOCALE_IFIRSTDAYOFWEEK:
599 case LOCALE_IFIRSTWEEKOFYEAR:
600 case LOCALE_ILDATE:
601 case LOCALE_IMONLZERO:
602 case LOCALE_IOPTIONALCALENDAR:
603 case LOCALE_ITIME:
604 case LOCALE_ITIMEMARKPOSN:
605 case LOCALE_ITLZERO:
606 case LOCALE_S1159:
607 case LOCALE_S2359:
608 case LOCALE_SABBREVDAYNAME1:
609 case LOCALE_SABBREVDAYNAME2:
610 case LOCALE_SABBREVDAYNAME3:
611 case LOCALE_SABBREVDAYNAME4:
612 case LOCALE_SABBREVDAYNAME5:
613 case LOCALE_SABBREVDAYNAME6:
614 case LOCALE_SABBREVDAYNAME7:
615 case LOCALE_SABBREVMONTHNAME1:
616 case LOCALE_SABBREVMONTHNAME2:
617 case LOCALE_SABBREVMONTHNAME3:
618 case LOCALE_SABBREVMONTHNAME4:
619 case LOCALE_SABBREVMONTHNAME5:
620 case LOCALE_SABBREVMONTHNAME6:
621 case LOCALE_SABBREVMONTHNAME7:
622 case LOCALE_SABBREVMONTHNAME8:
623 case LOCALE_SABBREVMONTHNAME9:
624 case LOCALE_SABBREVMONTHNAME10:
625 case LOCALE_SABBREVMONTHNAME11:
626 case LOCALE_SABBREVMONTHNAME12:
627 case LOCALE_SABBREVMONTHNAME13:
628 case LOCALE_SDATE:
629 case LOCALE_SDAYNAME1:
630 case LOCALE_SDAYNAME2:
631 case LOCALE_SDAYNAME3:
632 case LOCALE_SDAYNAME4:
633 case LOCALE_SDAYNAME5:
634 case LOCALE_SDAYNAME6:
635 case LOCALE_SDAYNAME7:
636 case LOCALE_SDURATION:
637 case LOCALE_SLONGDATE:
638 case LOCALE_SMONTHNAME1:
639 case LOCALE_SMONTHNAME2:
640 case LOCALE_SMONTHNAME3:
641 case LOCALE_SMONTHNAME4:
642 case LOCALE_SMONTHNAME5:
643 case LOCALE_SMONTHNAME6:
644 case LOCALE_SMONTHNAME7:
645 case LOCALE_SMONTHNAME8:
646 case LOCALE_SMONTHNAME9:
647 case LOCALE_SMONTHNAME10:
648 case LOCALE_SMONTHNAME11:
649 case LOCALE_SMONTHNAME12:
650 case LOCALE_SMONTHNAME13:
651 case LOCALE_SSHORTDATE:
652 case LOCALE_SSHORTESTDAYNAME1:
653 case LOCALE_SSHORTESTDAYNAME2:
654 case LOCALE_SSHORTESTDAYNAME3:
655 case LOCALE_SSHORTESTDAYNAME4:
656 case LOCALE_SSHORTESTDAYNAME5:
657 case LOCALE_SSHORTESTDAYNAME6:
658 case LOCALE_SSHORTESTDAYNAME7:
659 case LOCALE_STIME:
660 case LOCALE_STIMEFORMAT:
661 case LOCALE_SYEARMONTH:
662 default_id = lcid_LC_TIME;
663 break;
665 case LOCALE_IPAPERSIZE:
666 default_id = lcid_LC_PAPER;
667 break;
669 case LOCALE_IMEASURE:
670 default_id = lcid_LC_MEASUREMENT;
671 break;
673 case LOCALE_ICOUNTRY:
674 default_id = lcid_LC_TELEPHONE;
675 break;
677 if (default_id) lcid = default_id;
679 return ConvertDefaultLocale( lcid );
682 /***********************************************************************
683 * is_genitive_name_supported
685 * Determine could LCTYPE basically support genitive name form or not.
687 static BOOL is_genitive_name_supported( LCTYPE lctype )
689 switch(lctype & 0xffff)
691 case LOCALE_SMONTHNAME1:
692 case LOCALE_SMONTHNAME2:
693 case LOCALE_SMONTHNAME3:
694 case LOCALE_SMONTHNAME4:
695 case LOCALE_SMONTHNAME5:
696 case LOCALE_SMONTHNAME6:
697 case LOCALE_SMONTHNAME7:
698 case LOCALE_SMONTHNAME8:
699 case LOCALE_SMONTHNAME9:
700 case LOCALE_SMONTHNAME10:
701 case LOCALE_SMONTHNAME11:
702 case LOCALE_SMONTHNAME12:
703 case LOCALE_SMONTHNAME13:
704 return TRUE;
705 default:
706 return FALSE;
710 /***********************************************************************
711 * create_registry_key
713 * Create the Control Panel\\International registry key.
715 static inline HANDLE create_registry_key(void)
717 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
718 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
719 OBJECT_ATTRIBUTES attr;
720 UNICODE_STRING nameW;
721 HANDLE cpl_key, hkey = 0;
723 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
725 attr.Length = sizeof(attr);
726 attr.RootDirectory = hkey;
727 attr.ObjectName = &nameW;
728 attr.Attributes = 0;
729 attr.SecurityDescriptor = NULL;
730 attr.SecurityQualityOfService = NULL;
731 RtlInitUnicodeString( &nameW, cplW );
733 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
735 NtClose( attr.RootDirectory );
736 attr.RootDirectory = cpl_key;
737 RtlInitUnicodeString( &nameW, intlW );
738 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
740 NtClose( attr.RootDirectory );
741 return hkey;
745 /* update the registry settings for a given locale parameter */
746 /* return TRUE if an update was needed */
747 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
748 const LCTYPE *values, UINT nb_values )
750 static const WCHAR formatW[] = { '%','0','8','x',0 };
751 WCHAR bufferW[40];
752 UNICODE_STRING nameW;
753 DWORD count, i;
755 RtlInitUnicodeString( &nameW, name );
756 count = sizeof(bufferW);
757 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
759 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
760 LPCWSTR text = (LPCWSTR)info->Data;
762 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
763 TRACE( "updating registry, locale %s changed %s -> %08x\n",
764 debugstr_w(name), debugstr_w(text), lcid );
766 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
767 sprintfW( bufferW, formatW, lcid );
768 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
770 for (i = 0; i < nb_values; i++)
772 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
773 sizeof(bufferW)/sizeof(WCHAR) );
774 SetLocaleInfoW( lcid, values[i], bufferW );
776 return TRUE;
780 /***********************************************************************
781 * LOCALE_InitRegistry
783 * Update registry contents on startup if the user locale has changed.
784 * This simulates the action of the Windows control panel.
786 void LOCALE_InitRegistry(void)
788 static const WCHAR acpW[] = {'A','C','P',0};
789 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
790 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
791 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
792 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
793 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
794 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
795 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
796 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
797 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
798 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
799 static const struct
801 LPCWSTR name;
802 USHORT value;
803 } update_cp_values[] = {
804 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
805 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
806 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
808 static const LCTYPE lc_messages_values[] = {
809 LOCALE_SABBREVLANGNAME,
810 LOCALE_SCOUNTRY,
811 LOCALE_SLIST };
812 static const LCTYPE lc_monetary_values[] = {
813 LOCALE_SCURRENCY,
814 LOCALE_ICURRENCY,
815 LOCALE_INEGCURR,
816 LOCALE_ICURRDIGITS,
817 LOCALE_ILZERO,
818 LOCALE_SMONDECIMALSEP,
819 LOCALE_SMONGROUPING,
820 LOCALE_SMONTHOUSANDSEP };
821 static const LCTYPE lc_numeric_values[] = {
822 LOCALE_SDECIMAL,
823 LOCALE_STHOUSAND,
824 LOCALE_IDIGITS,
825 LOCALE_IDIGITSUBSTITUTION,
826 LOCALE_SNATIVEDIGITS,
827 LOCALE_INEGNUMBER,
828 LOCALE_SNEGATIVESIGN,
829 LOCALE_SPOSITIVESIGN,
830 LOCALE_SGROUPING };
831 static const LCTYPE lc_time_values[] = {
832 LOCALE_S1159,
833 LOCALE_S2359,
834 LOCALE_STIME,
835 LOCALE_ITIME,
836 LOCALE_ITLZERO,
837 LOCALE_SSHORTDATE,
838 LOCALE_SLONGDATE,
839 LOCALE_SDATE,
840 LOCALE_ITIMEMARKPOSN,
841 LOCALE_ICALENDARTYPE,
842 LOCALE_IFIRSTDAYOFWEEK,
843 LOCALE_IFIRSTWEEKOFYEAR,
844 LOCALE_STIMEFORMAT,
845 LOCALE_SYEARMONTH,
846 LOCALE_IDATE };
847 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
848 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
849 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
851 UNICODE_STRING nameW;
852 WCHAR bufferW[80];
853 DWORD count, i;
854 HANDLE hkey;
855 LCID lcid = GetUserDefaultLCID();
857 if (!(hkey = create_registry_key()))
858 return; /* don't do anything if we can't create the registry key */
860 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
861 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
862 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
863 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
864 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
865 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
866 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
867 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
868 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
869 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
870 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
871 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
872 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
873 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
875 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
877 static const WCHAR codepageW[] =
878 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
879 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
880 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
882 OBJECT_ATTRIBUTES attr;
883 HANDLE nls_key;
884 DWORD len = 14;
886 RtlInitUnicodeString( &nameW, codepageW );
887 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
888 while (codepageW[len])
890 nameW.Length = len * sizeof(WCHAR);
891 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
892 NtClose( nls_key );
893 len++;
894 while (codepageW[len] && codepageW[len] != '\\') len++;
896 nameW.Length = len * sizeof(WCHAR);
897 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
899 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
901 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
902 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
903 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
904 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
906 NtClose( nls_key );
910 NtClose( hkey );
914 /***********************************************************************
915 * setup_unix_locales
917 static UINT setup_unix_locales(void)
919 struct locale_name locale_name;
920 WCHAR buffer[128], ctype_buff[128];
921 char *locale;
922 UINT unix_cp = 0;
924 if ((locale = setlocale( LC_CTYPE, NULL )))
926 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
927 parse_locale_name( ctype_buff, &locale_name );
928 lcid_LC_CTYPE = locale_name.lcid;
929 unix_cp = locale_name.codepage;
931 if (!lcid_LC_CTYPE) /* this one needs a default value */
932 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
934 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
935 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
937 #define GET_UNIX_LOCALE(cat) do \
938 if ((locale = setlocale( cat, NULL ))) \
940 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
941 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
942 else { \
943 parse_locale_name( buffer, &locale_name ); \
944 lcid_##cat = locale_name.lcid; \
945 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
946 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
948 } while (0)
950 GET_UNIX_LOCALE( LC_COLLATE );
951 GET_UNIX_LOCALE( LC_MESSAGES );
952 GET_UNIX_LOCALE( LC_MONETARY );
953 GET_UNIX_LOCALE( LC_NUMERIC );
954 GET_UNIX_LOCALE( LC_TIME );
955 #ifdef LC_PAPER
956 GET_UNIX_LOCALE( LC_PAPER );
957 #endif
958 #ifdef LC_MEASUREMENT
959 GET_UNIX_LOCALE( LC_MEASUREMENT );
960 #endif
961 #ifdef LC_TELEPHONE
962 GET_UNIX_LOCALE( LC_TELEPHONE );
963 #endif
965 #undef GET_UNIX_LOCALE
967 return unix_cp;
971 /***********************************************************************
972 * GetUserDefaultLangID (KERNEL32.@)
974 * Get the default language Id for the current user.
976 * PARAMS
977 * None.
979 * RETURNS
980 * The current LANGID of the default language for the current user.
982 LANGID WINAPI GetUserDefaultLangID(void)
984 return LANGIDFROMLCID(GetUserDefaultLCID());
988 /***********************************************************************
989 * GetSystemDefaultLangID (KERNEL32.@)
991 * Get the default language Id for the system.
993 * PARAMS
994 * None.
996 * RETURNS
997 * The current LANGID of the default language for the system.
999 LANGID WINAPI GetSystemDefaultLangID(void)
1001 return LANGIDFROMLCID(GetSystemDefaultLCID());
1005 /***********************************************************************
1006 * GetUserDefaultLCID (KERNEL32.@)
1008 * Get the default locale Id for the current user.
1010 * PARAMS
1011 * None.
1013 * RETURNS
1014 * The current LCID of the default locale for the current user.
1016 LCID WINAPI GetUserDefaultLCID(void)
1018 LCID lcid;
1019 NtQueryDefaultLocale( TRUE, &lcid );
1020 return lcid;
1024 /***********************************************************************
1025 * GetSystemDefaultLCID (KERNEL32.@)
1027 * Get the default locale Id for the system.
1029 * PARAMS
1030 * None.
1032 * RETURNS
1033 * The current LCID of the default locale for the system.
1035 LCID WINAPI GetSystemDefaultLCID(void)
1037 LCID lcid;
1038 NtQueryDefaultLocale( FALSE, &lcid );
1039 return lcid;
1042 /***********************************************************************
1043 * GetSystemDefaultLocaleName (KERNEL32.@)
1045 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1047 LCID lcid = GetSystemDefaultLCID();
1048 return LCIDToLocaleName(lcid, localename, len, 0);
1051 /***********************************************************************
1052 * GetSystemPreferredUILanguages (KERNEL32.@)
1054 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1056 LCTYPE type;
1057 int lsize;
1058 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1060 SetLastError(ERROR_INVALID_PARAMETER);
1061 return FALSE;
1063 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1065 SetLastError(ERROR_INVALID_PARAMETER);
1066 return FALSE;
1068 if (*size && !buffer)
1070 SetLastError(ERROR_INVALID_PARAMETER);
1071 return FALSE;
1074 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1076 if (flags & MUI_LANGUAGE_ID)
1077 type = LOCALE_ILANGUAGE;
1078 else
1079 type = LOCALE_SNAME;
1081 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1082 if (!lsize)
1084 /* keep last error from callee */
1085 return FALSE;
1087 lsize++;
1088 if (!*size)
1090 *size = lsize;
1091 *count = 1;
1092 return TRUE;
1095 if (lsize > *size)
1097 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1098 return FALSE;
1101 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1103 /* keep last error from callee */
1104 return FALSE;
1107 buffer[lsize-1] = 0;
1108 *size = lsize;
1109 *count = 1;
1110 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1111 return TRUE;
1114 /***********************************************************************
1115 * GetUserDefaultUILanguage (KERNEL32.@)
1117 * Get the default user interface language Id for the current user.
1119 * PARAMS
1120 * None.
1122 * RETURNS
1123 * The current LANGID of the default UI language for the current user.
1125 LANGID WINAPI GetUserDefaultUILanguage(void)
1127 LANGID lang;
1128 NtQueryDefaultUILanguage( &lang );
1129 return lang;
1133 /***********************************************************************
1134 * GetSystemDefaultUILanguage (KERNEL32.@)
1136 * Get the default user interface language Id for the system.
1138 * PARAMS
1139 * None.
1141 * RETURNS
1142 * The current LANGID of the default UI language for the system. This is
1143 * typically the same language used during the installation process.
1145 LANGID WINAPI GetSystemDefaultUILanguage(void)
1147 LANGID lang;
1148 NtQueryInstallUILanguage( &lang );
1149 return lang;
1153 /***********************************************************************
1154 * LocaleNameToLCID (KERNEL32.@)
1156 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1158 struct locale_name locale_name;
1160 if (flags) FIXME( "unsupported flags %x\n", flags );
1162 if (name == LOCALE_NAME_USER_DEFAULT)
1163 return GetUserDefaultLCID();
1165 /* string parsing */
1166 parse_locale_name( name, &locale_name );
1168 TRACE( "found lcid %x for %s, matches %d\n",
1169 locale_name.lcid, debugstr_w(name), locale_name.matches );
1171 if (!locale_name.matches)
1173 SetLastError(ERROR_INVALID_PARAMETER);
1174 return 0;
1177 if (locale_name.matches == 1)
1178 WARN( "locale %s not recognized, defaulting to %s\n",
1179 debugstr_w(name), debugstr_w(locale_name.lang) );
1181 return locale_name.lcid;
1185 /***********************************************************************
1186 * LCIDToLocaleName (KERNEL32.@)
1188 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1190 if (flags) FIXME( "unsupported flags %x\n", flags );
1192 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1196 /******************************************************************************
1197 * get_locale_registry_value
1199 * Gets the registry value name and cache for a given lctype.
1201 static struct registry_value *get_locale_registry_value( DWORD lctype )
1203 int i;
1204 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1205 if (registry_values[i].lctype == lctype)
1206 return &registry_values[i];
1207 return NULL;
1211 /******************************************************************************
1212 * get_registry_locale_info
1214 * Retrieve user-modified locale info from the registry.
1215 * Return length, 0 on error, -1 if not found.
1217 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1219 DWORD size;
1220 INT ret;
1221 HANDLE hkey;
1222 NTSTATUS status;
1223 UNICODE_STRING nameW;
1224 KEY_VALUE_PARTIAL_INFORMATION *info;
1225 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1227 RtlEnterCriticalSection( &cache_section );
1229 if (!registry_value->cached_value)
1231 if (!(hkey = create_registry_key()))
1233 RtlLeaveCriticalSection( &cache_section );
1234 return -1;
1237 RtlInitUnicodeString( &nameW, registry_value->name );
1238 size = info_size + len * sizeof(WCHAR);
1240 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1242 NtClose( hkey );
1243 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1244 RtlLeaveCriticalSection( &cache_section );
1245 return 0;
1248 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1250 /* try again with a bigger buffer when we have to return the correct size */
1251 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1253 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1254 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1256 info = new_info;
1257 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1261 NtClose( hkey );
1263 if (!status)
1265 INT length = (size - info_size) / sizeof(WCHAR);
1266 LPWSTR cached_value;
1268 if (!length || ((WCHAR *)&info->Data)[length-1])
1269 length++;
1271 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1273 if (!cached_value)
1275 HeapFree( GetProcessHeap(), 0, info );
1276 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1277 RtlLeaveCriticalSection( &cache_section );
1278 return 0;
1281 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1282 cached_value[length-1] = 0;
1283 HeapFree( GetProcessHeap(), 0, info );
1284 registry_value->cached_value = cached_value;
1286 else
1288 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1290 ret = (size - info_size) / sizeof(WCHAR);
1292 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1294 ret = -1;
1296 else
1298 SetLastError( RtlNtStatusToDosError(status) );
1299 ret = 0;
1301 HeapFree( GetProcessHeap(), 0, info );
1302 RtlLeaveCriticalSection( &cache_section );
1303 return ret;
1307 ret = lstrlenW( registry_value->cached_value ) + 1;
1309 if (buffer)
1311 if (ret > len)
1313 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1314 ret = 0;
1316 else
1318 lstrcpyW( buffer, registry_value->cached_value );
1322 RtlLeaveCriticalSection( &cache_section );
1324 return ret;
1328 /******************************************************************************
1329 * GetLocaleInfoA (KERNEL32.@)
1331 * Get information about an aspect of a locale.
1333 * PARAMS
1334 * lcid [I] LCID of the locale
1335 * lctype [I] LCTYPE_ flags from "winnls.h"
1336 * buffer [O] Destination for the information
1337 * len [I] Length of buffer in characters
1339 * RETURNS
1340 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1341 * with the information.
1342 * Failure: 0. Use GetLastError() to determine the cause.
1344 * NOTES
1345 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1346 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1347 * which is a bit string.
1349 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1351 WCHAR *bufferW;
1352 INT lenW, ret;
1354 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1356 if (len < 0 || (len && !buffer))
1358 SetLastError( ERROR_INVALID_PARAMETER );
1359 return 0;
1361 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1362 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1364 SetLastError( ERROR_INVALID_FLAGS );
1365 return 0;
1368 if (!len) buffer = NULL;
1370 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1372 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1374 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1375 return 0;
1377 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1379 if ((lctype & LOCALE_RETURN_NUMBER) ||
1380 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1382 /* it's not an ASCII string, just bytes */
1383 ret *= sizeof(WCHAR);
1384 if (buffer)
1386 if (ret <= len) memcpy( buffer, bufferW, ret );
1387 else
1389 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1390 ret = 0;
1394 else
1396 UINT codepage = CP_ACP;
1397 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1398 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1401 HeapFree( GetProcessHeap(), 0, bufferW );
1402 return ret;
1405 static int get_value_base_by_lctype( LCTYPE lctype )
1407 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1410 /******************************************************************************
1411 * GetLocaleInfoW (KERNEL32.@)
1413 * See GetLocaleInfoA.
1415 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1417 LANGID lang_id;
1418 HRSRC hrsrc;
1419 HGLOBAL hmem;
1420 INT ret;
1421 UINT lcflags;
1422 const WCHAR *p;
1423 unsigned int i;
1425 if (len < 0 || (len && !buffer))
1427 SetLastError( ERROR_INVALID_PARAMETER );
1428 return 0;
1430 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1431 !is_genitive_name_supported( lctype ))
1433 SetLastError( ERROR_INVALID_FLAGS );
1434 return 0;
1437 if (!len) buffer = NULL;
1439 lcid = convert_default_lcid( lcid, lctype );
1441 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1442 lctype &= 0xffff;
1444 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1446 /* first check for overrides in the registry */
1448 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1449 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1451 struct registry_value *value = get_locale_registry_value(lctype);
1453 if (value)
1455 if (lcflags & LOCALE_RETURN_NUMBER)
1457 WCHAR tmp[16];
1458 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1459 if (ret > 0)
1461 WCHAR *end;
1462 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1463 if (*end) /* invalid number */
1465 SetLastError( ERROR_INVALID_FLAGS );
1466 return 0;
1468 ret = sizeof(UINT)/sizeof(WCHAR);
1469 if (!buffer) return ret;
1470 if (ret > len)
1472 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1473 return 0;
1475 memcpy( buffer, &number, sizeof(number) );
1478 else ret = get_registry_locale_info( value, buffer, len );
1480 if (ret != -1) return ret;
1484 /* now load it from kernel resources */
1486 lang_id = LANGIDFROMLCID( lcid );
1488 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1489 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1490 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1492 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1493 ULongToPtr((lctype >> 4) + 1), lang_id )))
1495 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1496 return 0;
1498 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1499 return 0;
1501 p = LockResource( hmem );
1502 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1504 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1505 else if (is_genitive_name_supported( lctype ) && *p)
1507 /* genitive form's stored after a null separator from a nominative */
1508 for (i = 1; i <= *p; i++) if (!p[i]) break;
1510 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1512 ret = *p - i + 1;
1513 p += i;
1515 else ret = i;
1517 else
1518 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1520 if (!buffer) return ret;
1522 if (ret > len)
1524 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1525 return 0;
1528 if (lcflags & LOCALE_RETURN_NUMBER)
1530 UINT number;
1531 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1532 if (!tmp) return 0;
1533 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1534 tmp[*p] = 0;
1535 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1536 if (!*end)
1537 memcpy( buffer, &number, sizeof(number) );
1538 else /* invalid number */
1540 SetLastError( ERROR_INVALID_FLAGS );
1541 ret = 0;
1543 HeapFree( GetProcessHeap(), 0, tmp );
1545 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1546 lcid, lctype, buffer, len, number );
1548 else
1550 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1551 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1553 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1554 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1556 return ret;
1559 /******************************************************************************
1560 * GetLocaleInfoEx (KERNEL32.@)
1562 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1564 LCID lcid = LocaleNameToLCID(locale, 0);
1566 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1568 if (!lcid) return 0;
1570 /* special handling for neutral locale names */
1571 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1573 if (len && len < 3)
1575 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1576 return 0;
1579 if (len) strcpyW(buffer, locale);
1580 return 3;
1583 return GetLocaleInfoW(lcid, info, buffer, len);
1586 /******************************************************************************
1587 * SetLocaleInfoA [KERNEL32.@]
1589 * Set information about an aspect of a locale.
1591 * PARAMS
1592 * lcid [I] LCID of the locale
1593 * lctype [I] LCTYPE_ flags from "winnls.h"
1594 * data [I] Information to set
1596 * RETURNS
1597 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1598 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1599 * Failure: FALSE. Use GetLastError() to determine the cause.
1601 * NOTES
1602 * - Values are only be set for the current user locale; the system locale
1603 * settings cannot be changed.
1604 * - Any settings changed by this call are lost when the locale is changed by
1605 * the control panel (in Wine, this happens every time you change LANG).
1606 * - The native implementation of this function does not check that lcid matches
1607 * the current user locale, and simply sets the new values. Wine warns you in
1608 * this case, but behaves the same.
1610 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1612 UINT codepage = CP_ACP;
1613 WCHAR *strW;
1614 DWORD len;
1615 BOOL ret;
1617 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1619 if (!data)
1621 SetLastError( ERROR_INVALID_PARAMETER );
1622 return FALSE;
1624 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1625 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1627 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1628 return FALSE;
1630 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1631 ret = SetLocaleInfoW( lcid, lctype, strW );
1632 HeapFree( GetProcessHeap(), 0, strW );
1633 return ret;
1637 /******************************************************************************
1638 * SetLocaleInfoW (KERNEL32.@)
1640 * See SetLocaleInfoA.
1642 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1644 struct registry_value *value;
1645 static const WCHAR intlW[] = {'i','n','t','l',0 };
1646 UNICODE_STRING valueW;
1647 NTSTATUS status;
1648 HANDLE hkey;
1650 lctype &= 0xffff;
1651 value = get_locale_registry_value( lctype );
1653 if (!data || !value)
1655 SetLastError( ERROR_INVALID_PARAMETER );
1656 return FALSE;
1659 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1661 SetLastError( ERROR_INVALID_FLAGS );
1662 return FALSE;
1665 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1667 /* FIXME: should check that data to set is sane */
1669 /* FIXME: profile functions should map to registry */
1670 WriteProfileStringW( intlW, value->name, data );
1672 if (!(hkey = create_registry_key())) return FALSE;
1673 RtlInitUnicodeString( &valueW, value->name );
1674 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1676 RtlEnterCriticalSection( &cache_section );
1677 HeapFree( GetProcessHeap(), 0, value->cached_value );
1678 value->cached_value = NULL;
1679 RtlLeaveCriticalSection( &cache_section );
1681 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1683 /* Set I-value from S value */
1684 WCHAR *lpD, *lpM, *lpY;
1685 WCHAR szBuff[2];
1687 lpD = strrchrW(data, 'd');
1688 lpM = strrchrW(data, 'M');
1689 lpY = strrchrW(data, 'y');
1691 if (lpD <= lpM)
1693 szBuff[0] = '1'; /* D-M-Y */
1695 else
1697 if (lpY <= lpM)
1698 szBuff[0] = '2'; /* Y-M-D */
1699 else
1700 szBuff[0] = '0'; /* M-D-Y */
1703 szBuff[1] = '\0';
1705 if (lctype == LOCALE_SSHORTDATE)
1706 lctype = LOCALE_IDATE;
1707 else
1708 lctype = LOCALE_ILDATE;
1710 value = get_locale_registry_value( lctype );
1712 WriteProfileStringW( intlW, value->name, szBuff );
1714 RtlInitUnicodeString( &valueW, value->name );
1715 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1717 RtlEnterCriticalSection( &cache_section );
1718 HeapFree( GetProcessHeap(), 0, value->cached_value );
1719 value->cached_value = NULL;
1720 RtlLeaveCriticalSection( &cache_section );
1723 NtClose( hkey );
1725 if (status) SetLastError( RtlNtStatusToDosError(status) );
1726 return !status;
1730 /******************************************************************************
1731 * GetACP (KERNEL32.@)
1733 * Get the current Ansi code page Id for the system.
1735 * PARAMS
1736 * None.
1738 * RETURNS
1739 * The current Ansi code page identifier for the system.
1741 UINT WINAPI GetACP(void)
1743 assert( ansi_cptable );
1744 return ansi_cptable->info.codepage;
1748 /******************************************************************************
1749 * SetCPGlobal (KERNEL32.@)
1751 * Set the current Ansi code page Id for the system.
1753 * PARAMS
1754 * acp [I] code page ID to be the new ACP.
1756 * RETURNS
1757 * The previous ACP.
1759 UINT WINAPI SetCPGlobal( UINT acp )
1761 UINT ret = GetACP();
1762 const union cptable *new_cptable = wine_cp_get_table( acp );
1764 if (new_cptable) ansi_cptable = new_cptable;
1765 return ret;
1769 /***********************************************************************
1770 * GetOEMCP (KERNEL32.@)
1772 * Get the current OEM code page Id for the system.
1774 * PARAMS
1775 * None.
1777 * RETURNS
1778 * The current OEM code page identifier for the system.
1780 UINT WINAPI GetOEMCP(void)
1782 assert( oem_cptable );
1783 return oem_cptable->info.codepage;
1787 /***********************************************************************
1788 * IsValidCodePage (KERNEL32.@)
1790 * Determine if a given code page identifier is valid.
1792 * PARAMS
1793 * codepage [I] Code page Id to verify.
1795 * RETURNS
1796 * TRUE, If codepage is valid and available on the system,
1797 * FALSE otherwise.
1799 BOOL WINAPI IsValidCodePage( UINT codepage )
1801 switch(codepage) {
1802 case CP_UTF7:
1803 case CP_UTF8:
1804 return TRUE;
1805 default:
1806 return wine_cp_get_table( codepage ) != NULL;
1811 /***********************************************************************
1812 * IsDBCSLeadByteEx (KERNEL32.@)
1814 * Determine if a character is a lead byte in a given code page.
1816 * PARAMS
1817 * codepage [I] Code page for the test.
1818 * testchar [I] Character to test
1820 * RETURNS
1821 * TRUE, if testchar is a lead byte in codepage,
1822 * FALSE otherwise.
1824 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1826 const union cptable *table = get_codepage_table( codepage );
1827 return table && wine_is_dbcs_leadbyte( table, testchar );
1831 /***********************************************************************
1832 * IsDBCSLeadByte (KERNEL32.@)
1833 * IsDBCSLeadByte (KERNEL.207)
1835 * Determine if a character is a lead byte.
1837 * PARAMS
1838 * testchar [I] Character to test
1840 * RETURNS
1841 * TRUE, if testchar is a lead byte in the ANSI code page,
1842 * FALSE otherwise.
1844 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1846 if (!ansi_cptable) return FALSE;
1847 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1851 /***********************************************************************
1852 * GetCPInfo (KERNEL32.@)
1854 * Get information about a code page.
1856 * PARAMS
1857 * codepage [I] Code page number
1858 * cpinfo [O] Destination for code page information
1860 * RETURNS
1861 * Success: TRUE. cpinfo is updated with the information about codepage.
1862 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1864 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1866 const union cptable *table;
1868 if (!cpinfo)
1870 SetLastError( ERROR_INVALID_PARAMETER );
1871 return FALSE;
1874 if (!(table = get_codepage_table( codepage )))
1876 switch(codepage)
1878 case CP_UTF7:
1879 case CP_UTF8:
1880 cpinfo->DefaultChar[0] = 0x3f;
1881 cpinfo->DefaultChar[1] = 0;
1882 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1883 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1884 return TRUE;
1887 SetLastError( ERROR_INVALID_PARAMETER );
1888 return FALSE;
1890 if (table->info.def_char & 0xff00)
1892 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1893 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1895 else
1897 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1898 cpinfo->DefaultChar[1] = 0;
1900 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1901 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1902 else
1903 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1905 return TRUE;
1908 /***********************************************************************
1909 * GetCPInfoExA (KERNEL32.@)
1911 * Get extended information about a code page.
1913 * PARAMS
1914 * codepage [I] Code page number
1915 * dwFlags [I] Reserved, must to 0.
1916 * cpinfo [O] Destination for code page information
1918 * RETURNS
1919 * Success: TRUE. cpinfo is updated with the information about codepage.
1920 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1922 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1924 CPINFOEXW cpinfoW;
1926 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1927 return FALSE;
1929 /* the layout is the same except for CodePageName */
1930 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1931 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1932 return TRUE;
1935 /***********************************************************************
1936 * GetCPInfoExW (KERNEL32.@)
1938 * Unicode version of GetCPInfoExA.
1940 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1942 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1943 return FALSE;
1945 switch(codepage)
1947 case CP_UTF7:
1949 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1951 cpinfo->CodePage = CP_UTF7;
1952 cpinfo->UnicodeDefaultChar = 0x3f;
1953 strcpyW(cpinfo->CodePageName, utf7);
1954 break;
1957 case CP_UTF8:
1959 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1961 cpinfo->CodePage = CP_UTF8;
1962 cpinfo->UnicodeDefaultChar = 0x3f;
1963 strcpyW(cpinfo->CodePageName, utf8);
1964 break;
1967 default:
1969 const union cptable *table = get_codepage_table( codepage );
1971 cpinfo->CodePage = table->info.codepage;
1972 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1973 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1974 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1975 break;
1978 return TRUE;
1981 /***********************************************************************
1982 * EnumSystemCodePagesA (KERNEL32.@)
1984 * Call a user defined function for every code page installed on the system.
1986 * PARAMS
1987 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1988 * flags [I] Reserved, set to 0.
1990 * RETURNS
1991 * TRUE, If all code pages have been enumerated, or
1992 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1994 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1996 const union cptable *table;
1997 char buffer[10];
1998 int index = 0;
2000 for (;;)
2002 if (!(table = wine_cp_enum_table( index++ ))) break;
2003 sprintf( buffer, "%d", table->info.codepage );
2004 if (!lpfnCodePageEnum( buffer )) break;
2006 return TRUE;
2010 /***********************************************************************
2011 * EnumSystemCodePagesW (KERNEL32.@)
2013 * See EnumSystemCodePagesA.
2015 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2017 const union cptable *table;
2018 WCHAR buffer[10], *p;
2019 int page, index = 0;
2021 for (;;)
2023 if (!(table = wine_cp_enum_table( index++ ))) break;
2024 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2025 *--p = 0;
2026 page = table->info.codepage;
2029 *--p = '0' + (page % 10);
2030 page /= 10;
2031 } while( page );
2032 if (!lpfnCodePageEnum( p )) break;
2034 return TRUE;
2038 /***********************************************************************
2039 * utf7_write_w
2041 * Helper for utf7_mbstowcs
2043 * RETURNS
2044 * TRUE on success, FALSE on error
2046 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2048 if (dstlen > 0)
2050 if (*index >= dstlen)
2051 return FALSE;
2053 dst[*index] = character;
2056 (*index)++;
2058 return TRUE;
2061 /***********************************************************************
2062 * utf7_mbstowcs
2064 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2066 * RETURNS
2067 * On success, the number of characters written
2068 * On dst buffer overflow, -1
2070 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2072 static const signed char base64_decoding_table[] =
2074 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2075 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2076 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2077 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2078 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2079 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2080 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2081 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2084 const char *source_end = src + srclen;
2085 int dest_index = 0;
2087 DWORD byte_pair = 0;
2088 short offset = 0;
2090 while (src < source_end)
2092 if (*src == '+')
2094 src++;
2095 if (src >= source_end)
2096 break;
2098 if (*src == '-')
2100 /* just a plus sign escaped as +- */
2101 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2102 return -1;
2103 src++;
2104 continue;
2109 signed char sextet = *src;
2110 if (sextet == '-')
2112 /* skip over the dash and end base64 decoding
2113 * the current, unfinished byte pair is discarded */
2114 src++;
2115 offset = 0;
2116 break;
2118 if (sextet < 0)
2120 /* the next character of src is < 0 and therefore not part of a base64 sequence
2121 * the current, unfinished byte pair is NOT discarded in this case
2122 * this is probably a bug in Windows */
2123 break;
2126 sextet = base64_decoding_table[sextet];
2127 if (sextet == -1)
2129 /* -1 means that the next character of src is not part of a base64 sequence
2130 * in other words, all sextets in this base64 sequence have been processed
2131 * the current, unfinished byte pair is discarded */
2132 offset = 0;
2133 break;
2136 byte_pair = (byte_pair << 6) | sextet;
2137 offset += 6;
2139 if (offset >= 16)
2141 /* this byte pair is done */
2142 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2143 return -1;
2144 offset -= 16;
2147 src++;
2149 while (src < source_end);
2151 else
2153 /* we have to convert to unsigned char in case *src < 0 */
2154 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2155 return -1;
2156 src++;
2160 return dest_index;
2163 /***********************************************************************
2164 * MultiByteToWideChar (KERNEL32.@)
2166 * Convert a multibyte character string into a Unicode string.
2168 * PARAMS
2169 * page [I] Codepage character set to convert from
2170 * flags [I] Character mapping flags
2171 * src [I] Source string buffer
2172 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2173 * dst [O] Destination buffer
2174 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2176 * RETURNS
2177 * Success: If dstlen > 0, the number of characters written to dst.
2178 * If dstlen == 0, the number of characters needed to perform the
2179 * conversion. In both cases the count includes the terminating NUL.
2180 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2181 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2182 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2183 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2184 * possible for src.
2186 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2187 LPWSTR dst, INT dstlen )
2189 const union cptable *table;
2190 int ret;
2192 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2194 SetLastError( ERROR_INVALID_PARAMETER );
2195 return 0;
2198 if (srclen < 0) srclen = strlen(src) + 1;
2200 switch(page)
2202 case CP_SYMBOL:
2203 if (flags)
2205 SetLastError( ERROR_INVALID_FLAGS );
2206 return 0;
2208 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2209 break;
2210 case CP_UTF7:
2211 if (flags)
2213 SetLastError( ERROR_INVALID_FLAGS );
2214 return 0;
2216 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2217 break;
2218 case CP_UNIXCP:
2219 if (unix_cptable)
2221 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2222 break;
2224 #ifdef __APPLE__
2225 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2226 #endif
2227 /* fall through */
2228 case CP_UTF8:
2229 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2230 break;
2231 default:
2232 if (!(table = get_codepage_table( page )))
2234 SetLastError( ERROR_INVALID_PARAMETER );
2235 return 0;
2237 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2238 break;
2241 if (ret < 0)
2243 switch(ret)
2245 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2246 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2248 ret = 0;
2250 TRACE("cp %d %s -> %s, ret = %d\n",
2251 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2252 return ret;
2256 /***********************************************************************
2257 * utf7_can_directly_encode
2259 * Helper for utf7_wcstombs
2261 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2263 static const BOOL directly_encodable_table[] =
2265 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2266 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2267 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2268 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2269 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2270 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2271 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2272 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2275 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2278 /***********************************************************************
2279 * utf7_write_c
2281 * Helper for utf7_wcstombs
2283 * RETURNS
2284 * TRUE on success, FALSE on error
2286 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2288 if (dstlen > 0)
2290 if (*index >= dstlen)
2291 return FALSE;
2293 dst[*index] = character;
2296 (*index)++;
2298 return TRUE;
2301 /***********************************************************************
2302 * utf7_wcstombs
2304 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2306 * RETURNS
2307 * On success, the number of characters written
2308 * On dst buffer overflow, -1
2310 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2312 static const char base64_encoding_table[] =
2313 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2315 const WCHAR *source_end = src + srclen;
2316 int dest_index = 0;
2318 while (src < source_end)
2320 if (*src == '+')
2322 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2323 return -1;
2324 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2325 return -1;
2326 src++;
2328 else if (utf7_can_directly_encode(*src))
2330 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2331 return -1;
2332 src++;
2334 else
2336 unsigned int offset = 0;
2337 DWORD byte_pair = 0;
2339 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2340 return -1;
2342 while (src < source_end && !utf7_can_directly_encode(*src))
2344 byte_pair = (byte_pair << 16) | *src;
2345 offset += 16;
2346 while (offset >= 6)
2348 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2349 return -1;
2350 offset -= 6;
2352 src++;
2355 if (offset)
2357 /* Windows won't create a padded base64 character if there's no room for the - sign
2358 * as well ; this is probably a bug in Windows */
2359 if (dstlen > 0 && dest_index + 1 >= dstlen)
2360 return -1;
2362 byte_pair <<= (6 - offset);
2363 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2364 return -1;
2367 /* Windows always explicitly terminates the base64 sequence
2368 even though RFC 2152 (page 3, rule 2) does not require this */
2369 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2370 return -1;
2374 return dest_index;
2377 /***********************************************************************
2378 * WideCharToMultiByte (KERNEL32.@)
2380 * Convert a Unicode character string into a multibyte string.
2382 * PARAMS
2383 * page [I] Code page character set to convert to
2384 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2385 * src [I] Source string buffer
2386 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2387 * dst [O] Destination buffer
2388 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2389 * defchar [I] Default character to use for conversion if no exact
2390 * conversion can be made
2391 * used [O] Set if default character was used in the conversion
2393 * RETURNS
2394 * Success: If dstlen > 0, the number of characters written to dst.
2395 * If dstlen == 0, number of characters needed to perform the
2396 * conversion. In both cases the count includes the terminating NUL.
2397 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2398 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2399 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2400 * parameter was given.
2402 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2403 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2405 const union cptable *table;
2406 int ret, used_tmp;
2408 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2410 SetLastError( ERROR_INVALID_PARAMETER );
2411 return 0;
2414 if (srclen < 0) srclen = strlenW(src) + 1;
2416 switch(page)
2418 case CP_SYMBOL:
2419 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2420 if (flags)
2422 SetLastError( ERROR_INVALID_FLAGS );
2423 return 0;
2425 if (defchar || used)
2427 SetLastError( ERROR_INVALID_PARAMETER );
2428 return 0;
2430 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2431 break;
2432 case CP_UTF7:
2433 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2434 if (defchar || used)
2436 SetLastError( ERROR_INVALID_PARAMETER );
2437 return 0;
2439 if (flags)
2441 SetLastError( ERROR_INVALID_FLAGS );
2442 return 0;
2444 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2445 break;
2446 case CP_UNIXCP:
2447 if (unix_cptable)
2449 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2450 defchar, used ? &used_tmp : NULL );
2451 break;
2453 /* fall through */
2454 case CP_UTF8:
2455 if (defchar || used)
2457 SetLastError( ERROR_INVALID_PARAMETER );
2458 return 0;
2460 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2461 break;
2462 default:
2463 if (!(table = get_codepage_table( page )))
2465 SetLastError( ERROR_INVALID_PARAMETER );
2466 return 0;
2468 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2469 defchar, used ? &used_tmp : NULL );
2470 if (used) *used = used_tmp;
2471 break;
2474 if (ret < 0)
2476 switch(ret)
2478 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2479 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2481 ret = 0;
2483 TRACE("cp %d %s -> %s, ret = %d\n",
2484 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2485 return ret;
2489 /***********************************************************************
2490 * GetThreadLocale (KERNEL32.@)
2492 * Get the current threads locale.
2494 * PARAMS
2495 * None.
2497 * RETURNS
2498 * The LCID currently associated with the calling thread.
2500 LCID WINAPI GetThreadLocale(void)
2502 LCID ret = NtCurrentTeb()->CurrentLocale;
2503 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2504 return ret;
2507 /**********************************************************************
2508 * SetThreadLocale (KERNEL32.@)
2510 * Set the current threads locale.
2512 * PARAMS
2513 * lcid [I] LCID of the locale to set
2515 * RETURNS
2516 * Success: TRUE. The threads locale is set to lcid.
2517 * Failure: FALSE. Use GetLastError() to determine the cause.
2519 BOOL WINAPI SetThreadLocale( LCID lcid )
2521 TRACE("(0x%04X)\n", lcid);
2523 lcid = ConvertDefaultLocale(lcid);
2525 if (lcid != GetThreadLocale())
2527 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2529 SetLastError(ERROR_INVALID_PARAMETER);
2530 return FALSE;
2533 NtCurrentTeb()->CurrentLocale = lcid;
2535 return TRUE;
2538 /**********************************************************************
2539 * SetThreadUILanguage (KERNEL32.@)
2541 * Set the current threads UI language.
2543 * PARAMS
2544 * langid [I] LANGID of the language to set, or 0 to use
2545 * the available language which is best supported
2546 * for console applications
2548 * RETURNS
2549 * Success: The return value is the same as the input value.
2550 * Failure: The return value differs from the input value.
2551 * Use GetLastError() to determine the cause.
2553 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2555 TRACE("(0x%04x) stub - returning success\n", langid);
2556 return langid;
2559 /******************************************************************************
2560 * ConvertDefaultLocale (KERNEL32.@)
2562 * Convert a default locale identifier into a real identifier.
2564 * PARAMS
2565 * lcid [I] LCID identifier of the locale to convert
2567 * RETURNS
2568 * lcid unchanged, if not a default locale or its sublanguage is
2569 * not SUBLANG_NEUTRAL.
2570 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2571 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2572 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2574 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2576 LANGID langid;
2578 switch (lcid)
2580 case LOCALE_INVARIANT:
2581 /* keep as-is */
2582 break;
2583 case LOCALE_SYSTEM_DEFAULT:
2584 lcid = GetSystemDefaultLCID();
2585 break;
2586 case LOCALE_USER_DEFAULT:
2587 case LOCALE_NEUTRAL:
2588 lcid = GetUserDefaultLCID();
2589 break;
2590 default:
2591 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2592 langid = LANGIDFROMLCID(lcid);
2593 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2595 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2596 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2599 return lcid;
2603 /******************************************************************************
2604 * IsValidLocale (KERNEL32.@)
2606 * Determine if a locale is valid.
2608 * PARAMS
2609 * lcid [I] LCID of the locale to check
2610 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2612 * RETURNS
2613 * TRUE, if lcid is valid,
2614 * FALSE, otherwise.
2616 * NOTES
2617 * Wine does not currently make the distinction between supported and installed. All
2618 * languages supported are installed by default.
2620 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2622 /* check if language is registered in the kernel32 resources */
2623 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2624 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2627 /******************************************************************************
2628 * IsValidLocaleName (KERNEL32.@)
2630 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2632 struct locale_name locale_name;
2634 /* string parsing */
2635 parse_locale_name( locale, &locale_name );
2637 TRACE( "found lcid %x for %s, matches %d\n",
2638 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2640 return locale_name.matches > 0;
2643 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2644 LPCSTR name, WORD LangID, LONG_PTR lParam )
2646 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2647 char buf[20];
2649 sprintf(buf, "%08x", (UINT)LangID);
2650 return lpfnLocaleEnum( buf );
2653 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2654 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2656 static const WCHAR formatW[] = {'%','0','8','x',0};
2657 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2658 WCHAR buf[20];
2659 sprintfW( buf, formatW, (UINT)LangID );
2660 return lpfnLocaleEnum( buf );
2663 /******************************************************************************
2664 * EnumSystemLocalesA (KERNEL32.@)
2666 * Call a users function for each locale available on the system.
2668 * PARAMS
2669 * lpfnLocaleEnum [I] Callback function to call for each locale
2670 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2672 * RETURNS
2673 * Success: TRUE.
2674 * Failure: FALSE. Use GetLastError() to determine the cause.
2676 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2678 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2679 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2680 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2681 (LONG_PTR)lpfnLocaleEnum);
2682 return TRUE;
2686 /******************************************************************************
2687 * EnumSystemLocalesW (KERNEL32.@)
2689 * See EnumSystemLocalesA.
2691 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2693 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2694 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2695 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2696 (LONG_PTR)lpfnLocaleEnum);
2697 return TRUE;
2701 struct enum_locale_ex_data
2703 LOCALE_ENUMPROCEX proc;
2704 DWORD flags;
2705 LPARAM lparam;
2708 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2709 LPCWSTR name, WORD lang, LONG_PTR lparam )
2711 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2712 WCHAR buffer[256];
2713 DWORD neutral;
2714 unsigned int flags;
2716 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2717 buffer, sizeof(buffer) / sizeof(WCHAR) );
2718 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2719 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2720 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2721 neutral = 0;
2722 flags = LOCALE_WINDOWS;
2723 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2724 if (data->flags && !(data->flags & flags)) return TRUE;
2725 return data->proc( buffer, flags, data->lparam );
2728 /******************************************************************************
2729 * EnumSystemLocalesEx (KERNEL32.@)
2731 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2733 struct enum_locale_ex_data data;
2735 if (reserved)
2737 SetLastError( ERROR_INVALID_PARAMETER );
2738 return FALSE;
2740 data.proc = proc;
2741 data.flags = flags;
2742 data.lparam = lparam;
2743 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2744 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2745 enum_locale_ex_proc, (LONG_PTR)&data );
2746 return TRUE;
2750 /***********************************************************************
2751 * VerLanguageNameA (KERNEL32.@)
2753 * Get the name of a language.
2755 * PARAMS
2756 * wLang [I] LANGID of the language
2757 * szLang [O] Destination for the language name
2759 * RETURNS
2760 * Success: The size of the language name. If szLang is non-NULL, it is filled
2761 * with the name.
2762 * Failure: 0. Use GetLastError() to determine the cause.
2765 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2767 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2771 /***********************************************************************
2772 * VerLanguageNameW (KERNEL32.@)
2774 * See VerLanguageNameA.
2776 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2778 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2782 /******************************************************************************
2783 * GetStringTypeW (KERNEL32.@)
2785 * See GetStringTypeA.
2787 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2789 static const unsigned char type2_map[16] =
2791 C2_NOTAPPLICABLE, /* unassigned */
2792 C2_LEFTTORIGHT, /* L */
2793 C2_RIGHTTOLEFT, /* R */
2794 C2_EUROPENUMBER, /* EN */
2795 C2_EUROPESEPARATOR, /* ES */
2796 C2_EUROPETERMINATOR, /* ET */
2797 C2_ARABICNUMBER, /* AN */
2798 C2_COMMONSEPARATOR, /* CS */
2799 C2_BLOCKSEPARATOR, /* B */
2800 C2_SEGMENTSEPARATOR, /* S */
2801 C2_WHITESPACE, /* WS */
2802 C2_OTHERNEUTRAL, /* ON */
2803 C2_RIGHTTOLEFT, /* AL */
2804 C2_NOTAPPLICABLE, /* NSM */
2805 C2_NOTAPPLICABLE, /* BN */
2806 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2809 if (!src)
2811 SetLastError( ERROR_INVALID_PARAMETER );
2812 return FALSE;
2815 if (count == -1) count = strlenW(src) + 1;
2816 switch(type)
2818 case CT_CTYPE1:
2819 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2820 break;
2821 case CT_CTYPE2:
2822 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2823 break;
2824 case CT_CTYPE3:
2826 WARN("CT_CTYPE3: semi-stub.\n");
2827 while (count--)
2829 int c = *src;
2830 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2832 type1 = get_char_typeW( *src++ ) & 0xfff;
2833 /* try to construct type3 from type1 */
2834 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2835 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2836 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2837 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2838 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2839 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2840 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2842 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
2843 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
2845 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2846 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2847 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2848 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2849 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2850 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2851 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2852 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2854 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2855 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2856 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2857 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2858 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2859 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2860 *chartype++ = type3;
2862 break;
2864 default:
2865 SetLastError( ERROR_INVALID_PARAMETER );
2866 return FALSE;
2868 return TRUE;
2872 /******************************************************************************
2873 * GetStringTypeExW (KERNEL32.@)
2875 * See GetStringTypeExA.
2877 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2879 /* locale is ignored for Unicode */
2880 return GetStringTypeW( type, src, count, chartype );
2884 /******************************************************************************
2885 * GetStringTypeA (KERNEL32.@)
2887 * Get characteristics of the characters making up a string.
2889 * PARAMS
2890 * locale [I] Locale Id for the string
2891 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2892 * src [I] String to analyse
2893 * count [I] Length of src in chars, or -1 if src is NUL terminated
2894 * chartype [O] Destination for the calculated characteristics
2896 * RETURNS
2897 * Success: TRUE. chartype is filled with the requested characteristics of each char
2898 * in src.
2899 * Failure: FALSE. Use GetLastError() to determine the cause.
2901 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2903 UINT cp;
2904 INT countW;
2905 LPWSTR srcW;
2906 BOOL ret = FALSE;
2908 if(count == -1) count = strlen(src) + 1;
2910 if (!(cp = get_lcid_codepage( locale )))
2912 FIXME("For locale %04x using current ANSI code page\n", locale);
2913 cp = GetACP();
2916 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2917 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2919 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2921 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2922 * string, with multibyte characters there maybe be more bytes in count
2923 * than character space in the buffer!
2925 ret = GetStringTypeW(type, srcW, countW, chartype);
2926 HeapFree(GetProcessHeap(), 0, srcW);
2928 return ret;
2931 /******************************************************************************
2932 * GetStringTypeExA (KERNEL32.@)
2934 * Get characteristics of the characters making up a string.
2936 * PARAMS
2937 * locale [I] Locale Id for the string
2938 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2939 * src [I] String to analyse
2940 * count [I] Length of src in chars, or -1 if src is NUL terminated
2941 * chartype [O] Destination for the calculated characteristics
2943 * RETURNS
2944 * Success: TRUE. chartype is filled with the requested characteristics of each char
2945 * in src.
2946 * Failure: FALSE. Use GetLastError() to determine the cause.
2948 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2950 return GetStringTypeA(locale, type, src, count, chartype);
2953 /*************************************************************************
2954 * LCMapStringEx (KERNEL32.@)
2956 * Map characters in a locale sensitive string.
2958 * PARAMS
2959 * name [I] Locale name for the conversion.
2960 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2961 * src [I] String to map
2962 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2963 * dst [O] Destination for mapped string
2964 * dstlen [I] Length of dst in characters
2965 * version [I] reserved, must be NULL
2966 * reserved [I] reserved, must be NULL
2967 * lparam [I] reserved, must be 0
2969 * RETURNS
2970 * Success: The length of the mapped string in dst, including the NUL terminator.
2971 * Failure: 0. Use GetLastError() to determine the cause.
2973 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2974 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2976 LPWSTR dst_ptr;
2978 if (version) FIXME("unsupported version structure %p\n", version);
2979 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2980 if (lparam)
2982 static int once;
2983 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
2986 if (!src || !srclen || dstlen < 0)
2988 SetLastError(ERROR_INVALID_PARAMETER);
2989 return 0;
2992 /* mutually exclusive flags */
2993 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2994 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2995 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2996 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2998 SetLastError(ERROR_INVALID_FLAGS);
2999 return 0;
3002 if (!dstlen) dst = NULL;
3004 if (flags & LCMAP_SORTKEY)
3006 INT ret;
3007 if (src == dst)
3009 SetLastError(ERROR_INVALID_FLAGS);
3010 return 0;
3013 if (srclen < 0) srclen = strlenW(src);
3015 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3016 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3018 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3019 if (ret == 0)
3020 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3021 else
3022 ret++;
3023 return ret;
3026 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3027 if (flags & SORT_STRINGSORT)
3029 SetLastError(ERROR_INVALID_FLAGS);
3030 return 0;
3033 if (srclen < 0) srclen = strlenW(src) + 1;
3035 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3036 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3038 if (!dst) /* return required string length */
3040 INT len;
3042 for (len = 0; srclen; src++, srclen--)
3044 WCHAR wch = *src;
3045 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3046 * and skips white space and punctuation characters for
3047 * NORM_IGNORESYMBOLS.
3049 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3050 continue;
3051 len++;
3053 return len;
3056 if (flags & LCMAP_UPPERCASE)
3058 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3060 WCHAR wch = *src;
3061 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3062 continue;
3063 *dst_ptr++ = toupperW(wch);
3064 dstlen--;
3067 else if (flags & LCMAP_LOWERCASE)
3069 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3071 WCHAR wch = *src;
3072 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3073 continue;
3074 *dst_ptr++ = tolowerW(wch);
3075 dstlen--;
3078 else
3080 if (src == dst)
3082 SetLastError(ERROR_INVALID_FLAGS);
3083 return 0;
3085 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3087 WCHAR wch = *src;
3088 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3089 continue;
3090 *dst_ptr++ = wch;
3091 dstlen--;
3095 if (srclen)
3097 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3098 return 0;
3101 return dst_ptr - dst;
3104 /*************************************************************************
3105 * LCMapStringW (KERNEL32.@)
3107 * See LCMapStringA.
3109 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3110 LPWSTR dst, INT dstlen)
3112 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3113 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3115 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3118 /*************************************************************************
3119 * LCMapStringA (KERNEL32.@)
3121 * Map characters in a locale sensitive string.
3123 * PARAMS
3124 * lcid [I] LCID for the conversion.
3125 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3126 * src [I] String to map
3127 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3128 * dst [O] Destination for mapped string
3129 * dstlen [I] Length of dst in characters
3131 * RETURNS
3132 * Success: The length of the mapped string in dst, including the NUL terminator.
3133 * Failure: 0. Use GetLastError() to determine the cause.
3135 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3136 LPSTR dst, INT dstlen)
3138 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3139 LPWSTR srcW, dstW;
3140 INT ret = 0, srclenW, dstlenW;
3141 UINT locale_cp = CP_ACP;
3143 if (!src || !srclen || dstlen < 0)
3145 SetLastError(ERROR_INVALID_PARAMETER);
3146 return 0;
3149 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3151 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3152 if (srclenW)
3153 srcW = bufW;
3154 else
3156 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3157 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3158 if (!srcW)
3160 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3161 return 0;
3163 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3166 if (flags & LCMAP_SORTKEY)
3168 if (src == dst)
3170 SetLastError(ERROR_INVALID_FLAGS);
3171 goto map_string_exit;
3173 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3174 if (ret == 0)
3175 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3176 else
3177 ret++;
3178 goto map_string_exit;
3181 if (flags & SORT_STRINGSORT)
3183 SetLastError(ERROR_INVALID_FLAGS);
3184 goto map_string_exit;
3187 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3188 if (!dstlenW)
3189 goto map_string_exit;
3191 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3192 if (!dstW)
3194 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3195 goto map_string_exit;
3198 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3199 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3200 HeapFree(GetProcessHeap(), 0, dstW);
3202 map_string_exit:
3203 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3204 return ret;
3207 /*************************************************************************
3208 * FoldStringA (KERNEL32.@)
3210 * Map characters in a string.
3212 * PARAMS
3213 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3214 * src [I] String to map
3215 * srclen [I] Length of src, or -1 if src is NUL terminated
3216 * dst [O] Destination for mapped string
3217 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3219 * RETURNS
3220 * Success: The length of the string written to dst, including the terminating NUL. If
3221 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3222 * and dst may be NULL.
3223 * Failure: 0. Use GetLastError() to determine the cause.
3225 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3226 LPSTR dst, INT dstlen)
3228 INT ret = 0, srclenW = 0;
3229 WCHAR *srcW = NULL, *dstW = NULL;
3231 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3233 SetLastError(ERROR_INVALID_PARAMETER);
3234 return 0;
3237 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3238 src, srclen, NULL, 0);
3239 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3241 if (!srcW)
3243 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3244 goto FoldStringA_exit;
3247 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3248 src, srclen, srcW, srclenW);
3250 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3252 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3253 if (ret && dstlen)
3255 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3257 if (!dstW)
3259 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3260 goto FoldStringA_exit;
3263 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3264 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3266 ret = 0;
3267 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3271 HeapFree(GetProcessHeap(), 0, dstW);
3273 FoldStringA_exit:
3274 HeapFree(GetProcessHeap(), 0, srcW);
3275 return ret;
3278 /*************************************************************************
3279 * FoldStringW (KERNEL32.@)
3281 * See FoldStringA.
3283 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3284 LPWSTR dst, INT dstlen)
3286 int ret;
3288 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3290 case 0:
3291 if (dwFlags)
3292 break;
3293 /* Fall through for dwFlags == 0 */
3294 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3295 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3296 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3297 SetLastError(ERROR_INVALID_FLAGS);
3298 return 0;
3301 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3303 SetLastError(ERROR_INVALID_PARAMETER);
3304 return 0;
3307 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3308 if (!ret)
3309 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3310 return ret;
3313 /******************************************************************************
3314 * CompareStringW (KERNEL32.@)
3316 * See CompareStringA.
3318 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3319 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3321 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3324 /******************************************************************************
3325 * CompareStringEx (KERNEL32.@)
3327 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3328 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3330 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3331 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3332 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3333 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3334 INT ret;
3335 static int once;
3337 if (version) FIXME("unexpected version parameter\n");
3338 if (reserved) FIXME("unexpected reserved value\n");
3339 if (lParam) FIXME("unexpected lParam\n");
3341 if (!str1 || !str2)
3343 SetLastError(ERROR_INVALID_PARAMETER);
3344 return 0;
3347 if (flags & ~(supported_flags|semistub_flags))
3349 SetLastError(ERROR_INVALID_FLAGS);
3350 return 0;
3353 if (flags & semistub_flags)
3355 if (!once++)
3356 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3359 if (len1 < 0) len1 = strlenW(str1);
3360 if (len2 < 0) len2 = strlenW(str2);
3362 ret = wine_compare_string(flags, str1, len1, str2, len2);
3364 if (ret) /* need to translate result */
3365 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3366 return CSTR_EQUAL;
3369 /******************************************************************************
3370 * CompareStringA (KERNEL32.@)
3372 * Compare two locale sensitive strings.
3374 * PARAMS
3375 * lcid [I] LCID for the comparison
3376 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3377 * str1 [I] First string to compare
3378 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3379 * str2 [I] Second string to compare
3380 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3382 * RETURNS
3383 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3384 * str1 is less than, equal to or greater than str2 respectively.
3385 * Failure: FALSE. Use GetLastError() to determine the cause.
3387 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3388 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3390 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3391 WCHAR *buf2W = buf1W + 130;
3392 LPWSTR str1W, str2W;
3393 INT len1W = 0, len2W = 0, ret;
3394 UINT locale_cp = CP_ACP;
3396 if (!str1 || !str2)
3398 SetLastError(ERROR_INVALID_PARAMETER);
3399 return 0;
3401 if (len1 < 0) len1 = strlen(str1);
3402 if (len2 < 0) len2 = strlen(str2);
3404 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3406 if (len1)
3408 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3409 if (len1W)
3410 str1W = buf1W;
3411 else
3413 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3414 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3415 if (!str1W)
3417 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3418 return 0;
3420 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3423 else
3425 len1W = 0;
3426 str1W = buf1W;
3429 if (len2)
3431 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3432 if (len2W)
3433 str2W = buf2W;
3434 else
3436 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3437 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3438 if (!str2W)
3440 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3441 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3442 return 0;
3444 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3447 else
3449 len2W = 0;
3450 str2W = buf2W;
3453 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3455 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3456 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3457 return ret;
3460 /******************************************************************************
3461 * CompareStringOrdinal (KERNEL32.@)
3463 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3465 int ret, len;
3467 if (!str1 || !str2)
3469 SetLastError(ERROR_INVALID_PARAMETER);
3470 return 0;
3472 if (len1 < 0) len1 = strlenW(str1);
3473 if (len2 < 0) len2 = strlenW(str2);
3475 len = min(len1, len2);
3476 if (ignore_case)
3478 ret = memicmpW(str1, str2, len);
3480 else
3482 ret = 0;
3483 for (; len > 0; len--)
3484 if ((ret = (*str1++ - *str2++))) break;
3486 if (!ret) ret = len1 - len2;
3488 if (ret < 0) return CSTR_LESS_THAN;
3489 if (ret > 0) return CSTR_GREATER_THAN;
3490 return CSTR_EQUAL;
3493 /*************************************************************************
3494 * lstrcmp (KERNEL32.@)
3495 * lstrcmpA (KERNEL32.@)
3497 * Compare two strings using the current thread locale.
3499 * PARAMS
3500 * str1 [I] First string to compare
3501 * str2 [I] Second string to compare
3503 * RETURNS
3504 * Success: A number less than, equal to or greater than 0 depending on whether
3505 * str1 is less than, equal to or greater than str2 respectively.
3506 * Failure: FALSE. Use GetLastError() to determine the cause.
3508 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3510 int ret;
3512 if ((str1 == NULL) && (str2 == NULL)) return 0;
3513 if (str1 == NULL) return -1;
3514 if (str2 == NULL) return 1;
3516 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3517 if (ret) ret -= 2;
3519 return ret;
3522 /*************************************************************************
3523 * lstrcmpi (KERNEL32.@)
3524 * lstrcmpiA (KERNEL32.@)
3526 * Compare two strings using the current thread locale, ignoring case.
3528 * PARAMS
3529 * str1 [I] First string to compare
3530 * str2 [I] Second string to compare
3532 * RETURNS
3533 * Success: A number less than, equal to or greater than 0 depending on whether
3534 * str2 is less than, equal to or greater than str1 respectively.
3535 * Failure: FALSE. Use GetLastError() to determine the cause.
3537 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3539 int ret;
3541 if ((str1 == NULL) && (str2 == NULL)) return 0;
3542 if (str1 == NULL) return -1;
3543 if (str2 == NULL) return 1;
3545 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3546 if (ret) ret -= 2;
3548 return ret;
3551 /*************************************************************************
3552 * lstrcmpW (KERNEL32.@)
3554 * See lstrcmpA.
3556 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3558 int ret;
3560 if ((str1 == NULL) && (str2 == NULL)) return 0;
3561 if (str1 == NULL) return -1;
3562 if (str2 == NULL) return 1;
3564 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3565 if (ret) ret -= 2;
3567 return ret;
3570 /*************************************************************************
3571 * lstrcmpiW (KERNEL32.@)
3573 * See lstrcmpiA.
3575 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3577 int ret;
3579 if ((str1 == NULL) && (str2 == NULL)) return 0;
3580 if (str1 == NULL) return -1;
3581 if (str2 == NULL) return 1;
3583 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3584 if (ret) ret -= 2;
3586 return ret;
3589 /******************************************************************************
3590 * LOCALE_Init
3592 void LOCALE_Init(void)
3594 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3595 const union cptable *unix_cp );
3597 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3599 #ifdef __APPLE__
3600 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3601 char user_locale[50];
3603 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3604 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3605 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3606 CFStringRef user_locale_string_ref;
3608 if (user_locale_country_ref)
3610 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3611 user_locale_lang_ref, user_locale_country_ref);
3613 else
3615 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3618 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3619 strcat(user_locale, ".UTF-8");
3621 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3622 setenv( "LANG", user_locale, 0 );
3623 TRACE( "setting locale to '%s'\n", user_locale );
3624 #endif /* __APPLE__ */
3626 setlocale( LC_ALL, "" );
3628 unix_cp = setup_unix_locales();
3629 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3631 #ifdef __APPLE__
3632 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3633 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3635 /* Retrieve the preferred language as chosen in System Preferences. */
3636 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3637 leave things be. */
3638 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3639 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3640 CFStringRef user_language_string_ref;
3641 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3642 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3643 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3644 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3646 struct locale_name locale_name;
3647 WCHAR buffer[128];
3648 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3649 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3650 parse_locale_name( buffer, &locale_name );
3651 lcid_LC_MESSAGES = locale_name.lcid;
3652 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3654 if (preferred_langs)
3655 CFRelease( preferred_langs );
3656 if (canonical_lang_string_ref)
3657 CFRelease( canonical_lang_string_ref );
3660 CFRelease( user_locale_ref );
3661 CFRelease( user_locale_string_ref );
3662 #endif
3664 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3665 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3666 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3668 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3669 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3670 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3671 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3672 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3673 if (!unix_cp)
3674 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3675 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3677 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3678 ansi_cptable = wine_cp_get_table( 1252 );
3679 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3680 oem_cptable = wine_cp_get_table( 437 );
3681 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3682 mac_cptable = wine_cp_get_table( 10000 );
3683 if (unix_cp != CP_UTF8)
3685 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3686 unix_cptable = wine_cp_get_table( 28591 );
3689 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3691 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3692 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3693 mac_cptable->info.codepage, unix_cp );
3695 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3698 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3700 UNICODE_STRING keyName;
3701 OBJECT_ATTRIBUTES attr;
3702 HANDLE hkey;
3704 RtlInitUnicodeString( &keyName, szKeyName );
3705 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3707 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3708 hkey = 0;
3710 return hkey;
3713 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3714 LPWSTR szValueName, ULONG valueNameSize,
3715 LPWSTR szValueData, ULONG valueDataSize)
3717 BYTE buffer[80];
3718 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3719 DWORD dwLen;
3721 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3722 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3723 info->NameLength > valueNameSize ||
3724 info->DataLength > valueDataSize)
3726 return FALSE;
3729 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3731 memcpy( szValueName, info->Name, info->NameLength);
3732 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3733 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3734 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3736 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3737 return TRUE;
3740 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3742 BYTE buffer[128];
3743 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3744 DWORD dwSize = sizeof(buffer);
3745 UNICODE_STRING valueName;
3747 RtlInitUnicodeString( &valueName, szValueName );
3749 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3750 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3751 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3752 info->DataLength == sizeof(DWORD))
3754 memcpy(lpVal, info->Data, sizeof(DWORD));
3755 return TRUE;
3758 return FALSE;
3761 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3763 LANGID langId;
3764 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3765 HRSRC hResource;
3766 BOOL bRet = FALSE;
3768 /* FIXME: Is it correct to use the system default langid? */
3769 langId = GetSystemDefaultLangID();
3771 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3772 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3774 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3776 if (hResource)
3778 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3780 if (hResDir)
3782 ULONG iResourceIndex = lgrpid & 0xf;
3783 LPCWSTR lpResEntry = LockResource( hResDir );
3784 ULONG i;
3786 for (i = 0; i < iResourceIndex; i++)
3787 lpResEntry += *lpResEntry + 1;
3789 if (*lpResEntry < nameSize)
3791 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3792 szName[*lpResEntry] = '\0';
3793 bRet = TRUE;
3797 FreeResource( hResource );
3799 return bRet;
3802 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3803 typedef struct
3805 LANGUAGEGROUP_ENUMPROCA procA;
3806 LANGUAGEGROUP_ENUMPROCW procW;
3807 DWORD dwFlags;
3808 LONG_PTR lParam;
3809 } ENUMLANGUAGEGROUP_CALLBACKS;
3811 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3812 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3814 WCHAR szNumber[10], szValue[4];
3815 HANDLE hKey;
3816 BOOL bContinue = TRUE;
3817 ULONG ulIndex = 0;
3819 if (!lpProcs)
3821 SetLastError(ERROR_INVALID_PARAMETER);
3822 return FALSE;
3825 switch (lpProcs->dwFlags)
3827 case 0:
3828 /* Default to LGRPID_INSTALLED */
3829 lpProcs->dwFlags = LGRPID_INSTALLED;
3830 /* Fall through... */
3831 case LGRPID_INSTALLED:
3832 case LGRPID_SUPPORTED:
3833 break;
3834 default:
3835 SetLastError(ERROR_INVALID_FLAGS);
3836 return FALSE;
3839 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3841 if (!hKey)
3842 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3844 while (bContinue)
3846 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3847 szValue, sizeof(szValue) ))
3849 BOOL bInstalled = szValue[0] == '1';
3850 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3852 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3853 bInstalled ? "" : "not ");
3855 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3857 WCHAR szGrpName[48];
3859 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3860 szGrpName[0] = '\0';
3862 if (lpProcs->procW)
3863 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3864 lpProcs->lParam );
3865 else
3867 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3868 char szGrpNameA[48];
3870 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3871 * or whether the language names are ever localised. Assume CP_ACP.
3874 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3875 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3877 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3878 lpProcs->lParam );
3882 ulIndex++;
3884 else
3885 bContinue = FALSE;
3887 if (!bContinue)
3888 break;
3891 if (hKey)
3892 NtClose( hKey );
3894 return TRUE;
3897 /******************************************************************************
3898 * EnumSystemLanguageGroupsA (KERNEL32.@)
3900 * Call a users function for each language group available on the system.
3902 * PARAMS
3903 * pLangGrpEnumProc [I] Callback function to call for each language group
3904 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3905 * lParam [I] User parameter to pass to pLangGrpEnumProc
3907 * RETURNS
3908 * Success: TRUE.
3909 * Failure: FALSE. Use GetLastError() to determine the cause.
3911 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3912 DWORD dwFlags, LONG_PTR lParam)
3914 ENUMLANGUAGEGROUP_CALLBACKS procs;
3916 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3918 procs.procA = pLangGrpEnumProc;
3919 procs.procW = NULL;
3920 procs.dwFlags = dwFlags;
3921 procs.lParam = lParam;
3923 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3926 /******************************************************************************
3927 * EnumSystemLanguageGroupsW (KERNEL32.@)
3929 * See EnumSystemLanguageGroupsA.
3931 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3932 DWORD dwFlags, LONG_PTR lParam)
3934 ENUMLANGUAGEGROUP_CALLBACKS procs;
3936 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3938 procs.procA = NULL;
3939 procs.procW = pLangGrpEnumProc;
3940 procs.dwFlags = dwFlags;
3941 procs.lParam = lParam;
3943 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3946 /******************************************************************************
3947 * IsValidLanguageGroup (KERNEL32.@)
3949 * Determine if a language group is supported and/or installed.
3951 * PARAMS
3952 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3953 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3955 * RETURNS
3956 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3957 * FALSE otherwise.
3959 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3961 static const WCHAR szFormat[] = { '%','x','\0' };
3962 WCHAR szValueName[16], szValue[2];
3963 BOOL bSupported = FALSE, bInstalled = FALSE;
3964 HANDLE hKey;
3967 switch (dwFlags)
3969 case LGRPID_INSTALLED:
3970 case LGRPID_SUPPORTED:
3972 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3974 sprintfW( szValueName, szFormat, lgrpid );
3976 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3978 bSupported = TRUE;
3980 if (szValue[0] == '1')
3981 bInstalled = TRUE;
3984 if (hKey)
3985 NtClose( hKey );
3987 break;
3990 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3991 (dwFlags == LGRPID_INSTALLED && bInstalled))
3992 return TRUE;
3994 return FALSE;
3997 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3998 typedef struct
4000 LANGGROUPLOCALE_ENUMPROCA procA;
4001 LANGGROUPLOCALE_ENUMPROCW procW;
4002 DWORD dwFlags;
4003 LGRPID lgrpid;
4004 LONG_PTR lParam;
4005 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4007 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4008 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4010 static const WCHAR szAlternateSortsKeyName[] = {
4011 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4013 WCHAR szNumber[10], szValue[4];
4014 HANDLE hKey;
4015 BOOL bContinue = TRUE, bAlternate = FALSE;
4016 LGRPID lgrpid;
4017 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4019 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4021 SetLastError(ERROR_INVALID_PARAMETER);
4022 return FALSE;
4025 if (lpProcs->dwFlags)
4027 SetLastError(ERROR_INVALID_FLAGS);
4028 return FALSE;
4031 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4033 if (!hKey)
4034 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4036 while (bContinue)
4038 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4039 szValue, sizeof(szValue) ))
4041 lgrpid = strtoulW( szValue, NULL, 16 );
4043 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4044 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4046 if (lgrpid == lpProcs->lgrpid)
4048 LCID lcid;
4050 lcid = strtoulW( szNumber, NULL, 16 );
4052 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4053 * '00000437 ;Georgian'
4054 * At present we only pass the LCID string.
4057 if (lpProcs->procW)
4058 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4059 else
4061 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4063 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4065 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4069 ulIndex++;
4071 else
4073 /* Finished enumerating this key */
4074 if (!bAlternate)
4076 /* Enumerate alternate sorts also */
4077 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4078 bAlternate = TRUE;
4079 ulIndex = 0;
4081 else
4082 bContinue = FALSE; /* Finished both keys */
4085 if (!bContinue)
4086 break;
4089 if (hKey)
4090 NtClose( hKey );
4092 return TRUE;
4095 /******************************************************************************
4096 * EnumLanguageGroupLocalesA (KERNEL32.@)
4098 * Call a users function for every locale in a language group available on the system.
4100 * PARAMS
4101 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4102 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4103 * dwFlags [I] Reserved, set to 0
4104 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4106 * RETURNS
4107 * Success: TRUE.
4108 * Failure: FALSE. Use GetLastError() to determine the cause.
4110 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4111 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4113 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4115 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4117 callbacks.procA = pLangGrpLcEnumProc;
4118 callbacks.procW = NULL;
4119 callbacks.dwFlags = dwFlags;
4120 callbacks.lgrpid = lgrpid;
4121 callbacks.lParam = lParam;
4123 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4126 /******************************************************************************
4127 * EnumLanguageGroupLocalesW (KERNEL32.@)
4129 * See EnumLanguageGroupLocalesA.
4131 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4132 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4134 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4136 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4138 callbacks.procA = NULL;
4139 callbacks.procW = pLangGrpLcEnumProc;
4140 callbacks.dwFlags = dwFlags;
4141 callbacks.lgrpid = lgrpid;
4142 callbacks.lParam = lParam;
4144 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4147 /******************************************************************************
4148 * InvalidateNLSCache (KERNEL32.@)
4150 * Invalidate the cache of NLS values.
4152 * PARAMS
4153 * None.
4155 * RETURNS
4156 * Success: TRUE.
4157 * Failure: FALSE.
4159 BOOL WINAPI InvalidateNLSCache(void)
4161 FIXME("() stub\n");
4162 return FALSE;
4165 /******************************************************************************
4166 * GetUserGeoID (KERNEL32.@)
4168 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4170 GEOID ret = GEOID_NOT_AVAILABLE;
4171 static const WCHAR geoW[] = {'G','e','o',0};
4172 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4173 WCHAR bufferW[40], *end;
4174 DWORD count;
4175 HANDLE hkey, hSubkey = 0;
4176 UNICODE_STRING keyW;
4177 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4178 RtlInitUnicodeString( &keyW, nationW );
4179 count = sizeof(bufferW);
4181 if(!(hkey = create_registry_key())) return ret;
4183 switch( GeoClass ){
4184 case GEOCLASS_NATION:
4185 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4187 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4188 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4189 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4191 break;
4192 case GEOCLASS_REGION:
4193 FIXME("GEOCLASS_REGION not handled yet\n");
4194 break;
4197 NtClose(hkey);
4198 if (hSubkey) NtClose(hSubkey);
4199 return ret;
4202 /******************************************************************************
4203 * SetUserGeoID (KERNEL32.@)
4205 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4207 static const WCHAR geoW[] = {'G','e','o',0};
4208 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4209 static const WCHAR formatW[] = {'%','i',0};
4210 UNICODE_STRING nameW,keyW;
4211 WCHAR bufferW[10];
4212 OBJECT_ATTRIBUTES attr;
4213 HANDLE hkey;
4215 if(!(hkey = create_registry_key())) return FALSE;
4217 attr.Length = sizeof(attr);
4218 attr.RootDirectory = hkey;
4219 attr.ObjectName = &nameW;
4220 attr.Attributes = 0;
4221 attr.SecurityDescriptor = NULL;
4222 attr.SecurityQualityOfService = NULL;
4223 RtlInitUnicodeString( &nameW, geoW );
4224 RtlInitUnicodeString( &keyW, nationW );
4226 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4229 NtClose(attr.RootDirectory);
4230 return FALSE;
4233 sprintfW(bufferW, formatW, GeoID);
4234 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4235 NtClose(attr.RootDirectory);
4236 NtClose(hkey);
4237 return TRUE;
4240 typedef struct
4242 union
4244 UILANGUAGE_ENUMPROCA procA;
4245 UILANGUAGE_ENUMPROCW procW;
4246 } u;
4247 DWORD flags;
4248 LONG_PTR param;
4249 } ENUM_UILANG_CALLBACK;
4251 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4252 LPCSTR name, WORD LangID, LONG_PTR lParam )
4254 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4255 char buf[20];
4257 sprintf(buf, "%08x", (UINT)LangID);
4258 return enum_uilang->u.procA( buf, enum_uilang->param );
4261 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4262 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4264 static const WCHAR formatW[] = {'%','0','8','x',0};
4265 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4266 WCHAR buf[20];
4268 sprintfW( buf, formatW, (UINT)LangID );
4269 return enum_uilang->u.procW( buf, enum_uilang->param );
4272 /******************************************************************************
4273 * EnumUILanguagesA (KERNEL32.@)
4275 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4277 ENUM_UILANG_CALLBACK enum_uilang;
4279 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4281 if(!pUILangEnumProc) {
4282 SetLastError(ERROR_INVALID_PARAMETER);
4283 return FALSE;
4285 if(dwFlags) {
4286 SetLastError(ERROR_INVALID_FLAGS);
4287 return FALSE;
4290 enum_uilang.u.procA = pUILangEnumProc;
4291 enum_uilang.flags = dwFlags;
4292 enum_uilang.param = lParam;
4294 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4295 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4296 (LONG_PTR)&enum_uilang);
4297 return TRUE;
4300 /******************************************************************************
4301 * EnumUILanguagesW (KERNEL32.@)
4303 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4305 ENUM_UILANG_CALLBACK enum_uilang;
4307 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4310 if(!pUILangEnumProc) {
4311 SetLastError(ERROR_INVALID_PARAMETER);
4312 return FALSE;
4314 if(dwFlags) {
4315 SetLastError(ERROR_INVALID_FLAGS);
4316 return FALSE;
4319 enum_uilang.u.procW = pUILangEnumProc;
4320 enum_uilang.flags = dwFlags;
4321 enum_uilang.param = lParam;
4323 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4324 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4325 (LONG_PTR)&enum_uilang);
4326 return TRUE;
4329 enum locationkind {
4330 LOCATION_NATION = 0,
4331 LOCATION_REGION,
4332 LOCATION_BOTH
4335 struct geoinfo_t {
4336 GEOID id;
4337 WCHAR iso2W[3];
4338 WCHAR iso3W[4];
4339 GEOID parent;
4340 INT uncode;
4341 enum locationkind kind;
4344 static const struct geoinfo_t geoinfodata[] = {
4345 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4346 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4347 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4348 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4349 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4350 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4351 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4352 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4353 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4354 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4355 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4356 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4357 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4358 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4359 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4360 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4361 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4362 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4363 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4364 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4365 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4366 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4367 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4368 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4369 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4370 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4371 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4372 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4373 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4374 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4375 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4376 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4377 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4378 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4379 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4380 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4381 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4382 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4383 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4384 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4385 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4386 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4387 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4388 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4389 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4390 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4391 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4392 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4393 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4394 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4395 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4396 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4397 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4398 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4399 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4400 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4401 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4402 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4403 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4404 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4405 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4406 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4407 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4408 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4409 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4410 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4411 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4412 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4413 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4414 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4415 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4416 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4417 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4418 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4419 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4420 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4421 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4422 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4423 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4424 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4425 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4426 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4427 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4428 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4429 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4430 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4431 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4432 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4433 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4434 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4435 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4436 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4437 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4438 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4439 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4440 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4441 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4442 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4443 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4444 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4445 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4446 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4447 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4448 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4449 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4450 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4451 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4452 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4453 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4454 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4455 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4456 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4457 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4458 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4459 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4460 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4461 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4462 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4463 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4464 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4465 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4466 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4467 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4468 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4469 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4470 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4471 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4472 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4473 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4474 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4475 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4476 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4477 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4478 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4479 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4480 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4481 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4482 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4483 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4484 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4485 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4486 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4487 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4488 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4489 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4490 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4491 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4492 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4493 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4494 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4495 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4496 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4497 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4498 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4499 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4500 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4501 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4502 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4503 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4504 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4505 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4506 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4507 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4508 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4509 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4510 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4511 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4512 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4513 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4514 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4515 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4516 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4517 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4518 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4519 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4520 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4521 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4522 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4523 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4524 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4525 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4526 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4527 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4528 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4529 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4530 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4531 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4532 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4533 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4534 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4535 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4536 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4537 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4538 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4539 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4540 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4541 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4542 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4543 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4544 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4545 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4546 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4547 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4548 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4549 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4550 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4551 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4552 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4553 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4554 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4555 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4556 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4557 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4558 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4559 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4560 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4561 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4562 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4563 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4564 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4565 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4566 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4567 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4568 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4569 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4570 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4571 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4572 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4573 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4574 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4575 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4576 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4577 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4578 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4579 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4580 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4581 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4582 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4583 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4584 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4585 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4586 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4587 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4588 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4589 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4590 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4591 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4592 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4593 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4594 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4595 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4596 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4597 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4598 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4599 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4600 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4601 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4602 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4603 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4604 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4605 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4606 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4607 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4608 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4609 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4610 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4611 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4612 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4613 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4614 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4615 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4616 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4617 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4618 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4619 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4620 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4621 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4622 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4623 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4624 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4625 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4626 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4627 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4628 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4629 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4630 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4631 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4632 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4633 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4634 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4635 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4636 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4637 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4638 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4639 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4640 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4641 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4642 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4643 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4646 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4648 int min, max;
4650 min = 0;
4651 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4653 while (min <= max) {
4654 const struct geoinfo_t *ptr;
4655 int n = (min+max)/2;
4657 ptr = &geoinfodata[n];
4658 if (geoid == ptr->id)
4659 /* we don't need empty entries */
4660 return *ptr->iso2W ? ptr : NULL;
4662 if (ptr->id > geoid)
4663 max = n-1;
4664 else
4665 min = n+1;
4668 return NULL;
4671 /******************************************************************************
4672 * GetGeoInfoW (KERNEL32.@)
4674 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4676 const struct geoinfo_t *ptr;
4677 const WCHAR *str = NULL;
4678 WCHAR buffW[12];
4679 LONG val = 0;
4680 INT len;
4682 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4684 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4685 SetLastError(ERROR_INVALID_PARAMETER);
4686 return 0;
4689 switch (geotype) {
4690 case GEO_NATION:
4691 val = geoid;
4692 break;
4693 case GEO_ISO_UN_NUMBER:
4694 val = ptr->uncode;
4695 break;
4696 case GEO_PARENT:
4697 val = ptr->parent;
4698 break;
4699 case GEO_ISO2:
4700 case GEO_ISO3:
4702 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4703 break;
4705 case GEO_RFC1766:
4706 case GEO_LCID:
4707 case GEO_FRIENDLYNAME:
4708 case GEO_OFFICIALNAME:
4709 case GEO_TIMEZONES:
4710 case GEO_OFFICIALLANGUAGES:
4711 case GEO_LATITUDE:
4712 case GEO_LONGITUDE:
4713 FIXME("type %d is not supported\n", geotype);
4714 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4715 return 0;
4716 default:
4717 WARN("unrecognized type %d\n", geotype);
4718 SetLastError(ERROR_INVALID_FLAGS);
4719 return 0;
4722 if (val) {
4723 static const WCHAR fmtW[] = {'%','d',0};
4724 sprintfW(buffW, fmtW, val);
4725 str = buffW;
4728 len = strlenW(str) + 1;
4729 if (!data || !data_len)
4730 return len;
4732 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4733 if (data_len < len)
4734 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4735 return data_len < len ? 0 : len;
4738 /******************************************************************************
4739 * GetGeoInfoA (KERNEL32.@)
4741 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4743 WCHAR *buffW;
4744 INT len;
4746 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4748 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4749 if (!len)
4750 return 0;
4752 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4753 if (!buffW)
4754 return 0;
4756 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4757 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4758 if (!data || !data_len) {
4759 HeapFree(GetProcessHeap(), 0, buffW);
4760 return len;
4763 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4764 HeapFree(GetProcessHeap(), 0, buffW);
4766 if (data_len < len)
4767 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4768 return data_len < len ? 0 : len;
4771 /******************************************************************************
4772 * EnumSystemGeoID (KERNEL32.@)
4774 * Call a users function for every location available on the system.
4776 * PARAMS
4777 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4778 * parent [I] GEOID for the parent
4779 * enumproc [I] Callback function to call for each location
4781 * RETURNS
4782 * Success: TRUE.
4783 * Failure: FALSE. Use GetLastError() to determine the cause.
4785 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4787 INT i;
4789 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4791 if (!enumproc) {
4792 SetLastError(ERROR_INVALID_PARAMETER);
4793 return FALSE;
4796 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4797 SetLastError(ERROR_INVALID_FLAGS);
4798 return FALSE;
4801 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4802 const struct geoinfo_t *ptr = &geoinfodata[i];
4804 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4805 continue;
4807 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4808 continue;
4810 if (parent && ptr->parent != parent)
4811 continue;
4813 if (!enumproc(ptr->id))
4814 return TRUE;
4817 return TRUE;
4820 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4822 LCID userlcid;
4824 TRACE("%p, %d\n", localename, buffersize);
4826 userlcid = GetUserDefaultLCID();
4827 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4830 /******************************************************************************
4831 * NormalizeString (KERNEL32.@)
4833 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4834 LPWSTR lpDstString, INT cwDstLength)
4836 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4837 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4838 return 0;
4841 /******************************************************************************
4842 * IsNormalizedString (KERNEL32.@)
4844 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4846 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4847 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4848 return FALSE;
4851 enum {
4852 BASE = 36,
4853 TMIN = 1,
4854 TMAX = 26,
4855 SKEW = 38,
4856 DAMP = 700,
4857 INIT_BIAS = 72,
4858 INIT_N = 128
4861 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4863 INT k;
4865 delta /= (firsttime ? DAMP : 2);
4866 delta += delta/numpoints;
4868 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4869 delta /= BASE-TMIN;
4870 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4873 /******************************************************************************
4874 * IdnToAscii (KERNEL32.@)
4875 * Implementation of Punycode based on RFC 3492.
4877 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4878 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4880 static const WCHAR prefixW[] = {'x','n','-','-'};
4882 WCHAR *norm_str;
4883 INT i, label_start, label_end, norm_len, out_label, out = 0;
4885 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4886 lpASCIICharStr, cchASCIIChar);
4888 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4889 if(!norm_len)
4890 return 0;
4891 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4892 if(!norm_str) {
4893 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4894 return 0;
4896 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4897 cchUnicodeChar, norm_str, norm_len);
4898 if(!norm_len) {
4899 HeapFree(GetProcessHeap(), 0, norm_str);
4900 return 0;
4903 for(label_start=0; label_start<norm_len;) {
4904 INT n = INIT_N, bias = INIT_BIAS;
4905 INT delta = 0, b = 0, h;
4907 out_label = out;
4908 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4909 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4910 if(norm_str[i] < 0x80)
4911 b++;
4912 label_end = i;
4914 if(b == label_end-label_start) {
4915 if(label_end < norm_len)
4916 b++;
4917 if(!lpASCIICharStr) {
4918 out += b;
4919 }else if(out+b <= cchASCIIChar) {
4920 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4921 out += b;
4922 }else {
4923 HeapFree(GetProcessHeap(), 0, norm_str);
4924 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4925 return 0;
4927 label_start = label_end+1;
4928 continue;
4931 if(!lpASCIICharStr) {
4932 out += 5+b; /* strlen(xn--...-) */
4933 }else if(out+5+b <= cchASCIIChar) {
4934 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4935 out += 4;
4936 for(i=label_start; i<label_end; i++)
4937 if(norm_str[i] < 0x80)
4938 lpASCIICharStr[out++] = norm_str[i];
4939 lpASCIICharStr[out++] = '-';
4940 }else {
4941 HeapFree(GetProcessHeap(), 0, norm_str);
4942 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4943 return 0;
4945 if(!b)
4946 out--;
4948 for(h=b; h<label_end-label_start;) {
4949 INT m = 0xffff, q, k;
4951 for(i=label_start; i<label_end; i++) {
4952 if(norm_str[i]>=n && m>norm_str[i])
4953 m = norm_str[i];
4955 delta += (m-n)*(h+1);
4956 n = m;
4958 for(i=label_start; i<label_end; i++) {
4959 if(norm_str[i] < n) {
4960 delta++;
4961 }else if(norm_str[i] == n) {
4962 for(q=delta, k=BASE; ; k+=BASE) {
4963 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4964 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4965 if(!lpASCIICharStr) {
4966 out++;
4967 }else if(out+1 <= cchASCIIChar) {
4968 lpASCIICharStr[out++] = disp<='z'-'a' ?
4969 'a'+disp : '0'+disp-'z'+'a'-1;
4970 }else {
4971 HeapFree(GetProcessHeap(), 0, norm_str);
4972 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4973 return 0;
4975 if(q < t)
4976 break;
4977 q = (q-t)/(BASE-t);
4979 bias = adapt(delta, h+1, h==b);
4980 delta = 0;
4981 h++;
4984 delta++;
4985 n++;
4988 if(out-out_label > 63) {
4989 HeapFree(GetProcessHeap(), 0, norm_str);
4990 SetLastError(ERROR_INVALID_NAME);
4991 return 0;
4994 if(label_end < norm_len) {
4995 if(!lpASCIICharStr) {
4996 out++;
4997 }else if(out+1 <= cchASCIIChar) {
4998 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4999 }else {
5000 HeapFree(GetProcessHeap(), 0, norm_str);
5001 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5002 return 0;
5005 label_start = label_end+1;
5008 HeapFree(GetProcessHeap(), 0, norm_str);
5009 return out;
5012 /******************************************************************************
5013 * IdnToNameprepUnicode (KERNEL32.@)
5015 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5016 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5018 enum {
5019 UNASSIGNED = 0x1,
5020 PROHIBITED = 0x2,
5021 BIDI_RAL = 0x4,
5022 BIDI_L = 0x8
5025 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5026 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5027 const WCHAR *ptr;
5028 WORD flags;
5029 WCHAR buf[64], *map_str, norm_str[64], ch;
5030 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5031 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5033 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5034 lpNameprepCharStr, cchNameprepChar);
5036 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5037 SetLastError(ERROR_INVALID_FLAGS);
5038 return 0;
5041 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5042 SetLastError(ERROR_INVALID_PARAMETER);
5043 return 0;
5046 if(cchUnicodeChar == -1)
5047 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5048 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5049 SetLastError(ERROR_INVALID_NAME);
5050 return 0;
5053 for(label_start=0; label_start<cchUnicodeChar;) {
5054 ascii_only = TRUE;
5055 for(i=label_start; i<cchUnicodeChar; i++) {
5056 ch = lpUnicodeCharStr[i];
5058 if(i!=cchUnicodeChar-1 && !ch) {
5059 SetLastError(ERROR_INVALID_NAME);
5060 return 0;
5062 /* check if ch is one of label separators defined in RFC3490 */
5063 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5064 break;
5066 if(ch > 0x7f) {
5067 ascii_only = FALSE;
5068 continue;
5071 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5072 continue;
5073 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5074 || (ch>='0' && ch<='9') || ch=='-')
5075 continue;
5077 SetLastError(ERROR_INVALID_NAME);
5078 return 0;
5080 label_end = i;
5081 /* last label may be empty */
5082 if(label_start==label_end && ch) {
5083 SetLastError(ERROR_INVALID_NAME);
5084 return 0;
5087 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5088 lpUnicodeCharStr[label_end-1]=='-')) {
5089 SetLastError(ERROR_INVALID_NAME);
5090 return 0;
5093 if(ascii_only) {
5094 /* maximal label length is 63 characters */
5095 if(label_end-label_start > 63) {
5096 SetLastError(ERROR_INVALID_NAME);
5097 return 0;
5099 if(label_end < cchUnicodeChar)
5100 label_end++;
5102 if(!lpNameprepCharStr) {
5103 out += label_end-label_start;
5104 }else if(out+label_end-label_start <= cchNameprepChar) {
5105 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5106 (label_end-label_start)*sizeof(WCHAR));
5107 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5108 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5109 out += label_end-label_start;
5110 }else {
5111 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5112 return 0;
5115 label_start = label_end;
5116 continue;
5119 map_len = 0;
5120 for(i=label_start; i<label_end; i++) {
5121 ch = lpUnicodeCharStr[i];
5122 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5123 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5125 if(!ptr[0]) map_len++;
5126 else if(!ptr[1]) map_len++;
5127 else if(!ptr[2]) map_len += 2;
5128 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5130 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5131 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5132 if(!map_str) {
5133 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5134 return 0;
5136 }else {
5137 map_str = buf;
5139 map_len = 0;
5140 for(i=label_start; i<label_end; i++) {
5141 ch = lpUnicodeCharStr[i];
5142 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5143 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5145 if(!ptr[0]) {
5146 map_str[map_len++] = ch;
5147 }else if(!ptr[1]) {
5148 map_str[map_len++] = ptr[0];
5149 }else if(!ptr[2]) {
5150 map_str[map_len++] = ptr[0];
5151 map_str[map_len++] = ptr[1];
5152 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5153 map_str[map_len++] = ptr[0];
5154 map_str[map_len++] = ptr[1];
5155 map_str[map_len++] = ptr[2];
5159 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5160 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5161 if(map_str != buf)
5162 HeapFree(GetProcessHeap(), 0, map_str);
5163 if(!norm_len) {
5164 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5165 SetLastError(ERROR_INVALID_NAME);
5166 return 0;
5169 if(label_end < cchUnicodeChar) {
5170 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5171 label_end++;
5174 if(!lpNameprepCharStr) {
5175 out += norm_len;
5176 }else if(out+norm_len <= cchNameprepChar) {
5177 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5178 out += norm_len;
5179 }else {
5180 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5181 return 0;
5184 have_bidi_ral = prohibit_bidi_ral = FALSE;
5185 mask = PROHIBITED;
5186 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5187 mask |= UNASSIGNED;
5188 for(i=0; i<norm_len; i++) {
5189 ch = norm_str[i];
5190 flags = get_table_entry( nameprep_char_type, ch );
5192 if(flags & mask) {
5193 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5194 : ERROR_NO_UNICODE_TRANSLATION);
5195 return 0;
5198 if(flags & BIDI_RAL)
5199 have_bidi_ral = TRUE;
5200 if(flags & BIDI_L)
5201 prohibit_bidi_ral = TRUE;
5204 if(have_bidi_ral) {
5205 ch = norm_str[0];
5206 flags = get_table_entry( nameprep_char_type, ch );
5207 if((flags & BIDI_RAL) == 0)
5208 prohibit_bidi_ral = TRUE;
5210 ch = norm_str[norm_len-1];
5211 flags = get_table_entry( nameprep_char_type, ch );
5212 if((flags & BIDI_RAL) == 0)
5213 prohibit_bidi_ral = TRUE;
5216 if(have_bidi_ral && prohibit_bidi_ral) {
5217 SetLastError(ERROR_INVALID_NAME);
5218 return 0;
5221 label_start = label_end;
5224 return out;
5227 /******************************************************************************
5228 * IdnToUnicode (KERNEL32.@)
5230 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5231 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5233 extern const unsigned short nameprep_char_type[];
5235 INT i, label_start, label_end, out_label, out = 0;
5236 WCHAR ch;
5238 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5239 lpUnicodeCharStr, cchUnicodeChar);
5241 for(label_start=0; label_start<cchASCIIChar;) {
5242 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5244 out_label = out;
5245 for(i=label_start; i<cchASCIIChar; i++) {
5246 ch = lpASCIICharStr[i];
5248 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5249 SetLastError(ERROR_INVALID_NAME);
5250 return 0;
5253 if(!ch || ch=='.')
5254 break;
5255 if(ch == '-')
5256 delim = i;
5258 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5259 continue;
5260 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5261 || (ch>='0' && ch<='9') || ch=='-')
5262 continue;
5264 SetLastError(ERROR_INVALID_NAME);
5265 return 0;
5267 label_end = i;
5268 /* last label may be empty */
5269 if(label_start==label_end && ch) {
5270 SetLastError(ERROR_INVALID_NAME);
5271 return 0;
5274 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5275 lpASCIICharStr[label_end-1]=='-')) {
5276 SetLastError(ERROR_INVALID_NAME);
5277 return 0;
5279 if(label_end-label_start > 63) {
5280 SetLastError(ERROR_INVALID_NAME);
5281 return 0;
5284 if(label_end-label_start<4 ||
5285 tolowerW(lpASCIICharStr[label_start])!='x' ||
5286 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5287 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5288 if(label_end < cchASCIIChar)
5289 label_end++;
5291 if(!lpUnicodeCharStr) {
5292 out += label_end-label_start;
5293 }else if(out+label_end-label_start <= cchUnicodeChar) {
5294 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5295 (label_end-label_start)*sizeof(WCHAR));
5296 out += label_end-label_start;
5297 }else {
5298 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5299 return 0;
5302 label_start = label_end;
5303 continue;
5306 if(delim == label_start+3)
5307 delim++;
5308 if(!lpUnicodeCharStr) {
5309 out += delim-label_start-4;
5310 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5311 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5312 (delim-label_start-4)*sizeof(WCHAR));
5313 out += delim-label_start-4;
5314 }else {
5315 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5316 return 0;
5318 if(out != out_label)
5319 delim++;
5321 for(i=delim; i<label_end;) {
5322 old_pos = pos;
5323 w = 1;
5324 for(k=BASE; ; k+=BASE) {
5325 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5326 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5327 SetLastError(ERROR_INVALID_NAME);
5328 return 0;
5330 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5331 pos += digit*w;
5332 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5333 if(digit < t)
5334 break;
5335 w *= BASE-t;
5337 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5338 n += pos/(out-out_label+1);
5339 pos %= out-out_label+1;
5341 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5342 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5343 SetLastError(ERROR_INVALID_NAME);
5344 return 0;
5346 if(!lpUnicodeCharStr) {
5347 out++;
5348 }else if(out+1 <= cchASCIIChar) {
5349 memmove(lpUnicodeCharStr+out_label+pos+1,
5350 lpUnicodeCharStr+out_label+pos,
5351 (out-out_label-pos)*sizeof(WCHAR));
5352 lpUnicodeCharStr[out_label+pos] = n;
5353 out++;
5354 }else {
5355 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5356 return 0;
5358 pos++;
5361 if(out-out_label > 63) {
5362 SetLastError(ERROR_INVALID_NAME);
5363 return 0;
5366 if(label_end < cchASCIIChar) {
5367 if(!lpUnicodeCharStr) {
5368 out++;
5369 }else if(out+1 <= cchUnicodeChar) {
5370 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5371 }else {
5372 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5373 return 0;
5376 label_start = label_end+1;
5379 return out;
5383 /******************************************************************************
5384 * GetUserPreferredUILanguages (KERNEL32.@)
5386 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5388 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5389 return FALSE;
5392 /******************************************************************************
5393 * GetFileMUIPath (KERNEL32.@)
5396 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5397 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5399 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5400 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5402 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5404 return FALSE;
5407 /******************************************************************************
5408 * GetFileMUIInfo (KERNEL32.@)
5411 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5413 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5415 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5416 return FALSE;