make_unicode: Mark most data tables as hidden.
[wine.git] / dlls / kernel32 / locale.c
blob7418da9f359ed5d237799101946906b43b33bd8c
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) FIXME("unsupported lparam %lx\n", lparam);
2982 if (!src || !srclen || dstlen < 0)
2984 SetLastError(ERROR_INVALID_PARAMETER);
2985 return 0;
2988 /* mutually exclusive flags */
2989 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2990 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2991 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2992 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2994 SetLastError(ERROR_INVALID_FLAGS);
2995 return 0;
2998 if (!dstlen) dst = NULL;
3000 if (flags & LCMAP_SORTKEY)
3002 INT ret;
3003 if (src == dst)
3005 SetLastError(ERROR_INVALID_FLAGS);
3006 return 0;
3009 if (srclen < 0) srclen = strlenW(src);
3011 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3012 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3014 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3015 if (ret == 0)
3016 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3017 else
3018 ret++;
3019 return ret;
3022 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3023 if (flags & SORT_STRINGSORT)
3025 SetLastError(ERROR_INVALID_FLAGS);
3026 return 0;
3029 if (srclen < 0) srclen = strlenW(src) + 1;
3031 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3032 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3034 if (!dst) /* return required string length */
3036 INT len;
3038 for (len = 0; srclen; src++, srclen--)
3040 WCHAR wch = *src;
3041 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3042 * and skips white space and punctuation characters for
3043 * NORM_IGNORESYMBOLS.
3045 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3046 continue;
3047 len++;
3049 return len;
3052 if (flags & LCMAP_UPPERCASE)
3054 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3056 WCHAR wch = *src;
3057 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3058 continue;
3059 *dst_ptr++ = toupperW(wch);
3060 dstlen--;
3063 else if (flags & LCMAP_LOWERCASE)
3065 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3067 WCHAR wch = *src;
3068 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3069 continue;
3070 *dst_ptr++ = tolowerW(wch);
3071 dstlen--;
3074 else
3076 if (src == dst)
3078 SetLastError(ERROR_INVALID_FLAGS);
3079 return 0;
3081 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3083 WCHAR wch = *src;
3084 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3085 continue;
3086 *dst_ptr++ = wch;
3087 dstlen--;
3091 if (srclen)
3093 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3094 return 0;
3097 return dst_ptr - dst;
3100 /*************************************************************************
3101 * LCMapStringW (KERNEL32.@)
3103 * See LCMapStringA.
3105 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3106 LPWSTR dst, INT dstlen)
3108 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3109 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3111 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3114 /*************************************************************************
3115 * LCMapStringA (KERNEL32.@)
3117 * Map characters in a locale sensitive string.
3119 * PARAMS
3120 * lcid [I] LCID for the conversion.
3121 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3122 * src [I] String to map
3123 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3124 * dst [O] Destination for mapped string
3125 * dstlen [I] Length of dst in characters
3127 * RETURNS
3128 * Success: The length of the mapped string in dst, including the NUL terminator.
3129 * Failure: 0. Use GetLastError() to determine the cause.
3131 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3132 LPSTR dst, INT dstlen)
3134 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3135 LPWSTR srcW, dstW;
3136 INT ret = 0, srclenW, dstlenW;
3137 UINT locale_cp = CP_ACP;
3139 if (!src || !srclen || dstlen < 0)
3141 SetLastError(ERROR_INVALID_PARAMETER);
3142 return 0;
3145 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3147 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3148 if (srclenW)
3149 srcW = bufW;
3150 else
3152 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3153 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3154 if (!srcW)
3156 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3157 return 0;
3159 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3162 if (flags & LCMAP_SORTKEY)
3164 if (src == dst)
3166 SetLastError(ERROR_INVALID_FLAGS);
3167 goto map_string_exit;
3169 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3170 if (ret == 0)
3171 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3172 else
3173 ret++;
3174 goto map_string_exit;
3177 if (flags & SORT_STRINGSORT)
3179 SetLastError(ERROR_INVALID_FLAGS);
3180 goto map_string_exit;
3183 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3184 if (!dstlenW)
3185 goto map_string_exit;
3187 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3188 if (!dstW)
3190 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3191 goto map_string_exit;
3194 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3195 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3196 HeapFree(GetProcessHeap(), 0, dstW);
3198 map_string_exit:
3199 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3200 return ret;
3203 /*************************************************************************
3204 * FoldStringA (KERNEL32.@)
3206 * Map characters in a string.
3208 * PARAMS
3209 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3210 * src [I] String to map
3211 * srclen [I] Length of src, or -1 if src is NUL terminated
3212 * dst [O] Destination for mapped string
3213 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3215 * RETURNS
3216 * Success: The length of the string written to dst, including the terminating NUL. If
3217 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3218 * and dst may be NULL.
3219 * Failure: 0. Use GetLastError() to determine the cause.
3221 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3222 LPSTR dst, INT dstlen)
3224 INT ret = 0, srclenW = 0;
3225 WCHAR *srcW = NULL, *dstW = NULL;
3227 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3229 SetLastError(ERROR_INVALID_PARAMETER);
3230 return 0;
3233 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3234 src, srclen, NULL, 0);
3235 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3237 if (!srcW)
3239 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3240 goto FoldStringA_exit;
3243 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3244 src, srclen, srcW, srclenW);
3246 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3248 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3249 if (ret && dstlen)
3251 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3253 if (!dstW)
3255 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3256 goto FoldStringA_exit;
3259 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3260 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3262 ret = 0;
3263 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3267 HeapFree(GetProcessHeap(), 0, dstW);
3269 FoldStringA_exit:
3270 HeapFree(GetProcessHeap(), 0, srcW);
3271 return ret;
3274 /*************************************************************************
3275 * FoldStringW (KERNEL32.@)
3277 * See FoldStringA.
3279 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3280 LPWSTR dst, INT dstlen)
3282 int ret;
3284 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3286 case 0:
3287 if (dwFlags)
3288 break;
3289 /* Fall through for dwFlags == 0 */
3290 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3291 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3292 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3293 SetLastError(ERROR_INVALID_FLAGS);
3294 return 0;
3297 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3299 SetLastError(ERROR_INVALID_PARAMETER);
3300 return 0;
3303 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3304 if (!ret)
3305 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3306 return ret;
3309 /******************************************************************************
3310 * CompareStringW (KERNEL32.@)
3312 * See CompareStringA.
3314 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3315 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3317 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3320 /******************************************************************************
3321 * CompareStringEx (KERNEL32.@)
3323 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3324 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3326 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3327 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3328 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3329 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3330 INT ret;
3331 static int once;
3333 if (version) FIXME("unexpected version parameter\n");
3334 if (reserved) FIXME("unexpected reserved value\n");
3335 if (lParam) FIXME("unexpected lParam\n");
3337 if (!str1 || !str2)
3339 SetLastError(ERROR_INVALID_PARAMETER);
3340 return 0;
3343 if (flags & ~(supported_flags|semistub_flags))
3345 SetLastError(ERROR_INVALID_FLAGS);
3346 return 0;
3349 if (flags & semistub_flags)
3351 if (!once++)
3352 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3355 if (len1 < 0) len1 = strlenW(str1);
3356 if (len2 < 0) len2 = strlenW(str2);
3358 ret = wine_compare_string(flags, str1, len1, str2, len2);
3360 if (ret) /* need to translate result */
3361 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3362 return CSTR_EQUAL;
3365 /******************************************************************************
3366 * CompareStringA (KERNEL32.@)
3368 * Compare two locale sensitive strings.
3370 * PARAMS
3371 * lcid [I] LCID for the comparison
3372 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3373 * str1 [I] First string to compare
3374 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3375 * str2 [I] Second string to compare
3376 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3378 * RETURNS
3379 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3380 * str1 is less than, equal to or greater than str2 respectively.
3381 * Failure: FALSE. Use GetLastError() to determine the cause.
3383 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3384 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3386 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3387 WCHAR *buf2W = buf1W + 130;
3388 LPWSTR str1W, str2W;
3389 INT len1W = 0, len2W = 0, ret;
3390 UINT locale_cp = CP_ACP;
3392 if (!str1 || !str2)
3394 SetLastError(ERROR_INVALID_PARAMETER);
3395 return 0;
3397 if (len1 < 0) len1 = strlen(str1);
3398 if (len2 < 0) len2 = strlen(str2);
3400 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3402 if (len1)
3404 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3405 if (len1W)
3406 str1W = buf1W;
3407 else
3409 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3410 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3411 if (!str1W)
3413 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3414 return 0;
3416 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3419 else
3421 len1W = 0;
3422 str1W = buf1W;
3425 if (len2)
3427 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3428 if (len2W)
3429 str2W = buf2W;
3430 else
3432 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3433 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3434 if (!str2W)
3436 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3437 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3438 return 0;
3440 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3443 else
3445 len2W = 0;
3446 str2W = buf2W;
3449 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3451 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3452 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3453 return ret;
3456 /******************************************************************************
3457 * CompareStringOrdinal (KERNEL32.@)
3459 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3461 int ret, len;
3463 if (!str1 || !str2)
3465 SetLastError(ERROR_INVALID_PARAMETER);
3466 return 0;
3468 if (len1 < 0) len1 = strlenW(str1);
3469 if (len2 < 0) len2 = strlenW(str2);
3471 len = min(len1, len2);
3472 if (ignore_case)
3474 ret = memicmpW(str1, str2, len);
3476 else
3478 ret = 0;
3479 for (; len > 0; len--)
3480 if ((ret = (*str1++ - *str2++))) break;
3482 if (!ret) ret = len1 - len2;
3484 if (ret < 0) return CSTR_LESS_THAN;
3485 if (ret > 0) return CSTR_GREATER_THAN;
3486 return CSTR_EQUAL;
3489 /*************************************************************************
3490 * lstrcmp (KERNEL32.@)
3491 * lstrcmpA (KERNEL32.@)
3493 * Compare two strings using the current thread locale.
3495 * PARAMS
3496 * str1 [I] First string to compare
3497 * str2 [I] Second string to compare
3499 * RETURNS
3500 * Success: A number less than, equal to or greater than 0 depending on whether
3501 * str1 is less than, equal to or greater than str2 respectively.
3502 * Failure: FALSE. Use GetLastError() to determine the cause.
3504 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3506 int ret;
3508 if ((str1 == NULL) && (str2 == NULL)) return 0;
3509 if (str1 == NULL) return -1;
3510 if (str2 == NULL) return 1;
3512 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3513 if (ret) ret -= 2;
3515 return ret;
3518 /*************************************************************************
3519 * lstrcmpi (KERNEL32.@)
3520 * lstrcmpiA (KERNEL32.@)
3522 * Compare two strings using the current thread locale, ignoring case.
3524 * PARAMS
3525 * str1 [I] First string to compare
3526 * str2 [I] Second string to compare
3528 * RETURNS
3529 * Success: A number less than, equal to or greater than 0 depending on whether
3530 * str2 is less than, equal to or greater than str1 respectively.
3531 * Failure: FALSE. Use GetLastError() to determine the cause.
3533 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3535 int ret;
3537 if ((str1 == NULL) && (str2 == NULL)) return 0;
3538 if (str1 == NULL) return -1;
3539 if (str2 == NULL) return 1;
3541 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3542 if (ret) ret -= 2;
3544 return ret;
3547 /*************************************************************************
3548 * lstrcmpW (KERNEL32.@)
3550 * See lstrcmpA.
3552 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3554 int ret;
3556 if ((str1 == NULL) && (str2 == NULL)) return 0;
3557 if (str1 == NULL) return -1;
3558 if (str2 == NULL) return 1;
3560 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3561 if (ret) ret -= 2;
3563 return ret;
3566 /*************************************************************************
3567 * lstrcmpiW (KERNEL32.@)
3569 * See lstrcmpiA.
3571 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3573 int ret;
3575 if ((str1 == NULL) && (str2 == NULL)) return 0;
3576 if (str1 == NULL) return -1;
3577 if (str2 == NULL) return 1;
3579 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3580 if (ret) ret -= 2;
3582 return ret;
3585 /******************************************************************************
3586 * LOCALE_Init
3588 void LOCALE_Init(void)
3590 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3591 const union cptable *unix_cp );
3593 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3595 #ifdef __APPLE__
3596 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3597 char user_locale[50];
3599 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3600 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3601 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3602 CFStringRef user_locale_string_ref;
3604 if (user_locale_country_ref)
3606 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3607 user_locale_lang_ref, user_locale_country_ref);
3609 else
3611 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3614 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3615 strcat(user_locale, ".UTF-8");
3617 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3618 setenv( "LANG", user_locale, 0 );
3619 TRACE( "setting locale to '%s'\n", user_locale );
3620 #endif /* __APPLE__ */
3622 setlocale( LC_ALL, "" );
3624 unix_cp = setup_unix_locales();
3625 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3627 #ifdef __APPLE__
3628 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3629 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3631 /* Retrieve the preferred language as chosen in System Preferences. */
3632 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3633 leave things be. */
3634 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3635 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3636 CFStringRef user_language_string_ref;
3637 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3638 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3639 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3640 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3642 struct locale_name locale_name;
3643 WCHAR buffer[128];
3644 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3645 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3646 parse_locale_name( buffer, &locale_name );
3647 lcid_LC_MESSAGES = locale_name.lcid;
3648 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3650 if (preferred_langs)
3651 CFRelease( preferred_langs );
3652 if (canonical_lang_string_ref)
3653 CFRelease( canonical_lang_string_ref );
3656 CFRelease( user_locale_ref );
3657 CFRelease( user_locale_string_ref );
3658 #endif
3660 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3661 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3662 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3664 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3665 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3666 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3667 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3668 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3669 if (!unix_cp)
3670 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3671 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3673 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3674 ansi_cptable = wine_cp_get_table( 1252 );
3675 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3676 oem_cptable = wine_cp_get_table( 437 );
3677 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3678 mac_cptable = wine_cp_get_table( 10000 );
3679 if (unix_cp != CP_UTF8)
3681 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3682 unix_cptable = wine_cp_get_table( 28591 );
3685 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3687 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3688 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3689 mac_cptable->info.codepage, unix_cp );
3691 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3694 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3696 UNICODE_STRING keyName;
3697 OBJECT_ATTRIBUTES attr;
3698 HANDLE hkey;
3700 RtlInitUnicodeString( &keyName, szKeyName );
3701 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3703 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3704 hkey = 0;
3706 return hkey;
3709 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3710 LPWSTR szValueName, ULONG valueNameSize,
3711 LPWSTR szValueData, ULONG valueDataSize)
3713 BYTE buffer[80];
3714 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3715 DWORD dwLen;
3717 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3718 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3719 info->NameLength > valueNameSize ||
3720 info->DataLength > valueDataSize)
3722 return FALSE;
3725 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3727 memcpy( szValueName, info->Name, info->NameLength);
3728 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3729 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3730 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3732 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3733 return TRUE;
3736 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3738 BYTE buffer[128];
3739 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3740 DWORD dwSize = sizeof(buffer);
3741 UNICODE_STRING valueName;
3743 RtlInitUnicodeString( &valueName, szValueName );
3745 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3746 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3747 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3748 info->DataLength == sizeof(DWORD))
3750 memcpy(lpVal, info->Data, sizeof(DWORD));
3751 return TRUE;
3754 return FALSE;
3757 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3759 LANGID langId;
3760 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3761 HRSRC hResource;
3762 BOOL bRet = FALSE;
3764 /* FIXME: Is it correct to use the system default langid? */
3765 langId = GetSystemDefaultLangID();
3767 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3768 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3770 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3772 if (hResource)
3774 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3776 if (hResDir)
3778 ULONG iResourceIndex = lgrpid & 0xf;
3779 LPCWSTR lpResEntry = LockResource( hResDir );
3780 ULONG i;
3782 for (i = 0; i < iResourceIndex; i++)
3783 lpResEntry += *lpResEntry + 1;
3785 if (*lpResEntry < nameSize)
3787 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3788 szName[*lpResEntry] = '\0';
3789 bRet = TRUE;
3793 FreeResource( hResource );
3795 return bRet;
3798 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3799 typedef struct
3801 LANGUAGEGROUP_ENUMPROCA procA;
3802 LANGUAGEGROUP_ENUMPROCW procW;
3803 DWORD dwFlags;
3804 LONG_PTR lParam;
3805 } ENUMLANGUAGEGROUP_CALLBACKS;
3807 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3808 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3810 WCHAR szNumber[10], szValue[4];
3811 HANDLE hKey;
3812 BOOL bContinue = TRUE;
3813 ULONG ulIndex = 0;
3815 if (!lpProcs)
3817 SetLastError(ERROR_INVALID_PARAMETER);
3818 return FALSE;
3821 switch (lpProcs->dwFlags)
3823 case 0:
3824 /* Default to LGRPID_INSTALLED */
3825 lpProcs->dwFlags = LGRPID_INSTALLED;
3826 /* Fall through... */
3827 case LGRPID_INSTALLED:
3828 case LGRPID_SUPPORTED:
3829 break;
3830 default:
3831 SetLastError(ERROR_INVALID_FLAGS);
3832 return FALSE;
3835 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3837 if (!hKey)
3838 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3840 while (bContinue)
3842 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3843 szValue, sizeof(szValue) ))
3845 BOOL bInstalled = szValue[0] == '1';
3846 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3848 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3849 bInstalled ? "" : "not ");
3851 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3853 WCHAR szGrpName[48];
3855 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3856 szGrpName[0] = '\0';
3858 if (lpProcs->procW)
3859 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3860 lpProcs->lParam );
3861 else
3863 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3864 char szGrpNameA[48];
3866 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3867 * or whether the language names are ever localised. Assume CP_ACP.
3870 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3871 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3873 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3874 lpProcs->lParam );
3878 ulIndex++;
3880 else
3881 bContinue = FALSE;
3883 if (!bContinue)
3884 break;
3887 if (hKey)
3888 NtClose( hKey );
3890 return TRUE;
3893 /******************************************************************************
3894 * EnumSystemLanguageGroupsA (KERNEL32.@)
3896 * Call a users function for each language group available on the system.
3898 * PARAMS
3899 * pLangGrpEnumProc [I] Callback function to call for each language group
3900 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3901 * lParam [I] User parameter to pass to pLangGrpEnumProc
3903 * RETURNS
3904 * Success: TRUE.
3905 * Failure: FALSE. Use GetLastError() to determine the cause.
3907 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3908 DWORD dwFlags, LONG_PTR lParam)
3910 ENUMLANGUAGEGROUP_CALLBACKS procs;
3912 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3914 procs.procA = pLangGrpEnumProc;
3915 procs.procW = NULL;
3916 procs.dwFlags = dwFlags;
3917 procs.lParam = lParam;
3919 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3922 /******************************************************************************
3923 * EnumSystemLanguageGroupsW (KERNEL32.@)
3925 * See EnumSystemLanguageGroupsA.
3927 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3928 DWORD dwFlags, LONG_PTR lParam)
3930 ENUMLANGUAGEGROUP_CALLBACKS procs;
3932 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3934 procs.procA = NULL;
3935 procs.procW = pLangGrpEnumProc;
3936 procs.dwFlags = dwFlags;
3937 procs.lParam = lParam;
3939 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3942 /******************************************************************************
3943 * IsValidLanguageGroup (KERNEL32.@)
3945 * Determine if a language group is supported and/or installed.
3947 * PARAMS
3948 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3949 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3951 * RETURNS
3952 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3953 * FALSE otherwise.
3955 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3957 static const WCHAR szFormat[] = { '%','x','\0' };
3958 WCHAR szValueName[16], szValue[2];
3959 BOOL bSupported = FALSE, bInstalled = FALSE;
3960 HANDLE hKey;
3963 switch (dwFlags)
3965 case LGRPID_INSTALLED:
3966 case LGRPID_SUPPORTED:
3968 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3970 sprintfW( szValueName, szFormat, lgrpid );
3972 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3974 bSupported = TRUE;
3976 if (szValue[0] == '1')
3977 bInstalled = TRUE;
3980 if (hKey)
3981 NtClose( hKey );
3983 break;
3986 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3987 (dwFlags == LGRPID_INSTALLED && bInstalled))
3988 return TRUE;
3990 return FALSE;
3993 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3994 typedef struct
3996 LANGGROUPLOCALE_ENUMPROCA procA;
3997 LANGGROUPLOCALE_ENUMPROCW procW;
3998 DWORD dwFlags;
3999 LGRPID lgrpid;
4000 LONG_PTR lParam;
4001 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4003 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4004 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4006 static const WCHAR szAlternateSortsKeyName[] = {
4007 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4009 WCHAR szNumber[10], szValue[4];
4010 HANDLE hKey;
4011 BOOL bContinue = TRUE, bAlternate = FALSE;
4012 LGRPID lgrpid;
4013 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4015 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4017 SetLastError(ERROR_INVALID_PARAMETER);
4018 return FALSE;
4021 if (lpProcs->dwFlags)
4023 SetLastError(ERROR_INVALID_FLAGS);
4024 return FALSE;
4027 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4029 if (!hKey)
4030 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4032 while (bContinue)
4034 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4035 szValue, sizeof(szValue) ))
4037 lgrpid = strtoulW( szValue, NULL, 16 );
4039 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4040 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4042 if (lgrpid == lpProcs->lgrpid)
4044 LCID lcid;
4046 lcid = strtoulW( szNumber, NULL, 16 );
4048 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4049 * '00000437 ;Georgian'
4050 * At present we only pass the LCID string.
4053 if (lpProcs->procW)
4054 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4055 else
4057 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4059 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4061 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4065 ulIndex++;
4067 else
4069 /* Finished enumerating this key */
4070 if (!bAlternate)
4072 /* Enumerate alternate sorts also */
4073 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4074 bAlternate = TRUE;
4075 ulIndex = 0;
4077 else
4078 bContinue = FALSE; /* Finished both keys */
4081 if (!bContinue)
4082 break;
4085 if (hKey)
4086 NtClose( hKey );
4088 return TRUE;
4091 /******************************************************************************
4092 * EnumLanguageGroupLocalesA (KERNEL32.@)
4094 * Call a users function for every locale in a language group available on the system.
4096 * PARAMS
4097 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4098 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4099 * dwFlags [I] Reserved, set to 0
4100 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4102 * RETURNS
4103 * Success: TRUE.
4104 * Failure: FALSE. Use GetLastError() to determine the cause.
4106 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4107 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4109 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4111 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4113 callbacks.procA = pLangGrpLcEnumProc;
4114 callbacks.procW = NULL;
4115 callbacks.dwFlags = dwFlags;
4116 callbacks.lgrpid = lgrpid;
4117 callbacks.lParam = lParam;
4119 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4122 /******************************************************************************
4123 * EnumLanguageGroupLocalesW (KERNEL32.@)
4125 * See EnumLanguageGroupLocalesA.
4127 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4128 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4130 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4132 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4134 callbacks.procA = NULL;
4135 callbacks.procW = pLangGrpLcEnumProc;
4136 callbacks.dwFlags = dwFlags;
4137 callbacks.lgrpid = lgrpid;
4138 callbacks.lParam = lParam;
4140 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4143 /******************************************************************************
4144 * InvalidateNLSCache (KERNEL32.@)
4146 * Invalidate the cache of NLS values.
4148 * PARAMS
4149 * None.
4151 * RETURNS
4152 * Success: TRUE.
4153 * Failure: FALSE.
4155 BOOL WINAPI InvalidateNLSCache(void)
4157 FIXME("() stub\n");
4158 return FALSE;
4161 /******************************************************************************
4162 * GetUserGeoID (KERNEL32.@)
4164 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4166 GEOID ret = GEOID_NOT_AVAILABLE;
4167 static const WCHAR geoW[] = {'G','e','o',0};
4168 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4169 WCHAR bufferW[40], *end;
4170 DWORD count;
4171 HANDLE hkey, hSubkey = 0;
4172 UNICODE_STRING keyW;
4173 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4174 RtlInitUnicodeString( &keyW, nationW );
4175 count = sizeof(bufferW);
4177 if(!(hkey = create_registry_key())) return ret;
4179 switch( GeoClass ){
4180 case GEOCLASS_NATION:
4181 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4183 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4184 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4185 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4187 break;
4188 case GEOCLASS_REGION:
4189 FIXME("GEOCLASS_REGION not handled yet\n");
4190 break;
4193 NtClose(hkey);
4194 if (hSubkey) NtClose(hSubkey);
4195 return ret;
4198 /******************************************************************************
4199 * SetUserGeoID (KERNEL32.@)
4201 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4203 static const WCHAR geoW[] = {'G','e','o',0};
4204 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4205 static const WCHAR formatW[] = {'%','i',0};
4206 UNICODE_STRING nameW,keyW;
4207 WCHAR bufferW[10];
4208 OBJECT_ATTRIBUTES attr;
4209 HANDLE hkey;
4211 if(!(hkey = create_registry_key())) return FALSE;
4213 attr.Length = sizeof(attr);
4214 attr.RootDirectory = hkey;
4215 attr.ObjectName = &nameW;
4216 attr.Attributes = 0;
4217 attr.SecurityDescriptor = NULL;
4218 attr.SecurityQualityOfService = NULL;
4219 RtlInitUnicodeString( &nameW, geoW );
4220 RtlInitUnicodeString( &keyW, nationW );
4222 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4225 NtClose(attr.RootDirectory);
4226 return FALSE;
4229 sprintfW(bufferW, formatW, GeoID);
4230 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4231 NtClose(attr.RootDirectory);
4232 NtClose(hkey);
4233 return TRUE;
4236 typedef struct
4238 union
4240 UILANGUAGE_ENUMPROCA procA;
4241 UILANGUAGE_ENUMPROCW procW;
4242 } u;
4243 DWORD flags;
4244 LONG_PTR param;
4245 } ENUM_UILANG_CALLBACK;
4247 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4248 LPCSTR name, WORD LangID, LONG_PTR lParam )
4250 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4251 char buf[20];
4253 sprintf(buf, "%08x", (UINT)LangID);
4254 return enum_uilang->u.procA( buf, enum_uilang->param );
4257 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4258 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4260 static const WCHAR formatW[] = {'%','0','8','x',0};
4261 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4262 WCHAR buf[20];
4264 sprintfW( buf, formatW, (UINT)LangID );
4265 return enum_uilang->u.procW( buf, enum_uilang->param );
4268 /******************************************************************************
4269 * EnumUILanguagesA (KERNEL32.@)
4271 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4273 ENUM_UILANG_CALLBACK enum_uilang;
4275 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4277 if(!pUILangEnumProc) {
4278 SetLastError(ERROR_INVALID_PARAMETER);
4279 return FALSE;
4281 if(dwFlags) {
4282 SetLastError(ERROR_INVALID_FLAGS);
4283 return FALSE;
4286 enum_uilang.u.procA = pUILangEnumProc;
4287 enum_uilang.flags = dwFlags;
4288 enum_uilang.param = lParam;
4290 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4291 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4292 (LONG_PTR)&enum_uilang);
4293 return TRUE;
4296 /******************************************************************************
4297 * EnumUILanguagesW (KERNEL32.@)
4299 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4301 ENUM_UILANG_CALLBACK enum_uilang;
4303 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4306 if(!pUILangEnumProc) {
4307 SetLastError(ERROR_INVALID_PARAMETER);
4308 return FALSE;
4310 if(dwFlags) {
4311 SetLastError(ERROR_INVALID_FLAGS);
4312 return FALSE;
4315 enum_uilang.u.procW = pUILangEnumProc;
4316 enum_uilang.flags = dwFlags;
4317 enum_uilang.param = lParam;
4319 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4320 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4321 (LONG_PTR)&enum_uilang);
4322 return TRUE;
4325 enum locationkind {
4326 LOCATION_NATION = 0,
4327 LOCATION_REGION,
4328 LOCATION_BOTH
4331 struct geoinfo_t {
4332 GEOID id;
4333 WCHAR iso2W[3];
4334 WCHAR iso3W[4];
4335 GEOID parent;
4336 INT uncode;
4337 enum locationkind kind;
4340 static const struct geoinfo_t geoinfodata[] = {
4341 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4342 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4343 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4344 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4345 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4346 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4347 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4348 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4349 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4350 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4351 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4352 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4353 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4354 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4355 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4356 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4357 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4358 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4359 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4360 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4361 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4362 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4363 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4364 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4365 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4366 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4367 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4368 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4369 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4370 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4371 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4372 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4373 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4374 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4375 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4376 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4377 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4378 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4379 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4380 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4381 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4382 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4383 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4384 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4385 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4386 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4387 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4388 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4389 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4390 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4391 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4392 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4393 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4394 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4395 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4396 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4397 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4398 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4399 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4400 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4401 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4402 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4403 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4404 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4405 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4406 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4407 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4408 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4409 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4410 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4411 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4412 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4413 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4414 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4415 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4416 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4417 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4418 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4419 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4420 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4421 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4422 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4423 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4424 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4425 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4426 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4427 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4428 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4429 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4430 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4431 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4432 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4433 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4434 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4435 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4436 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4437 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4438 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4439 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4440 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4441 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4442 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4443 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4444 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4445 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4446 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4447 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4448 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4449 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4450 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4451 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4452 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4453 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4454 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4455 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4456 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4457 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4458 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4459 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4460 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4461 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4462 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4463 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4464 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4465 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4466 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4467 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4468 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4469 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4470 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4471 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4472 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4473 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4474 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4475 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4476 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4477 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4478 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4479 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4480 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4481 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4482 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4483 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4484 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4485 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4486 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4487 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4488 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4489 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4490 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4491 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4492 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4493 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4494 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4495 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4496 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4497 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4498 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4499 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4500 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4501 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4502 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4503 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4504 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4505 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4506 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4507 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4508 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4509 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4510 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4511 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4512 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4513 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4514 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4515 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4516 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4517 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4518 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4519 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4520 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4521 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4522 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4523 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4524 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4525 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4526 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4527 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4528 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4529 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4530 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4531 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4532 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4533 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4534 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4535 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4536 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4537 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4538 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4539 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4540 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4541 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4542 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4543 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4544 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4545 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4546 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4547 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4548 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4549 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4550 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4551 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4552 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4553 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4554 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4555 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4556 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4557 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4558 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4559 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4560 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4561 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4562 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4563 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4564 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4565 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4566 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4567 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4568 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4569 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4570 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4571 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4572 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4573 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4574 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4575 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4576 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4577 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4578 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4579 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4580 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4581 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4582 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4583 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4584 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4585 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4586 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4587 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4588 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4589 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4590 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4591 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4592 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4593 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4594 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4595 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4596 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4597 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4598 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4599 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4600 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4601 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4602 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4603 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4604 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4605 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4606 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4607 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4608 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4609 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4610 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4611 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4612 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4613 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4614 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4615 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4616 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4617 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4618 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4619 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4620 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4621 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4622 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4623 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4624 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4625 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4626 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4627 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4628 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4629 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4630 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4631 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4632 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4633 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4634 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4635 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4636 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4637 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4638 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4639 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4642 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4644 int min, max;
4646 min = 0;
4647 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4649 while (min <= max) {
4650 const struct geoinfo_t *ptr;
4651 int n = (min+max)/2;
4653 ptr = &geoinfodata[n];
4654 if (geoid == ptr->id)
4655 /* we don't need empty entries */
4656 return *ptr->iso2W ? ptr : NULL;
4658 if (ptr->id > geoid)
4659 max = n-1;
4660 else
4661 min = n+1;
4664 return NULL;
4667 /******************************************************************************
4668 * GetGeoInfoW (KERNEL32.@)
4670 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4672 const struct geoinfo_t *ptr;
4673 const WCHAR *str = NULL;
4674 WCHAR buffW[12];
4675 LONG val = 0;
4676 INT len;
4678 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4680 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4681 SetLastError(ERROR_INVALID_PARAMETER);
4682 return 0;
4685 switch (geotype) {
4686 case GEO_NATION:
4687 val = geoid;
4688 break;
4689 case GEO_ISO_UN_NUMBER:
4690 val = ptr->uncode;
4691 break;
4692 case GEO_PARENT:
4693 val = ptr->parent;
4694 break;
4695 case GEO_ISO2:
4696 case GEO_ISO3:
4698 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4699 break;
4701 case GEO_RFC1766:
4702 case GEO_LCID:
4703 case GEO_FRIENDLYNAME:
4704 case GEO_OFFICIALNAME:
4705 case GEO_TIMEZONES:
4706 case GEO_OFFICIALLANGUAGES:
4707 case GEO_LATITUDE:
4708 case GEO_LONGITUDE:
4709 FIXME("type %d is not supported\n", geotype);
4710 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4711 return 0;
4712 default:
4713 WARN("unrecognized type %d\n", geotype);
4714 SetLastError(ERROR_INVALID_FLAGS);
4715 return 0;
4718 if (val) {
4719 static const WCHAR fmtW[] = {'%','d',0};
4720 sprintfW(buffW, fmtW, val);
4721 str = buffW;
4724 len = strlenW(str) + 1;
4725 if (!data || !data_len)
4726 return len;
4728 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4729 if (data_len < len)
4730 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4731 return data_len < len ? 0 : len;
4734 /******************************************************************************
4735 * GetGeoInfoA (KERNEL32.@)
4737 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4739 WCHAR *buffW;
4740 INT len;
4742 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4744 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4745 if (!len)
4746 return 0;
4748 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4749 if (!buffW)
4750 return 0;
4752 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4753 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4754 if (!data || !data_len) {
4755 HeapFree(GetProcessHeap(), 0, buffW);
4756 return len;
4759 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4760 HeapFree(GetProcessHeap(), 0, buffW);
4762 if (data_len < len)
4763 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4764 return data_len < len ? 0 : len;
4767 /******************************************************************************
4768 * EnumSystemGeoID (KERNEL32.@)
4770 * Call a users function for every location available on the system.
4772 * PARAMS
4773 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4774 * parent [I] GEOID for the parent
4775 * enumproc [I] Callback function to call for each location
4777 * RETURNS
4778 * Success: TRUE.
4779 * Failure: FALSE. Use GetLastError() to determine the cause.
4781 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4783 INT i;
4785 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4787 if (!enumproc) {
4788 SetLastError(ERROR_INVALID_PARAMETER);
4789 return FALSE;
4792 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4793 SetLastError(ERROR_INVALID_FLAGS);
4794 return FALSE;
4797 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4798 const struct geoinfo_t *ptr = &geoinfodata[i];
4800 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4801 continue;
4803 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4804 continue;
4806 if (parent && ptr->parent != parent)
4807 continue;
4809 if (!enumproc(ptr->id))
4810 return TRUE;
4813 return TRUE;
4816 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4818 LCID userlcid;
4820 TRACE("%p, %d\n", localename, buffersize);
4822 userlcid = GetUserDefaultLCID();
4823 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4826 /******************************************************************************
4827 * NormalizeString (KERNEL32.@)
4829 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4830 LPWSTR lpDstString, INT cwDstLength)
4832 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4833 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4834 return 0;
4837 /******************************************************************************
4838 * IsNormalizedString (KERNEL32.@)
4840 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4842 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4843 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4844 return FALSE;
4847 enum {
4848 BASE = 36,
4849 TMIN = 1,
4850 TMAX = 26,
4851 SKEW = 38,
4852 DAMP = 700,
4853 INIT_BIAS = 72,
4854 INIT_N = 128
4857 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4859 INT k;
4861 delta /= (firsttime ? DAMP : 2);
4862 delta += delta/numpoints;
4864 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4865 delta /= BASE-TMIN;
4866 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4869 /******************************************************************************
4870 * IdnToAscii (KERNEL32.@)
4871 * Implementation of Punycode based on RFC 3492.
4873 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4874 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4876 static const WCHAR prefixW[] = {'x','n','-','-'};
4878 WCHAR *norm_str;
4879 INT i, label_start, label_end, norm_len, out_label, out = 0;
4881 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4882 lpASCIICharStr, cchASCIIChar);
4884 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4885 if(!norm_len)
4886 return 0;
4887 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4888 if(!norm_str) {
4889 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4890 return 0;
4892 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4893 cchUnicodeChar, norm_str, norm_len);
4894 if(!norm_len) {
4895 HeapFree(GetProcessHeap(), 0, norm_str);
4896 return 0;
4899 for(label_start=0; label_start<norm_len;) {
4900 INT n = INIT_N, bias = INIT_BIAS;
4901 INT delta = 0, b = 0, h;
4903 out_label = out;
4904 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4905 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4906 if(norm_str[i] < 0x80)
4907 b++;
4908 label_end = i;
4910 if(b == label_end-label_start) {
4911 if(label_end < norm_len)
4912 b++;
4913 if(!lpASCIICharStr) {
4914 out += b;
4915 }else if(out+b <= cchASCIIChar) {
4916 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4917 out += b;
4918 }else {
4919 HeapFree(GetProcessHeap(), 0, norm_str);
4920 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4921 return 0;
4923 label_start = label_end+1;
4924 continue;
4927 if(!lpASCIICharStr) {
4928 out += 5+b; /* strlen(xn--...-) */
4929 }else if(out+5+b <= cchASCIIChar) {
4930 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4931 out += 4;
4932 for(i=label_start; i<label_end; i++)
4933 if(norm_str[i] < 0x80)
4934 lpASCIICharStr[out++] = norm_str[i];
4935 lpASCIICharStr[out++] = '-';
4936 }else {
4937 HeapFree(GetProcessHeap(), 0, norm_str);
4938 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4939 return 0;
4941 if(!b)
4942 out--;
4944 for(h=b; h<label_end-label_start;) {
4945 INT m = 0xffff, q, k;
4947 for(i=label_start; i<label_end; i++) {
4948 if(norm_str[i]>=n && m>norm_str[i])
4949 m = norm_str[i];
4951 delta += (m-n)*(h+1);
4952 n = m;
4954 for(i=label_start; i<label_end; i++) {
4955 if(norm_str[i] < n) {
4956 delta++;
4957 }else if(norm_str[i] == n) {
4958 for(q=delta, k=BASE; ; k+=BASE) {
4959 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4960 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4961 if(!lpASCIICharStr) {
4962 out++;
4963 }else if(out+1 <= cchASCIIChar) {
4964 lpASCIICharStr[out++] = disp<='z'-'a' ?
4965 'a'+disp : '0'+disp-'z'+'a'-1;
4966 }else {
4967 HeapFree(GetProcessHeap(), 0, norm_str);
4968 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4969 return 0;
4971 if(q < t)
4972 break;
4973 q = (q-t)/(BASE-t);
4975 bias = adapt(delta, h+1, h==b);
4976 delta = 0;
4977 h++;
4980 delta++;
4981 n++;
4984 if(out-out_label > 63) {
4985 HeapFree(GetProcessHeap(), 0, norm_str);
4986 SetLastError(ERROR_INVALID_NAME);
4987 return 0;
4990 if(label_end < norm_len) {
4991 if(!lpASCIICharStr) {
4992 out++;
4993 }else if(out+1 <= cchASCIIChar) {
4994 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4995 }else {
4996 HeapFree(GetProcessHeap(), 0, norm_str);
4997 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4998 return 0;
5001 label_start = label_end+1;
5004 HeapFree(GetProcessHeap(), 0, norm_str);
5005 return out;
5008 /******************************************************************************
5009 * IdnToNameprepUnicode (KERNEL32.@)
5011 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5012 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5014 enum {
5015 UNASSIGNED = 0x1,
5016 PROHIBITED = 0x2,
5017 BIDI_RAL = 0x4,
5018 BIDI_L = 0x8
5021 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5022 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5023 const WCHAR *ptr;
5024 WORD flags;
5025 WCHAR buf[64], *map_str, norm_str[64], ch;
5026 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5027 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5029 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5030 lpNameprepCharStr, cchNameprepChar);
5032 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5033 SetLastError(ERROR_INVALID_FLAGS);
5034 return 0;
5037 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5038 SetLastError(ERROR_INVALID_PARAMETER);
5039 return 0;
5042 if(cchUnicodeChar == -1)
5043 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5044 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5045 SetLastError(ERROR_INVALID_NAME);
5046 return 0;
5049 for(label_start=0; label_start<cchUnicodeChar;) {
5050 ascii_only = TRUE;
5051 for(i=label_start; i<cchUnicodeChar; i++) {
5052 ch = lpUnicodeCharStr[i];
5054 if(i!=cchUnicodeChar-1 && !ch) {
5055 SetLastError(ERROR_INVALID_NAME);
5056 return 0;
5058 /* check if ch is one of label separators defined in RFC3490 */
5059 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5060 break;
5062 if(ch > 0x7f) {
5063 ascii_only = FALSE;
5064 continue;
5067 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5068 continue;
5069 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5070 || (ch>='0' && ch<='9') || ch=='-')
5071 continue;
5073 SetLastError(ERROR_INVALID_NAME);
5074 return 0;
5076 label_end = i;
5077 /* last label may be empty */
5078 if(label_start==label_end && ch) {
5079 SetLastError(ERROR_INVALID_NAME);
5080 return 0;
5083 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5084 lpUnicodeCharStr[label_end-1]=='-')) {
5085 SetLastError(ERROR_INVALID_NAME);
5086 return 0;
5089 if(ascii_only) {
5090 /* maximal label length is 63 characters */
5091 if(label_end-label_start > 63) {
5092 SetLastError(ERROR_INVALID_NAME);
5093 return 0;
5095 if(label_end < cchUnicodeChar)
5096 label_end++;
5098 if(!lpNameprepCharStr) {
5099 out += label_end-label_start;
5100 }else if(out+label_end-label_start <= cchNameprepChar) {
5101 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5102 (label_end-label_start)*sizeof(WCHAR));
5103 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5104 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5105 out += label_end-label_start;
5106 }else {
5107 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5108 return 0;
5111 label_start = label_end;
5112 continue;
5115 map_len = 0;
5116 for(i=label_start; i<label_end; i++) {
5117 ch = lpUnicodeCharStr[i];
5118 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5119 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5121 if(!ptr[0]) map_len++;
5122 else if(!ptr[1]) map_len++;
5123 else if(!ptr[2]) map_len += 2;
5124 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5126 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5127 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5128 if(!map_str) {
5129 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5130 return 0;
5132 }else {
5133 map_str = buf;
5135 map_len = 0;
5136 for(i=label_start; i<label_end; i++) {
5137 ch = lpUnicodeCharStr[i];
5138 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5139 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5141 if(!ptr[0]) {
5142 map_str[map_len++] = ch;
5143 }else if(!ptr[1]) {
5144 map_str[map_len++] = ptr[0];
5145 }else if(!ptr[2]) {
5146 map_str[map_len++] = ptr[0];
5147 map_str[map_len++] = ptr[1];
5148 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5149 map_str[map_len++] = ptr[0];
5150 map_str[map_len++] = ptr[1];
5151 map_str[map_len++] = ptr[2];
5155 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5156 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5157 if(map_str != buf)
5158 HeapFree(GetProcessHeap(), 0, map_str);
5159 if(!norm_len) {
5160 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5161 SetLastError(ERROR_INVALID_NAME);
5162 return 0;
5165 if(label_end < cchUnicodeChar) {
5166 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5167 label_end++;
5170 if(!lpNameprepCharStr) {
5171 out += norm_len;
5172 }else if(out+norm_len <= cchNameprepChar) {
5173 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5174 out += norm_len;
5175 }else {
5176 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5177 return 0;
5180 have_bidi_ral = prohibit_bidi_ral = FALSE;
5181 mask = PROHIBITED;
5182 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5183 mask |= UNASSIGNED;
5184 for(i=0; i<norm_len; i++) {
5185 ch = norm_str[i];
5186 flags = get_table_entry( nameprep_char_type, ch );
5188 if(flags & mask) {
5189 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5190 : ERROR_NO_UNICODE_TRANSLATION);
5191 return 0;
5194 if(flags & BIDI_RAL)
5195 have_bidi_ral = TRUE;
5196 if(flags & BIDI_L)
5197 prohibit_bidi_ral = TRUE;
5200 if(have_bidi_ral) {
5201 ch = norm_str[0];
5202 flags = get_table_entry( nameprep_char_type, ch );
5203 if((flags & BIDI_RAL) == 0)
5204 prohibit_bidi_ral = TRUE;
5206 ch = norm_str[norm_len-1];
5207 flags = get_table_entry( nameprep_char_type, ch );
5208 if((flags & BIDI_RAL) == 0)
5209 prohibit_bidi_ral = TRUE;
5212 if(have_bidi_ral && prohibit_bidi_ral) {
5213 SetLastError(ERROR_INVALID_NAME);
5214 return 0;
5217 label_start = label_end;
5220 return out;
5223 /******************************************************************************
5224 * IdnToUnicode (KERNEL32.@)
5226 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5227 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5229 extern const unsigned short nameprep_char_type[];
5231 INT i, label_start, label_end, out_label, out = 0;
5232 WCHAR ch;
5234 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5235 lpUnicodeCharStr, cchUnicodeChar);
5237 for(label_start=0; label_start<cchASCIIChar;) {
5238 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5240 out_label = out;
5241 for(i=label_start; i<cchASCIIChar; i++) {
5242 ch = lpASCIICharStr[i];
5244 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5245 SetLastError(ERROR_INVALID_NAME);
5246 return 0;
5249 if(!ch || ch=='.')
5250 break;
5251 if(ch == '-')
5252 delim = i;
5254 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5255 continue;
5256 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5257 || (ch>='0' && ch<='9') || ch=='-')
5258 continue;
5260 SetLastError(ERROR_INVALID_NAME);
5261 return 0;
5263 label_end = i;
5264 /* last label may be empty */
5265 if(label_start==label_end && ch) {
5266 SetLastError(ERROR_INVALID_NAME);
5267 return 0;
5270 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5271 lpASCIICharStr[label_end-1]=='-')) {
5272 SetLastError(ERROR_INVALID_NAME);
5273 return 0;
5275 if(label_end-label_start > 63) {
5276 SetLastError(ERROR_INVALID_NAME);
5277 return 0;
5280 if(label_end-label_start<4 ||
5281 tolowerW(lpASCIICharStr[label_start])!='x' ||
5282 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5283 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5284 if(label_end < cchASCIIChar)
5285 label_end++;
5287 if(!lpUnicodeCharStr) {
5288 out += label_end-label_start;
5289 }else if(out+label_end-label_start <= cchUnicodeChar) {
5290 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5291 (label_end-label_start)*sizeof(WCHAR));
5292 out += label_end-label_start;
5293 }else {
5294 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5295 return 0;
5298 label_start = label_end;
5299 continue;
5302 if(delim == label_start+3)
5303 delim++;
5304 if(!lpUnicodeCharStr) {
5305 out += delim-label_start-4;
5306 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5307 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5308 (delim-label_start-4)*sizeof(WCHAR));
5309 out += delim-label_start-4;
5310 }else {
5311 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5312 return 0;
5314 if(out != out_label)
5315 delim++;
5317 for(i=delim; i<label_end;) {
5318 old_pos = pos;
5319 w = 1;
5320 for(k=BASE; ; k+=BASE) {
5321 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5322 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5323 SetLastError(ERROR_INVALID_NAME);
5324 return 0;
5326 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5327 pos += digit*w;
5328 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5329 if(digit < t)
5330 break;
5331 w *= BASE-t;
5333 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5334 n += pos/(out-out_label+1);
5335 pos %= out-out_label+1;
5337 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5338 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5339 SetLastError(ERROR_INVALID_NAME);
5340 return 0;
5342 if(!lpUnicodeCharStr) {
5343 out++;
5344 }else if(out+1 <= cchASCIIChar) {
5345 memmove(lpUnicodeCharStr+out_label+pos+1,
5346 lpUnicodeCharStr+out_label+pos,
5347 (out-out_label-pos)*sizeof(WCHAR));
5348 lpUnicodeCharStr[out_label+pos] = n;
5349 out++;
5350 }else {
5351 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5352 return 0;
5354 pos++;
5357 if(out-out_label > 63) {
5358 SetLastError(ERROR_INVALID_NAME);
5359 return 0;
5362 if(label_end < cchASCIIChar) {
5363 if(!lpUnicodeCharStr) {
5364 out++;
5365 }else if(out+1 <= cchUnicodeChar) {
5366 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5367 }else {
5368 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5369 return 0;
5372 label_start = label_end+1;
5375 return out;
5379 /******************************************************************************
5380 * GetUserPreferredUILanguages (KERNEL32.@)
5382 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5384 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5385 return FALSE;
5388 /******************************************************************************
5389 * GetFileMUIPath (KERNEL32.@)
5392 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5393 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5395 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5396 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5398 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5400 return FALSE;
5403 /******************************************************************************
5404 * GetFileMUIInfo (KERNEL32.@)
5407 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5409 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5411 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5412 return FALSE;