po: Follow GNU specification for Language and Language-Team headers.
[wine.git] / dlls / kernel32 / locale.c
blobbcdcdbadf6258748242efeddb05eaaa361aad25f
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 '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 '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 (!(p = strpbrkW( name->lang, sepW )))
458 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
460 name->matches = 4; /* perfect match for default English lcid */
461 return;
463 strcpyW( name->win_name, name->lang );
465 else if (*p == '-') /* Windows format */
467 strcpyW( name->win_name, name->lang );
468 *p++ = 0;
469 name->country = p;
470 if (!(p = strpbrkW( p, winsepW ))) goto done;
471 if (*p == '-')
473 *p++ = 0;
474 name->script = name->country;
475 name->country = p;
476 if (!(p = strpbrkW( p, winsepW ))) goto done;
478 *p++ = 0;
479 name->modifier = p;
481 else /* Unix format */
483 if (*p == '_')
485 *p++ = 0;
486 name->country = p;
487 p = strpbrkW( p, sepW + 2 );
489 if (p && *p == '.')
491 *p++ = 0;
492 name->charset = p;
493 p = strchrW( p, '@' );
495 if (p)
497 *p++ = 0;
498 name->modifier = p;
501 if (name->charset)
502 name->codepage = find_charset( name->charset );
504 /* rebuild a Windows name if possible */
506 if (name->charset) goto done; /* can't specify charset in Windows format */
507 if (name->modifier && strcmpW( name->modifier, latinW ))
508 goto done; /* only Latn script supported for now */
509 strcpyW( name->win_name, name->lang );
510 if (name->modifier) strcatW( name->win_name, latnW );
511 if (name->country)
513 p = name->win_name + strlenW(name->win_name);
514 *p++ = '-';
515 strcpyW( p, name->country );
518 done:
519 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
520 find_locale_id_callback, (LPARAM)name );
524 /***********************************************************************
525 * convert_default_lcid
527 * Get the default LCID to use for a given lctype in GetLocaleInfo.
529 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
531 if (lcid == LOCALE_SYSTEM_DEFAULT ||
532 lcid == LOCALE_USER_DEFAULT ||
533 lcid == LOCALE_NEUTRAL)
535 LCID default_id = 0;
537 switch(lctype & 0xffff)
539 case LOCALE_SSORTNAME:
540 default_id = lcid_LC_COLLATE;
541 break;
543 case LOCALE_FONTSIGNATURE:
544 case LOCALE_IDEFAULTANSICODEPAGE:
545 case LOCALE_IDEFAULTCODEPAGE:
546 case LOCALE_IDEFAULTEBCDICCODEPAGE:
547 case LOCALE_IDEFAULTMACCODEPAGE:
548 case LOCALE_IDEFAULTUNIXCODEPAGE:
549 default_id = lcid_LC_CTYPE;
550 break;
552 case LOCALE_ICURRDIGITS:
553 case LOCALE_ICURRENCY:
554 case LOCALE_IINTLCURRDIGITS:
555 case LOCALE_INEGCURR:
556 case LOCALE_INEGSEPBYSPACE:
557 case LOCALE_INEGSIGNPOSN:
558 case LOCALE_INEGSYMPRECEDES:
559 case LOCALE_IPOSSEPBYSPACE:
560 case LOCALE_IPOSSIGNPOSN:
561 case LOCALE_IPOSSYMPRECEDES:
562 case LOCALE_SCURRENCY:
563 case LOCALE_SINTLSYMBOL:
564 case LOCALE_SMONDECIMALSEP:
565 case LOCALE_SMONGROUPING:
566 case LOCALE_SMONTHOUSANDSEP:
567 case LOCALE_SNATIVECURRNAME:
568 default_id = lcid_LC_MONETARY;
569 break;
571 case LOCALE_IDIGITS:
572 case LOCALE_IDIGITSUBSTITUTION:
573 case LOCALE_ILZERO:
574 case LOCALE_INEGNUMBER:
575 case LOCALE_SDECIMAL:
576 case LOCALE_SGROUPING:
577 case LOCALE_SNAN:
578 case LOCALE_SNATIVEDIGITS:
579 case LOCALE_SNEGATIVESIGN:
580 case LOCALE_SNEGINFINITY:
581 case LOCALE_SPOSINFINITY:
582 case LOCALE_SPOSITIVESIGN:
583 case LOCALE_STHOUSAND:
584 default_id = lcid_LC_NUMERIC;
585 break;
587 case LOCALE_ICALENDARTYPE:
588 case LOCALE_ICENTURY:
589 case LOCALE_IDATE:
590 case LOCALE_IDAYLZERO:
591 case LOCALE_IFIRSTDAYOFWEEK:
592 case LOCALE_IFIRSTWEEKOFYEAR:
593 case LOCALE_ILDATE:
594 case LOCALE_IMONLZERO:
595 case LOCALE_IOPTIONALCALENDAR:
596 case LOCALE_ITIME:
597 case LOCALE_ITIMEMARKPOSN:
598 case LOCALE_ITLZERO:
599 case LOCALE_S1159:
600 case LOCALE_S2359:
601 case LOCALE_SABBREVDAYNAME1:
602 case LOCALE_SABBREVDAYNAME2:
603 case LOCALE_SABBREVDAYNAME3:
604 case LOCALE_SABBREVDAYNAME4:
605 case LOCALE_SABBREVDAYNAME5:
606 case LOCALE_SABBREVDAYNAME6:
607 case LOCALE_SABBREVDAYNAME7:
608 case LOCALE_SABBREVMONTHNAME1:
609 case LOCALE_SABBREVMONTHNAME2:
610 case LOCALE_SABBREVMONTHNAME3:
611 case LOCALE_SABBREVMONTHNAME4:
612 case LOCALE_SABBREVMONTHNAME5:
613 case LOCALE_SABBREVMONTHNAME6:
614 case LOCALE_SABBREVMONTHNAME7:
615 case LOCALE_SABBREVMONTHNAME8:
616 case LOCALE_SABBREVMONTHNAME9:
617 case LOCALE_SABBREVMONTHNAME10:
618 case LOCALE_SABBREVMONTHNAME11:
619 case LOCALE_SABBREVMONTHNAME12:
620 case LOCALE_SABBREVMONTHNAME13:
621 case LOCALE_SDATE:
622 case LOCALE_SDAYNAME1:
623 case LOCALE_SDAYNAME2:
624 case LOCALE_SDAYNAME3:
625 case LOCALE_SDAYNAME4:
626 case LOCALE_SDAYNAME5:
627 case LOCALE_SDAYNAME6:
628 case LOCALE_SDAYNAME7:
629 case LOCALE_SDURATION:
630 case LOCALE_SLONGDATE:
631 case LOCALE_SMONTHNAME1:
632 case LOCALE_SMONTHNAME2:
633 case LOCALE_SMONTHNAME3:
634 case LOCALE_SMONTHNAME4:
635 case LOCALE_SMONTHNAME5:
636 case LOCALE_SMONTHNAME6:
637 case LOCALE_SMONTHNAME7:
638 case LOCALE_SMONTHNAME8:
639 case LOCALE_SMONTHNAME9:
640 case LOCALE_SMONTHNAME10:
641 case LOCALE_SMONTHNAME11:
642 case LOCALE_SMONTHNAME12:
643 case LOCALE_SMONTHNAME13:
644 case LOCALE_SSHORTDATE:
645 case LOCALE_SSHORTESTDAYNAME1:
646 case LOCALE_SSHORTESTDAYNAME2:
647 case LOCALE_SSHORTESTDAYNAME3:
648 case LOCALE_SSHORTESTDAYNAME4:
649 case LOCALE_SSHORTESTDAYNAME5:
650 case LOCALE_SSHORTESTDAYNAME6:
651 case LOCALE_SSHORTESTDAYNAME7:
652 case LOCALE_STIME:
653 case LOCALE_STIMEFORMAT:
654 case LOCALE_SYEARMONTH:
655 default_id = lcid_LC_TIME;
656 break;
658 case LOCALE_IPAPERSIZE:
659 default_id = lcid_LC_PAPER;
660 break;
662 case LOCALE_IMEASURE:
663 default_id = lcid_LC_MEASUREMENT;
664 break;
666 case LOCALE_ICOUNTRY:
667 default_id = lcid_LC_TELEPHONE;
668 break;
670 if (default_id) lcid = default_id;
672 return ConvertDefaultLocale( lcid );
675 /***********************************************************************
676 * is_genitive_name_supported
678 * Determine could LCTYPE basically support genitive name form or not.
680 static BOOL is_genitive_name_supported( LCTYPE lctype )
682 switch(lctype & 0xffff)
684 case LOCALE_SMONTHNAME1:
685 case LOCALE_SMONTHNAME2:
686 case LOCALE_SMONTHNAME3:
687 case LOCALE_SMONTHNAME4:
688 case LOCALE_SMONTHNAME5:
689 case LOCALE_SMONTHNAME6:
690 case LOCALE_SMONTHNAME7:
691 case LOCALE_SMONTHNAME8:
692 case LOCALE_SMONTHNAME9:
693 case LOCALE_SMONTHNAME10:
694 case LOCALE_SMONTHNAME11:
695 case LOCALE_SMONTHNAME12:
696 case LOCALE_SMONTHNAME13:
697 return TRUE;
698 default:
699 return FALSE;
703 /***********************************************************************
704 * create_registry_key
706 * Create the Control Panel\\International registry key.
708 static inline HANDLE create_registry_key(void)
710 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
711 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
712 OBJECT_ATTRIBUTES attr;
713 UNICODE_STRING nameW;
714 HANDLE cpl_key, hkey = 0;
716 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
718 attr.Length = sizeof(attr);
719 attr.RootDirectory = hkey;
720 attr.ObjectName = &nameW;
721 attr.Attributes = 0;
722 attr.SecurityDescriptor = NULL;
723 attr.SecurityQualityOfService = NULL;
724 RtlInitUnicodeString( &nameW, cplW );
726 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
728 NtClose( attr.RootDirectory );
729 attr.RootDirectory = cpl_key;
730 RtlInitUnicodeString( &nameW, intlW );
731 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
733 NtClose( attr.RootDirectory );
734 return hkey;
738 /* update the registry settings for a given locale parameter */
739 /* return TRUE if an update was needed */
740 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
741 const LCTYPE *values, UINT nb_values )
743 static const WCHAR formatW[] = { '%','0','8','x',0 };
744 WCHAR bufferW[40];
745 UNICODE_STRING nameW;
746 DWORD count, i;
748 RtlInitUnicodeString( &nameW, name );
749 count = sizeof(bufferW);
750 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
752 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
753 LPCWSTR text = (LPCWSTR)info->Data;
755 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
756 TRACE( "updating registry, locale %s changed %s -> %08x\n",
757 debugstr_w(name), debugstr_w(text), lcid );
759 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
760 sprintfW( bufferW, formatW, lcid );
761 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
763 for (i = 0; i < nb_values; i++)
765 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
766 sizeof(bufferW)/sizeof(WCHAR) );
767 SetLocaleInfoW( lcid, values[i], bufferW );
769 return TRUE;
773 /***********************************************************************
774 * LOCALE_InitRegistry
776 * Update registry contents on startup if the user locale has changed.
777 * This simulates the action of the Windows control panel.
779 void LOCALE_InitRegistry(void)
781 static const WCHAR acpW[] = {'A','C','P',0};
782 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
783 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
784 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
785 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
786 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
787 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
788 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
789 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
790 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
791 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
792 static const struct
794 LPCWSTR name;
795 USHORT value;
796 } update_cp_values[] = {
797 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
798 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
799 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
801 static const LCTYPE lc_messages_values[] = {
802 LOCALE_SABBREVLANGNAME,
803 LOCALE_SCOUNTRY,
804 LOCALE_SLIST };
805 static const LCTYPE lc_monetary_values[] = {
806 LOCALE_SCURRENCY,
807 LOCALE_ICURRENCY,
808 LOCALE_INEGCURR,
809 LOCALE_ICURRDIGITS,
810 LOCALE_ILZERO,
811 LOCALE_SMONDECIMALSEP,
812 LOCALE_SMONGROUPING,
813 LOCALE_SMONTHOUSANDSEP };
814 static const LCTYPE lc_numeric_values[] = {
815 LOCALE_SDECIMAL,
816 LOCALE_STHOUSAND,
817 LOCALE_IDIGITS,
818 LOCALE_IDIGITSUBSTITUTION,
819 LOCALE_SNATIVEDIGITS,
820 LOCALE_INEGNUMBER,
821 LOCALE_SNEGATIVESIGN,
822 LOCALE_SPOSITIVESIGN,
823 LOCALE_SGROUPING };
824 static const LCTYPE lc_time_values[] = {
825 LOCALE_S1159,
826 LOCALE_S2359,
827 LOCALE_STIME,
828 LOCALE_ITIME,
829 LOCALE_ITLZERO,
830 LOCALE_SSHORTDATE,
831 LOCALE_SLONGDATE,
832 LOCALE_SDATE,
833 LOCALE_ITIMEMARKPOSN,
834 LOCALE_ICALENDARTYPE,
835 LOCALE_IFIRSTDAYOFWEEK,
836 LOCALE_IFIRSTWEEKOFYEAR,
837 LOCALE_STIMEFORMAT,
838 LOCALE_SYEARMONTH,
839 LOCALE_IDATE };
840 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
841 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
842 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
844 UNICODE_STRING nameW;
845 WCHAR bufferW[80];
846 DWORD count, i;
847 HANDLE hkey;
848 LCID lcid = GetUserDefaultLCID();
850 if (!(hkey = create_registry_key()))
851 return; /* don't do anything if we can't create the registry key */
853 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
854 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
855 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
856 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
857 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
858 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
859 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
860 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
861 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
862 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
863 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
864 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
865 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
866 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
868 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
870 static const WCHAR codepageW[] =
871 {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
872 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
873 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
875 OBJECT_ATTRIBUTES attr;
876 HANDLE nls_key;
877 DWORD len = 14;
879 RtlInitUnicodeString( &nameW, codepageW );
880 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
881 while (codepageW[len])
883 nameW.Length = len * sizeof(WCHAR);
884 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
885 NtClose( nls_key );
886 len++;
887 while (codepageW[len] && codepageW[len] != '\\') len++;
889 nameW.Length = len * sizeof(WCHAR);
890 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
892 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
894 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
895 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
896 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
897 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
899 NtClose( nls_key );
903 NtClose( hkey );
907 /***********************************************************************
908 * setup_unix_locales
910 static UINT setup_unix_locales(void)
912 struct locale_name locale_name;
913 WCHAR buffer[128], ctype_buff[128];
914 char *locale;
915 UINT unix_cp = 0;
917 if ((locale = setlocale( LC_CTYPE, NULL )))
919 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
920 parse_locale_name( ctype_buff, &locale_name );
921 lcid_LC_CTYPE = locale_name.lcid;
922 unix_cp = locale_name.codepage;
924 if (!lcid_LC_CTYPE) /* this one needs a default value */
925 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
927 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
928 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
930 #define GET_UNIX_LOCALE(cat) do \
931 if ((locale = setlocale( cat, NULL ))) \
933 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
934 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
935 else { \
936 parse_locale_name( buffer, &locale_name ); \
937 lcid_##cat = locale_name.lcid; \
938 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
939 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
941 } while (0)
943 GET_UNIX_LOCALE( LC_COLLATE );
944 GET_UNIX_LOCALE( LC_MESSAGES );
945 GET_UNIX_LOCALE( LC_MONETARY );
946 GET_UNIX_LOCALE( LC_NUMERIC );
947 GET_UNIX_LOCALE( LC_TIME );
948 #ifdef LC_PAPER
949 GET_UNIX_LOCALE( LC_PAPER );
950 #endif
951 #ifdef LC_MEASUREMENT
952 GET_UNIX_LOCALE( LC_MEASUREMENT );
953 #endif
954 #ifdef LC_TELEPHONE
955 GET_UNIX_LOCALE( LC_TELEPHONE );
956 #endif
958 #undef GET_UNIX_LOCALE
960 return unix_cp;
964 /***********************************************************************
965 * GetUserDefaultLangID (KERNEL32.@)
967 * Get the default language Id for the current user.
969 * PARAMS
970 * None.
972 * RETURNS
973 * The current LANGID of the default language for the current user.
975 LANGID WINAPI GetUserDefaultLangID(void)
977 return LANGIDFROMLCID(GetUserDefaultLCID());
981 /***********************************************************************
982 * GetSystemDefaultLangID (KERNEL32.@)
984 * Get the default language Id for the system.
986 * PARAMS
987 * None.
989 * RETURNS
990 * The current LANGID of the default language for the system.
992 LANGID WINAPI GetSystemDefaultLangID(void)
994 return LANGIDFROMLCID(GetSystemDefaultLCID());
998 /***********************************************************************
999 * GetUserDefaultLCID (KERNEL32.@)
1001 * Get the default locale Id for the current user.
1003 * PARAMS
1004 * None.
1006 * RETURNS
1007 * The current LCID of the default locale for the current user.
1009 LCID WINAPI GetUserDefaultLCID(void)
1011 LCID lcid;
1012 NtQueryDefaultLocale( TRUE, &lcid );
1013 return lcid;
1017 /***********************************************************************
1018 * GetSystemDefaultLCID (KERNEL32.@)
1020 * Get the default locale Id for the system.
1022 * PARAMS
1023 * None.
1025 * RETURNS
1026 * The current LCID of the default locale for the system.
1028 LCID WINAPI GetSystemDefaultLCID(void)
1030 LCID lcid;
1031 NtQueryDefaultLocale( FALSE, &lcid );
1032 return lcid;
1035 /***********************************************************************
1036 * GetSystemDefaultLocaleName (KERNEL32.@)
1038 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1040 LCID lcid = GetSystemDefaultLCID();
1041 return LCIDToLocaleName(lcid, localename, len, 0);
1044 /***********************************************************************
1045 * GetUserDefaultUILanguage (KERNEL32.@)
1047 * Get the default user interface language Id for the current user.
1049 * PARAMS
1050 * None.
1052 * RETURNS
1053 * The current LANGID of the default UI language for the current user.
1055 LANGID WINAPI GetUserDefaultUILanguage(void)
1057 LANGID lang;
1058 NtQueryDefaultUILanguage( &lang );
1059 return lang;
1063 /***********************************************************************
1064 * GetSystemDefaultUILanguage (KERNEL32.@)
1066 * Get the default user interface language Id for the system.
1068 * PARAMS
1069 * None.
1071 * RETURNS
1072 * The current LANGID of the default UI language for the system. This is
1073 * typically the same language used during the installation process.
1075 LANGID WINAPI GetSystemDefaultUILanguage(void)
1077 LANGID lang;
1078 NtQueryInstallUILanguage( &lang );
1079 return lang;
1083 /***********************************************************************
1084 * LocaleNameToLCID (KERNEL32.@)
1086 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1088 struct locale_name locale_name;
1090 if (flags) FIXME( "unsupported flags %x\n", flags );
1092 if (name == LOCALE_NAME_USER_DEFAULT)
1093 return GetUserDefaultLCID();
1095 /* string parsing */
1096 parse_locale_name( name, &locale_name );
1098 TRACE( "found lcid %x for %s, matches %d\n",
1099 locale_name.lcid, debugstr_w(name), locale_name.matches );
1101 if (!locale_name.matches)
1103 SetLastError(ERROR_INVALID_PARAMETER);
1104 return 0;
1107 if (locale_name.matches == 1)
1108 WARN( "locale %s not recognized, defaulting to %s\n",
1109 debugstr_w(name), debugstr_w(locale_name.lang) );
1111 return locale_name.lcid;
1115 /***********************************************************************
1116 * LCIDToLocaleName (KERNEL32.@)
1118 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1120 if (flags) FIXME( "unsupported flags %x\n", flags );
1122 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1126 /******************************************************************************
1127 * get_locale_registry_value
1129 * Gets the registry value name and cache for a given lctype.
1131 static struct registry_value *get_locale_registry_value( DWORD lctype )
1133 int i;
1134 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1135 if (registry_values[i].lctype == lctype)
1136 return &registry_values[i];
1137 return NULL;
1141 /******************************************************************************
1142 * get_registry_locale_info
1144 * Retrieve user-modified locale info from the registry.
1145 * Return length, 0 on error, -1 if not found.
1147 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1149 DWORD size;
1150 INT ret;
1151 HANDLE hkey;
1152 NTSTATUS status;
1153 UNICODE_STRING nameW;
1154 KEY_VALUE_PARTIAL_INFORMATION *info;
1155 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1157 RtlEnterCriticalSection( &cache_section );
1159 if (!registry_value->cached_value)
1161 if (!(hkey = create_registry_key()))
1163 RtlLeaveCriticalSection( &cache_section );
1164 return -1;
1167 RtlInitUnicodeString( &nameW, registry_value->name );
1168 size = info_size + len * sizeof(WCHAR);
1170 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1172 NtClose( hkey );
1173 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1174 RtlLeaveCriticalSection( &cache_section );
1175 return 0;
1178 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1180 /* try again with a bigger buffer when we have to return the correct size */
1181 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1183 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1184 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1186 info = new_info;
1187 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1191 NtClose( hkey );
1193 if (!status)
1195 INT length = (size - info_size) / sizeof(WCHAR);
1196 LPWSTR cached_value;
1198 if (!length || ((WCHAR *)&info->Data)[length-1])
1199 length++;
1201 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1203 if (!cached_value)
1205 HeapFree( GetProcessHeap(), 0, info );
1206 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1207 RtlLeaveCriticalSection( &cache_section );
1208 return 0;
1211 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1212 cached_value[length-1] = 0;
1213 HeapFree( GetProcessHeap(), 0, info );
1214 registry_value->cached_value = cached_value;
1216 else
1218 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1220 ret = (size - info_size) / sizeof(WCHAR);
1222 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1224 ret = -1;
1226 else
1228 SetLastError( RtlNtStatusToDosError(status) );
1229 ret = 0;
1231 HeapFree( GetProcessHeap(), 0, info );
1232 RtlLeaveCriticalSection( &cache_section );
1233 return ret;
1237 ret = lstrlenW( registry_value->cached_value ) + 1;
1239 if (buffer)
1241 if (ret > len)
1243 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1244 ret = 0;
1246 else
1248 lstrcpyW( buffer, registry_value->cached_value );
1252 RtlLeaveCriticalSection( &cache_section );
1254 return ret;
1258 /******************************************************************************
1259 * GetLocaleInfoA (KERNEL32.@)
1261 * Get information about an aspect of a locale.
1263 * PARAMS
1264 * lcid [I] LCID of the locale
1265 * lctype [I] LCTYPE_ flags from "winnls.h"
1266 * buffer [O] Destination for the information
1267 * len [I] Length of buffer in characters
1269 * RETURNS
1270 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1271 * with the information.
1272 * Failure: 0. Use GetLastError() to determine the cause.
1274 * NOTES
1275 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1276 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1277 * which is a bit string.
1279 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1281 WCHAR *bufferW;
1282 INT lenW, ret;
1284 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1286 if (len < 0 || (len && !buffer))
1288 SetLastError( ERROR_INVALID_PARAMETER );
1289 return 0;
1291 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1293 SetLastError( ERROR_INVALID_FLAGS );
1294 return 0;
1297 if (!len) buffer = NULL;
1299 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1301 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1303 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1304 return 0;
1306 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1308 if ((lctype & LOCALE_RETURN_NUMBER) ||
1309 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1311 /* it's not an ASCII string, just bytes */
1312 ret *= sizeof(WCHAR);
1313 if (buffer)
1315 if (ret <= len) memcpy( buffer, bufferW, ret );
1316 else
1318 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1319 ret = 0;
1323 else
1325 UINT codepage = CP_ACP;
1326 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1327 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1330 HeapFree( GetProcessHeap(), 0, bufferW );
1331 return ret;
1334 static int get_value_base_by_lctype( LCTYPE lctype )
1336 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1339 /******************************************************************************
1340 * GetLocaleInfoW (KERNEL32.@)
1342 * See GetLocaleInfoA.
1344 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1346 LANGID lang_id;
1347 HRSRC hrsrc;
1348 HGLOBAL hmem;
1349 INT ret;
1350 UINT lcflags;
1351 const WCHAR *p;
1352 unsigned int i;
1354 if (len < 0 || (len && !buffer))
1356 SetLastError( ERROR_INVALID_PARAMETER );
1357 return 0;
1359 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1360 !is_genitive_name_supported( lctype ))
1362 SetLastError( ERROR_INVALID_FLAGS );
1363 return 0;
1366 if (!len) buffer = NULL;
1368 lcid = convert_default_lcid( lcid, lctype );
1370 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1371 lctype &= 0xffff;
1373 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1375 /* first check for overrides in the registry */
1377 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1378 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1380 struct registry_value *value = get_locale_registry_value(lctype);
1382 if (value)
1384 if (lcflags & LOCALE_RETURN_NUMBER)
1386 WCHAR tmp[16];
1387 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1388 if (ret > 0)
1390 WCHAR *end;
1391 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1392 if (*end) /* invalid number */
1394 SetLastError( ERROR_INVALID_FLAGS );
1395 return 0;
1397 ret = sizeof(UINT)/sizeof(WCHAR);
1398 if (!buffer) return ret;
1399 if (ret > len)
1401 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1402 return 0;
1404 memcpy( buffer, &number, sizeof(number) );
1407 else ret = get_registry_locale_info( value, buffer, len );
1409 if (ret != -1) return ret;
1413 /* now load it from kernel resources */
1415 lang_id = LANGIDFROMLCID( lcid );
1417 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1418 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1419 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1421 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1422 ULongToPtr((lctype >> 4) + 1), lang_id )))
1424 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1425 return 0;
1427 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1428 return 0;
1430 p = LockResource( hmem );
1431 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1433 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1434 else if (is_genitive_name_supported( lctype ) && *p)
1436 /* genitive form's stored after a null separator from a nominative */
1437 for (i = 1; i <= *p; i++) if (!p[i]) break;
1439 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1441 ret = *p - i + 1;
1442 p += i;
1444 else ret = i;
1446 else
1447 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1449 if (!buffer) return ret;
1451 if (ret > len)
1453 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1454 return 0;
1457 if (lcflags & LOCALE_RETURN_NUMBER)
1459 UINT number;
1460 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1461 if (!tmp) return 0;
1462 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1463 tmp[*p] = 0;
1464 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1465 if (!*end)
1466 memcpy( buffer, &number, sizeof(number) );
1467 else /* invalid number */
1469 SetLastError( ERROR_INVALID_FLAGS );
1470 ret = 0;
1472 HeapFree( GetProcessHeap(), 0, tmp );
1474 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1475 lcid, lctype, buffer, len, number );
1477 else
1479 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1480 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1482 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1483 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1485 return ret;
1488 /******************************************************************************
1489 * GetLocaleInfoEx (KERNEL32.@)
1491 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1493 LCID lcid = LocaleNameToLCID(locale, 0);
1495 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1497 if (!lcid) return 0;
1499 /* special handling for neutral locale names */
1500 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1502 if (len && len < 3)
1504 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1505 return 0;
1508 if (len) strcpyW(buffer, locale);
1509 return 3;
1512 return GetLocaleInfoW(lcid, info, buffer, len);
1515 /******************************************************************************
1516 * SetLocaleInfoA [KERNEL32.@]
1518 * Set information about an aspect of a locale.
1520 * PARAMS
1521 * lcid [I] LCID of the locale
1522 * lctype [I] LCTYPE_ flags from "winnls.h"
1523 * data [I] Information to set
1525 * RETURNS
1526 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1527 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1528 * Failure: FALSE. Use GetLastError() to determine the cause.
1530 * NOTES
1531 * - Values are only be set for the current user locale; the system locale
1532 * settings cannot be changed.
1533 * - Any settings changed by this call are lost when the locale is changed by
1534 * the control panel (in Wine, this happens every time you change LANG).
1535 * - The native implementation of this function does not check that lcid matches
1536 * the current user locale, and simply sets the new values. Wine warns you in
1537 * this case, but behaves the same.
1539 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1541 UINT codepage = CP_ACP;
1542 WCHAR *strW;
1543 DWORD len;
1544 BOOL ret;
1546 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1548 if (!data)
1550 SetLastError( ERROR_INVALID_PARAMETER );
1551 return FALSE;
1553 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1554 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1556 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1557 return FALSE;
1559 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1560 ret = SetLocaleInfoW( lcid, lctype, strW );
1561 HeapFree( GetProcessHeap(), 0, strW );
1562 return ret;
1566 /******************************************************************************
1567 * SetLocaleInfoW (KERNEL32.@)
1569 * See SetLocaleInfoA.
1571 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1573 struct registry_value *value;
1574 static const WCHAR intlW[] = {'i','n','t','l',0 };
1575 UNICODE_STRING valueW;
1576 NTSTATUS status;
1577 HANDLE hkey;
1579 lctype &= 0xffff;
1580 value = get_locale_registry_value( lctype );
1582 if (!data || !value)
1584 SetLastError( ERROR_INVALID_PARAMETER );
1585 return FALSE;
1588 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1590 SetLastError( ERROR_INVALID_FLAGS );
1591 return FALSE;
1594 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1596 /* FIXME: should check that data to set is sane */
1598 /* FIXME: profile functions should map to registry */
1599 WriteProfileStringW( intlW, value->name, data );
1601 if (!(hkey = create_registry_key())) return FALSE;
1602 RtlInitUnicodeString( &valueW, value->name );
1603 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1605 RtlEnterCriticalSection( &cache_section );
1606 HeapFree( GetProcessHeap(), 0, value->cached_value );
1607 value->cached_value = NULL;
1608 RtlLeaveCriticalSection( &cache_section );
1610 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1612 /* Set I-value from S value */
1613 WCHAR *lpD, *lpM, *lpY;
1614 WCHAR szBuff[2];
1616 lpD = strrchrW(data, 'd');
1617 lpM = strrchrW(data, 'M');
1618 lpY = strrchrW(data, 'y');
1620 if (lpD <= lpM)
1622 szBuff[0] = '1'; /* D-M-Y */
1624 else
1626 if (lpY <= lpM)
1627 szBuff[0] = '2'; /* Y-M-D */
1628 else
1629 szBuff[0] = '0'; /* M-D-Y */
1632 szBuff[1] = '\0';
1634 if (lctype == LOCALE_SSHORTDATE)
1635 lctype = LOCALE_IDATE;
1636 else
1637 lctype = LOCALE_ILDATE;
1639 value = get_locale_registry_value( lctype );
1641 WriteProfileStringW( intlW, value->name, szBuff );
1643 RtlInitUnicodeString( &valueW, value->name );
1644 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1646 RtlEnterCriticalSection( &cache_section );
1647 HeapFree( GetProcessHeap(), 0, value->cached_value );
1648 value->cached_value = NULL;
1649 RtlLeaveCriticalSection( &cache_section );
1652 NtClose( hkey );
1654 if (status) SetLastError( RtlNtStatusToDosError(status) );
1655 return !status;
1659 /******************************************************************************
1660 * GetACP (KERNEL32.@)
1662 * Get the current Ansi code page Id for the system.
1664 * PARAMS
1665 * None.
1667 * RETURNS
1668 * The current Ansi code page identifier for the system.
1670 UINT WINAPI GetACP(void)
1672 assert( ansi_cptable );
1673 return ansi_cptable->info.codepage;
1677 /******************************************************************************
1678 * SetCPGlobal (KERNEL32.@)
1680 * Set the current Ansi code page Id for the system.
1682 * PARAMS
1683 * acp [I] code page ID to be the new ACP.
1685 * RETURNS
1686 * The previous ACP.
1688 UINT WINAPI SetCPGlobal( UINT acp )
1690 UINT ret = GetACP();
1691 const union cptable *new_cptable = wine_cp_get_table( acp );
1693 if (new_cptable) ansi_cptable = new_cptable;
1694 return ret;
1698 /***********************************************************************
1699 * GetOEMCP (KERNEL32.@)
1701 * Get the current OEM code page Id for the system.
1703 * PARAMS
1704 * None.
1706 * RETURNS
1707 * The current OEM code page identifier for the system.
1709 UINT WINAPI GetOEMCP(void)
1711 assert( oem_cptable );
1712 return oem_cptable->info.codepage;
1716 /***********************************************************************
1717 * IsValidCodePage (KERNEL32.@)
1719 * Determine if a given code page identifier is valid.
1721 * PARAMS
1722 * codepage [I] Code page Id to verify.
1724 * RETURNS
1725 * TRUE, If codepage is valid and available on the system,
1726 * FALSE otherwise.
1728 BOOL WINAPI IsValidCodePage( UINT codepage )
1730 switch(codepage) {
1731 case CP_UTF7:
1732 case CP_UTF8:
1733 return TRUE;
1734 default:
1735 return wine_cp_get_table( codepage ) != NULL;
1740 /***********************************************************************
1741 * IsDBCSLeadByteEx (KERNEL32.@)
1743 * Determine if a character is a lead byte in a given code page.
1745 * PARAMS
1746 * codepage [I] Code page for the test.
1747 * testchar [I] Character to test
1749 * RETURNS
1750 * TRUE, if testchar is a lead byte in codepage,
1751 * FALSE otherwise.
1753 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1755 const union cptable *table = get_codepage_table( codepage );
1756 return table && wine_is_dbcs_leadbyte( table, testchar );
1760 /***********************************************************************
1761 * IsDBCSLeadByte (KERNEL32.@)
1762 * IsDBCSLeadByte (KERNEL.207)
1764 * Determine if a character is a lead byte.
1766 * PARAMS
1767 * testchar [I] Character to test
1769 * RETURNS
1770 * TRUE, if testchar is a lead byte in the ANSI code page,
1771 * FALSE otherwise.
1773 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1775 if (!ansi_cptable) return FALSE;
1776 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1780 /***********************************************************************
1781 * GetCPInfo (KERNEL32.@)
1783 * Get information about a code page.
1785 * PARAMS
1786 * codepage [I] Code page number
1787 * cpinfo [O] Destination for code page information
1789 * RETURNS
1790 * Success: TRUE. cpinfo is updated with the information about codepage.
1791 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1793 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1795 const union cptable *table;
1797 if (!cpinfo)
1799 SetLastError( ERROR_INVALID_PARAMETER );
1800 return FALSE;
1803 if (!(table = get_codepage_table( codepage )))
1805 switch(codepage)
1807 case CP_UTF7:
1808 case CP_UTF8:
1809 cpinfo->DefaultChar[0] = 0x3f;
1810 cpinfo->DefaultChar[1] = 0;
1811 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1812 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1813 return TRUE;
1816 SetLastError( ERROR_INVALID_PARAMETER );
1817 return FALSE;
1819 if (table->info.def_char & 0xff00)
1821 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1822 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1824 else
1826 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1827 cpinfo->DefaultChar[1] = 0;
1829 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1830 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1831 else
1832 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1834 return TRUE;
1837 /***********************************************************************
1838 * GetCPInfoExA (KERNEL32.@)
1840 * Get extended information about a code page.
1842 * PARAMS
1843 * codepage [I] Code page number
1844 * dwFlags [I] Reserved, must to 0.
1845 * cpinfo [O] Destination for code page information
1847 * RETURNS
1848 * Success: TRUE. cpinfo is updated with the information about codepage.
1849 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1851 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1853 CPINFOEXW cpinfoW;
1855 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1856 return FALSE;
1858 /* the layout is the same except for CodePageName */
1859 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1860 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1861 return TRUE;
1864 /***********************************************************************
1865 * GetCPInfoExW (KERNEL32.@)
1867 * Unicode version of GetCPInfoExA.
1869 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1871 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1872 return FALSE;
1874 switch(codepage)
1876 case CP_UTF7:
1878 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1880 cpinfo->CodePage = CP_UTF7;
1881 cpinfo->UnicodeDefaultChar = 0x3f;
1882 strcpyW(cpinfo->CodePageName, utf7);
1883 break;
1886 case CP_UTF8:
1888 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1890 cpinfo->CodePage = CP_UTF8;
1891 cpinfo->UnicodeDefaultChar = 0x3f;
1892 strcpyW(cpinfo->CodePageName, utf8);
1893 break;
1896 default:
1898 const union cptable *table = get_codepage_table( codepage );
1900 cpinfo->CodePage = table->info.codepage;
1901 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1902 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1903 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1904 break;
1907 return TRUE;
1910 /***********************************************************************
1911 * EnumSystemCodePagesA (KERNEL32.@)
1913 * Call a user defined function for every code page installed on the system.
1915 * PARAMS
1916 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1917 * flags [I] Reserved, set to 0.
1919 * RETURNS
1920 * TRUE, If all code pages have been enumerated, or
1921 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1923 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1925 const union cptable *table;
1926 char buffer[10];
1927 int index = 0;
1929 for (;;)
1931 if (!(table = wine_cp_enum_table( index++ ))) break;
1932 sprintf( buffer, "%d", table->info.codepage );
1933 if (!lpfnCodePageEnum( buffer )) break;
1935 return TRUE;
1939 /***********************************************************************
1940 * EnumSystemCodePagesW (KERNEL32.@)
1942 * See EnumSystemCodePagesA.
1944 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1946 const union cptable *table;
1947 WCHAR buffer[10], *p;
1948 int page, index = 0;
1950 for (;;)
1952 if (!(table = wine_cp_enum_table( index++ ))) break;
1953 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1954 *--p = 0;
1955 page = table->info.codepage;
1958 *--p = '0' + (page % 10);
1959 page /= 10;
1960 } while( page );
1961 if (!lpfnCodePageEnum( p )) break;
1963 return TRUE;
1967 /***********************************************************************
1968 * utf7_write_w
1970 * Helper for utf7_mbstowcs
1972 * RETURNS
1973 * TRUE on success, FALSE on error
1975 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
1977 if (dstlen > 0)
1979 if (*index >= dstlen)
1980 return FALSE;
1982 dst[*index] = character;
1985 (*index)++;
1987 return TRUE;
1990 /***********************************************************************
1991 * utf7_mbstowcs
1993 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
1995 * RETURNS
1996 * On success, the number of characters written
1997 * On dst buffer overflow, -1
1999 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2001 static const signed char base64_decoding_table[] =
2003 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2004 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2005 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2006 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2007 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2008 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2009 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2010 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2013 const char *source_end = src + srclen;
2014 int dest_index = 0;
2016 DWORD byte_pair = 0;
2017 short offset = 0;
2019 while (src < source_end)
2021 if (*src == '+')
2023 src++;
2024 if (src >= source_end)
2025 break;
2027 if (*src == '-')
2029 /* just a plus sign escaped as +- */
2030 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2031 return -1;
2032 src++;
2033 continue;
2038 signed char sextet = *src;
2039 if (sextet == '-')
2041 /* skip over the dash and end base64 decoding
2042 * the current, unfinished byte pair is discarded */
2043 src++;
2044 offset = 0;
2045 break;
2047 if (sextet < 0)
2049 /* the next character of src is < 0 and therefore not part of a base64 sequence
2050 * the current, unfinished byte pair is NOT discarded in this case
2051 * this is probably a bug in Windows */
2052 break;
2055 sextet = base64_decoding_table[sextet];
2056 if (sextet == -1)
2058 /* -1 means that the next character of src is not part of a base64 sequence
2059 * in other words, all sextets in this base64 sequence have been processed
2060 * the current, unfinished byte pair is discarded */
2061 offset = 0;
2062 break;
2065 byte_pair = (byte_pair << 6) | sextet;
2066 offset += 6;
2068 if (offset >= 16)
2070 /* this byte pair is done */
2071 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2072 return -1;
2073 offset -= 16;
2076 src++;
2078 while (src < source_end);
2080 else
2082 /* we have to convert to unsigned char in case *src < 0 */
2083 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2084 return -1;
2085 src++;
2089 return dest_index;
2092 /***********************************************************************
2093 * MultiByteToWideChar (KERNEL32.@)
2095 * Convert a multibyte character string into a Unicode string.
2097 * PARAMS
2098 * page [I] Codepage character set to convert from
2099 * flags [I] Character mapping flags
2100 * src [I] Source string buffer
2101 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2102 * dst [O] Destination buffer
2103 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2105 * RETURNS
2106 * Success: If dstlen > 0, the number of characters written to dst.
2107 * If dstlen == 0, the number of characters needed to perform the
2108 * conversion. In both cases the count includes the terminating NUL.
2109 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2110 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2111 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2112 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2113 * possible for src.
2115 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2116 LPWSTR dst, INT dstlen )
2118 const union cptable *table;
2119 int ret;
2121 if (!src || !srclen || (!dst && dstlen))
2123 SetLastError( ERROR_INVALID_PARAMETER );
2124 return 0;
2127 if (srclen < 0) srclen = strlen(src) + 1;
2129 switch(page)
2131 case CP_SYMBOL:
2132 if (flags)
2134 SetLastError( ERROR_INVALID_FLAGS );
2135 return 0;
2137 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2138 break;
2139 case CP_UTF7:
2140 if (flags)
2142 SetLastError( ERROR_INVALID_FLAGS );
2143 return 0;
2145 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2146 break;
2147 case CP_UNIXCP:
2148 if (unix_cptable)
2150 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2151 break;
2153 #ifdef __APPLE__
2154 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2155 #endif
2156 /* fall through */
2157 case CP_UTF8:
2158 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2159 break;
2160 default:
2161 if (!(table = get_codepage_table( page )))
2163 SetLastError( ERROR_INVALID_PARAMETER );
2164 return 0;
2166 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2167 break;
2170 if (ret < 0)
2172 switch(ret)
2174 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2175 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2177 ret = 0;
2179 TRACE("cp %d %s -> %s, ret = %d\n",
2180 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2181 return ret;
2185 /***********************************************************************
2186 * utf7_can_directly_encode
2188 * Helper for utf7_wcstombs
2190 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2192 static const BOOL directly_encodable_table[] =
2194 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2195 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2196 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2197 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2198 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2199 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2200 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2201 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2204 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2207 /***********************************************************************
2208 * utf7_write_c
2210 * Helper for utf7_wcstombs
2212 * RETURNS
2213 * TRUE on success, FALSE on error
2215 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2217 if (dstlen > 0)
2219 if (*index >= dstlen)
2220 return FALSE;
2222 dst[*index] = character;
2225 (*index)++;
2227 return TRUE;
2230 /***********************************************************************
2231 * utf7_wcstombs
2233 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2235 * RETURNS
2236 * On success, the number of characters written
2237 * On dst buffer overflow, -1
2239 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2241 static const char base64_encoding_table[] =
2242 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2244 const WCHAR *source_end = src + srclen;
2245 int dest_index = 0;
2247 while (src < source_end)
2249 if (*src == '+')
2251 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2252 return -1;
2253 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2254 return -1;
2255 src++;
2257 else if (utf7_can_directly_encode(*src))
2259 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2260 return -1;
2261 src++;
2263 else
2265 unsigned int offset = 0;
2266 DWORD byte_pair = 0;
2268 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2269 return -1;
2271 while (src < source_end && !utf7_can_directly_encode(*src))
2273 byte_pair = (byte_pair << 16) | *src;
2274 offset += 16;
2275 while (offset >= 6)
2277 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2278 return -1;
2279 offset -= 6;
2281 src++;
2284 if (offset)
2286 /* Windows won't create a padded base64 character if there's no room for the - sign
2287 * as well ; this is probably a bug in Windows */
2288 if (dstlen > 0 && dest_index + 1 >= dstlen)
2289 return -1;
2291 byte_pair <<= (6 - offset);
2292 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2293 return -1;
2296 /* Windows always explicitly terminates the base64 sequence
2297 even though RFC 2152 (page 3, rule 2) does not require this */
2298 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2299 return -1;
2303 return dest_index;
2306 /***********************************************************************
2307 * WideCharToMultiByte (KERNEL32.@)
2309 * Convert a Unicode character string into a multibyte string.
2311 * PARAMS
2312 * page [I] Code page character set to convert to
2313 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2314 * src [I] Source string buffer
2315 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2316 * dst [O] Destination buffer
2317 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2318 * defchar [I] Default character to use for conversion if no exact
2319 * conversion can be made
2320 * used [O] Set if default character was used in the conversion
2322 * RETURNS
2323 * Success: If dstlen > 0, the number of characters written to dst.
2324 * If dstlen == 0, number of characters needed to perform the
2325 * conversion. In both cases the count includes the terminating NUL.
2326 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2327 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2328 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2329 * parameter was given.
2331 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2332 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2334 const union cptable *table;
2335 int ret, used_tmp;
2337 if (!src || !srclen || (!dst && dstlen))
2339 SetLastError( ERROR_INVALID_PARAMETER );
2340 return 0;
2343 if (srclen < 0) srclen = strlenW(src) + 1;
2345 switch(page)
2347 case CP_SYMBOL:
2348 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2349 if (flags)
2351 SetLastError( ERROR_INVALID_FLAGS );
2352 return 0;
2354 if (defchar || used)
2356 SetLastError( ERROR_INVALID_PARAMETER );
2357 return 0;
2359 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2360 break;
2361 case CP_UTF7:
2362 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2363 if (defchar || used)
2365 SetLastError( ERROR_INVALID_PARAMETER );
2366 return 0;
2368 if (flags)
2370 SetLastError( ERROR_INVALID_FLAGS );
2371 return 0;
2373 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2374 break;
2375 case CP_UNIXCP:
2376 if (unix_cptable)
2378 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2379 defchar, used ? &used_tmp : NULL );
2380 break;
2382 /* fall through */
2383 case CP_UTF8:
2384 if (defchar || used)
2386 SetLastError( ERROR_INVALID_PARAMETER );
2387 return 0;
2389 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2390 break;
2391 default:
2392 if (!(table = get_codepage_table( page )))
2394 SetLastError( ERROR_INVALID_PARAMETER );
2395 return 0;
2397 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2398 defchar, used ? &used_tmp : NULL );
2399 if (used) *used = used_tmp;
2400 break;
2403 if (ret < 0)
2405 switch(ret)
2407 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2408 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2410 ret = 0;
2412 TRACE("cp %d %s -> %s, ret = %d\n",
2413 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2414 return ret;
2418 /***********************************************************************
2419 * GetThreadLocale (KERNEL32.@)
2421 * Get the current threads locale.
2423 * PARAMS
2424 * None.
2426 * RETURNS
2427 * The LCID currently associated with the calling thread.
2429 LCID WINAPI GetThreadLocale(void)
2431 LCID ret = NtCurrentTeb()->CurrentLocale;
2432 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2433 return ret;
2436 /**********************************************************************
2437 * SetThreadLocale (KERNEL32.@)
2439 * Set the current threads locale.
2441 * PARAMS
2442 * lcid [I] LCID of the locale to set
2444 * RETURNS
2445 * Success: TRUE. The threads locale is set to lcid.
2446 * Failure: FALSE. Use GetLastError() to determine the cause.
2448 BOOL WINAPI SetThreadLocale( LCID lcid )
2450 TRACE("(0x%04X)\n", lcid);
2452 lcid = ConvertDefaultLocale(lcid);
2454 if (lcid != GetThreadLocale())
2456 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2458 SetLastError(ERROR_INVALID_PARAMETER);
2459 return FALSE;
2462 NtCurrentTeb()->CurrentLocale = lcid;
2464 return TRUE;
2467 /**********************************************************************
2468 * SetThreadUILanguage (KERNEL32.@)
2470 * Set the current threads UI language.
2472 * PARAMS
2473 * langid [I] LANGID of the language to set, or 0 to use
2474 * the available language which is best supported
2475 * for console applications
2477 * RETURNS
2478 * Success: The return value is the same as the input value.
2479 * Failure: The return value differs from the input value.
2480 * Use GetLastError() to determine the cause.
2482 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2484 TRACE("(0x%04x) stub - returning success\n", langid);
2485 return langid;
2488 /******************************************************************************
2489 * ConvertDefaultLocale (KERNEL32.@)
2491 * Convert a default locale identifier into a real identifier.
2493 * PARAMS
2494 * lcid [I] LCID identifier of the locale to convert
2496 * RETURNS
2497 * lcid unchanged, if not a default locale or its sublanguage is
2498 * not SUBLANG_NEUTRAL.
2499 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2500 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2501 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2503 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2505 LANGID langid;
2507 switch (lcid)
2509 case LOCALE_SYSTEM_DEFAULT:
2510 lcid = GetSystemDefaultLCID();
2511 break;
2512 case LOCALE_USER_DEFAULT:
2513 case LOCALE_NEUTRAL:
2514 lcid = GetUserDefaultLCID();
2515 break;
2516 default:
2517 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2518 langid = LANGIDFROMLCID(lcid);
2519 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2521 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2522 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2525 return lcid;
2529 /******************************************************************************
2530 * IsValidLocale (KERNEL32.@)
2532 * Determine if a locale is valid.
2534 * PARAMS
2535 * lcid [I] LCID of the locale to check
2536 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2538 * RETURNS
2539 * TRUE, if lcid is valid,
2540 * FALSE, otherwise.
2542 * NOTES
2543 * Wine does not currently make the distinction between supported and installed. All
2544 * languages supported are installed by default.
2546 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2548 /* check if language is registered in the kernel32 resources */
2549 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2550 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2553 /******************************************************************************
2554 * IsValidLocaleName (KERNEL32.@)
2556 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2558 struct locale_name locale_name;
2560 /* string parsing */
2561 parse_locale_name( locale, &locale_name );
2563 TRACE( "found lcid %x for %s, matches %d\n",
2564 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2566 return locale_name.matches > 0;
2569 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2570 LPCSTR name, WORD LangID, LONG_PTR lParam )
2572 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2573 char buf[20];
2575 sprintf(buf, "%08x", (UINT)LangID);
2576 return lpfnLocaleEnum( buf );
2579 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2580 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2582 static const WCHAR formatW[] = {'%','0','8','x',0};
2583 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2584 WCHAR buf[20];
2585 sprintfW( buf, formatW, (UINT)LangID );
2586 return lpfnLocaleEnum( buf );
2589 /******************************************************************************
2590 * EnumSystemLocalesA (KERNEL32.@)
2592 * Call a users function for each locale available on the system.
2594 * PARAMS
2595 * lpfnLocaleEnum [I] Callback function to call for each locale
2596 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2598 * RETURNS
2599 * Success: TRUE.
2600 * Failure: FALSE. Use GetLastError() to determine the cause.
2602 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2604 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2605 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2606 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2607 (LONG_PTR)lpfnLocaleEnum);
2608 return TRUE;
2612 /******************************************************************************
2613 * EnumSystemLocalesW (KERNEL32.@)
2615 * See EnumSystemLocalesA.
2617 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2619 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2620 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2621 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2622 (LONG_PTR)lpfnLocaleEnum);
2623 return TRUE;
2627 struct enum_locale_ex_data
2629 LOCALE_ENUMPROCEX proc;
2630 DWORD flags;
2631 LPARAM lparam;
2634 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2635 LPCWSTR name, WORD lang, LONG_PTR lparam )
2637 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2638 WCHAR buffer[256];
2639 DWORD neutral;
2640 unsigned int flags;
2642 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2643 buffer, sizeof(buffer) / sizeof(WCHAR) );
2644 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2645 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2646 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2647 neutral = 0;
2648 flags = LOCALE_WINDOWS;
2649 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2650 if (data->flags && !(data->flags & flags)) return TRUE;
2651 return data->proc( buffer, flags, data->lparam );
2654 /******************************************************************************
2655 * EnumSystemLocalesEx (KERNEL32.@)
2657 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2659 struct enum_locale_ex_data data;
2661 if (reserved)
2663 SetLastError( ERROR_INVALID_PARAMETER );
2664 return FALSE;
2666 data.proc = proc;
2667 data.flags = flags;
2668 data.lparam = lparam;
2669 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2670 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2671 enum_locale_ex_proc, (LONG_PTR)&data );
2672 return TRUE;
2676 /***********************************************************************
2677 * VerLanguageNameA (KERNEL32.@)
2679 * Get the name of a language.
2681 * PARAMS
2682 * wLang [I] LANGID of the language
2683 * szLang [O] Destination for the language name
2685 * RETURNS
2686 * Success: The size of the language name. If szLang is non-NULL, it is filled
2687 * with the name.
2688 * Failure: 0. Use GetLastError() to determine the cause.
2691 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2693 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2697 /***********************************************************************
2698 * VerLanguageNameW (KERNEL32.@)
2700 * See VerLanguageNameA.
2702 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2704 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2708 /******************************************************************************
2709 * GetStringTypeW (KERNEL32.@)
2711 * See GetStringTypeA.
2713 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2715 static const unsigned char type2_map[16] =
2717 C2_NOTAPPLICABLE, /* unassigned */
2718 C2_LEFTTORIGHT, /* L */
2719 C2_RIGHTTOLEFT, /* R */
2720 C2_EUROPENUMBER, /* EN */
2721 C2_EUROPESEPARATOR, /* ES */
2722 C2_EUROPETERMINATOR, /* ET */
2723 C2_ARABICNUMBER, /* AN */
2724 C2_COMMONSEPARATOR, /* CS */
2725 C2_BLOCKSEPARATOR, /* B */
2726 C2_SEGMENTSEPARATOR, /* S */
2727 C2_WHITESPACE, /* WS */
2728 C2_OTHERNEUTRAL, /* ON */
2729 C2_RIGHTTOLEFT, /* AL */
2730 C2_NOTAPPLICABLE, /* NSM */
2731 C2_NOTAPPLICABLE, /* BN */
2732 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2735 if (count == -1) count = strlenW(src) + 1;
2736 switch(type)
2738 case CT_CTYPE1:
2739 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2740 break;
2741 case CT_CTYPE2:
2742 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2743 break;
2744 case CT_CTYPE3:
2746 WARN("CT_CTYPE3: semi-stub.\n");
2747 while (count--)
2749 int c = *src;
2750 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2752 type1 = get_char_typeW( *src++ ) & 0xfff;
2753 /* try to construct type3 from type1 */
2754 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2755 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2756 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2757 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2758 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2759 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2760 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2762 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
2763 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
2765 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2766 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2767 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2768 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2769 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2770 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2771 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2772 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2774 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2775 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2776 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2777 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2778 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2779 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2780 *chartype++ = type3;
2782 break;
2784 default:
2785 SetLastError( ERROR_INVALID_PARAMETER );
2786 return FALSE;
2788 return TRUE;
2792 /******************************************************************************
2793 * GetStringTypeExW (KERNEL32.@)
2795 * See GetStringTypeExA.
2797 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2799 /* locale is ignored for Unicode */
2800 return GetStringTypeW( type, src, count, chartype );
2804 /******************************************************************************
2805 * GetStringTypeA (KERNEL32.@)
2807 * Get characteristics of the characters making up a string.
2809 * PARAMS
2810 * locale [I] Locale Id for the string
2811 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2812 * src [I] String to analyse
2813 * count [I] Length of src in chars, or -1 if src is NUL terminated
2814 * chartype [O] Destination for the calculated characteristics
2816 * RETURNS
2817 * Success: TRUE. chartype is filled with the requested characteristics of each char
2818 * in src.
2819 * Failure: FALSE. Use GetLastError() to determine the cause.
2821 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2823 UINT cp;
2824 INT countW;
2825 LPWSTR srcW;
2826 BOOL ret = FALSE;
2828 if(count == -1) count = strlen(src) + 1;
2830 if (!(cp = get_lcid_codepage( locale )))
2832 FIXME("For locale %04x using current ANSI code page\n", locale);
2833 cp = GetACP();
2836 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2837 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2839 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2841 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2842 * string, with multibyte characters there maybe be more bytes in count
2843 * than character space in the buffer!
2845 ret = GetStringTypeW(type, srcW, countW, chartype);
2846 HeapFree(GetProcessHeap(), 0, srcW);
2848 return ret;
2851 /******************************************************************************
2852 * GetStringTypeExA (KERNEL32.@)
2854 * Get characteristics of the characters making up a string.
2856 * PARAMS
2857 * locale [I] Locale Id for the string
2858 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2859 * src [I] String to analyse
2860 * count [I] Length of src in chars, or -1 if src is NUL terminated
2861 * chartype [O] Destination for the calculated characteristics
2863 * RETURNS
2864 * Success: TRUE. chartype is filled with the requested characteristics of each char
2865 * in src.
2866 * Failure: FALSE. Use GetLastError() to determine the cause.
2868 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2870 return GetStringTypeA(locale, type, src, count, chartype);
2873 /*************************************************************************
2874 * LCMapStringEx (KERNEL32.@)
2876 * Map characters in a locale sensitive string.
2878 * PARAMS
2879 * name [I] Locale name for the conversion.
2880 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2881 * src [I] String to map
2882 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2883 * dst [O] Destination for mapped string
2884 * dstlen [I] Length of dst in characters
2885 * version [I] reserved, must be NULL
2886 * reserved [I] reserved, must be NULL
2887 * lparam [I] reserved, must be 0
2889 * RETURNS
2890 * Success: The length of the mapped string in dst, including the NUL terminator.
2891 * Failure: 0. Use GetLastError() to determine the cause.
2893 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2894 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2896 LPWSTR dst_ptr;
2898 if (version) FIXME("unsupported version structure %p\n", version);
2899 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2900 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2902 if (!src || !srclen || dstlen < 0)
2904 SetLastError(ERROR_INVALID_PARAMETER);
2905 return 0;
2908 /* mutually exclusive flags */
2909 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2910 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2911 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2912 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2914 SetLastError(ERROR_INVALID_FLAGS);
2915 return 0;
2918 if (!dstlen) dst = NULL;
2920 if (flags & LCMAP_SORTKEY)
2922 INT ret;
2923 if (src == dst)
2925 SetLastError(ERROR_INVALID_FLAGS);
2926 return 0;
2929 if (srclen < 0) srclen = strlenW(src);
2931 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2932 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2934 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2935 if (ret == 0)
2936 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2937 else
2938 ret++;
2939 return ret;
2942 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2943 if (flags & SORT_STRINGSORT)
2945 SetLastError(ERROR_INVALID_FLAGS);
2946 return 0;
2949 if (srclen < 0) srclen = strlenW(src) + 1;
2951 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2952 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2954 if (!dst) /* return required string length */
2956 INT len;
2958 for (len = 0; srclen; src++, srclen--)
2960 WCHAR wch = *src;
2961 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2962 * and skips white space and punctuation characters for
2963 * NORM_IGNORESYMBOLS.
2965 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2966 continue;
2967 len++;
2969 return len;
2972 if (flags & LCMAP_UPPERCASE)
2974 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2976 WCHAR wch = *src;
2977 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2978 continue;
2979 *dst_ptr++ = toupperW(wch);
2980 dstlen--;
2983 else if (flags & LCMAP_LOWERCASE)
2985 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2987 WCHAR wch = *src;
2988 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2989 continue;
2990 *dst_ptr++ = tolowerW(wch);
2991 dstlen--;
2994 else
2996 if (src == dst)
2998 SetLastError(ERROR_INVALID_FLAGS);
2999 return 0;
3001 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3003 WCHAR wch = *src;
3004 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3005 continue;
3006 *dst_ptr++ = wch;
3007 dstlen--;
3011 if (srclen)
3013 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3014 return 0;
3017 return dst_ptr - dst;
3020 /*************************************************************************
3021 * LCMapStringW (KERNEL32.@)
3023 * See LCMapStringA.
3025 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3026 LPWSTR dst, INT dstlen)
3028 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3029 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3031 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3034 /*************************************************************************
3035 * LCMapStringA (KERNEL32.@)
3037 * Map characters in a locale sensitive string.
3039 * PARAMS
3040 * lcid [I] LCID for the conversion.
3041 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3042 * src [I] String to map
3043 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3044 * dst [O] Destination for mapped string
3045 * dstlen [I] Length of dst in characters
3047 * RETURNS
3048 * Success: The length of the mapped string in dst, including the NUL terminator.
3049 * Failure: 0. Use GetLastError() to determine the cause.
3051 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3052 LPSTR dst, INT dstlen)
3054 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3055 LPWSTR srcW, dstW;
3056 INT ret = 0, srclenW, dstlenW;
3057 UINT locale_cp = CP_ACP;
3059 if (!src || !srclen || dstlen < 0)
3061 SetLastError(ERROR_INVALID_PARAMETER);
3062 return 0;
3065 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3067 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3068 if (srclenW)
3069 srcW = bufW;
3070 else
3072 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3073 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3074 if (!srcW)
3076 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3077 return 0;
3079 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3082 if (flags & LCMAP_SORTKEY)
3084 if (src == dst)
3086 SetLastError(ERROR_INVALID_FLAGS);
3087 goto map_string_exit;
3089 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3090 if (ret == 0)
3091 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3092 else
3093 ret++;
3094 goto map_string_exit;
3097 if (flags & SORT_STRINGSORT)
3099 SetLastError(ERROR_INVALID_FLAGS);
3100 goto map_string_exit;
3103 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3104 if (!dstlenW)
3105 goto map_string_exit;
3107 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3108 if (!dstW)
3110 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3111 goto map_string_exit;
3114 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3115 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3116 HeapFree(GetProcessHeap(), 0, dstW);
3118 map_string_exit:
3119 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3120 return ret;
3123 /*************************************************************************
3124 * FoldStringA (KERNEL32.@)
3126 * Map characters in a string.
3128 * PARAMS
3129 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3130 * src [I] String to map
3131 * srclen [I] Length of src, or -1 if src is NUL terminated
3132 * dst [O] Destination for mapped string
3133 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3135 * RETURNS
3136 * Success: The length of the string written to dst, including the terminating NUL. If
3137 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3138 * and dst may be NULL.
3139 * Failure: 0. Use GetLastError() to determine the cause.
3141 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3142 LPSTR dst, INT dstlen)
3144 INT ret = 0, srclenW = 0;
3145 WCHAR *srcW = NULL, *dstW = NULL;
3147 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3149 SetLastError(ERROR_INVALID_PARAMETER);
3150 return 0;
3153 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3154 src, srclen, NULL, 0);
3155 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3157 if (!srcW)
3159 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3160 goto FoldStringA_exit;
3163 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3164 src, srclen, srcW, srclenW);
3166 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3168 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3169 if (ret && dstlen)
3171 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3173 if (!dstW)
3175 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3176 goto FoldStringA_exit;
3179 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3180 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3182 ret = 0;
3183 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3187 HeapFree(GetProcessHeap(), 0, dstW);
3189 FoldStringA_exit:
3190 HeapFree(GetProcessHeap(), 0, srcW);
3191 return ret;
3194 /*************************************************************************
3195 * FoldStringW (KERNEL32.@)
3197 * See FoldStringA.
3199 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3200 LPWSTR dst, INT dstlen)
3202 int ret;
3204 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3206 case 0:
3207 if (dwFlags)
3208 break;
3209 /* Fall through for dwFlags == 0 */
3210 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3211 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3212 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3213 SetLastError(ERROR_INVALID_FLAGS);
3214 return 0;
3217 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3219 SetLastError(ERROR_INVALID_PARAMETER);
3220 return 0;
3223 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3224 if (!ret)
3225 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3226 return ret;
3229 /******************************************************************************
3230 * CompareStringW (KERNEL32.@)
3232 * See CompareStringA.
3234 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3235 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3237 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3240 /******************************************************************************
3241 * CompareStringEx (KERNEL32.@)
3243 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3244 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3246 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3247 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3248 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3249 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3250 INT ret;
3252 if (version) FIXME("unexpected version parameter\n");
3253 if (reserved) FIXME("unexpected reserved value\n");
3254 if (lParam) FIXME("unexpected lParam\n");
3256 if (!str1 || !str2)
3258 SetLastError(ERROR_INVALID_PARAMETER);
3259 return 0;
3262 if (flags & ~(supported_flags|semistub_flags))
3264 SetLastError(ERROR_INVALID_FLAGS);
3265 return 0;
3268 if (flags & semistub_flags)
3269 FIXME("semi-stub behavor for flag(s) 0x%x\n", flags & semistub_flags);
3271 if (len1 < 0) len1 = strlenW(str1);
3272 if (len2 < 0) len2 = strlenW(str2);
3274 ret = wine_compare_string(flags, str1, len1, str2, len2);
3276 if (ret) /* need to translate result */
3277 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3278 return CSTR_EQUAL;
3281 /******************************************************************************
3282 * CompareStringA (KERNEL32.@)
3284 * Compare two locale sensitive strings.
3286 * PARAMS
3287 * lcid [I] LCID for the comparison
3288 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3289 * str1 [I] First string to compare
3290 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3291 * str2 [I] Second string to compare
3292 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3294 * RETURNS
3295 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3296 * str1 is less than, equal to or greater than str2 respectively.
3297 * Failure: FALSE. Use GetLastError() to determine the cause.
3299 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3300 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3302 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3303 WCHAR *buf2W = buf1W + 130;
3304 LPWSTR str1W, str2W;
3305 INT len1W = 0, len2W = 0, ret;
3306 UINT locale_cp = CP_ACP;
3308 if (!str1 || !str2)
3310 SetLastError(ERROR_INVALID_PARAMETER);
3311 return 0;
3313 if (len1 < 0) len1 = strlen(str1);
3314 if (len2 < 0) len2 = strlen(str2);
3316 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3318 if (len1)
3320 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3321 if (len1W)
3322 str1W = buf1W;
3323 else
3325 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3326 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3327 if (!str1W)
3329 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3330 return 0;
3332 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3335 else
3337 len1W = 0;
3338 str1W = buf1W;
3341 if (len2)
3343 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3344 if (len2W)
3345 str2W = buf2W;
3346 else
3348 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3349 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3350 if (!str2W)
3352 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3353 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3354 return 0;
3356 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3359 else
3361 len2W = 0;
3362 str2W = buf2W;
3365 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3367 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3368 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3369 return ret;
3372 /******************************************************************************
3373 * CompareStringOrdinal (KERNEL32.@)
3375 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3377 int ret, len;
3379 if (!str1 || !str2)
3381 SetLastError(ERROR_INVALID_PARAMETER);
3382 return 0;
3384 if (len1 < 0) len1 = strlenW(str1);
3385 if (len2 < 0) len2 = strlenW(str2);
3387 len = min(len1, len2);
3388 if (ignore_case)
3390 ret = memicmpW(str1, str2, len);
3392 else
3394 ret = 0;
3395 for (; len > 0; len--)
3396 if ((ret = (*str1++ - *str2++))) break;
3398 if (!ret) ret = len1 - len2;
3400 if (ret < 0) return CSTR_LESS_THAN;
3401 if (ret > 0) return CSTR_GREATER_THAN;
3402 return CSTR_EQUAL;
3405 /*************************************************************************
3406 * lstrcmp (KERNEL32.@)
3407 * lstrcmpA (KERNEL32.@)
3409 * Compare two strings using the current thread locale.
3411 * PARAMS
3412 * str1 [I] First string to compare
3413 * str2 [I] Second string to compare
3415 * RETURNS
3416 * Success: A number less than, equal to or greater than 0 depending on whether
3417 * str1 is less than, equal to or greater than str2 respectively.
3418 * Failure: FALSE. Use GetLastError() to determine the cause.
3420 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3422 int ret;
3424 if ((str1 == NULL) && (str2 == NULL)) return 0;
3425 if (str1 == NULL) return -1;
3426 if (str2 == NULL) return 1;
3428 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3429 if (ret) ret -= 2;
3431 return ret;
3434 /*************************************************************************
3435 * lstrcmpi (KERNEL32.@)
3436 * lstrcmpiA (KERNEL32.@)
3438 * Compare two strings using the current thread locale, ignoring case.
3440 * PARAMS
3441 * str1 [I] First string to compare
3442 * str2 [I] Second string to compare
3444 * RETURNS
3445 * Success: A number less than, equal to or greater than 0 depending on whether
3446 * str2 is less than, equal to or greater than str1 respectively.
3447 * Failure: FALSE. Use GetLastError() to determine the cause.
3449 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3451 int ret;
3453 if ((str1 == NULL) && (str2 == NULL)) return 0;
3454 if (str1 == NULL) return -1;
3455 if (str2 == NULL) return 1;
3457 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3458 if (ret) ret -= 2;
3460 return ret;
3463 /*************************************************************************
3464 * lstrcmpW (KERNEL32.@)
3466 * See lstrcmpA.
3468 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3470 int ret;
3472 if ((str1 == NULL) && (str2 == NULL)) return 0;
3473 if (str1 == NULL) return -1;
3474 if (str2 == NULL) return 1;
3476 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3477 if (ret) ret -= 2;
3479 return ret;
3482 /*************************************************************************
3483 * lstrcmpiW (KERNEL32.@)
3485 * See lstrcmpiA.
3487 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3489 int ret;
3491 if ((str1 == NULL) && (str2 == NULL)) return 0;
3492 if (str1 == NULL) return -1;
3493 if (str2 == NULL) return 1;
3495 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3496 if (ret) ret -= 2;
3498 return ret;
3501 /******************************************************************************
3502 * LOCALE_Init
3504 void LOCALE_Init(void)
3506 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3507 const union cptable *unix_cp );
3509 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3511 #ifdef __APPLE__
3512 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3513 char user_locale[50];
3515 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3516 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3517 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3518 CFStringRef user_locale_string_ref;
3520 if (user_locale_country_ref)
3522 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3523 user_locale_lang_ref, user_locale_country_ref);
3525 else
3527 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3530 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3531 strcat(user_locale, ".UTF-8");
3533 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3534 setenv( "LANG", user_locale, 0 );
3535 TRACE( "setting locale to '%s'\n", user_locale );
3536 #endif /* __APPLE__ */
3538 setlocale( LC_ALL, "" );
3540 unix_cp = setup_unix_locales();
3541 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3543 #ifdef __APPLE__
3544 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3545 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3547 /* Retrieve the preferred language as chosen in System Preferences. */
3548 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3549 leave things be. */
3550 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3551 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3552 CFStringRef user_language_string_ref;
3553 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3554 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3555 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3556 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3558 struct locale_name locale_name;
3559 WCHAR buffer[128];
3560 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3561 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3562 parse_locale_name( buffer, &locale_name );
3563 lcid_LC_MESSAGES = locale_name.lcid;
3564 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3566 if (preferred_langs)
3567 CFRelease( preferred_langs );
3568 if (canonical_lang_string_ref)
3569 CFRelease( canonical_lang_string_ref );
3572 CFRelease( user_locale_ref );
3573 CFRelease( user_locale_string_ref );
3574 #endif
3576 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3577 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3578 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3580 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3581 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3582 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3583 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3584 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3585 if (!unix_cp)
3586 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3587 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3589 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3590 ansi_cptable = wine_cp_get_table( 1252 );
3591 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3592 oem_cptable = wine_cp_get_table( 437 );
3593 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3594 mac_cptable = wine_cp_get_table( 10000 );
3595 if (unix_cp != CP_UTF8)
3597 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3598 unix_cptable = wine_cp_get_table( 28591 );
3601 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3603 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3604 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3605 mac_cptable->info.codepage, unix_cp );
3607 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3610 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3612 UNICODE_STRING keyName;
3613 OBJECT_ATTRIBUTES attr;
3614 HANDLE hkey;
3616 RtlInitUnicodeString( &keyName, szKeyName );
3617 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3619 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3620 hkey = 0;
3622 return hkey;
3625 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3626 LPWSTR szValueName, ULONG valueNameSize,
3627 LPWSTR szValueData, ULONG valueDataSize)
3629 BYTE buffer[80];
3630 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3631 DWORD dwLen;
3633 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3634 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3635 info->NameLength > valueNameSize ||
3636 info->DataLength > valueDataSize)
3638 return FALSE;
3641 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3643 memcpy( szValueName, info->Name, info->NameLength);
3644 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3645 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3646 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3648 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3649 return TRUE;
3652 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3654 BYTE buffer[128];
3655 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3656 DWORD dwSize = sizeof(buffer);
3657 UNICODE_STRING valueName;
3659 RtlInitUnicodeString( &valueName, szValueName );
3661 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3662 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3663 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3664 info->DataLength == sizeof(DWORD))
3666 memcpy(lpVal, info->Data, sizeof(DWORD));
3667 return TRUE;
3670 return FALSE;
3673 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3675 LANGID langId;
3676 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3677 HRSRC hResource;
3678 BOOL bRet = FALSE;
3680 /* FIXME: Is it correct to use the system default langid? */
3681 langId = GetSystemDefaultLangID();
3683 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3684 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3686 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3688 if (hResource)
3690 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3692 if (hResDir)
3694 ULONG iResourceIndex = lgrpid & 0xf;
3695 LPCWSTR lpResEntry = LockResource( hResDir );
3696 ULONG i;
3698 for (i = 0; i < iResourceIndex; i++)
3699 lpResEntry += *lpResEntry + 1;
3701 if (*lpResEntry < nameSize)
3703 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3704 szName[*lpResEntry] = '\0';
3705 bRet = TRUE;
3709 FreeResource( hResource );
3711 return bRet;
3714 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3715 typedef struct
3717 LANGUAGEGROUP_ENUMPROCA procA;
3718 LANGUAGEGROUP_ENUMPROCW procW;
3719 DWORD dwFlags;
3720 LONG_PTR lParam;
3721 } ENUMLANGUAGEGROUP_CALLBACKS;
3723 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3724 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3726 WCHAR szNumber[10], szValue[4];
3727 HANDLE hKey;
3728 BOOL bContinue = TRUE;
3729 ULONG ulIndex = 0;
3731 if (!lpProcs)
3733 SetLastError(ERROR_INVALID_PARAMETER);
3734 return FALSE;
3737 switch (lpProcs->dwFlags)
3739 case 0:
3740 /* Default to LGRPID_INSTALLED */
3741 lpProcs->dwFlags = LGRPID_INSTALLED;
3742 /* Fall through... */
3743 case LGRPID_INSTALLED:
3744 case LGRPID_SUPPORTED:
3745 break;
3746 default:
3747 SetLastError(ERROR_INVALID_FLAGS);
3748 return FALSE;
3751 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3753 if (!hKey)
3754 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3756 while (bContinue)
3758 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3759 szValue, sizeof(szValue) ))
3761 BOOL bInstalled = szValue[0] == '1';
3762 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3764 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3765 bInstalled ? "" : "not ");
3767 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3769 WCHAR szGrpName[48];
3771 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3772 szGrpName[0] = '\0';
3774 if (lpProcs->procW)
3775 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3776 lpProcs->lParam );
3777 else
3779 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3780 char szGrpNameA[48];
3782 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3783 * or whether the language names are ever localised. Assume CP_ACP.
3786 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3787 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3789 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3790 lpProcs->lParam );
3794 ulIndex++;
3796 else
3797 bContinue = FALSE;
3799 if (!bContinue)
3800 break;
3803 if (hKey)
3804 NtClose( hKey );
3806 return TRUE;
3809 /******************************************************************************
3810 * EnumSystemLanguageGroupsA (KERNEL32.@)
3812 * Call a users function for each language group available on the system.
3814 * PARAMS
3815 * pLangGrpEnumProc [I] Callback function to call for each language group
3816 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3817 * lParam [I] User parameter to pass to pLangGrpEnumProc
3819 * RETURNS
3820 * Success: TRUE.
3821 * Failure: FALSE. Use GetLastError() to determine the cause.
3823 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3824 DWORD dwFlags, LONG_PTR lParam)
3826 ENUMLANGUAGEGROUP_CALLBACKS procs;
3828 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3830 procs.procA = pLangGrpEnumProc;
3831 procs.procW = NULL;
3832 procs.dwFlags = dwFlags;
3833 procs.lParam = lParam;
3835 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3838 /******************************************************************************
3839 * EnumSystemLanguageGroupsW (KERNEL32.@)
3841 * See EnumSystemLanguageGroupsA.
3843 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3844 DWORD dwFlags, LONG_PTR lParam)
3846 ENUMLANGUAGEGROUP_CALLBACKS procs;
3848 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3850 procs.procA = NULL;
3851 procs.procW = pLangGrpEnumProc;
3852 procs.dwFlags = dwFlags;
3853 procs.lParam = lParam;
3855 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3858 /******************************************************************************
3859 * IsValidLanguageGroup (KERNEL32.@)
3861 * Determine if a language group is supported and/or installed.
3863 * PARAMS
3864 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3865 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3867 * RETURNS
3868 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3869 * FALSE otherwise.
3871 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3873 static const WCHAR szFormat[] = { '%','x','\0' };
3874 WCHAR szValueName[16], szValue[2];
3875 BOOL bSupported = FALSE, bInstalled = FALSE;
3876 HANDLE hKey;
3879 switch (dwFlags)
3881 case LGRPID_INSTALLED:
3882 case LGRPID_SUPPORTED:
3884 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3886 sprintfW( szValueName, szFormat, lgrpid );
3888 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3890 bSupported = TRUE;
3892 if (szValue[0] == '1')
3893 bInstalled = TRUE;
3896 if (hKey)
3897 NtClose( hKey );
3899 break;
3902 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3903 (dwFlags == LGRPID_INSTALLED && bInstalled))
3904 return TRUE;
3906 return FALSE;
3909 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3910 typedef struct
3912 LANGGROUPLOCALE_ENUMPROCA procA;
3913 LANGGROUPLOCALE_ENUMPROCW procW;
3914 DWORD dwFlags;
3915 LGRPID lgrpid;
3916 LONG_PTR lParam;
3917 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3919 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3920 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3922 static const WCHAR szAlternateSortsKeyName[] = {
3923 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3925 WCHAR szNumber[10], szValue[4];
3926 HANDLE hKey;
3927 BOOL bContinue = TRUE, bAlternate = FALSE;
3928 LGRPID lgrpid;
3929 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3931 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3933 SetLastError(ERROR_INVALID_PARAMETER);
3934 return FALSE;
3937 if (lpProcs->dwFlags)
3939 SetLastError(ERROR_INVALID_FLAGS);
3940 return FALSE;
3943 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3945 if (!hKey)
3946 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3948 while (bContinue)
3950 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3951 szValue, sizeof(szValue) ))
3953 lgrpid = strtoulW( szValue, NULL, 16 );
3955 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3956 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3958 if (lgrpid == lpProcs->lgrpid)
3960 LCID lcid;
3962 lcid = strtoulW( szNumber, NULL, 16 );
3964 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3965 * '00000437 ;Georgian'
3966 * At present we only pass the LCID string.
3969 if (lpProcs->procW)
3970 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3971 else
3973 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3975 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3977 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3981 ulIndex++;
3983 else
3985 /* Finished enumerating this key */
3986 if (!bAlternate)
3988 /* Enumerate alternate sorts also */
3989 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3990 bAlternate = TRUE;
3991 ulIndex = 0;
3993 else
3994 bContinue = FALSE; /* Finished both keys */
3997 if (!bContinue)
3998 break;
4001 if (hKey)
4002 NtClose( hKey );
4004 return TRUE;
4007 /******************************************************************************
4008 * EnumLanguageGroupLocalesA (KERNEL32.@)
4010 * Call a users function for every locale in a language group available on the system.
4012 * PARAMS
4013 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4014 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4015 * dwFlags [I] Reserved, set to 0
4016 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4018 * RETURNS
4019 * Success: TRUE.
4020 * Failure: FALSE. Use GetLastError() to determine the cause.
4022 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4023 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4025 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4027 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4029 callbacks.procA = pLangGrpLcEnumProc;
4030 callbacks.procW = NULL;
4031 callbacks.dwFlags = dwFlags;
4032 callbacks.lgrpid = lgrpid;
4033 callbacks.lParam = lParam;
4035 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4038 /******************************************************************************
4039 * EnumLanguageGroupLocalesW (KERNEL32.@)
4041 * See EnumLanguageGroupLocalesA.
4043 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4044 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4046 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4048 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4050 callbacks.procA = NULL;
4051 callbacks.procW = pLangGrpLcEnumProc;
4052 callbacks.dwFlags = dwFlags;
4053 callbacks.lgrpid = lgrpid;
4054 callbacks.lParam = lParam;
4056 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4059 /******************************************************************************
4060 * InvalidateNLSCache (KERNEL32.@)
4062 * Invalidate the cache of NLS values.
4064 * PARAMS
4065 * None.
4067 * RETURNS
4068 * Success: TRUE.
4069 * Failure: FALSE.
4071 BOOL WINAPI InvalidateNLSCache(void)
4073 FIXME("() stub\n");
4074 return FALSE;
4077 /******************************************************************************
4078 * GetUserGeoID (KERNEL32.@)
4080 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4082 GEOID ret = GEOID_NOT_AVAILABLE;
4083 static const WCHAR geoW[] = {'G','e','o',0};
4084 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4085 WCHAR bufferW[40], *end;
4086 DWORD count;
4087 HANDLE hkey, hSubkey = 0;
4088 UNICODE_STRING keyW;
4089 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4090 RtlInitUnicodeString( &keyW, nationW );
4091 count = sizeof(bufferW);
4093 if(!(hkey = create_registry_key())) return ret;
4095 switch( GeoClass ){
4096 case GEOCLASS_NATION:
4097 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4099 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4100 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4101 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4103 break;
4104 case GEOCLASS_REGION:
4105 FIXME("GEOCLASS_REGION not handled yet\n");
4106 break;
4109 NtClose(hkey);
4110 if (hSubkey) NtClose(hSubkey);
4111 return ret;
4114 /******************************************************************************
4115 * SetUserGeoID (KERNEL32.@)
4117 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4119 static const WCHAR geoW[] = {'G','e','o',0};
4120 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4121 static const WCHAR formatW[] = {'%','i',0};
4122 UNICODE_STRING nameW,keyW;
4123 WCHAR bufferW[10];
4124 OBJECT_ATTRIBUTES attr;
4125 HANDLE hkey;
4127 if(!(hkey = create_registry_key())) return FALSE;
4129 attr.Length = sizeof(attr);
4130 attr.RootDirectory = hkey;
4131 attr.ObjectName = &nameW;
4132 attr.Attributes = 0;
4133 attr.SecurityDescriptor = NULL;
4134 attr.SecurityQualityOfService = NULL;
4135 RtlInitUnicodeString( &nameW, geoW );
4136 RtlInitUnicodeString( &keyW, nationW );
4138 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4141 NtClose(attr.RootDirectory);
4142 return FALSE;
4145 sprintfW(bufferW, formatW, GeoID);
4146 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4147 NtClose(attr.RootDirectory);
4148 NtClose(hkey);
4149 return TRUE;
4152 typedef struct
4154 union
4156 UILANGUAGE_ENUMPROCA procA;
4157 UILANGUAGE_ENUMPROCW procW;
4158 } u;
4159 DWORD flags;
4160 LONG_PTR param;
4161 } ENUM_UILANG_CALLBACK;
4163 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4164 LPCSTR name, WORD LangID, LONG_PTR lParam )
4166 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4167 char buf[20];
4169 sprintf(buf, "%08x", (UINT)LangID);
4170 return enum_uilang->u.procA( buf, enum_uilang->param );
4173 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4174 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4176 static const WCHAR formatW[] = {'%','0','8','x',0};
4177 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4178 WCHAR buf[20];
4180 sprintfW( buf, formatW, (UINT)LangID );
4181 return enum_uilang->u.procW( buf, enum_uilang->param );
4184 /******************************************************************************
4185 * EnumUILanguagesA (KERNEL32.@)
4187 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4189 ENUM_UILANG_CALLBACK enum_uilang;
4191 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4193 if(!pUILangEnumProc) {
4194 SetLastError(ERROR_INVALID_PARAMETER);
4195 return FALSE;
4197 if(dwFlags) {
4198 SetLastError(ERROR_INVALID_FLAGS);
4199 return FALSE;
4202 enum_uilang.u.procA = pUILangEnumProc;
4203 enum_uilang.flags = dwFlags;
4204 enum_uilang.param = lParam;
4206 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4207 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4208 (LONG_PTR)&enum_uilang);
4209 return TRUE;
4212 /******************************************************************************
4213 * EnumUILanguagesW (KERNEL32.@)
4215 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4217 ENUM_UILANG_CALLBACK enum_uilang;
4219 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4222 if(!pUILangEnumProc) {
4223 SetLastError(ERROR_INVALID_PARAMETER);
4224 return FALSE;
4226 if(dwFlags) {
4227 SetLastError(ERROR_INVALID_FLAGS);
4228 return FALSE;
4231 enum_uilang.u.procW = pUILangEnumProc;
4232 enum_uilang.flags = dwFlags;
4233 enum_uilang.param = lParam;
4235 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4236 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4237 (LONG_PTR)&enum_uilang);
4238 return TRUE;
4241 enum locationkind {
4242 LOCATION_NATION = 0,
4243 LOCATION_REGION,
4244 LOCATION_BOTH
4247 struct geoinfo_t {
4248 GEOID id;
4249 WCHAR iso2W[3];
4250 WCHAR iso3W[4];
4251 GEOID parent;
4252 INT uncode;
4253 enum locationkind kind;
4256 static const struct geoinfo_t geoinfodata[] = {
4257 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4258 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4259 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4260 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4261 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4262 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4263 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4264 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4265 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4266 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4267 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4268 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4269 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4270 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4271 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4272 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4273 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4274 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4275 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4276 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4277 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4278 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4279 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4280 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4281 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4282 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4283 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4284 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4285 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4286 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4287 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4288 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4289 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4290 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4291 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4292 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4293 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4294 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4295 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4296 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4297 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4298 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4299 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4300 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4301 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4302 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4303 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4304 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4305 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4306 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4307 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4308 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4309 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4310 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4311 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4312 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4313 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4314 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4315 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4316 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4317 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4318 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4319 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4320 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4321 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4322 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4323 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4324 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4325 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4326 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4327 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4328 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4329 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4330 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4331 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4332 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4333 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4334 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4335 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4336 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4337 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4338 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4339 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4340 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4341 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4342 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4343 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4344 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4345 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4346 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4347 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4348 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4349 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4350 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4351 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4352 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4353 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4354 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4355 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4356 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4357 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4358 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4359 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4360 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4361 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4362 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4363 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4364 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4365 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4366 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4367 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4368 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4369 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4370 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4371 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4372 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4373 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4374 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4375 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4376 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4377 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4378 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4379 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4380 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4381 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4382 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4383 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4384 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4385 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4386 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4387 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4388 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4389 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4390 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4391 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4392 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4393 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4394 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4395 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4396 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4397 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4398 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4399 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4400 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4401 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4402 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4403 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4404 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4405 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4406 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4407 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4408 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4409 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4410 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4411 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4412 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4413 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4414 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4415 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4416 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4417 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4418 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4419 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4420 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4421 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4422 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4423 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4424 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4425 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4426 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4427 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4428 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4429 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4430 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4431 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4432 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4433 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4434 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4435 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4436 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4437 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4438 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4439 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4440 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4441 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4442 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4443 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4444 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4445 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4446 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4447 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4448 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4449 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4450 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4451 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4452 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4453 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4454 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4455 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4456 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4457 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4458 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4459 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4460 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4461 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4462 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4463 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4464 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4465 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4466 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4467 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4468 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4469 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4470 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4471 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4472 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4473 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4474 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4475 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4476 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4477 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4478 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4479 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4480 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4481 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4482 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4483 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4484 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4485 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4486 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4487 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4488 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4489 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4490 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4491 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4492 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4493 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4494 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4495 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4496 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4497 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4498 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4499 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4500 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4501 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4502 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4503 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4504 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4505 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4506 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4507 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4508 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4509 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4510 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4511 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4512 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4513 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4514 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4515 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4516 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4517 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4518 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4519 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4520 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4521 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4522 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4523 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4524 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4525 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4526 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4527 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4528 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4529 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4530 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4531 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4532 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4533 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4534 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4535 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4536 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4537 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4538 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4539 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4540 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4541 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4542 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4543 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4544 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4545 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4546 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4547 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4548 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4549 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4550 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4551 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4552 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4553 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4554 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4555 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4558 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4560 int min, max;
4562 min = 0;
4563 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4565 while (min <= max) {
4566 const struct geoinfo_t *ptr;
4567 int n = (min+max)/2;
4569 ptr = &geoinfodata[n];
4570 if (geoid == ptr->id)
4571 /* we don't need empty entries */
4572 return *ptr->iso2W ? ptr : NULL;
4574 if (ptr->id > geoid)
4575 max = n-1;
4576 else
4577 min = n+1;
4580 return NULL;
4583 /******************************************************************************
4584 * GetGeoInfoW (KERNEL32.@)
4586 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4588 const struct geoinfo_t *ptr;
4589 const WCHAR *str = NULL;
4590 WCHAR buffW[12];
4591 LONG val = 0;
4592 INT len;
4594 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4596 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4597 SetLastError(ERROR_INVALID_PARAMETER);
4598 return 0;
4601 switch (geotype) {
4602 case GEO_NATION:
4603 val = geoid;
4604 break;
4605 case GEO_ISO_UN_NUMBER:
4606 val = ptr->uncode;
4607 break;
4608 case GEO_PARENT:
4609 val = ptr->parent;
4610 break;
4611 case GEO_ISO2:
4612 case GEO_ISO3:
4614 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4615 break;
4617 case GEO_RFC1766:
4618 case GEO_LCID:
4619 case GEO_FRIENDLYNAME:
4620 case GEO_OFFICIALNAME:
4621 case GEO_TIMEZONES:
4622 case GEO_OFFICIALLANGUAGES:
4623 case GEO_LATITUDE:
4624 case GEO_LONGITUDE:
4625 FIXME("type %d is not supported\n", geotype);
4626 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4627 return 0;
4628 default:
4629 WARN("unrecognized type %d\n", geotype);
4630 SetLastError(ERROR_INVALID_FLAGS);
4631 return 0;
4634 if (val) {
4635 static const WCHAR fmtW[] = {'%','d',0};
4636 sprintfW(buffW, fmtW, val);
4637 str = buffW;
4640 len = strlenW(str) + 1;
4641 if (!data || !data_len)
4642 return len;
4644 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4645 if (data_len < len)
4646 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4647 return data_len < len ? 0 : len;
4650 /******************************************************************************
4651 * GetGeoInfoA (KERNEL32.@)
4653 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4655 WCHAR *buffW;
4656 INT len;
4658 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4660 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4661 if (!len)
4662 return 0;
4664 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4665 if (!buffW)
4666 return 0;
4668 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4669 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4670 if (!data || !data_len) {
4671 HeapFree(GetProcessHeap(), 0, buffW);
4672 return len;
4675 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4676 HeapFree(GetProcessHeap(), 0, buffW);
4678 if (data_len < len)
4679 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4680 return data_len < len ? 0 : len;
4683 /******************************************************************************
4684 * EnumSystemGeoID (KERNEL32.@)
4686 * Call a users function for every location available on the system.
4688 * PARAMS
4689 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4690 * parent [I] GEOID for the parent
4691 * enumproc [I] Callback function to call for each location
4693 * RETURNS
4694 * Success: TRUE.
4695 * Failure: FALSE. Use GetLastError() to determine the cause.
4697 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4699 INT i;
4701 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4703 if (!enumproc) {
4704 SetLastError(ERROR_INVALID_PARAMETER);
4705 return FALSE;
4708 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4709 SetLastError(ERROR_INVALID_FLAGS);
4710 return FALSE;
4713 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4714 const struct geoinfo_t *ptr = &geoinfodata[i];
4716 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4717 continue;
4719 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4720 continue;
4722 if (parent && ptr->parent != parent)
4723 continue;
4725 if (!enumproc(ptr->id))
4726 return TRUE;
4729 return TRUE;
4732 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4734 LCID userlcid;
4736 TRACE("%p, %d\n", localename, buffersize);
4738 userlcid = GetUserDefaultLCID();
4739 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4742 /******************************************************************************
4743 * NormalizeString (KERNEL32.@)
4745 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4746 LPWSTR lpDstString, INT cwDstLength)
4748 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4749 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4750 return 0;
4753 /******************************************************************************
4754 * IsNormalizedString (KERNEL32.@)
4756 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4758 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4759 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4760 return FALSE;
4763 enum {
4764 BASE = 36,
4765 TMIN = 1,
4766 TMAX = 26,
4767 SKEW = 38,
4768 DAMP = 700,
4769 INIT_BIAS = 72,
4770 INIT_N = 128
4773 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4775 INT k;
4777 delta /= (firsttime ? DAMP : 2);
4778 delta += delta/numpoints;
4780 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4781 delta /= BASE-TMIN;
4782 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4785 /******************************************************************************
4786 * IdnToAscii (KERNEL32.@)
4787 * Implementation of Punycode based on RFC 3492.
4789 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4790 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4792 static const WCHAR prefixW[] = {'x','n','-','-'};
4794 WCHAR *norm_str;
4795 INT i, label_start, label_end, norm_len, out_label, out = 0;
4797 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4798 lpASCIICharStr, cchASCIIChar);
4800 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4801 if(!norm_len)
4802 return 0;
4803 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4804 if(!norm_str) {
4805 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4806 return 0;
4808 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4809 cchUnicodeChar, norm_str, norm_len);
4810 if(!norm_len) {
4811 HeapFree(GetProcessHeap(), 0, norm_str);
4812 return 0;
4815 for(label_start=0; label_start<norm_len;) {
4816 INT n = INIT_N, bias = INIT_BIAS;
4817 INT delta = 0, b = 0, h;
4819 out_label = out;
4820 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4821 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4822 if(norm_str[i] < 0x80)
4823 b++;
4824 label_end = i;
4826 if(b == label_end-label_start) {
4827 if(label_end < norm_len)
4828 b++;
4829 if(!lpASCIICharStr) {
4830 out += b;
4831 }else if(out+b <= cchASCIIChar) {
4832 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4833 out += b;
4834 }else {
4835 HeapFree(GetProcessHeap(), 0, norm_str);
4836 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4837 return 0;
4839 label_start = label_end+1;
4840 continue;
4843 if(!lpASCIICharStr) {
4844 out += 5+b; /* strlen(xn--...-) */
4845 }else if(out+5+b <= cchASCIIChar) {
4846 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4847 out += 4;
4848 for(i=label_start; i<label_end; i++)
4849 if(norm_str[i] < 0x80)
4850 lpASCIICharStr[out++] = norm_str[i];
4851 lpASCIICharStr[out++] = '-';
4852 }else {
4853 HeapFree(GetProcessHeap(), 0, norm_str);
4854 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4855 return 0;
4857 if(!b)
4858 out--;
4860 for(h=b; h<label_end-label_start;) {
4861 INT m = 0xffff, q, k;
4863 for(i=label_start; i<label_end; i++) {
4864 if(norm_str[i]>=n && m>norm_str[i])
4865 m = norm_str[i];
4867 delta += (m-n)*(h+1);
4868 n = m;
4870 for(i=label_start; i<label_end; i++) {
4871 if(norm_str[i] < n) {
4872 delta++;
4873 }else if(norm_str[i] == n) {
4874 for(q=delta, k=BASE; ; k+=BASE) {
4875 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4876 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4877 if(!lpASCIICharStr) {
4878 out++;
4879 }else if(out+1 <= cchASCIIChar) {
4880 lpASCIICharStr[out++] = disp<='z'-'a' ?
4881 'a'+disp : '0'+disp-'z'+'a'-1;
4882 }else {
4883 HeapFree(GetProcessHeap(), 0, norm_str);
4884 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4885 return 0;
4887 if(q < t)
4888 break;
4889 q = (q-t)/(BASE-t);
4891 bias = adapt(delta, h+1, h==b);
4892 delta = 0;
4893 h++;
4896 delta++;
4897 n++;
4900 if(out-out_label > 63) {
4901 HeapFree(GetProcessHeap(), 0, norm_str);
4902 SetLastError(ERROR_INVALID_NAME);
4903 return 0;
4906 if(label_end < norm_len) {
4907 if(!lpASCIICharStr) {
4908 out++;
4909 }else if(out+1 <= cchASCIIChar) {
4910 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4911 }else {
4912 HeapFree(GetProcessHeap(), 0, norm_str);
4913 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4914 return 0;
4917 label_start = label_end+1;
4920 HeapFree(GetProcessHeap(), 0, norm_str);
4921 return out;
4924 /******************************************************************************
4925 * IdnToNameprepUnicode (KERNEL32.@)
4927 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4928 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4930 enum {
4931 UNASSIGNED = 0x1,
4932 PROHIBITED = 0x2,
4933 BIDI_RAL = 0x4,
4934 BIDI_L = 0x8
4937 extern const unsigned short nameprep_char_type[];
4938 extern const WCHAR nameprep_mapping[];
4939 const WCHAR *ptr;
4940 WORD flags;
4941 WCHAR buf[64], *map_str, norm_str[64], ch;
4942 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4943 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4945 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4946 lpNameprepCharStr, cchNameprepChar);
4948 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4949 SetLastError(ERROR_INVALID_FLAGS);
4950 return 0;
4953 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4954 SetLastError(ERROR_INVALID_PARAMETER);
4955 return 0;
4958 if(cchUnicodeChar == -1)
4959 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4960 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4961 SetLastError(ERROR_INVALID_NAME);
4962 return 0;
4965 for(label_start=0; label_start<cchUnicodeChar;) {
4966 ascii_only = TRUE;
4967 for(i=label_start; i<cchUnicodeChar; i++) {
4968 ch = lpUnicodeCharStr[i];
4970 if(i!=cchUnicodeChar-1 && !ch) {
4971 SetLastError(ERROR_INVALID_NAME);
4972 return 0;
4974 /* check if ch is one of label separators defined in RFC3490 */
4975 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4976 break;
4978 if(ch > 0x7f) {
4979 ascii_only = FALSE;
4980 continue;
4983 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4984 continue;
4985 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4986 || (ch>='0' && ch<='9') || ch=='-')
4987 continue;
4989 SetLastError(ERROR_INVALID_NAME);
4990 return 0;
4992 label_end = i;
4993 /* last label may be empty */
4994 if(label_start==label_end && ch) {
4995 SetLastError(ERROR_INVALID_NAME);
4996 return 0;
4999 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5000 lpUnicodeCharStr[label_end-1]=='-')) {
5001 SetLastError(ERROR_INVALID_NAME);
5002 return 0;
5005 if(ascii_only) {
5006 /* maximal label length is 63 characters */
5007 if(label_end-label_start > 63) {
5008 SetLastError(ERROR_INVALID_NAME);
5009 return 0;
5011 if(label_end < cchUnicodeChar)
5012 label_end++;
5014 if(!lpNameprepCharStr) {
5015 out += label_end-label_start;
5016 }else if(out+label_end-label_start <= cchNameprepChar) {
5017 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5018 (label_end-label_start)*sizeof(WCHAR));
5019 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5020 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5021 out += label_end-label_start;
5022 }else {
5023 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5024 return 0;
5027 label_start = label_end;
5028 continue;
5031 map_len = 0;
5032 for(i=label_start; i<label_end; i++) {
5033 ch = lpUnicodeCharStr[i];
5034 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5035 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5037 if(!ptr[0]) map_len++;
5038 else if(!ptr[1]) map_len++;
5039 else if(!ptr[2]) map_len += 2;
5040 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5042 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5043 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5044 if(!map_str) {
5045 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5046 return 0;
5048 }else {
5049 map_str = buf;
5051 map_len = 0;
5052 for(i=label_start; i<label_end; i++) {
5053 ch = lpUnicodeCharStr[i];
5054 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5055 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5057 if(!ptr[0]) {
5058 map_str[map_len++] = ch;
5059 }else if(!ptr[1]) {
5060 map_str[map_len++] = ptr[0];
5061 }else if(!ptr[2]) {
5062 map_str[map_len++] = ptr[0];
5063 map_str[map_len++] = ptr[1];
5064 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5065 map_str[map_len++] = ptr[0];
5066 map_str[map_len++] = ptr[1];
5067 map_str[map_len++] = ptr[2];
5071 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5072 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5073 if(map_str != buf)
5074 HeapFree(GetProcessHeap(), 0, map_str);
5075 if(!norm_len) {
5076 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5077 SetLastError(ERROR_INVALID_NAME);
5078 return 0;
5081 if(label_end < cchUnicodeChar) {
5082 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5083 label_end++;
5086 if(!lpNameprepCharStr) {
5087 out += norm_len;
5088 }else if(out+norm_len <= cchNameprepChar) {
5089 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5090 out += norm_len;
5091 }else {
5092 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5093 return 0;
5096 have_bidi_ral = prohibit_bidi_ral = FALSE;
5097 mask = PROHIBITED;
5098 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5099 mask |= UNASSIGNED;
5100 for(i=0; i<norm_len; i++) {
5101 ch = norm_str[i];
5102 flags = get_table_entry( nameprep_char_type, ch );
5104 if(flags & mask) {
5105 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5106 : ERROR_NO_UNICODE_TRANSLATION);
5107 return 0;
5110 if(flags & BIDI_RAL)
5111 have_bidi_ral = TRUE;
5112 if(flags & BIDI_L)
5113 prohibit_bidi_ral = TRUE;
5116 if(have_bidi_ral) {
5117 ch = norm_str[0];
5118 flags = get_table_entry( nameprep_char_type, ch );
5119 if((flags & BIDI_RAL) == 0)
5120 prohibit_bidi_ral = TRUE;
5122 ch = norm_str[norm_len-1];
5123 flags = get_table_entry( nameprep_char_type, ch );
5124 if((flags & BIDI_RAL) == 0)
5125 prohibit_bidi_ral = TRUE;
5128 if(have_bidi_ral && prohibit_bidi_ral) {
5129 SetLastError(ERROR_INVALID_NAME);
5130 return 0;
5133 label_start = label_end;
5136 return out;
5139 /******************************************************************************
5140 * IdnToUnicode (KERNEL32.@)
5142 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5143 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5145 extern const unsigned short nameprep_char_type[];
5147 INT i, label_start, label_end, out_label, out = 0;
5148 WCHAR ch;
5150 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5151 lpUnicodeCharStr, cchUnicodeChar);
5153 for(label_start=0; label_start<cchASCIIChar;) {
5154 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5156 out_label = out;
5157 for(i=label_start; i<cchASCIIChar; i++) {
5158 ch = lpASCIICharStr[i];
5160 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5161 SetLastError(ERROR_INVALID_NAME);
5162 return 0;
5165 if(!ch || ch=='.')
5166 break;
5167 if(ch == '-')
5168 delim = i;
5170 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5171 continue;
5172 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5173 || (ch>='0' && ch<='9') || ch=='-')
5174 continue;
5176 SetLastError(ERROR_INVALID_NAME);
5177 return 0;
5179 label_end = i;
5180 /* last label may be empty */
5181 if(label_start==label_end && ch) {
5182 SetLastError(ERROR_INVALID_NAME);
5183 return 0;
5186 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5187 lpASCIICharStr[label_end-1]=='-')) {
5188 SetLastError(ERROR_INVALID_NAME);
5189 return 0;
5191 if(label_end-label_start > 63) {
5192 SetLastError(ERROR_INVALID_NAME);
5193 return 0;
5196 if(label_end-label_start<4 ||
5197 tolowerW(lpASCIICharStr[label_start])!='x' ||
5198 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5199 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5200 if(label_end < cchASCIIChar)
5201 label_end++;
5203 if(!lpUnicodeCharStr) {
5204 out += label_end-label_start;
5205 }else if(out+label_end-label_start <= cchUnicodeChar) {
5206 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5207 (label_end-label_start)*sizeof(WCHAR));
5208 out += label_end-label_start;
5209 }else {
5210 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5211 return 0;
5214 label_start = label_end;
5215 continue;
5218 if(delim == label_start+3)
5219 delim++;
5220 if(!lpUnicodeCharStr) {
5221 out += delim-label_start-4;
5222 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5223 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5224 (delim-label_start-4)*sizeof(WCHAR));
5225 out += delim-label_start-4;
5226 }else {
5227 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5228 return 0;
5230 if(out != out_label)
5231 delim++;
5233 for(i=delim; i<label_end;) {
5234 old_pos = pos;
5235 w = 1;
5236 for(k=BASE; ; k+=BASE) {
5237 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5238 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5239 SetLastError(ERROR_INVALID_NAME);
5240 return 0;
5242 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5243 pos += digit*w;
5244 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5245 if(digit < t)
5246 break;
5247 w *= BASE-t;
5249 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5250 n += pos/(out-out_label+1);
5251 pos %= out-out_label+1;
5253 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5254 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5255 SetLastError(ERROR_INVALID_NAME);
5256 return 0;
5258 if(!lpUnicodeCharStr) {
5259 out++;
5260 }else if(out+1 <= cchASCIIChar) {
5261 memmove(lpUnicodeCharStr+out_label+pos+1,
5262 lpUnicodeCharStr+out_label+pos,
5263 (out-out_label-pos)*sizeof(WCHAR));
5264 lpUnicodeCharStr[out_label+pos] = n;
5265 out++;
5266 }else {
5267 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5268 return 0;
5270 pos++;
5273 if(out-out_label > 63) {
5274 SetLastError(ERROR_INVALID_NAME);
5275 return 0;
5278 if(label_end < cchASCIIChar) {
5279 if(!lpUnicodeCharStr) {
5280 out++;
5281 }else if(out+1 <= cchUnicodeChar) {
5282 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5283 }else {
5284 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5285 return 0;
5288 label_start = label_end+1;
5291 return out;
5295 /******************************************************************************
5296 * GetUserPreferredUILanguages (KERNEL32.@)
5298 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5300 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5301 return FALSE;
5304 /******************************************************************************
5305 * GetFileMUIPath (KERNEL32.@)
5308 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5309 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5311 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5312 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5314 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5316 return FALSE;
5319 /******************************************************************************
5320 * GetFileMUIInfo (KERNEL32.@)
5323 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5325 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5327 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5328 return FALSE;