TESTING -- override pthreads to fix gstreamer v5
[wine/multimedia.git] / dlls / kernel32 / locale.c
blob3b632b4a8442e7854974636d911b1bc199fbf510
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 * GetUserDefaultUILanguage (KERNEL32.@)
1054 * Get the default user interface language Id for the current user.
1056 * PARAMS
1057 * None.
1059 * RETURNS
1060 * The current LANGID of the default UI language for the current user.
1062 LANGID WINAPI GetUserDefaultUILanguage(void)
1064 LANGID lang;
1065 NtQueryDefaultUILanguage( &lang );
1066 return lang;
1070 /***********************************************************************
1071 * GetSystemDefaultUILanguage (KERNEL32.@)
1073 * Get the default user interface language Id for the system.
1075 * PARAMS
1076 * None.
1078 * RETURNS
1079 * The current LANGID of the default UI language for the system. This is
1080 * typically the same language used during the installation process.
1082 LANGID WINAPI GetSystemDefaultUILanguage(void)
1084 LANGID lang;
1085 NtQueryInstallUILanguage( &lang );
1086 return lang;
1090 /***********************************************************************
1091 * LocaleNameToLCID (KERNEL32.@)
1093 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1095 struct locale_name locale_name;
1097 if (flags) FIXME( "unsupported flags %x\n", flags );
1099 if (name == LOCALE_NAME_USER_DEFAULT)
1100 return GetUserDefaultLCID();
1102 /* string parsing */
1103 parse_locale_name( name, &locale_name );
1105 TRACE( "found lcid %x for %s, matches %d\n",
1106 locale_name.lcid, debugstr_w(name), locale_name.matches );
1108 if (!locale_name.matches)
1110 SetLastError(ERROR_INVALID_PARAMETER);
1111 return 0;
1114 if (locale_name.matches == 1)
1115 WARN( "locale %s not recognized, defaulting to %s\n",
1116 debugstr_w(name), debugstr_w(locale_name.lang) );
1118 return locale_name.lcid;
1122 /***********************************************************************
1123 * LCIDToLocaleName (KERNEL32.@)
1125 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1127 if (flags) FIXME( "unsupported flags %x\n", flags );
1129 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1133 /******************************************************************************
1134 * get_locale_registry_value
1136 * Gets the registry value name and cache for a given lctype.
1138 static struct registry_value *get_locale_registry_value( DWORD lctype )
1140 int i;
1141 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1142 if (registry_values[i].lctype == lctype)
1143 return &registry_values[i];
1144 return NULL;
1148 /******************************************************************************
1149 * get_registry_locale_info
1151 * Retrieve user-modified locale info from the registry.
1152 * Return length, 0 on error, -1 if not found.
1154 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1156 DWORD size;
1157 INT ret;
1158 HANDLE hkey;
1159 NTSTATUS status;
1160 UNICODE_STRING nameW;
1161 KEY_VALUE_PARTIAL_INFORMATION *info;
1162 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1164 RtlEnterCriticalSection( &cache_section );
1166 if (!registry_value->cached_value)
1168 if (!(hkey = create_registry_key()))
1170 RtlLeaveCriticalSection( &cache_section );
1171 return -1;
1174 RtlInitUnicodeString( &nameW, registry_value->name );
1175 size = info_size + len * sizeof(WCHAR);
1177 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1179 NtClose( hkey );
1180 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1181 RtlLeaveCriticalSection( &cache_section );
1182 return 0;
1185 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1187 /* try again with a bigger buffer when we have to return the correct size */
1188 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1190 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1191 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1193 info = new_info;
1194 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1198 NtClose( hkey );
1200 if (!status)
1202 INT length = (size - info_size) / sizeof(WCHAR);
1203 LPWSTR cached_value;
1205 if (!length || ((WCHAR *)&info->Data)[length-1])
1206 length++;
1208 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1210 if (!cached_value)
1212 HeapFree( GetProcessHeap(), 0, info );
1213 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1214 RtlLeaveCriticalSection( &cache_section );
1215 return 0;
1218 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1219 cached_value[length-1] = 0;
1220 HeapFree( GetProcessHeap(), 0, info );
1221 registry_value->cached_value = cached_value;
1223 else
1225 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1227 ret = (size - info_size) / sizeof(WCHAR);
1229 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1231 ret = -1;
1233 else
1235 SetLastError( RtlNtStatusToDosError(status) );
1236 ret = 0;
1238 HeapFree( GetProcessHeap(), 0, info );
1239 RtlLeaveCriticalSection( &cache_section );
1240 return ret;
1244 ret = lstrlenW( registry_value->cached_value ) + 1;
1246 if (buffer)
1248 if (ret > len)
1250 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1251 ret = 0;
1253 else
1255 lstrcpyW( buffer, registry_value->cached_value );
1259 RtlLeaveCriticalSection( &cache_section );
1261 return ret;
1265 /******************************************************************************
1266 * GetLocaleInfoA (KERNEL32.@)
1268 * Get information about an aspect of a locale.
1270 * PARAMS
1271 * lcid [I] LCID of the locale
1272 * lctype [I] LCTYPE_ flags from "winnls.h"
1273 * buffer [O] Destination for the information
1274 * len [I] Length of buffer in characters
1276 * RETURNS
1277 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1278 * with the information.
1279 * Failure: 0. Use GetLastError() to determine the cause.
1281 * NOTES
1282 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1283 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1284 * which is a bit string.
1286 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1288 WCHAR *bufferW;
1289 INT lenW, ret;
1291 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1293 if (len < 0 || (len && !buffer))
1295 SetLastError( ERROR_INVALID_PARAMETER );
1296 return 0;
1298 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1299 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1301 SetLastError( ERROR_INVALID_FLAGS );
1302 return 0;
1305 if (!len) buffer = NULL;
1307 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1309 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1311 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1312 return 0;
1314 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1316 if ((lctype & LOCALE_RETURN_NUMBER) ||
1317 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1319 /* it's not an ASCII string, just bytes */
1320 ret *= sizeof(WCHAR);
1321 if (buffer)
1323 if (ret <= len) memcpy( buffer, bufferW, ret );
1324 else
1326 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1327 ret = 0;
1331 else
1333 UINT codepage = CP_ACP;
1334 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1335 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1338 HeapFree( GetProcessHeap(), 0, bufferW );
1339 return ret;
1342 static int get_value_base_by_lctype( LCTYPE lctype )
1344 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1347 /******************************************************************************
1348 * GetLocaleInfoW (KERNEL32.@)
1350 * See GetLocaleInfoA.
1352 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1354 LANGID lang_id;
1355 HRSRC hrsrc;
1356 HGLOBAL hmem;
1357 INT ret;
1358 UINT lcflags;
1359 const WCHAR *p;
1360 unsigned int i;
1362 if (len < 0 || (len && !buffer))
1364 SetLastError( ERROR_INVALID_PARAMETER );
1365 return 0;
1367 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1368 !is_genitive_name_supported( lctype ))
1370 SetLastError( ERROR_INVALID_FLAGS );
1371 return 0;
1374 if (!len) buffer = NULL;
1376 lcid = convert_default_lcid( lcid, lctype );
1378 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1379 lctype &= 0xffff;
1381 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1383 /* first check for overrides in the registry */
1385 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1386 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1388 struct registry_value *value = get_locale_registry_value(lctype);
1390 if (value)
1392 if (lcflags & LOCALE_RETURN_NUMBER)
1394 WCHAR tmp[16];
1395 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1396 if (ret > 0)
1398 WCHAR *end;
1399 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1400 if (*end) /* invalid number */
1402 SetLastError( ERROR_INVALID_FLAGS );
1403 return 0;
1405 ret = sizeof(UINT)/sizeof(WCHAR);
1406 if (!buffer) return ret;
1407 if (ret > len)
1409 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1410 return 0;
1412 memcpy( buffer, &number, sizeof(number) );
1415 else ret = get_registry_locale_info( value, buffer, len );
1417 if (ret != -1) return ret;
1421 /* now load it from kernel resources */
1423 lang_id = LANGIDFROMLCID( lcid );
1425 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1426 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1427 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1429 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1430 ULongToPtr((lctype >> 4) + 1), lang_id )))
1432 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1433 return 0;
1435 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1436 return 0;
1438 p = LockResource( hmem );
1439 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1441 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1442 else if (is_genitive_name_supported( lctype ) && *p)
1444 /* genitive form's stored after a null separator from a nominative */
1445 for (i = 1; i <= *p; i++) if (!p[i]) break;
1447 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1449 ret = *p - i + 1;
1450 p += i;
1452 else ret = i;
1454 else
1455 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1457 if (!buffer) return ret;
1459 if (ret > len)
1461 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1462 return 0;
1465 if (lcflags & LOCALE_RETURN_NUMBER)
1467 UINT number;
1468 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1469 if (!tmp) return 0;
1470 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1471 tmp[*p] = 0;
1472 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1473 if (!*end)
1474 memcpy( buffer, &number, sizeof(number) );
1475 else /* invalid number */
1477 SetLastError( ERROR_INVALID_FLAGS );
1478 ret = 0;
1480 HeapFree( GetProcessHeap(), 0, tmp );
1482 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1483 lcid, lctype, buffer, len, number );
1485 else
1487 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1488 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1490 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1491 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1493 return ret;
1496 /******************************************************************************
1497 * GetLocaleInfoEx (KERNEL32.@)
1499 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1501 LCID lcid = LocaleNameToLCID(locale, 0);
1503 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1505 if (!lcid) return 0;
1507 /* special handling for neutral locale names */
1508 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1510 if (len && len < 3)
1512 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1513 return 0;
1516 if (len) strcpyW(buffer, locale);
1517 return 3;
1520 return GetLocaleInfoW(lcid, info, buffer, len);
1523 /******************************************************************************
1524 * SetLocaleInfoA [KERNEL32.@]
1526 * Set information about an aspect of a locale.
1528 * PARAMS
1529 * lcid [I] LCID of the locale
1530 * lctype [I] LCTYPE_ flags from "winnls.h"
1531 * data [I] Information to set
1533 * RETURNS
1534 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1535 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1536 * Failure: FALSE. Use GetLastError() to determine the cause.
1538 * NOTES
1539 * - Values are only be set for the current user locale; the system locale
1540 * settings cannot be changed.
1541 * - Any settings changed by this call are lost when the locale is changed by
1542 * the control panel (in Wine, this happens every time you change LANG).
1543 * - The native implementation of this function does not check that lcid matches
1544 * the current user locale, and simply sets the new values. Wine warns you in
1545 * this case, but behaves the same.
1547 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1549 UINT codepage = CP_ACP;
1550 WCHAR *strW;
1551 DWORD len;
1552 BOOL ret;
1554 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1556 if (!data)
1558 SetLastError( ERROR_INVALID_PARAMETER );
1559 return FALSE;
1561 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1562 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1564 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1565 return FALSE;
1567 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1568 ret = SetLocaleInfoW( lcid, lctype, strW );
1569 HeapFree( GetProcessHeap(), 0, strW );
1570 return ret;
1574 /******************************************************************************
1575 * SetLocaleInfoW (KERNEL32.@)
1577 * See SetLocaleInfoA.
1579 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1581 struct registry_value *value;
1582 static const WCHAR intlW[] = {'i','n','t','l',0 };
1583 UNICODE_STRING valueW;
1584 NTSTATUS status;
1585 HANDLE hkey;
1587 lctype &= 0xffff;
1588 value = get_locale_registry_value( lctype );
1590 if (!data || !value)
1592 SetLastError( ERROR_INVALID_PARAMETER );
1593 return FALSE;
1596 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1598 SetLastError( ERROR_INVALID_FLAGS );
1599 return FALSE;
1602 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1604 /* FIXME: should check that data to set is sane */
1606 /* FIXME: profile functions should map to registry */
1607 WriteProfileStringW( intlW, value->name, data );
1609 if (!(hkey = create_registry_key())) return FALSE;
1610 RtlInitUnicodeString( &valueW, value->name );
1611 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1613 RtlEnterCriticalSection( &cache_section );
1614 HeapFree( GetProcessHeap(), 0, value->cached_value );
1615 value->cached_value = NULL;
1616 RtlLeaveCriticalSection( &cache_section );
1618 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1620 /* Set I-value from S value */
1621 WCHAR *lpD, *lpM, *lpY;
1622 WCHAR szBuff[2];
1624 lpD = strrchrW(data, 'd');
1625 lpM = strrchrW(data, 'M');
1626 lpY = strrchrW(data, 'y');
1628 if (lpD <= lpM)
1630 szBuff[0] = '1'; /* D-M-Y */
1632 else
1634 if (lpY <= lpM)
1635 szBuff[0] = '2'; /* Y-M-D */
1636 else
1637 szBuff[0] = '0'; /* M-D-Y */
1640 szBuff[1] = '\0';
1642 if (lctype == LOCALE_SSHORTDATE)
1643 lctype = LOCALE_IDATE;
1644 else
1645 lctype = LOCALE_ILDATE;
1647 value = get_locale_registry_value( lctype );
1649 WriteProfileStringW( intlW, value->name, szBuff );
1651 RtlInitUnicodeString( &valueW, value->name );
1652 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1654 RtlEnterCriticalSection( &cache_section );
1655 HeapFree( GetProcessHeap(), 0, value->cached_value );
1656 value->cached_value = NULL;
1657 RtlLeaveCriticalSection( &cache_section );
1660 NtClose( hkey );
1662 if (status) SetLastError( RtlNtStatusToDosError(status) );
1663 return !status;
1667 /******************************************************************************
1668 * GetACP (KERNEL32.@)
1670 * Get the current Ansi code page Id for the system.
1672 * PARAMS
1673 * None.
1675 * RETURNS
1676 * The current Ansi code page identifier for the system.
1678 UINT WINAPI GetACP(void)
1680 assert( ansi_cptable );
1681 return ansi_cptable->info.codepage;
1685 /******************************************************************************
1686 * SetCPGlobal (KERNEL32.@)
1688 * Set the current Ansi code page Id for the system.
1690 * PARAMS
1691 * acp [I] code page ID to be the new ACP.
1693 * RETURNS
1694 * The previous ACP.
1696 UINT WINAPI SetCPGlobal( UINT acp )
1698 UINT ret = GetACP();
1699 const union cptable *new_cptable = wine_cp_get_table( acp );
1701 if (new_cptable) ansi_cptable = new_cptable;
1702 return ret;
1706 /***********************************************************************
1707 * GetOEMCP (KERNEL32.@)
1709 * Get the current OEM code page Id for the system.
1711 * PARAMS
1712 * None.
1714 * RETURNS
1715 * The current OEM code page identifier for the system.
1717 UINT WINAPI GetOEMCP(void)
1719 assert( oem_cptable );
1720 return oem_cptable->info.codepage;
1724 /***********************************************************************
1725 * IsValidCodePage (KERNEL32.@)
1727 * Determine if a given code page identifier is valid.
1729 * PARAMS
1730 * codepage [I] Code page Id to verify.
1732 * RETURNS
1733 * TRUE, If codepage is valid and available on the system,
1734 * FALSE otherwise.
1736 BOOL WINAPI IsValidCodePage( UINT codepage )
1738 switch(codepage) {
1739 case CP_UTF7:
1740 case CP_UTF8:
1741 return TRUE;
1742 default:
1743 return wine_cp_get_table( codepage ) != NULL;
1748 /***********************************************************************
1749 * IsDBCSLeadByteEx (KERNEL32.@)
1751 * Determine if a character is a lead byte in a given code page.
1753 * PARAMS
1754 * codepage [I] Code page for the test.
1755 * testchar [I] Character to test
1757 * RETURNS
1758 * TRUE, if testchar is a lead byte in codepage,
1759 * FALSE otherwise.
1761 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1763 const union cptable *table = get_codepage_table( codepage );
1764 return table && wine_is_dbcs_leadbyte( table, testchar );
1768 /***********************************************************************
1769 * IsDBCSLeadByte (KERNEL32.@)
1770 * IsDBCSLeadByte (KERNEL.207)
1772 * Determine if a character is a lead byte.
1774 * PARAMS
1775 * testchar [I] Character to test
1777 * RETURNS
1778 * TRUE, if testchar is a lead byte in the ANSI code page,
1779 * FALSE otherwise.
1781 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1783 if (!ansi_cptable) return FALSE;
1784 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1788 /***********************************************************************
1789 * GetCPInfo (KERNEL32.@)
1791 * Get information about a code page.
1793 * PARAMS
1794 * codepage [I] Code page number
1795 * cpinfo [O] Destination for code page information
1797 * RETURNS
1798 * Success: TRUE. cpinfo is updated with the information about codepage.
1799 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1801 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1803 const union cptable *table;
1805 if (!cpinfo)
1807 SetLastError( ERROR_INVALID_PARAMETER );
1808 return FALSE;
1811 if (!(table = get_codepage_table( codepage )))
1813 switch(codepage)
1815 case CP_UTF7:
1816 case CP_UTF8:
1817 cpinfo->DefaultChar[0] = 0x3f;
1818 cpinfo->DefaultChar[1] = 0;
1819 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1820 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1821 return TRUE;
1824 SetLastError( ERROR_INVALID_PARAMETER );
1825 return FALSE;
1827 if (table->info.def_char & 0xff00)
1829 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1830 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1832 else
1834 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1835 cpinfo->DefaultChar[1] = 0;
1837 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1838 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1839 else
1840 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1842 return TRUE;
1845 /***********************************************************************
1846 * GetCPInfoExA (KERNEL32.@)
1848 * Get extended information about a code page.
1850 * PARAMS
1851 * codepage [I] Code page number
1852 * dwFlags [I] Reserved, must to 0.
1853 * cpinfo [O] Destination for code page information
1855 * RETURNS
1856 * Success: TRUE. cpinfo is updated with the information about codepage.
1857 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1859 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1861 CPINFOEXW cpinfoW;
1863 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1864 return FALSE;
1866 /* the layout is the same except for CodePageName */
1867 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1868 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1869 return TRUE;
1872 /***********************************************************************
1873 * GetCPInfoExW (KERNEL32.@)
1875 * Unicode version of GetCPInfoExA.
1877 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1879 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1880 return FALSE;
1882 switch(codepage)
1884 case CP_UTF7:
1886 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1888 cpinfo->CodePage = CP_UTF7;
1889 cpinfo->UnicodeDefaultChar = 0x3f;
1890 strcpyW(cpinfo->CodePageName, utf7);
1891 break;
1894 case CP_UTF8:
1896 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1898 cpinfo->CodePage = CP_UTF8;
1899 cpinfo->UnicodeDefaultChar = 0x3f;
1900 strcpyW(cpinfo->CodePageName, utf8);
1901 break;
1904 default:
1906 const union cptable *table = get_codepage_table( codepage );
1908 cpinfo->CodePage = table->info.codepage;
1909 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1910 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1911 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1912 break;
1915 return TRUE;
1918 /***********************************************************************
1919 * EnumSystemCodePagesA (KERNEL32.@)
1921 * Call a user defined function for every code page installed on the system.
1923 * PARAMS
1924 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1925 * flags [I] Reserved, set to 0.
1927 * RETURNS
1928 * TRUE, If all code pages have been enumerated, or
1929 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1931 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1933 const union cptable *table;
1934 char buffer[10];
1935 int index = 0;
1937 for (;;)
1939 if (!(table = wine_cp_enum_table( index++ ))) break;
1940 sprintf( buffer, "%d", table->info.codepage );
1941 if (!lpfnCodePageEnum( buffer )) break;
1943 return TRUE;
1947 /***********************************************************************
1948 * EnumSystemCodePagesW (KERNEL32.@)
1950 * See EnumSystemCodePagesA.
1952 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1954 const union cptable *table;
1955 WCHAR buffer[10], *p;
1956 int page, index = 0;
1958 for (;;)
1960 if (!(table = wine_cp_enum_table( index++ ))) break;
1961 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1962 *--p = 0;
1963 page = table->info.codepage;
1966 *--p = '0' + (page % 10);
1967 page /= 10;
1968 } while( page );
1969 if (!lpfnCodePageEnum( p )) break;
1971 return TRUE;
1975 /***********************************************************************
1976 * utf7_write_w
1978 * Helper for utf7_mbstowcs
1980 * RETURNS
1981 * TRUE on success, FALSE on error
1983 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
1985 if (dstlen > 0)
1987 if (*index >= dstlen)
1988 return FALSE;
1990 dst[*index] = character;
1993 (*index)++;
1995 return TRUE;
1998 /***********************************************************************
1999 * utf7_mbstowcs
2001 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2003 * RETURNS
2004 * On success, the number of characters written
2005 * On dst buffer overflow, -1
2007 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2009 static const signed char base64_decoding_table[] =
2011 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2012 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2013 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2014 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2015 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2016 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2017 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2018 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2021 const char *source_end = src + srclen;
2022 int dest_index = 0;
2024 DWORD byte_pair = 0;
2025 short offset = 0;
2027 while (src < source_end)
2029 if (*src == '+')
2031 src++;
2032 if (src >= source_end)
2033 break;
2035 if (*src == '-')
2037 /* just a plus sign escaped as +- */
2038 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2039 return -1;
2040 src++;
2041 continue;
2046 signed char sextet = *src;
2047 if (sextet == '-')
2049 /* skip over the dash and end base64 decoding
2050 * the current, unfinished byte pair is discarded */
2051 src++;
2052 offset = 0;
2053 break;
2055 if (sextet < 0)
2057 /* the next character of src is < 0 and therefore not part of a base64 sequence
2058 * the current, unfinished byte pair is NOT discarded in this case
2059 * this is probably a bug in Windows */
2060 break;
2063 sextet = base64_decoding_table[sextet];
2064 if (sextet == -1)
2066 /* -1 means that the next character of src is not part of a base64 sequence
2067 * in other words, all sextets in this base64 sequence have been processed
2068 * the current, unfinished byte pair is discarded */
2069 offset = 0;
2070 break;
2073 byte_pair = (byte_pair << 6) | sextet;
2074 offset += 6;
2076 if (offset >= 16)
2078 /* this byte pair is done */
2079 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2080 return -1;
2081 offset -= 16;
2084 src++;
2086 while (src < source_end);
2088 else
2090 /* we have to convert to unsigned char in case *src < 0 */
2091 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2092 return -1;
2093 src++;
2097 return dest_index;
2100 /***********************************************************************
2101 * MultiByteToWideChar (KERNEL32.@)
2103 * Convert a multibyte character string into a Unicode string.
2105 * PARAMS
2106 * page [I] Codepage character set to convert from
2107 * flags [I] Character mapping flags
2108 * src [I] Source string buffer
2109 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2110 * dst [O] Destination buffer
2111 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2113 * RETURNS
2114 * Success: If dstlen > 0, the number of characters written to dst.
2115 * If dstlen == 0, the number of characters needed to perform the
2116 * conversion. In both cases the count includes the terminating NUL.
2117 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2118 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2119 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2120 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2121 * possible for src.
2123 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2124 LPWSTR dst, INT dstlen )
2126 const union cptable *table;
2127 int ret;
2129 if (!src || !srclen || (!dst && dstlen))
2131 SetLastError( ERROR_INVALID_PARAMETER );
2132 return 0;
2135 if (srclen < 0) srclen = strlen(src) + 1;
2137 switch(page)
2139 case CP_SYMBOL:
2140 if (flags)
2142 SetLastError( ERROR_INVALID_FLAGS );
2143 return 0;
2145 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2146 break;
2147 case CP_UTF7:
2148 if (flags)
2150 SetLastError( ERROR_INVALID_FLAGS );
2151 return 0;
2153 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2154 break;
2155 case CP_UNIXCP:
2156 if (unix_cptable)
2158 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2159 break;
2161 #ifdef __APPLE__
2162 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2163 #endif
2164 /* fall through */
2165 case CP_UTF8:
2166 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2167 break;
2168 default:
2169 if (!(table = get_codepage_table( page )))
2171 SetLastError( ERROR_INVALID_PARAMETER );
2172 return 0;
2174 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2175 break;
2178 if (ret < 0)
2180 switch(ret)
2182 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2183 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2185 ret = 0;
2187 TRACE("cp %d %s -> %s, ret = %d\n",
2188 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2189 return ret;
2193 /***********************************************************************
2194 * utf7_can_directly_encode
2196 * Helper for utf7_wcstombs
2198 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2200 static const BOOL directly_encodable_table[] =
2202 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2204 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2205 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2206 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2207 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2208 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2209 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2212 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2215 /***********************************************************************
2216 * utf7_write_c
2218 * Helper for utf7_wcstombs
2220 * RETURNS
2221 * TRUE on success, FALSE on error
2223 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2225 if (dstlen > 0)
2227 if (*index >= dstlen)
2228 return FALSE;
2230 dst[*index] = character;
2233 (*index)++;
2235 return TRUE;
2238 /***********************************************************************
2239 * utf7_wcstombs
2241 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2243 * RETURNS
2244 * On success, the number of characters written
2245 * On dst buffer overflow, -1
2247 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2249 static const char base64_encoding_table[] =
2250 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2252 const WCHAR *source_end = src + srclen;
2253 int dest_index = 0;
2255 while (src < source_end)
2257 if (*src == '+')
2259 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2260 return -1;
2261 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2262 return -1;
2263 src++;
2265 else if (utf7_can_directly_encode(*src))
2267 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2268 return -1;
2269 src++;
2271 else
2273 unsigned int offset = 0;
2274 DWORD byte_pair = 0;
2276 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2277 return -1;
2279 while (src < source_end && !utf7_can_directly_encode(*src))
2281 byte_pair = (byte_pair << 16) | *src;
2282 offset += 16;
2283 while (offset >= 6)
2285 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2286 return -1;
2287 offset -= 6;
2289 src++;
2292 if (offset)
2294 /* Windows won't create a padded base64 character if there's no room for the - sign
2295 * as well ; this is probably a bug in Windows */
2296 if (dstlen > 0 && dest_index + 1 >= dstlen)
2297 return -1;
2299 byte_pair <<= (6 - offset);
2300 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2301 return -1;
2304 /* Windows always explicitly terminates the base64 sequence
2305 even though RFC 2152 (page 3, rule 2) does not require this */
2306 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2307 return -1;
2311 return dest_index;
2314 /***********************************************************************
2315 * WideCharToMultiByte (KERNEL32.@)
2317 * Convert a Unicode character string into a multibyte string.
2319 * PARAMS
2320 * page [I] Code page character set to convert to
2321 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2322 * src [I] Source string buffer
2323 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2324 * dst [O] Destination buffer
2325 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2326 * defchar [I] Default character to use for conversion if no exact
2327 * conversion can be made
2328 * used [O] Set if default character was used in the conversion
2330 * RETURNS
2331 * Success: If dstlen > 0, the number of characters written to dst.
2332 * If dstlen == 0, number of characters needed to perform the
2333 * conversion. In both cases the count includes the terminating NUL.
2334 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2335 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2336 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2337 * parameter was given.
2339 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2340 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2342 const union cptable *table;
2343 int ret, used_tmp;
2345 if (!src || !srclen || (!dst && dstlen))
2347 SetLastError( ERROR_INVALID_PARAMETER );
2348 return 0;
2351 if (srclen < 0) srclen = strlenW(src) + 1;
2353 switch(page)
2355 case CP_SYMBOL:
2356 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2357 if (flags)
2359 SetLastError( ERROR_INVALID_FLAGS );
2360 return 0;
2362 if (defchar || used)
2364 SetLastError( ERROR_INVALID_PARAMETER );
2365 return 0;
2367 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2368 break;
2369 case CP_UTF7:
2370 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2371 if (defchar || used)
2373 SetLastError( ERROR_INVALID_PARAMETER );
2374 return 0;
2376 if (flags)
2378 SetLastError( ERROR_INVALID_FLAGS );
2379 return 0;
2381 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2382 break;
2383 case CP_UNIXCP:
2384 if (unix_cptable)
2386 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2387 defchar, used ? &used_tmp : NULL );
2388 break;
2390 /* fall through */
2391 case CP_UTF8:
2392 if (defchar || used)
2394 SetLastError( ERROR_INVALID_PARAMETER );
2395 return 0;
2397 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2398 break;
2399 default:
2400 if (!(table = get_codepage_table( page )))
2402 SetLastError( ERROR_INVALID_PARAMETER );
2403 return 0;
2405 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2406 defchar, used ? &used_tmp : NULL );
2407 if (used) *used = used_tmp;
2408 break;
2411 if (ret < 0)
2413 switch(ret)
2415 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2416 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2418 ret = 0;
2420 TRACE("cp %d %s -> %s, ret = %d\n",
2421 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2422 return ret;
2426 /***********************************************************************
2427 * GetThreadLocale (KERNEL32.@)
2429 * Get the current threads locale.
2431 * PARAMS
2432 * None.
2434 * RETURNS
2435 * The LCID currently associated with the calling thread.
2437 LCID WINAPI GetThreadLocale(void)
2439 LCID ret = NtCurrentTeb()->CurrentLocale;
2440 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2441 return ret;
2444 /**********************************************************************
2445 * SetThreadLocale (KERNEL32.@)
2447 * Set the current threads locale.
2449 * PARAMS
2450 * lcid [I] LCID of the locale to set
2452 * RETURNS
2453 * Success: TRUE. The threads locale is set to lcid.
2454 * Failure: FALSE. Use GetLastError() to determine the cause.
2456 BOOL WINAPI SetThreadLocale( LCID lcid )
2458 TRACE("(0x%04X)\n", lcid);
2460 lcid = ConvertDefaultLocale(lcid);
2462 if (lcid != GetThreadLocale())
2464 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2466 SetLastError(ERROR_INVALID_PARAMETER);
2467 return FALSE;
2470 NtCurrentTeb()->CurrentLocale = lcid;
2472 return TRUE;
2475 /**********************************************************************
2476 * SetThreadUILanguage (KERNEL32.@)
2478 * Set the current threads UI language.
2480 * PARAMS
2481 * langid [I] LANGID of the language to set, or 0 to use
2482 * the available language which is best supported
2483 * for console applications
2485 * RETURNS
2486 * Success: The return value is the same as the input value.
2487 * Failure: The return value differs from the input value.
2488 * Use GetLastError() to determine the cause.
2490 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2492 TRACE("(0x%04x) stub - returning success\n", langid);
2493 return langid;
2496 /******************************************************************************
2497 * ConvertDefaultLocale (KERNEL32.@)
2499 * Convert a default locale identifier into a real identifier.
2501 * PARAMS
2502 * lcid [I] LCID identifier of the locale to convert
2504 * RETURNS
2505 * lcid unchanged, if not a default locale or its sublanguage is
2506 * not SUBLANG_NEUTRAL.
2507 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2508 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2509 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2511 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2513 LANGID langid;
2515 switch (lcid)
2517 case LOCALE_INVARIANT:
2518 /* keep as-is */
2519 break;
2520 case LOCALE_SYSTEM_DEFAULT:
2521 lcid = GetSystemDefaultLCID();
2522 break;
2523 case LOCALE_USER_DEFAULT:
2524 case LOCALE_NEUTRAL:
2525 lcid = GetUserDefaultLCID();
2526 break;
2527 default:
2528 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2529 langid = LANGIDFROMLCID(lcid);
2530 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2532 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2533 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2536 return lcid;
2540 /******************************************************************************
2541 * IsValidLocale (KERNEL32.@)
2543 * Determine if a locale is valid.
2545 * PARAMS
2546 * lcid [I] LCID of the locale to check
2547 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2549 * RETURNS
2550 * TRUE, if lcid is valid,
2551 * FALSE, otherwise.
2553 * NOTES
2554 * Wine does not currently make the distinction between supported and installed. All
2555 * languages supported are installed by default.
2557 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2559 /* check if language is registered in the kernel32 resources */
2560 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2561 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2564 /******************************************************************************
2565 * IsValidLocaleName (KERNEL32.@)
2567 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2569 struct locale_name locale_name;
2571 /* string parsing */
2572 parse_locale_name( locale, &locale_name );
2574 TRACE( "found lcid %x for %s, matches %d\n",
2575 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2577 return locale_name.matches > 0;
2580 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2581 LPCSTR name, WORD LangID, LONG_PTR lParam )
2583 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2584 char buf[20];
2586 sprintf(buf, "%08x", (UINT)LangID);
2587 return lpfnLocaleEnum( buf );
2590 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2591 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2593 static const WCHAR formatW[] = {'%','0','8','x',0};
2594 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2595 WCHAR buf[20];
2596 sprintfW( buf, formatW, (UINT)LangID );
2597 return lpfnLocaleEnum( buf );
2600 /******************************************************************************
2601 * EnumSystemLocalesA (KERNEL32.@)
2603 * Call a users function for each locale available on the system.
2605 * PARAMS
2606 * lpfnLocaleEnum [I] Callback function to call for each locale
2607 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2609 * RETURNS
2610 * Success: TRUE.
2611 * Failure: FALSE. Use GetLastError() to determine the cause.
2613 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2615 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2616 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2617 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2618 (LONG_PTR)lpfnLocaleEnum);
2619 return TRUE;
2623 /******************************************************************************
2624 * EnumSystemLocalesW (KERNEL32.@)
2626 * See EnumSystemLocalesA.
2628 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2630 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2631 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2632 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2633 (LONG_PTR)lpfnLocaleEnum);
2634 return TRUE;
2638 struct enum_locale_ex_data
2640 LOCALE_ENUMPROCEX proc;
2641 DWORD flags;
2642 LPARAM lparam;
2645 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2646 LPCWSTR name, WORD lang, LONG_PTR lparam )
2648 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2649 WCHAR buffer[256];
2650 DWORD neutral;
2651 unsigned int flags;
2653 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2654 buffer, sizeof(buffer) / sizeof(WCHAR) );
2655 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2656 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2657 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2658 neutral = 0;
2659 flags = LOCALE_WINDOWS;
2660 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2661 if (data->flags && !(data->flags & flags)) return TRUE;
2662 return data->proc( buffer, flags, data->lparam );
2665 /******************************************************************************
2666 * EnumSystemLocalesEx (KERNEL32.@)
2668 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2670 struct enum_locale_ex_data data;
2672 if (reserved)
2674 SetLastError( ERROR_INVALID_PARAMETER );
2675 return FALSE;
2677 data.proc = proc;
2678 data.flags = flags;
2679 data.lparam = lparam;
2680 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2681 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2682 enum_locale_ex_proc, (LONG_PTR)&data );
2683 return TRUE;
2687 /***********************************************************************
2688 * VerLanguageNameA (KERNEL32.@)
2690 * Get the name of a language.
2692 * PARAMS
2693 * wLang [I] LANGID of the language
2694 * szLang [O] Destination for the language name
2696 * RETURNS
2697 * Success: The size of the language name. If szLang is non-NULL, it is filled
2698 * with the name.
2699 * Failure: 0. Use GetLastError() to determine the cause.
2702 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2704 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2708 /***********************************************************************
2709 * VerLanguageNameW (KERNEL32.@)
2711 * See VerLanguageNameA.
2713 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2715 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2719 /******************************************************************************
2720 * GetStringTypeW (KERNEL32.@)
2722 * See GetStringTypeA.
2724 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2726 static const unsigned char type2_map[16] =
2728 C2_NOTAPPLICABLE, /* unassigned */
2729 C2_LEFTTORIGHT, /* L */
2730 C2_RIGHTTOLEFT, /* R */
2731 C2_EUROPENUMBER, /* EN */
2732 C2_EUROPESEPARATOR, /* ES */
2733 C2_EUROPETERMINATOR, /* ET */
2734 C2_ARABICNUMBER, /* AN */
2735 C2_COMMONSEPARATOR, /* CS */
2736 C2_BLOCKSEPARATOR, /* B */
2737 C2_SEGMENTSEPARATOR, /* S */
2738 C2_WHITESPACE, /* WS */
2739 C2_OTHERNEUTRAL, /* ON */
2740 C2_RIGHTTOLEFT, /* AL */
2741 C2_NOTAPPLICABLE, /* NSM */
2742 C2_NOTAPPLICABLE, /* BN */
2743 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2746 if (!src)
2748 SetLastError( ERROR_INVALID_PARAMETER );
2749 return FALSE;
2752 if (count == -1) count = strlenW(src) + 1;
2753 switch(type)
2755 case CT_CTYPE1:
2756 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2757 break;
2758 case CT_CTYPE2:
2759 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2760 break;
2761 case CT_CTYPE3:
2763 WARN("CT_CTYPE3: semi-stub.\n");
2764 while (count--)
2766 int c = *src;
2767 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2769 type1 = get_char_typeW( *src++ ) & 0xfff;
2770 /* try to construct type3 from type1 */
2771 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2772 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2773 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2774 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2775 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2776 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2777 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2779 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
2780 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
2782 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2783 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2784 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2785 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2786 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2787 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2788 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2789 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2791 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2792 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2793 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2794 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2795 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2796 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2797 *chartype++ = type3;
2799 break;
2801 default:
2802 SetLastError( ERROR_INVALID_PARAMETER );
2803 return FALSE;
2805 return TRUE;
2809 /******************************************************************************
2810 * GetStringTypeExW (KERNEL32.@)
2812 * See GetStringTypeExA.
2814 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2816 /* locale is ignored for Unicode */
2817 return GetStringTypeW( type, src, count, chartype );
2821 /******************************************************************************
2822 * GetStringTypeA (KERNEL32.@)
2824 * Get characteristics of the characters making up a string.
2826 * PARAMS
2827 * locale [I] Locale Id for the string
2828 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2829 * src [I] String to analyse
2830 * count [I] Length of src in chars, or -1 if src is NUL terminated
2831 * chartype [O] Destination for the calculated characteristics
2833 * RETURNS
2834 * Success: TRUE. chartype is filled with the requested characteristics of each char
2835 * in src.
2836 * Failure: FALSE. Use GetLastError() to determine the cause.
2838 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2840 UINT cp;
2841 INT countW;
2842 LPWSTR srcW;
2843 BOOL ret = FALSE;
2845 if(count == -1) count = strlen(src) + 1;
2847 if (!(cp = get_lcid_codepage( locale )))
2849 FIXME("For locale %04x using current ANSI code page\n", locale);
2850 cp = GetACP();
2853 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2854 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2856 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2858 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2859 * string, with multibyte characters there maybe be more bytes in count
2860 * than character space in the buffer!
2862 ret = GetStringTypeW(type, srcW, countW, chartype);
2863 HeapFree(GetProcessHeap(), 0, srcW);
2865 return ret;
2868 /******************************************************************************
2869 * GetStringTypeExA (KERNEL32.@)
2871 * Get characteristics of the characters making up a string.
2873 * PARAMS
2874 * locale [I] Locale Id for the string
2875 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2876 * src [I] String to analyse
2877 * count [I] Length of src in chars, or -1 if src is NUL terminated
2878 * chartype [O] Destination for the calculated characteristics
2880 * RETURNS
2881 * Success: TRUE. chartype is filled with the requested characteristics of each char
2882 * in src.
2883 * Failure: FALSE. Use GetLastError() to determine the cause.
2885 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2887 return GetStringTypeA(locale, type, src, count, chartype);
2890 /*************************************************************************
2891 * LCMapStringEx (KERNEL32.@)
2893 * Map characters in a locale sensitive string.
2895 * PARAMS
2896 * name [I] Locale name for the conversion.
2897 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2898 * src [I] String to map
2899 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2900 * dst [O] Destination for mapped string
2901 * dstlen [I] Length of dst in characters
2902 * version [I] reserved, must be NULL
2903 * reserved [I] reserved, must be NULL
2904 * lparam [I] reserved, must be 0
2906 * RETURNS
2907 * Success: The length of the mapped string in dst, including the NUL terminator.
2908 * Failure: 0. Use GetLastError() to determine the cause.
2910 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2911 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2913 LPWSTR dst_ptr;
2915 if (version) FIXME("unsupported version structure %p\n", version);
2916 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2917 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2919 if (!src || !srclen || dstlen < 0)
2921 SetLastError(ERROR_INVALID_PARAMETER);
2922 return 0;
2925 /* mutually exclusive flags */
2926 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2927 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2928 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2929 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2931 SetLastError(ERROR_INVALID_FLAGS);
2932 return 0;
2935 if (!dstlen) dst = NULL;
2937 if (flags & LCMAP_SORTKEY)
2939 INT ret;
2940 if (src == dst)
2942 SetLastError(ERROR_INVALID_FLAGS);
2943 return 0;
2946 if (srclen < 0) srclen = strlenW(src);
2948 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2949 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2951 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2952 if (ret == 0)
2953 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2954 else
2955 ret++;
2956 return ret;
2959 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2960 if (flags & SORT_STRINGSORT)
2962 SetLastError(ERROR_INVALID_FLAGS);
2963 return 0;
2966 if (srclen < 0) srclen = strlenW(src) + 1;
2968 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2969 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2971 if (!dst) /* return required string length */
2973 INT len;
2975 for (len = 0; srclen; src++, srclen--)
2977 WCHAR wch = *src;
2978 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2979 * and skips white space and punctuation characters for
2980 * NORM_IGNORESYMBOLS.
2982 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2983 continue;
2984 len++;
2986 return len;
2989 if (flags & LCMAP_UPPERCASE)
2991 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2993 WCHAR wch = *src;
2994 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2995 continue;
2996 *dst_ptr++ = toupperW(wch);
2997 dstlen--;
3000 else if (flags & LCMAP_LOWERCASE)
3002 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3004 WCHAR wch = *src;
3005 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3006 continue;
3007 *dst_ptr++ = tolowerW(wch);
3008 dstlen--;
3011 else
3013 if (src == dst)
3015 SetLastError(ERROR_INVALID_FLAGS);
3016 return 0;
3018 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3020 WCHAR wch = *src;
3021 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3022 continue;
3023 *dst_ptr++ = wch;
3024 dstlen--;
3028 if (srclen)
3030 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3031 return 0;
3034 return dst_ptr - dst;
3037 /*************************************************************************
3038 * LCMapStringW (KERNEL32.@)
3040 * See LCMapStringA.
3042 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3043 LPWSTR dst, INT dstlen)
3045 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3046 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3048 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3051 /*************************************************************************
3052 * LCMapStringA (KERNEL32.@)
3054 * Map characters in a locale sensitive string.
3056 * PARAMS
3057 * lcid [I] LCID for the conversion.
3058 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3059 * src [I] String to map
3060 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3061 * dst [O] Destination for mapped string
3062 * dstlen [I] Length of dst in characters
3064 * RETURNS
3065 * Success: The length of the mapped string in dst, including the NUL terminator.
3066 * Failure: 0. Use GetLastError() to determine the cause.
3068 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3069 LPSTR dst, INT dstlen)
3071 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3072 LPWSTR srcW, dstW;
3073 INT ret = 0, srclenW, dstlenW;
3074 UINT locale_cp = CP_ACP;
3076 if (!src || !srclen || dstlen < 0)
3078 SetLastError(ERROR_INVALID_PARAMETER);
3079 return 0;
3082 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3084 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3085 if (srclenW)
3086 srcW = bufW;
3087 else
3089 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3090 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3091 if (!srcW)
3093 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3094 return 0;
3096 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3099 if (flags & LCMAP_SORTKEY)
3101 if (src == dst)
3103 SetLastError(ERROR_INVALID_FLAGS);
3104 goto map_string_exit;
3106 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3107 if (ret == 0)
3108 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3109 else
3110 ret++;
3111 goto map_string_exit;
3114 if (flags & SORT_STRINGSORT)
3116 SetLastError(ERROR_INVALID_FLAGS);
3117 goto map_string_exit;
3120 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3121 if (!dstlenW)
3122 goto map_string_exit;
3124 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3125 if (!dstW)
3127 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3128 goto map_string_exit;
3131 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3132 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3133 HeapFree(GetProcessHeap(), 0, dstW);
3135 map_string_exit:
3136 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3137 return ret;
3140 /*************************************************************************
3141 * FoldStringA (KERNEL32.@)
3143 * Map characters in a string.
3145 * PARAMS
3146 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3147 * src [I] String to map
3148 * srclen [I] Length of src, or -1 if src is NUL terminated
3149 * dst [O] Destination for mapped string
3150 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3152 * RETURNS
3153 * Success: The length of the string written to dst, including the terminating NUL. If
3154 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3155 * and dst may be NULL.
3156 * Failure: 0. Use GetLastError() to determine the cause.
3158 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3159 LPSTR dst, INT dstlen)
3161 INT ret = 0, srclenW = 0;
3162 WCHAR *srcW = NULL, *dstW = NULL;
3164 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3166 SetLastError(ERROR_INVALID_PARAMETER);
3167 return 0;
3170 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3171 src, srclen, NULL, 0);
3172 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3174 if (!srcW)
3176 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3177 goto FoldStringA_exit;
3180 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3181 src, srclen, srcW, srclenW);
3183 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3185 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3186 if (ret && dstlen)
3188 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3190 if (!dstW)
3192 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3193 goto FoldStringA_exit;
3196 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3197 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3199 ret = 0;
3200 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3204 HeapFree(GetProcessHeap(), 0, dstW);
3206 FoldStringA_exit:
3207 HeapFree(GetProcessHeap(), 0, srcW);
3208 return ret;
3211 /*************************************************************************
3212 * FoldStringW (KERNEL32.@)
3214 * See FoldStringA.
3216 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3217 LPWSTR dst, INT dstlen)
3219 int ret;
3221 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3223 case 0:
3224 if (dwFlags)
3225 break;
3226 /* Fall through for dwFlags == 0 */
3227 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3228 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3229 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3230 SetLastError(ERROR_INVALID_FLAGS);
3231 return 0;
3234 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3236 SetLastError(ERROR_INVALID_PARAMETER);
3237 return 0;
3240 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3241 if (!ret)
3242 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3243 return ret;
3246 /******************************************************************************
3247 * CompareStringW (KERNEL32.@)
3249 * See CompareStringA.
3251 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3252 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3254 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3257 /******************************************************************************
3258 * CompareStringEx (KERNEL32.@)
3260 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3261 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3263 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3264 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3265 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3266 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3267 INT ret;
3269 if (version) FIXME("unexpected version parameter\n");
3270 if (reserved) FIXME("unexpected reserved value\n");
3271 if (lParam) FIXME("unexpected lParam\n");
3273 if (!str1 || !str2)
3275 SetLastError(ERROR_INVALID_PARAMETER);
3276 return 0;
3279 if (flags & ~(supported_flags|semistub_flags))
3281 SetLastError(ERROR_INVALID_FLAGS);
3282 return 0;
3285 if (flags & semistub_flags)
3286 FIXME("semi-stub behavor for flag(s) 0x%x\n", flags & semistub_flags);
3288 if (len1 < 0) len1 = strlenW(str1);
3289 if (len2 < 0) len2 = strlenW(str2);
3291 ret = wine_compare_string(flags, str1, len1, str2, len2);
3293 if (ret) /* need to translate result */
3294 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3295 return CSTR_EQUAL;
3298 /******************************************************************************
3299 * CompareStringA (KERNEL32.@)
3301 * Compare two locale sensitive strings.
3303 * PARAMS
3304 * lcid [I] LCID for the comparison
3305 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3306 * str1 [I] First string to compare
3307 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3308 * str2 [I] Second string to compare
3309 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3311 * RETURNS
3312 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3313 * str1 is less than, equal to or greater than str2 respectively.
3314 * Failure: FALSE. Use GetLastError() to determine the cause.
3316 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3317 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3319 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3320 WCHAR *buf2W = buf1W + 130;
3321 LPWSTR str1W, str2W;
3322 INT len1W = 0, len2W = 0, ret;
3323 UINT locale_cp = CP_ACP;
3325 if (!str1 || !str2)
3327 SetLastError(ERROR_INVALID_PARAMETER);
3328 return 0;
3330 if (len1 < 0) len1 = strlen(str1);
3331 if (len2 < 0) len2 = strlen(str2);
3333 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3335 if (len1)
3337 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3338 if (len1W)
3339 str1W = buf1W;
3340 else
3342 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3343 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3344 if (!str1W)
3346 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3347 return 0;
3349 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3352 else
3354 len1W = 0;
3355 str1W = buf1W;
3358 if (len2)
3360 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3361 if (len2W)
3362 str2W = buf2W;
3363 else
3365 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3366 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3367 if (!str2W)
3369 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3370 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3371 return 0;
3373 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3376 else
3378 len2W = 0;
3379 str2W = buf2W;
3382 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3384 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3385 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3386 return ret;
3389 /******************************************************************************
3390 * CompareStringOrdinal (KERNEL32.@)
3392 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3394 int ret, len;
3396 if (!str1 || !str2)
3398 SetLastError(ERROR_INVALID_PARAMETER);
3399 return 0;
3401 if (len1 < 0) len1 = strlenW(str1);
3402 if (len2 < 0) len2 = strlenW(str2);
3404 len = min(len1, len2);
3405 if (ignore_case)
3407 ret = memicmpW(str1, str2, len);
3409 else
3411 ret = 0;
3412 for (; len > 0; len--)
3413 if ((ret = (*str1++ - *str2++))) break;
3415 if (!ret) ret = len1 - len2;
3417 if (ret < 0) return CSTR_LESS_THAN;
3418 if (ret > 0) return CSTR_GREATER_THAN;
3419 return CSTR_EQUAL;
3422 /*************************************************************************
3423 * lstrcmp (KERNEL32.@)
3424 * lstrcmpA (KERNEL32.@)
3426 * Compare two strings using the current thread locale.
3428 * PARAMS
3429 * str1 [I] First string to compare
3430 * str2 [I] Second string to compare
3432 * RETURNS
3433 * Success: A number less than, equal to or greater than 0 depending on whether
3434 * str1 is less than, equal to or greater than str2 respectively.
3435 * Failure: FALSE. Use GetLastError() to determine the cause.
3437 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3439 int ret;
3441 if ((str1 == NULL) && (str2 == NULL)) return 0;
3442 if (str1 == NULL) return -1;
3443 if (str2 == NULL) return 1;
3445 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3446 if (ret) ret -= 2;
3448 return ret;
3451 /*************************************************************************
3452 * lstrcmpi (KERNEL32.@)
3453 * lstrcmpiA (KERNEL32.@)
3455 * Compare two strings using the current thread locale, ignoring case.
3457 * PARAMS
3458 * str1 [I] First string to compare
3459 * str2 [I] Second string to compare
3461 * RETURNS
3462 * Success: A number less than, equal to or greater than 0 depending on whether
3463 * str2 is less than, equal to or greater than str1 respectively.
3464 * Failure: FALSE. Use GetLastError() to determine the cause.
3466 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3468 int ret;
3470 if ((str1 == NULL) && (str2 == NULL)) return 0;
3471 if (str1 == NULL) return -1;
3472 if (str2 == NULL) return 1;
3474 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3475 if (ret) ret -= 2;
3477 return ret;
3480 /*************************************************************************
3481 * lstrcmpW (KERNEL32.@)
3483 * See lstrcmpA.
3485 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3487 int ret;
3489 if ((str1 == NULL) && (str2 == NULL)) return 0;
3490 if (str1 == NULL) return -1;
3491 if (str2 == NULL) return 1;
3493 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3494 if (ret) ret -= 2;
3496 return ret;
3499 /*************************************************************************
3500 * lstrcmpiW (KERNEL32.@)
3502 * See lstrcmpiA.
3504 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR 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 = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3513 if (ret) ret -= 2;
3515 return ret;
3518 /******************************************************************************
3519 * LOCALE_Init
3521 void LOCALE_Init(void)
3523 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3524 const union cptable *unix_cp );
3526 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3528 #ifdef __APPLE__
3529 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3530 char user_locale[50];
3532 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3533 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3534 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3535 CFStringRef user_locale_string_ref;
3537 if (user_locale_country_ref)
3539 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3540 user_locale_lang_ref, user_locale_country_ref);
3542 else
3544 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3547 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3548 strcat(user_locale, ".UTF-8");
3550 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3551 setenv( "LANG", user_locale, 0 );
3552 TRACE( "setting locale to '%s'\n", user_locale );
3553 #endif /* __APPLE__ */
3555 setlocale( LC_ALL, "" );
3557 unix_cp = setup_unix_locales();
3558 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3560 #ifdef __APPLE__
3561 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3562 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3564 /* Retrieve the preferred language as chosen in System Preferences. */
3565 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3566 leave things be. */
3567 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3568 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3569 CFStringRef user_language_string_ref;
3570 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3571 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3572 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3573 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3575 struct locale_name locale_name;
3576 WCHAR buffer[128];
3577 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3578 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3579 parse_locale_name( buffer, &locale_name );
3580 lcid_LC_MESSAGES = locale_name.lcid;
3581 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3583 if (preferred_langs)
3584 CFRelease( preferred_langs );
3585 if (canonical_lang_string_ref)
3586 CFRelease( canonical_lang_string_ref );
3589 CFRelease( user_locale_ref );
3590 CFRelease( user_locale_string_ref );
3591 #endif
3593 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3594 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3595 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3597 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3598 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3599 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3600 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3601 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3602 if (!unix_cp)
3603 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3604 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3606 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3607 ansi_cptable = wine_cp_get_table( 1252 );
3608 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3609 oem_cptable = wine_cp_get_table( 437 );
3610 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3611 mac_cptable = wine_cp_get_table( 10000 );
3612 if (unix_cp != CP_UTF8)
3614 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3615 unix_cptable = wine_cp_get_table( 28591 );
3618 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3620 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3621 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3622 mac_cptable->info.codepage, unix_cp );
3624 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3627 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3629 UNICODE_STRING keyName;
3630 OBJECT_ATTRIBUTES attr;
3631 HANDLE hkey;
3633 RtlInitUnicodeString( &keyName, szKeyName );
3634 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3636 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3637 hkey = 0;
3639 return hkey;
3642 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3643 LPWSTR szValueName, ULONG valueNameSize,
3644 LPWSTR szValueData, ULONG valueDataSize)
3646 BYTE buffer[80];
3647 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3648 DWORD dwLen;
3650 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3651 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3652 info->NameLength > valueNameSize ||
3653 info->DataLength > valueDataSize)
3655 return FALSE;
3658 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3660 memcpy( szValueName, info->Name, info->NameLength);
3661 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3662 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3663 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3665 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3666 return TRUE;
3669 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3671 BYTE buffer[128];
3672 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3673 DWORD dwSize = sizeof(buffer);
3674 UNICODE_STRING valueName;
3676 RtlInitUnicodeString( &valueName, szValueName );
3678 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3679 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3680 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3681 info->DataLength == sizeof(DWORD))
3683 memcpy(lpVal, info->Data, sizeof(DWORD));
3684 return TRUE;
3687 return FALSE;
3690 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3692 LANGID langId;
3693 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3694 HRSRC hResource;
3695 BOOL bRet = FALSE;
3697 /* FIXME: Is it correct to use the system default langid? */
3698 langId = GetSystemDefaultLangID();
3700 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3701 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3703 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3705 if (hResource)
3707 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3709 if (hResDir)
3711 ULONG iResourceIndex = lgrpid & 0xf;
3712 LPCWSTR lpResEntry = LockResource( hResDir );
3713 ULONG i;
3715 for (i = 0; i < iResourceIndex; i++)
3716 lpResEntry += *lpResEntry + 1;
3718 if (*lpResEntry < nameSize)
3720 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3721 szName[*lpResEntry] = '\0';
3722 bRet = TRUE;
3726 FreeResource( hResource );
3728 return bRet;
3731 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3732 typedef struct
3734 LANGUAGEGROUP_ENUMPROCA procA;
3735 LANGUAGEGROUP_ENUMPROCW procW;
3736 DWORD dwFlags;
3737 LONG_PTR lParam;
3738 } ENUMLANGUAGEGROUP_CALLBACKS;
3740 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3741 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3743 WCHAR szNumber[10], szValue[4];
3744 HANDLE hKey;
3745 BOOL bContinue = TRUE;
3746 ULONG ulIndex = 0;
3748 if (!lpProcs)
3750 SetLastError(ERROR_INVALID_PARAMETER);
3751 return FALSE;
3754 switch (lpProcs->dwFlags)
3756 case 0:
3757 /* Default to LGRPID_INSTALLED */
3758 lpProcs->dwFlags = LGRPID_INSTALLED;
3759 /* Fall through... */
3760 case LGRPID_INSTALLED:
3761 case LGRPID_SUPPORTED:
3762 break;
3763 default:
3764 SetLastError(ERROR_INVALID_FLAGS);
3765 return FALSE;
3768 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3770 if (!hKey)
3771 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3773 while (bContinue)
3775 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3776 szValue, sizeof(szValue) ))
3778 BOOL bInstalled = szValue[0] == '1';
3779 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3781 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3782 bInstalled ? "" : "not ");
3784 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3786 WCHAR szGrpName[48];
3788 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3789 szGrpName[0] = '\0';
3791 if (lpProcs->procW)
3792 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3793 lpProcs->lParam );
3794 else
3796 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3797 char szGrpNameA[48];
3799 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3800 * or whether the language names are ever localised. Assume CP_ACP.
3803 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3804 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3806 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3807 lpProcs->lParam );
3811 ulIndex++;
3813 else
3814 bContinue = FALSE;
3816 if (!bContinue)
3817 break;
3820 if (hKey)
3821 NtClose( hKey );
3823 return TRUE;
3826 /******************************************************************************
3827 * EnumSystemLanguageGroupsA (KERNEL32.@)
3829 * Call a users function for each language group available on the system.
3831 * PARAMS
3832 * pLangGrpEnumProc [I] Callback function to call for each language group
3833 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3834 * lParam [I] User parameter to pass to pLangGrpEnumProc
3836 * RETURNS
3837 * Success: TRUE.
3838 * Failure: FALSE. Use GetLastError() to determine the cause.
3840 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3841 DWORD dwFlags, LONG_PTR lParam)
3843 ENUMLANGUAGEGROUP_CALLBACKS procs;
3845 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3847 procs.procA = pLangGrpEnumProc;
3848 procs.procW = NULL;
3849 procs.dwFlags = dwFlags;
3850 procs.lParam = lParam;
3852 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3855 /******************************************************************************
3856 * EnumSystemLanguageGroupsW (KERNEL32.@)
3858 * See EnumSystemLanguageGroupsA.
3860 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3861 DWORD dwFlags, LONG_PTR lParam)
3863 ENUMLANGUAGEGROUP_CALLBACKS procs;
3865 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3867 procs.procA = NULL;
3868 procs.procW = pLangGrpEnumProc;
3869 procs.dwFlags = dwFlags;
3870 procs.lParam = lParam;
3872 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3875 /******************************************************************************
3876 * IsValidLanguageGroup (KERNEL32.@)
3878 * Determine if a language group is supported and/or installed.
3880 * PARAMS
3881 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3882 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3884 * RETURNS
3885 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3886 * FALSE otherwise.
3888 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3890 static const WCHAR szFormat[] = { '%','x','\0' };
3891 WCHAR szValueName[16], szValue[2];
3892 BOOL bSupported = FALSE, bInstalled = FALSE;
3893 HANDLE hKey;
3896 switch (dwFlags)
3898 case LGRPID_INSTALLED:
3899 case LGRPID_SUPPORTED:
3901 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3903 sprintfW( szValueName, szFormat, lgrpid );
3905 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3907 bSupported = TRUE;
3909 if (szValue[0] == '1')
3910 bInstalled = TRUE;
3913 if (hKey)
3914 NtClose( hKey );
3916 break;
3919 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3920 (dwFlags == LGRPID_INSTALLED && bInstalled))
3921 return TRUE;
3923 return FALSE;
3926 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3927 typedef struct
3929 LANGGROUPLOCALE_ENUMPROCA procA;
3930 LANGGROUPLOCALE_ENUMPROCW procW;
3931 DWORD dwFlags;
3932 LGRPID lgrpid;
3933 LONG_PTR lParam;
3934 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3936 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3937 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3939 static const WCHAR szAlternateSortsKeyName[] = {
3940 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3942 WCHAR szNumber[10], szValue[4];
3943 HANDLE hKey;
3944 BOOL bContinue = TRUE, bAlternate = FALSE;
3945 LGRPID lgrpid;
3946 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3948 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3950 SetLastError(ERROR_INVALID_PARAMETER);
3951 return FALSE;
3954 if (lpProcs->dwFlags)
3956 SetLastError(ERROR_INVALID_FLAGS);
3957 return FALSE;
3960 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3962 if (!hKey)
3963 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3965 while (bContinue)
3967 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3968 szValue, sizeof(szValue) ))
3970 lgrpid = strtoulW( szValue, NULL, 16 );
3972 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3973 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3975 if (lgrpid == lpProcs->lgrpid)
3977 LCID lcid;
3979 lcid = strtoulW( szNumber, NULL, 16 );
3981 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3982 * '00000437 ;Georgian'
3983 * At present we only pass the LCID string.
3986 if (lpProcs->procW)
3987 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3988 else
3990 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3992 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3994 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3998 ulIndex++;
4000 else
4002 /* Finished enumerating this key */
4003 if (!bAlternate)
4005 /* Enumerate alternate sorts also */
4006 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4007 bAlternate = TRUE;
4008 ulIndex = 0;
4010 else
4011 bContinue = FALSE; /* Finished both keys */
4014 if (!bContinue)
4015 break;
4018 if (hKey)
4019 NtClose( hKey );
4021 return TRUE;
4024 /******************************************************************************
4025 * EnumLanguageGroupLocalesA (KERNEL32.@)
4027 * Call a users function for every locale in a language group available on the system.
4029 * PARAMS
4030 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4031 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4032 * dwFlags [I] Reserved, set to 0
4033 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4035 * RETURNS
4036 * Success: TRUE.
4037 * Failure: FALSE. Use GetLastError() to determine the cause.
4039 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4040 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4042 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4044 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4046 callbacks.procA = pLangGrpLcEnumProc;
4047 callbacks.procW = NULL;
4048 callbacks.dwFlags = dwFlags;
4049 callbacks.lgrpid = lgrpid;
4050 callbacks.lParam = lParam;
4052 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4055 /******************************************************************************
4056 * EnumLanguageGroupLocalesW (KERNEL32.@)
4058 * See EnumLanguageGroupLocalesA.
4060 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4061 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4063 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4065 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4067 callbacks.procA = NULL;
4068 callbacks.procW = pLangGrpLcEnumProc;
4069 callbacks.dwFlags = dwFlags;
4070 callbacks.lgrpid = lgrpid;
4071 callbacks.lParam = lParam;
4073 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4076 /******************************************************************************
4077 * InvalidateNLSCache (KERNEL32.@)
4079 * Invalidate the cache of NLS values.
4081 * PARAMS
4082 * None.
4084 * RETURNS
4085 * Success: TRUE.
4086 * Failure: FALSE.
4088 BOOL WINAPI InvalidateNLSCache(void)
4090 FIXME("() stub\n");
4091 return FALSE;
4094 /******************************************************************************
4095 * GetUserGeoID (KERNEL32.@)
4097 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4099 GEOID ret = GEOID_NOT_AVAILABLE;
4100 static const WCHAR geoW[] = {'G','e','o',0};
4101 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4102 WCHAR bufferW[40], *end;
4103 DWORD count;
4104 HANDLE hkey, hSubkey = 0;
4105 UNICODE_STRING keyW;
4106 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4107 RtlInitUnicodeString( &keyW, nationW );
4108 count = sizeof(bufferW);
4110 if(!(hkey = create_registry_key())) return ret;
4112 switch( GeoClass ){
4113 case GEOCLASS_NATION:
4114 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4116 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4117 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4118 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4120 break;
4121 case GEOCLASS_REGION:
4122 FIXME("GEOCLASS_REGION not handled yet\n");
4123 break;
4126 NtClose(hkey);
4127 if (hSubkey) NtClose(hSubkey);
4128 return ret;
4131 /******************************************************************************
4132 * SetUserGeoID (KERNEL32.@)
4134 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4136 static const WCHAR geoW[] = {'G','e','o',0};
4137 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4138 static const WCHAR formatW[] = {'%','i',0};
4139 UNICODE_STRING nameW,keyW;
4140 WCHAR bufferW[10];
4141 OBJECT_ATTRIBUTES attr;
4142 HANDLE hkey;
4144 if(!(hkey = create_registry_key())) return FALSE;
4146 attr.Length = sizeof(attr);
4147 attr.RootDirectory = hkey;
4148 attr.ObjectName = &nameW;
4149 attr.Attributes = 0;
4150 attr.SecurityDescriptor = NULL;
4151 attr.SecurityQualityOfService = NULL;
4152 RtlInitUnicodeString( &nameW, geoW );
4153 RtlInitUnicodeString( &keyW, nationW );
4155 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4158 NtClose(attr.RootDirectory);
4159 return FALSE;
4162 sprintfW(bufferW, formatW, GeoID);
4163 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4164 NtClose(attr.RootDirectory);
4165 NtClose(hkey);
4166 return TRUE;
4169 typedef struct
4171 union
4173 UILANGUAGE_ENUMPROCA procA;
4174 UILANGUAGE_ENUMPROCW procW;
4175 } u;
4176 DWORD flags;
4177 LONG_PTR param;
4178 } ENUM_UILANG_CALLBACK;
4180 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4181 LPCSTR name, WORD LangID, LONG_PTR lParam )
4183 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4184 char buf[20];
4186 sprintf(buf, "%08x", (UINT)LangID);
4187 return enum_uilang->u.procA( buf, enum_uilang->param );
4190 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4191 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4193 static const WCHAR formatW[] = {'%','0','8','x',0};
4194 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4195 WCHAR buf[20];
4197 sprintfW( buf, formatW, (UINT)LangID );
4198 return enum_uilang->u.procW( buf, enum_uilang->param );
4201 /******************************************************************************
4202 * EnumUILanguagesA (KERNEL32.@)
4204 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4206 ENUM_UILANG_CALLBACK enum_uilang;
4208 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4210 if(!pUILangEnumProc) {
4211 SetLastError(ERROR_INVALID_PARAMETER);
4212 return FALSE;
4214 if(dwFlags) {
4215 SetLastError(ERROR_INVALID_FLAGS);
4216 return FALSE;
4219 enum_uilang.u.procA = pUILangEnumProc;
4220 enum_uilang.flags = dwFlags;
4221 enum_uilang.param = lParam;
4223 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4224 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4225 (LONG_PTR)&enum_uilang);
4226 return TRUE;
4229 /******************************************************************************
4230 * EnumUILanguagesW (KERNEL32.@)
4232 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4234 ENUM_UILANG_CALLBACK enum_uilang;
4236 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4239 if(!pUILangEnumProc) {
4240 SetLastError(ERROR_INVALID_PARAMETER);
4241 return FALSE;
4243 if(dwFlags) {
4244 SetLastError(ERROR_INVALID_FLAGS);
4245 return FALSE;
4248 enum_uilang.u.procW = pUILangEnumProc;
4249 enum_uilang.flags = dwFlags;
4250 enum_uilang.param = lParam;
4252 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4253 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4254 (LONG_PTR)&enum_uilang);
4255 return TRUE;
4258 enum locationkind {
4259 LOCATION_NATION = 0,
4260 LOCATION_REGION,
4261 LOCATION_BOTH
4264 struct geoinfo_t {
4265 GEOID id;
4266 WCHAR iso2W[3];
4267 WCHAR iso3W[4];
4268 GEOID parent;
4269 INT uncode;
4270 enum locationkind kind;
4273 static const struct geoinfo_t geoinfodata[] = {
4274 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4275 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4276 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4277 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4278 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4279 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4280 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4281 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4282 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4283 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4284 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4285 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4286 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4287 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4288 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4289 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4290 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4291 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4292 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4293 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4294 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4295 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4296 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4297 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4298 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4299 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4300 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4301 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4302 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4303 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4304 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4305 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4306 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4307 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4308 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4309 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4310 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4311 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4312 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4313 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4314 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4315 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4316 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4317 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4318 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4319 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4320 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4321 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4322 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4323 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4324 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4325 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4326 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4327 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4328 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4329 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4330 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4331 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4332 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4333 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4334 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4335 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4336 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4337 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4338 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4339 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4340 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4341 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4342 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4343 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4344 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4345 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4346 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4347 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4348 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4349 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4350 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4351 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4352 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4353 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4354 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4355 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4356 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4357 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4358 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4359 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4360 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4361 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4362 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4363 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4364 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4365 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4366 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4367 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4368 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4369 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4370 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4371 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4372 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4373 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4374 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4375 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4376 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4377 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4378 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4379 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4380 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4381 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4382 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4383 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4384 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4385 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4386 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4387 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4388 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4389 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4390 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4391 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4392 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4393 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4394 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4395 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4396 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4397 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4398 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4399 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4400 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4401 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4402 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4403 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4404 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4405 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4406 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4407 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4408 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4409 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4410 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4411 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4412 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4413 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4414 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4415 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4416 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4417 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4418 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4419 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4420 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4421 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4422 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4423 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4424 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4425 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4426 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4427 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4428 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4429 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4430 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4431 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4432 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4433 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4434 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4435 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4436 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4437 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4438 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4439 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4440 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4441 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4442 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4443 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4444 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4445 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4446 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4447 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4448 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4449 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4450 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4451 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4452 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4453 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4454 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4455 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4456 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4457 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4458 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4459 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4460 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4461 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4462 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4463 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4464 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4465 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4466 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4467 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4468 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4469 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4470 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4471 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4472 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4473 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4474 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4475 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4476 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4477 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4478 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4479 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4480 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4481 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4482 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4483 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4484 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4485 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4486 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4487 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4488 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4489 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4490 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4491 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4492 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4493 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4494 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4495 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4496 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4497 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4498 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4499 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4500 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4501 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4502 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4503 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4504 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4505 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4506 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4507 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4508 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4509 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4510 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4511 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4512 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4513 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4514 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4515 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4516 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4517 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4518 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4519 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4520 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4521 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4522 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4523 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4524 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4525 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4526 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4527 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4528 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4529 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4530 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4531 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4532 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4533 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4534 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4535 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4536 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4537 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4538 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4539 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4540 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4541 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4542 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4543 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4544 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4545 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4546 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4547 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4548 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4549 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4550 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4551 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4552 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4553 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4554 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4555 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4556 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4557 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4558 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4559 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4560 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4561 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4562 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4563 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4564 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4565 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4566 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4567 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4568 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4569 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4570 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4571 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4572 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4575 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4577 int min, max;
4579 min = 0;
4580 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4582 while (min <= max) {
4583 const struct geoinfo_t *ptr;
4584 int n = (min+max)/2;
4586 ptr = &geoinfodata[n];
4587 if (geoid == ptr->id)
4588 /* we don't need empty entries */
4589 return *ptr->iso2W ? ptr : NULL;
4591 if (ptr->id > geoid)
4592 max = n-1;
4593 else
4594 min = n+1;
4597 return NULL;
4600 /******************************************************************************
4601 * GetGeoInfoW (KERNEL32.@)
4603 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4605 const struct geoinfo_t *ptr;
4606 const WCHAR *str = NULL;
4607 WCHAR buffW[12];
4608 LONG val = 0;
4609 INT len;
4611 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4613 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4614 SetLastError(ERROR_INVALID_PARAMETER);
4615 return 0;
4618 switch (geotype) {
4619 case GEO_NATION:
4620 val = geoid;
4621 break;
4622 case GEO_ISO_UN_NUMBER:
4623 val = ptr->uncode;
4624 break;
4625 case GEO_PARENT:
4626 val = ptr->parent;
4627 break;
4628 case GEO_ISO2:
4629 case GEO_ISO3:
4631 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4632 break;
4634 case GEO_RFC1766:
4635 case GEO_LCID:
4636 case GEO_FRIENDLYNAME:
4637 case GEO_OFFICIALNAME:
4638 case GEO_TIMEZONES:
4639 case GEO_OFFICIALLANGUAGES:
4640 case GEO_LATITUDE:
4641 case GEO_LONGITUDE:
4642 FIXME("type %d is not supported\n", geotype);
4643 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4644 return 0;
4645 default:
4646 WARN("unrecognized type %d\n", geotype);
4647 SetLastError(ERROR_INVALID_FLAGS);
4648 return 0;
4651 if (val) {
4652 static const WCHAR fmtW[] = {'%','d',0};
4653 sprintfW(buffW, fmtW, val);
4654 str = buffW;
4657 len = strlenW(str) + 1;
4658 if (!data || !data_len)
4659 return len;
4661 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4662 if (data_len < len)
4663 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4664 return data_len < len ? 0 : len;
4667 /******************************************************************************
4668 * GetGeoInfoA (KERNEL32.@)
4670 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4672 WCHAR *buffW;
4673 INT len;
4675 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4677 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4678 if (!len)
4679 return 0;
4681 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4682 if (!buffW)
4683 return 0;
4685 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4686 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4687 if (!data || !data_len) {
4688 HeapFree(GetProcessHeap(), 0, buffW);
4689 return len;
4692 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4693 HeapFree(GetProcessHeap(), 0, buffW);
4695 if (data_len < len)
4696 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4697 return data_len < len ? 0 : len;
4700 /******************************************************************************
4701 * EnumSystemGeoID (KERNEL32.@)
4703 * Call a users function for every location available on the system.
4705 * PARAMS
4706 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4707 * parent [I] GEOID for the parent
4708 * enumproc [I] Callback function to call for each location
4710 * RETURNS
4711 * Success: TRUE.
4712 * Failure: FALSE. Use GetLastError() to determine the cause.
4714 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4716 INT i;
4718 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4720 if (!enumproc) {
4721 SetLastError(ERROR_INVALID_PARAMETER);
4722 return FALSE;
4725 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4726 SetLastError(ERROR_INVALID_FLAGS);
4727 return FALSE;
4730 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4731 const struct geoinfo_t *ptr = &geoinfodata[i];
4733 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4734 continue;
4736 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4737 continue;
4739 if (parent && ptr->parent != parent)
4740 continue;
4742 if (!enumproc(ptr->id))
4743 return TRUE;
4746 return TRUE;
4749 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4751 LCID userlcid;
4753 TRACE("%p, %d\n", localename, buffersize);
4755 userlcid = GetUserDefaultLCID();
4756 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4759 /******************************************************************************
4760 * NormalizeString (KERNEL32.@)
4762 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4763 LPWSTR lpDstString, INT cwDstLength)
4765 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4766 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4767 return 0;
4770 /******************************************************************************
4771 * IsNormalizedString (KERNEL32.@)
4773 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4775 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4776 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4777 return FALSE;
4780 enum {
4781 BASE = 36,
4782 TMIN = 1,
4783 TMAX = 26,
4784 SKEW = 38,
4785 DAMP = 700,
4786 INIT_BIAS = 72,
4787 INIT_N = 128
4790 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4792 INT k;
4794 delta /= (firsttime ? DAMP : 2);
4795 delta += delta/numpoints;
4797 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4798 delta /= BASE-TMIN;
4799 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4802 /******************************************************************************
4803 * IdnToAscii (KERNEL32.@)
4804 * Implementation of Punycode based on RFC 3492.
4806 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4807 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4809 static const WCHAR prefixW[] = {'x','n','-','-'};
4811 WCHAR *norm_str;
4812 INT i, label_start, label_end, norm_len, out_label, out = 0;
4814 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4815 lpASCIICharStr, cchASCIIChar);
4817 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4818 if(!norm_len)
4819 return 0;
4820 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4821 if(!norm_str) {
4822 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4823 return 0;
4825 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4826 cchUnicodeChar, norm_str, norm_len);
4827 if(!norm_len) {
4828 HeapFree(GetProcessHeap(), 0, norm_str);
4829 return 0;
4832 for(label_start=0; label_start<norm_len;) {
4833 INT n = INIT_N, bias = INIT_BIAS;
4834 INT delta = 0, b = 0, h;
4836 out_label = out;
4837 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4838 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4839 if(norm_str[i] < 0x80)
4840 b++;
4841 label_end = i;
4843 if(b == label_end-label_start) {
4844 if(label_end < norm_len)
4845 b++;
4846 if(!lpASCIICharStr) {
4847 out += b;
4848 }else if(out+b <= cchASCIIChar) {
4849 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4850 out += b;
4851 }else {
4852 HeapFree(GetProcessHeap(), 0, norm_str);
4853 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4854 return 0;
4856 label_start = label_end+1;
4857 continue;
4860 if(!lpASCIICharStr) {
4861 out += 5+b; /* strlen(xn--...-) */
4862 }else if(out+5+b <= cchASCIIChar) {
4863 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4864 out += 4;
4865 for(i=label_start; i<label_end; i++)
4866 if(norm_str[i] < 0x80)
4867 lpASCIICharStr[out++] = norm_str[i];
4868 lpASCIICharStr[out++] = '-';
4869 }else {
4870 HeapFree(GetProcessHeap(), 0, norm_str);
4871 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4872 return 0;
4874 if(!b)
4875 out--;
4877 for(h=b; h<label_end-label_start;) {
4878 INT m = 0xffff, q, k;
4880 for(i=label_start; i<label_end; i++) {
4881 if(norm_str[i]>=n && m>norm_str[i])
4882 m = norm_str[i];
4884 delta += (m-n)*(h+1);
4885 n = m;
4887 for(i=label_start; i<label_end; i++) {
4888 if(norm_str[i] < n) {
4889 delta++;
4890 }else if(norm_str[i] == n) {
4891 for(q=delta, k=BASE; ; k+=BASE) {
4892 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4893 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4894 if(!lpASCIICharStr) {
4895 out++;
4896 }else if(out+1 <= cchASCIIChar) {
4897 lpASCIICharStr[out++] = disp<='z'-'a' ?
4898 'a'+disp : '0'+disp-'z'+'a'-1;
4899 }else {
4900 HeapFree(GetProcessHeap(), 0, norm_str);
4901 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4902 return 0;
4904 if(q < t)
4905 break;
4906 q = (q-t)/(BASE-t);
4908 bias = adapt(delta, h+1, h==b);
4909 delta = 0;
4910 h++;
4913 delta++;
4914 n++;
4917 if(out-out_label > 63) {
4918 HeapFree(GetProcessHeap(), 0, norm_str);
4919 SetLastError(ERROR_INVALID_NAME);
4920 return 0;
4923 if(label_end < norm_len) {
4924 if(!lpASCIICharStr) {
4925 out++;
4926 }else if(out+1 <= cchASCIIChar) {
4927 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4928 }else {
4929 HeapFree(GetProcessHeap(), 0, norm_str);
4930 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4931 return 0;
4934 label_start = label_end+1;
4937 HeapFree(GetProcessHeap(), 0, norm_str);
4938 return out;
4941 /******************************************************************************
4942 * IdnToNameprepUnicode (KERNEL32.@)
4944 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4945 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4947 enum {
4948 UNASSIGNED = 0x1,
4949 PROHIBITED = 0x2,
4950 BIDI_RAL = 0x4,
4951 BIDI_L = 0x8
4954 extern const unsigned short nameprep_char_type[];
4955 extern const WCHAR nameprep_mapping[];
4956 const WCHAR *ptr;
4957 WORD flags;
4958 WCHAR buf[64], *map_str, norm_str[64], ch;
4959 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4960 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4962 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4963 lpNameprepCharStr, cchNameprepChar);
4965 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4966 SetLastError(ERROR_INVALID_FLAGS);
4967 return 0;
4970 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4971 SetLastError(ERROR_INVALID_PARAMETER);
4972 return 0;
4975 if(cchUnicodeChar == -1)
4976 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4977 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4978 SetLastError(ERROR_INVALID_NAME);
4979 return 0;
4982 for(label_start=0; label_start<cchUnicodeChar;) {
4983 ascii_only = TRUE;
4984 for(i=label_start; i<cchUnicodeChar; i++) {
4985 ch = lpUnicodeCharStr[i];
4987 if(i!=cchUnicodeChar-1 && !ch) {
4988 SetLastError(ERROR_INVALID_NAME);
4989 return 0;
4991 /* check if ch is one of label separators defined in RFC3490 */
4992 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4993 break;
4995 if(ch > 0x7f) {
4996 ascii_only = FALSE;
4997 continue;
5000 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5001 continue;
5002 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5003 || (ch>='0' && ch<='9') || ch=='-')
5004 continue;
5006 SetLastError(ERROR_INVALID_NAME);
5007 return 0;
5009 label_end = i;
5010 /* last label may be empty */
5011 if(label_start==label_end && ch) {
5012 SetLastError(ERROR_INVALID_NAME);
5013 return 0;
5016 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5017 lpUnicodeCharStr[label_end-1]=='-')) {
5018 SetLastError(ERROR_INVALID_NAME);
5019 return 0;
5022 if(ascii_only) {
5023 /* maximal label length is 63 characters */
5024 if(label_end-label_start > 63) {
5025 SetLastError(ERROR_INVALID_NAME);
5026 return 0;
5028 if(label_end < cchUnicodeChar)
5029 label_end++;
5031 if(!lpNameprepCharStr) {
5032 out += label_end-label_start;
5033 }else if(out+label_end-label_start <= cchNameprepChar) {
5034 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5035 (label_end-label_start)*sizeof(WCHAR));
5036 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5037 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5038 out += label_end-label_start;
5039 }else {
5040 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5041 return 0;
5044 label_start = label_end;
5045 continue;
5048 map_len = 0;
5049 for(i=label_start; i<label_end; i++) {
5050 ch = lpUnicodeCharStr[i];
5051 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5052 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5054 if(!ptr[0]) map_len++;
5055 else if(!ptr[1]) map_len++;
5056 else if(!ptr[2]) map_len += 2;
5057 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5059 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5060 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5061 if(!map_str) {
5062 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5063 return 0;
5065 }else {
5066 map_str = buf;
5068 map_len = 0;
5069 for(i=label_start; i<label_end; i++) {
5070 ch = lpUnicodeCharStr[i];
5071 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5072 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5074 if(!ptr[0]) {
5075 map_str[map_len++] = ch;
5076 }else if(!ptr[1]) {
5077 map_str[map_len++] = ptr[0];
5078 }else if(!ptr[2]) {
5079 map_str[map_len++] = ptr[0];
5080 map_str[map_len++] = ptr[1];
5081 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5082 map_str[map_len++] = ptr[0];
5083 map_str[map_len++] = ptr[1];
5084 map_str[map_len++] = ptr[2];
5088 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5089 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5090 if(map_str != buf)
5091 HeapFree(GetProcessHeap(), 0, map_str);
5092 if(!norm_len) {
5093 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5094 SetLastError(ERROR_INVALID_NAME);
5095 return 0;
5098 if(label_end < cchUnicodeChar) {
5099 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5100 label_end++;
5103 if(!lpNameprepCharStr) {
5104 out += norm_len;
5105 }else if(out+norm_len <= cchNameprepChar) {
5106 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5107 out += norm_len;
5108 }else {
5109 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5110 return 0;
5113 have_bidi_ral = prohibit_bidi_ral = FALSE;
5114 mask = PROHIBITED;
5115 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5116 mask |= UNASSIGNED;
5117 for(i=0; i<norm_len; i++) {
5118 ch = norm_str[i];
5119 flags = get_table_entry( nameprep_char_type, ch );
5121 if(flags & mask) {
5122 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5123 : ERROR_NO_UNICODE_TRANSLATION);
5124 return 0;
5127 if(flags & BIDI_RAL)
5128 have_bidi_ral = TRUE;
5129 if(flags & BIDI_L)
5130 prohibit_bidi_ral = TRUE;
5133 if(have_bidi_ral) {
5134 ch = norm_str[0];
5135 flags = get_table_entry( nameprep_char_type, ch );
5136 if((flags & BIDI_RAL) == 0)
5137 prohibit_bidi_ral = TRUE;
5139 ch = norm_str[norm_len-1];
5140 flags = get_table_entry( nameprep_char_type, ch );
5141 if((flags & BIDI_RAL) == 0)
5142 prohibit_bidi_ral = TRUE;
5145 if(have_bidi_ral && prohibit_bidi_ral) {
5146 SetLastError(ERROR_INVALID_NAME);
5147 return 0;
5150 label_start = label_end;
5153 return out;
5156 /******************************************************************************
5157 * IdnToUnicode (KERNEL32.@)
5159 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5160 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5162 extern const unsigned short nameprep_char_type[];
5164 INT i, label_start, label_end, out_label, out = 0;
5165 WCHAR ch;
5167 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5168 lpUnicodeCharStr, cchUnicodeChar);
5170 for(label_start=0; label_start<cchASCIIChar;) {
5171 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5173 out_label = out;
5174 for(i=label_start; i<cchASCIIChar; i++) {
5175 ch = lpASCIICharStr[i];
5177 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5178 SetLastError(ERROR_INVALID_NAME);
5179 return 0;
5182 if(!ch || ch=='.')
5183 break;
5184 if(ch == '-')
5185 delim = i;
5187 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5188 continue;
5189 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5190 || (ch>='0' && ch<='9') || ch=='-')
5191 continue;
5193 SetLastError(ERROR_INVALID_NAME);
5194 return 0;
5196 label_end = i;
5197 /* last label may be empty */
5198 if(label_start==label_end && ch) {
5199 SetLastError(ERROR_INVALID_NAME);
5200 return 0;
5203 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5204 lpASCIICharStr[label_end-1]=='-')) {
5205 SetLastError(ERROR_INVALID_NAME);
5206 return 0;
5208 if(label_end-label_start > 63) {
5209 SetLastError(ERROR_INVALID_NAME);
5210 return 0;
5213 if(label_end-label_start<4 ||
5214 tolowerW(lpASCIICharStr[label_start])!='x' ||
5215 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5216 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5217 if(label_end < cchASCIIChar)
5218 label_end++;
5220 if(!lpUnicodeCharStr) {
5221 out += label_end-label_start;
5222 }else if(out+label_end-label_start <= cchUnicodeChar) {
5223 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5224 (label_end-label_start)*sizeof(WCHAR));
5225 out += label_end-label_start;
5226 }else {
5227 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5228 return 0;
5231 label_start = label_end;
5232 continue;
5235 if(delim == label_start+3)
5236 delim++;
5237 if(!lpUnicodeCharStr) {
5238 out += delim-label_start-4;
5239 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5240 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5241 (delim-label_start-4)*sizeof(WCHAR));
5242 out += delim-label_start-4;
5243 }else {
5244 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5245 return 0;
5247 if(out != out_label)
5248 delim++;
5250 for(i=delim; i<label_end;) {
5251 old_pos = pos;
5252 w = 1;
5253 for(k=BASE; ; k+=BASE) {
5254 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5255 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5256 SetLastError(ERROR_INVALID_NAME);
5257 return 0;
5259 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5260 pos += digit*w;
5261 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5262 if(digit < t)
5263 break;
5264 w *= BASE-t;
5266 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5267 n += pos/(out-out_label+1);
5268 pos %= out-out_label+1;
5270 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5271 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5272 SetLastError(ERROR_INVALID_NAME);
5273 return 0;
5275 if(!lpUnicodeCharStr) {
5276 out++;
5277 }else if(out+1 <= cchASCIIChar) {
5278 memmove(lpUnicodeCharStr+out_label+pos+1,
5279 lpUnicodeCharStr+out_label+pos,
5280 (out-out_label-pos)*sizeof(WCHAR));
5281 lpUnicodeCharStr[out_label+pos] = n;
5282 out++;
5283 }else {
5284 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5285 return 0;
5287 pos++;
5290 if(out-out_label > 63) {
5291 SetLastError(ERROR_INVALID_NAME);
5292 return 0;
5295 if(label_end < cchASCIIChar) {
5296 if(!lpUnicodeCharStr) {
5297 out++;
5298 }else if(out+1 <= cchUnicodeChar) {
5299 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5300 }else {
5301 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5302 return 0;
5305 label_start = label_end+1;
5308 return out;
5312 /******************************************************************************
5313 * GetUserPreferredUILanguages (KERNEL32.@)
5315 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5317 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5318 return FALSE;
5321 /******************************************************************************
5322 * GetFileMUIPath (KERNEL32.@)
5325 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5326 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5328 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5329 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5331 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5333 return FALSE;
5336 /******************************************************************************
5337 * GetFileMUIInfo (KERNEL32.@)
5340 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5342 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5344 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5345 return FALSE;