winejoystick: Fix a crash on accessing a CFArray past its end due to an off-by-one...
[wine/multimedia.git] / dlls / kernel32 / locale.c
blobf20fe5bc32639161b577a70873082edb04918e82
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 NtClose( hkey );
1182 if (!status)
1184 INT length = (size - info_size) / sizeof(WCHAR);
1185 LPWSTR cached_value;
1187 if (!length || ((WCHAR *)&info->Data)[length-1])
1188 length++;
1190 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1192 if (!cached_value)
1194 HeapFree( GetProcessHeap(), 0, info );
1195 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1196 RtlLeaveCriticalSection( &cache_section );
1197 return 0;
1200 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1201 cached_value[length-1] = 0;
1202 HeapFree( GetProcessHeap(), 0, info );
1203 registry_value->cached_value = cached_value;
1205 else
1207 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1209 ret = (size - info_size) / sizeof(WCHAR);
1210 if (!ret || ((WCHAR *)&info->Data)[ret-1])
1211 ret++;
1213 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1215 ret = -1;
1217 else
1219 SetLastError( RtlNtStatusToDosError(status) );
1220 ret = 0;
1222 HeapFree( GetProcessHeap(), 0, info );
1223 RtlLeaveCriticalSection( &cache_section );
1224 return ret;
1228 ret = lstrlenW( registry_value->cached_value ) + 1;
1230 if (buffer)
1232 if (ret > len)
1234 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1235 ret = 0;
1237 else
1239 lstrcpyW( buffer, registry_value->cached_value );
1243 RtlLeaveCriticalSection( &cache_section );
1245 return ret;
1249 /******************************************************************************
1250 * GetLocaleInfoA (KERNEL32.@)
1252 * Get information about an aspect of a locale.
1254 * PARAMS
1255 * lcid [I] LCID of the locale
1256 * lctype [I] LCTYPE_ flags from "winnls.h"
1257 * buffer [O] Destination for the information
1258 * len [I] Length of buffer in characters
1260 * RETURNS
1261 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1262 * with the information.
1263 * Failure: 0. Use GetLastError() to determine the cause.
1265 * NOTES
1266 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1267 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1268 * which is a bit string.
1270 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1272 WCHAR *bufferW;
1273 INT lenW, ret;
1275 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1277 if (len < 0 || (len && !buffer))
1279 SetLastError( ERROR_INVALID_PARAMETER );
1280 return 0;
1282 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1284 SetLastError( ERROR_INVALID_FLAGS );
1285 return 0;
1288 if (!len) buffer = NULL;
1290 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1292 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1294 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1295 return 0;
1297 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1299 if ((lctype & LOCALE_RETURN_NUMBER) ||
1300 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1302 /* it's not an ASCII string, just bytes */
1303 ret *= sizeof(WCHAR);
1304 if (buffer)
1306 if (ret <= len) memcpy( buffer, bufferW, ret );
1307 else
1309 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1310 ret = 0;
1314 else
1316 UINT codepage = CP_ACP;
1317 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1318 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1321 HeapFree( GetProcessHeap(), 0, bufferW );
1322 return ret;
1325 static int get_value_base_by_lctype( LCTYPE lctype )
1327 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1330 /******************************************************************************
1331 * GetLocaleInfoW (KERNEL32.@)
1333 * See GetLocaleInfoA.
1335 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1337 LANGID lang_id;
1338 HRSRC hrsrc;
1339 HGLOBAL hmem;
1340 INT ret;
1341 UINT lcflags;
1342 const WCHAR *p;
1343 unsigned int i;
1345 if (len < 0 || (len && !buffer))
1347 SetLastError( ERROR_INVALID_PARAMETER );
1348 return 0;
1350 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1351 !is_genitive_name_supported( lctype ))
1353 SetLastError( ERROR_INVALID_FLAGS );
1354 return 0;
1357 if (!len) buffer = NULL;
1359 lcid = convert_default_lcid( lcid, lctype );
1361 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1362 lctype &= 0xffff;
1364 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1366 /* first check for overrides in the registry */
1368 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1369 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1371 struct registry_value *value = get_locale_registry_value(lctype);
1373 if (value)
1375 if (lcflags & LOCALE_RETURN_NUMBER)
1377 WCHAR tmp[16];
1378 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1379 if (ret > 0)
1381 WCHAR *end;
1382 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1383 if (*end) /* invalid number */
1385 SetLastError( ERROR_INVALID_FLAGS );
1386 return 0;
1388 ret = sizeof(UINT)/sizeof(WCHAR);
1389 if (!buffer) return ret;
1390 if (ret > len)
1392 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1393 return 0;
1395 memcpy( buffer, &number, sizeof(number) );
1398 else ret = get_registry_locale_info( value, buffer, len );
1400 if (ret != -1) return ret;
1404 /* now load it from kernel resources */
1406 lang_id = LANGIDFROMLCID( lcid );
1408 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1409 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1410 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1412 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1413 ULongToPtr((lctype >> 4) + 1), lang_id )))
1415 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1416 return 0;
1418 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1419 return 0;
1421 p = LockResource( hmem );
1422 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1424 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1425 else if (is_genitive_name_supported( lctype ) && *p)
1427 /* genitive form's stored after a null separator from a nominative */
1428 for (i = 1; i <= *p; i++) if (!p[i]) break;
1430 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1432 ret = *p - i + 1;
1433 p += i;
1435 else ret = i;
1437 else
1438 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1440 if (!buffer) return ret;
1442 if (ret > len)
1444 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1445 return 0;
1448 if (lcflags & LOCALE_RETURN_NUMBER)
1450 UINT number;
1451 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1452 if (!tmp) return 0;
1453 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1454 tmp[*p] = 0;
1455 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1456 if (!*end)
1457 memcpy( buffer, &number, sizeof(number) );
1458 else /* invalid number */
1460 SetLastError( ERROR_INVALID_FLAGS );
1461 ret = 0;
1463 HeapFree( GetProcessHeap(), 0, tmp );
1465 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1466 lcid, lctype, buffer, len, number );
1468 else
1470 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1471 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1473 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1474 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1476 return ret;
1479 /******************************************************************************
1480 * GetLocaleInfoEx (KERNEL32.@)
1482 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1484 LCID lcid = LocaleNameToLCID(locale, 0);
1486 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1488 if (!lcid) return 0;
1490 /* special handling for neutral locale names */
1491 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1493 if (len && len < 3)
1495 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1496 return 0;
1499 if (len) strcpyW(buffer, locale);
1500 return 3;
1503 return GetLocaleInfoW(lcid, info, buffer, len);
1506 /******************************************************************************
1507 * SetLocaleInfoA [KERNEL32.@]
1509 * Set information about an aspect of a locale.
1511 * PARAMS
1512 * lcid [I] LCID of the locale
1513 * lctype [I] LCTYPE_ flags from "winnls.h"
1514 * data [I] Information to set
1516 * RETURNS
1517 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1518 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1519 * Failure: FALSE. Use GetLastError() to determine the cause.
1521 * NOTES
1522 * - Values are only be set for the current user locale; the system locale
1523 * settings cannot be changed.
1524 * - Any settings changed by this call are lost when the locale is changed by
1525 * the control panel (in Wine, this happens every time you change LANG).
1526 * - The native implementation of this function does not check that lcid matches
1527 * the current user locale, and simply sets the new values. Wine warns you in
1528 * this case, but behaves the same.
1530 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1532 UINT codepage = CP_ACP;
1533 WCHAR *strW;
1534 DWORD len;
1535 BOOL ret;
1537 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1539 if (!data)
1541 SetLastError( ERROR_INVALID_PARAMETER );
1542 return FALSE;
1544 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1545 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1547 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1548 return FALSE;
1550 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1551 ret = SetLocaleInfoW( lcid, lctype, strW );
1552 HeapFree( GetProcessHeap(), 0, strW );
1553 return ret;
1557 /******************************************************************************
1558 * SetLocaleInfoW (KERNEL32.@)
1560 * See SetLocaleInfoA.
1562 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1564 struct registry_value *value;
1565 static const WCHAR intlW[] = {'i','n','t','l',0 };
1566 UNICODE_STRING valueW;
1567 NTSTATUS status;
1568 HANDLE hkey;
1570 lctype &= 0xffff;
1571 value = get_locale_registry_value( lctype );
1573 if (!data || !value)
1575 SetLastError( ERROR_INVALID_PARAMETER );
1576 return FALSE;
1579 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1581 SetLastError( ERROR_INVALID_FLAGS );
1582 return FALSE;
1585 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1587 /* FIXME: should check that data to set is sane */
1589 /* FIXME: profile functions should map to registry */
1590 WriteProfileStringW( intlW, value->name, data );
1592 if (!(hkey = create_registry_key())) return FALSE;
1593 RtlInitUnicodeString( &valueW, value->name );
1594 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1596 RtlEnterCriticalSection( &cache_section );
1597 HeapFree( GetProcessHeap(), 0, value->cached_value );
1598 value->cached_value = NULL;
1599 RtlLeaveCriticalSection( &cache_section );
1601 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1603 /* Set I-value from S value */
1604 WCHAR *lpD, *lpM, *lpY;
1605 WCHAR szBuff[2];
1607 lpD = strrchrW(data, 'd');
1608 lpM = strrchrW(data, 'M');
1609 lpY = strrchrW(data, 'y');
1611 if (lpD <= lpM)
1613 szBuff[0] = '1'; /* D-M-Y */
1615 else
1617 if (lpY <= lpM)
1618 szBuff[0] = '2'; /* Y-M-D */
1619 else
1620 szBuff[0] = '0'; /* M-D-Y */
1623 szBuff[1] = '\0';
1625 if (lctype == LOCALE_SSHORTDATE)
1626 lctype = LOCALE_IDATE;
1627 else
1628 lctype = LOCALE_ILDATE;
1630 value = get_locale_registry_value( lctype );
1632 WriteProfileStringW( intlW, value->name, szBuff );
1634 RtlInitUnicodeString( &valueW, value->name );
1635 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1637 RtlEnterCriticalSection( &cache_section );
1638 HeapFree( GetProcessHeap(), 0, value->cached_value );
1639 value->cached_value = NULL;
1640 RtlLeaveCriticalSection( &cache_section );
1643 NtClose( hkey );
1645 if (status) SetLastError( RtlNtStatusToDosError(status) );
1646 return !status;
1650 /******************************************************************************
1651 * GetACP (KERNEL32.@)
1653 * Get the current Ansi code page Id for the system.
1655 * PARAMS
1656 * None.
1658 * RETURNS
1659 * The current Ansi code page identifier for the system.
1661 UINT WINAPI GetACP(void)
1663 assert( ansi_cptable );
1664 return ansi_cptable->info.codepage;
1668 /******************************************************************************
1669 * SetCPGlobal (KERNEL32.@)
1671 * Set the current Ansi code page Id for the system.
1673 * PARAMS
1674 * acp [I] code page ID to be the new ACP.
1676 * RETURNS
1677 * The previous ACP.
1679 UINT WINAPI SetCPGlobal( UINT acp )
1681 UINT ret = GetACP();
1682 const union cptable *new_cptable = wine_cp_get_table( acp );
1684 if (new_cptable) ansi_cptable = new_cptable;
1685 return ret;
1689 /***********************************************************************
1690 * GetOEMCP (KERNEL32.@)
1692 * Get the current OEM code page Id for the system.
1694 * PARAMS
1695 * None.
1697 * RETURNS
1698 * The current OEM code page identifier for the system.
1700 UINT WINAPI GetOEMCP(void)
1702 assert( oem_cptable );
1703 return oem_cptable->info.codepage;
1707 /***********************************************************************
1708 * IsValidCodePage (KERNEL32.@)
1710 * Determine if a given code page identifier is valid.
1712 * PARAMS
1713 * codepage [I] Code page Id to verify.
1715 * RETURNS
1716 * TRUE, If codepage is valid and available on the system,
1717 * FALSE otherwise.
1719 BOOL WINAPI IsValidCodePage( UINT codepage )
1721 switch(codepage) {
1722 case CP_UTF7:
1723 case CP_UTF8:
1724 return TRUE;
1725 default:
1726 return wine_cp_get_table( codepage ) != NULL;
1731 /***********************************************************************
1732 * IsDBCSLeadByteEx (KERNEL32.@)
1734 * Determine if a character is a lead byte in a given code page.
1736 * PARAMS
1737 * codepage [I] Code page for the test.
1738 * testchar [I] Character to test
1740 * RETURNS
1741 * TRUE, if testchar is a lead byte in codepage,
1742 * FALSE otherwise.
1744 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1746 const union cptable *table = get_codepage_table( codepage );
1747 return table && wine_is_dbcs_leadbyte( table, testchar );
1751 /***********************************************************************
1752 * IsDBCSLeadByte (KERNEL32.@)
1753 * IsDBCSLeadByte (KERNEL.207)
1755 * Determine if a character is a lead byte.
1757 * PARAMS
1758 * testchar [I] Character to test
1760 * RETURNS
1761 * TRUE, if testchar is a lead byte in the ANSI code page,
1762 * FALSE otherwise.
1764 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1766 if (!ansi_cptable) return FALSE;
1767 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1771 /***********************************************************************
1772 * GetCPInfo (KERNEL32.@)
1774 * Get information about a code page.
1776 * PARAMS
1777 * codepage [I] Code page number
1778 * cpinfo [O] Destination for code page information
1780 * RETURNS
1781 * Success: TRUE. cpinfo is updated with the information about codepage.
1782 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1784 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1786 const union cptable *table;
1788 if (!cpinfo)
1790 SetLastError( ERROR_INVALID_PARAMETER );
1791 return FALSE;
1794 if (!(table = get_codepage_table( codepage )))
1796 switch(codepage)
1798 case CP_UTF7:
1799 case CP_UTF8:
1800 cpinfo->DefaultChar[0] = 0x3f;
1801 cpinfo->DefaultChar[1] = 0;
1802 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1803 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1804 return TRUE;
1807 SetLastError( ERROR_INVALID_PARAMETER );
1808 return FALSE;
1810 if (table->info.def_char & 0xff00)
1812 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1813 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1815 else
1817 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1818 cpinfo->DefaultChar[1] = 0;
1820 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1821 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1822 else
1823 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1825 return TRUE;
1828 /***********************************************************************
1829 * GetCPInfoExA (KERNEL32.@)
1831 * Get extended information about a code page.
1833 * PARAMS
1834 * codepage [I] Code page number
1835 * dwFlags [I] Reserved, must to 0.
1836 * cpinfo [O] Destination for code page information
1838 * RETURNS
1839 * Success: TRUE. cpinfo is updated with the information about codepage.
1840 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1842 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1844 CPINFOEXW cpinfoW;
1846 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1847 return FALSE;
1849 /* the layout is the same except for CodePageName */
1850 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1851 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1852 return TRUE;
1855 /***********************************************************************
1856 * GetCPInfoExW (KERNEL32.@)
1858 * Unicode version of GetCPInfoExA.
1860 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1862 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1863 return FALSE;
1865 switch(codepage)
1867 case CP_UTF7:
1869 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1871 cpinfo->CodePage = CP_UTF7;
1872 cpinfo->UnicodeDefaultChar = 0x3f;
1873 strcpyW(cpinfo->CodePageName, utf7);
1874 break;
1877 case CP_UTF8:
1879 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1881 cpinfo->CodePage = CP_UTF8;
1882 cpinfo->UnicodeDefaultChar = 0x3f;
1883 strcpyW(cpinfo->CodePageName, utf8);
1884 break;
1887 default:
1889 const union cptable *table = get_codepage_table( codepage );
1891 cpinfo->CodePage = table->info.codepage;
1892 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1893 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1894 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1895 break;
1898 return TRUE;
1901 /***********************************************************************
1902 * EnumSystemCodePagesA (KERNEL32.@)
1904 * Call a user defined function for every code page installed on the system.
1906 * PARAMS
1907 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1908 * flags [I] Reserved, set to 0.
1910 * RETURNS
1911 * TRUE, If all code pages have been enumerated, or
1912 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1914 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1916 const union cptable *table;
1917 char buffer[10];
1918 int index = 0;
1920 for (;;)
1922 if (!(table = wine_cp_enum_table( index++ ))) break;
1923 sprintf( buffer, "%d", table->info.codepage );
1924 if (!lpfnCodePageEnum( buffer )) break;
1926 return TRUE;
1930 /***********************************************************************
1931 * EnumSystemCodePagesW (KERNEL32.@)
1933 * See EnumSystemCodePagesA.
1935 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1937 const union cptable *table;
1938 WCHAR buffer[10], *p;
1939 int page, index = 0;
1941 for (;;)
1943 if (!(table = wine_cp_enum_table( index++ ))) break;
1944 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1945 *--p = 0;
1946 page = table->info.codepage;
1949 *--p = '0' + (page % 10);
1950 page /= 10;
1951 } while( page );
1952 if (!lpfnCodePageEnum( p )) break;
1954 return TRUE;
1958 /***********************************************************************
1959 * utf7_write_w
1961 * Helper for utf7_mbstowcs
1963 * RETURNS
1964 * TRUE on success, FALSE on error
1966 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
1968 if (dstlen > 0)
1970 if (*index >= dstlen)
1971 return FALSE;
1973 dst[*index] = character;
1976 (*index)++;
1978 return TRUE;
1981 /***********************************************************************
1982 * utf7_mbstowcs
1984 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
1986 * RETURNS
1987 * On success, the number of characters written
1988 * On dst buffer overflow, -1
1990 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
1992 static const signed char base64_decoding_table[] =
1994 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1995 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1996 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1997 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1998 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1999 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2000 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2001 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2004 const char *source_end = src + srclen;
2005 int dest_index = 0;
2007 DWORD byte_pair = 0;
2008 short offset = 0;
2010 while (src < source_end)
2012 if (*src == '+')
2014 src++;
2015 if (src >= source_end)
2016 break;
2018 if (*src == '-')
2020 /* just a plus sign escaped as +- */
2021 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2022 return -1;
2023 src++;
2024 continue;
2029 signed char sextet = *src;
2030 if (sextet == '-')
2032 /* skip over the dash and end base64 decoding
2033 * the current, unfinished byte pair is discarded */
2034 src++;
2035 offset = 0;
2036 break;
2038 if (sextet < 0)
2040 /* the next character of src is < 0 and therefore not part of a base64 sequence
2041 * the current, unfinished byte pair is NOT discarded in this case
2042 * this is probably a bug in Windows */
2043 break;
2046 sextet = base64_decoding_table[sextet];
2047 if (sextet == -1)
2049 /* -1 means that the next character of src is not part of a base64 sequence
2050 * in other words, all sextets in this base64 sequence have been processed
2051 * the current, unfinished byte pair is discarded */
2052 offset = 0;
2053 break;
2056 byte_pair = (byte_pair << 6) | sextet;
2057 offset += 6;
2059 if (offset >= 16)
2061 /* this byte pair is done */
2062 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2063 return -1;
2064 offset -= 16;
2067 src++;
2069 while (src < source_end);
2071 else
2073 /* we have to convert to unsigned char in case *src < 0 */
2074 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2075 return -1;
2076 src++;
2080 return dest_index;
2083 /***********************************************************************
2084 * MultiByteToWideChar (KERNEL32.@)
2086 * Convert a multibyte character string into a Unicode string.
2088 * PARAMS
2089 * page [I] Codepage character set to convert from
2090 * flags [I] Character mapping flags
2091 * src [I] Source string buffer
2092 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2093 * dst [O] Destination buffer
2094 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2096 * RETURNS
2097 * Success: If dstlen > 0, the number of characters written to dst.
2098 * If dstlen == 0, the number of characters needed to perform the
2099 * conversion. In both cases the count includes the terminating NUL.
2100 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2101 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2102 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2103 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2104 * possible for src.
2106 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2107 LPWSTR dst, INT dstlen )
2109 const union cptable *table;
2110 int ret;
2112 if (!src || !srclen || (!dst && dstlen))
2114 SetLastError( ERROR_INVALID_PARAMETER );
2115 return 0;
2118 if (srclen < 0) srclen = strlen(src) + 1;
2120 switch(page)
2122 case CP_SYMBOL:
2123 if (flags)
2125 SetLastError( ERROR_INVALID_FLAGS );
2126 return 0;
2128 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2129 break;
2130 case CP_UTF7:
2131 if (flags)
2133 SetLastError( ERROR_INVALID_FLAGS );
2134 return 0;
2136 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2137 break;
2138 case CP_UNIXCP:
2139 if (unix_cptable)
2141 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2142 break;
2144 #ifdef __APPLE__
2145 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2146 #endif
2147 /* fall through */
2148 case CP_UTF8:
2149 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2150 break;
2151 default:
2152 if (!(table = get_codepage_table( page )))
2154 SetLastError( ERROR_INVALID_PARAMETER );
2155 return 0;
2157 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2158 break;
2161 if (ret < 0)
2163 switch(ret)
2165 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2166 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2168 ret = 0;
2170 TRACE("cp %d %s -> %s, ret = %d\n",
2171 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2172 return ret;
2176 /***********************************************************************
2177 * utf7_can_directly_encode
2179 * Helper for utf7_wcstombs
2181 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2183 static const BOOL directly_encodable_table[] =
2185 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2186 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2187 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2188 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2189 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2190 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2191 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2192 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2195 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2198 /***********************************************************************
2199 * utf7_write_c
2201 * Helper for utf7_wcstombs
2203 * RETURNS
2204 * TRUE on success, FALSE on error
2206 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2208 if (dstlen > 0)
2210 if (*index >= dstlen)
2211 return FALSE;
2213 dst[*index] = character;
2216 (*index)++;
2218 return TRUE;
2221 /***********************************************************************
2222 * utf7_wcstombs
2224 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2226 * RETURNS
2227 * On success, the number of characters written
2228 * On dst buffer overflow, -1
2230 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2232 static const char base64_encoding_table[] =
2233 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2235 const WCHAR *source_end = src + srclen;
2236 int dest_index = 0;
2238 while (src < source_end)
2240 if (*src == '+')
2242 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2243 return -1;
2244 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2245 return -1;
2246 src++;
2248 else if (utf7_can_directly_encode(*src))
2250 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2251 return -1;
2252 src++;
2254 else
2256 unsigned int offset = 0;
2257 DWORD byte_pair = 0;
2259 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2260 return -1;
2262 while (src < source_end && !utf7_can_directly_encode(*src))
2264 byte_pair = (byte_pair << 16) | *src;
2265 offset += 16;
2266 while (offset >= 6)
2268 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2269 return -1;
2270 offset -= 6;
2272 src++;
2275 if (offset)
2277 /* Windows won't create a padded base64 character if there's not room for the - sign too
2278 * this is probably a bug in Windows */
2279 if (dstlen > 0 && dest_index + 1 >= dstlen)
2280 return -1;
2282 byte_pair <<= (6 - offset);
2283 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2284 return -1;
2287 /* Windows always explicitly terminates the base64 sequence
2288 even though RFC 2152 (page 3, rule 2) does not require this */
2289 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2290 return -1;
2294 return dest_index;
2297 /***********************************************************************
2298 * WideCharToMultiByte (KERNEL32.@)
2300 * Convert a Unicode character string into a multibyte string.
2302 * PARAMS
2303 * page [I] Code page character set to convert to
2304 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2305 * src [I] Source string buffer
2306 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2307 * dst [O] Destination buffer
2308 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2309 * defchar [I] Default character to use for conversion if no exact
2310 * conversion can be made
2311 * used [O] Set if default character was used in the conversion
2313 * RETURNS
2314 * Success: If dstlen > 0, the number of characters written to dst.
2315 * If dstlen == 0, number of characters needed to perform the
2316 * conversion. In both cases the count includes the terminating NUL.
2317 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2318 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2319 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2320 * parameter was given.
2322 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2323 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2325 const union cptable *table;
2326 int ret, used_tmp;
2328 if (!src || !srclen || (!dst && dstlen))
2330 SetLastError( ERROR_INVALID_PARAMETER );
2331 return 0;
2334 if (srclen < 0) srclen = strlenW(src) + 1;
2336 switch(page)
2338 case CP_SYMBOL:
2339 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2340 if (flags)
2342 SetLastError( ERROR_INVALID_FLAGS );
2343 return 0;
2345 if (defchar || used)
2347 SetLastError( ERROR_INVALID_PARAMETER );
2348 return 0;
2350 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2351 break;
2352 case CP_UTF7:
2353 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2354 if (defchar || used)
2356 SetLastError( ERROR_INVALID_PARAMETER );
2357 return 0;
2359 if (flags)
2361 SetLastError( ERROR_INVALID_FLAGS );
2362 return 0;
2364 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2365 break;
2366 case CP_UNIXCP:
2367 if (unix_cptable)
2369 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2370 defchar, used ? &used_tmp : NULL );
2371 break;
2373 /* fall through */
2374 case CP_UTF8:
2375 if (defchar || used)
2377 SetLastError( ERROR_INVALID_PARAMETER );
2378 return 0;
2380 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2381 break;
2382 default:
2383 if (!(table = get_codepage_table( page )))
2385 SetLastError( ERROR_INVALID_PARAMETER );
2386 return 0;
2388 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2389 defchar, used ? &used_tmp : NULL );
2390 if (used) *used = used_tmp;
2391 break;
2394 if (ret < 0)
2396 switch(ret)
2398 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2399 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2401 ret = 0;
2403 TRACE("cp %d %s -> %s, ret = %d\n",
2404 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2405 return ret;
2409 /***********************************************************************
2410 * GetThreadLocale (KERNEL32.@)
2412 * Get the current threads locale.
2414 * PARAMS
2415 * None.
2417 * RETURNS
2418 * The LCID currently associated with the calling thread.
2420 LCID WINAPI GetThreadLocale(void)
2422 LCID ret = NtCurrentTeb()->CurrentLocale;
2423 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2424 return ret;
2427 /**********************************************************************
2428 * SetThreadLocale (KERNEL32.@)
2430 * Set the current threads locale.
2432 * PARAMS
2433 * lcid [I] LCID of the locale to set
2435 * RETURNS
2436 * Success: TRUE. The threads locale is set to lcid.
2437 * Failure: FALSE. Use GetLastError() to determine the cause.
2439 BOOL WINAPI SetThreadLocale( LCID lcid )
2441 TRACE("(0x%04X)\n", lcid);
2443 lcid = ConvertDefaultLocale(lcid);
2445 if (lcid != GetThreadLocale())
2447 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2449 SetLastError(ERROR_INVALID_PARAMETER);
2450 return FALSE;
2453 NtCurrentTeb()->CurrentLocale = lcid;
2455 return TRUE;
2458 /**********************************************************************
2459 * SetThreadUILanguage (KERNEL32.@)
2461 * Set the current threads UI language.
2463 * PARAMS
2464 * langid [I] LANGID of the language to set, or 0 to use
2465 * the available language which is best supported
2466 * for console applications
2468 * RETURNS
2469 * Success: The return value is the same as the input value.
2470 * Failure: The return value differs from the input value.
2471 * Use GetLastError() to determine the cause.
2473 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2475 TRACE("(0x%04x) stub - returning success\n", langid);
2476 return langid;
2479 /******************************************************************************
2480 * ConvertDefaultLocale (KERNEL32.@)
2482 * Convert a default locale identifier into a real identifier.
2484 * PARAMS
2485 * lcid [I] LCID identifier of the locale to convert
2487 * RETURNS
2488 * lcid unchanged, if not a default locale or its sublanguage is
2489 * not SUBLANG_NEUTRAL.
2490 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2491 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2492 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2494 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2496 LANGID langid;
2498 switch (lcid)
2500 case LOCALE_SYSTEM_DEFAULT:
2501 lcid = GetSystemDefaultLCID();
2502 break;
2503 case LOCALE_USER_DEFAULT:
2504 case LOCALE_NEUTRAL:
2505 lcid = GetUserDefaultLCID();
2506 break;
2507 default:
2508 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2509 langid = LANGIDFROMLCID(lcid);
2510 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2512 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2513 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2516 return lcid;
2520 /******************************************************************************
2521 * IsValidLocale (KERNEL32.@)
2523 * Determine if a locale is valid.
2525 * PARAMS
2526 * lcid [I] LCID of the locale to check
2527 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2529 * RETURNS
2530 * TRUE, if lcid is valid,
2531 * FALSE, otherwise.
2533 * NOTES
2534 * Wine does not currently make the distinction between supported and installed. All
2535 * languages supported are installed by default.
2537 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2539 /* check if language is registered in the kernel32 resources */
2540 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2541 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2544 /******************************************************************************
2545 * IsValidLocaleName (KERNEL32.@)
2547 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2549 struct locale_name locale_name;
2551 /* string parsing */
2552 parse_locale_name( locale, &locale_name );
2554 TRACE( "found lcid %x for %s, matches %d\n",
2555 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2557 return locale_name.matches > 0;
2560 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2561 LPCSTR name, WORD LangID, LONG_PTR lParam )
2563 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2564 char buf[20];
2566 sprintf(buf, "%08x", (UINT)LangID);
2567 return lpfnLocaleEnum( buf );
2570 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2571 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2573 static const WCHAR formatW[] = {'%','0','8','x',0};
2574 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2575 WCHAR buf[20];
2576 sprintfW( buf, formatW, (UINT)LangID );
2577 return lpfnLocaleEnum( buf );
2580 /******************************************************************************
2581 * EnumSystemLocalesA (KERNEL32.@)
2583 * Call a users function for each locale available on the system.
2585 * PARAMS
2586 * lpfnLocaleEnum [I] Callback function to call for each locale
2587 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2589 * RETURNS
2590 * Success: TRUE.
2591 * Failure: FALSE. Use GetLastError() to determine the cause.
2593 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2595 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2596 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2597 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2598 (LONG_PTR)lpfnLocaleEnum);
2599 return TRUE;
2603 /******************************************************************************
2604 * EnumSystemLocalesW (KERNEL32.@)
2606 * See EnumSystemLocalesA.
2608 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2610 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2611 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2612 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2613 (LONG_PTR)lpfnLocaleEnum);
2614 return TRUE;
2618 struct enum_locale_ex_data
2620 LOCALE_ENUMPROCEX proc;
2621 DWORD flags;
2622 LPARAM lparam;
2625 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2626 LPCWSTR name, WORD lang, LONG_PTR lparam )
2628 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2629 WCHAR buffer[256];
2630 DWORD neutral;
2631 unsigned int flags;
2633 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2634 buffer, sizeof(buffer) / sizeof(WCHAR) );
2635 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2636 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2637 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2638 neutral = 0;
2639 flags = LOCALE_WINDOWS;
2640 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2641 if (data->flags && !(data->flags & flags)) return TRUE;
2642 return data->proc( buffer, flags, data->lparam );
2645 /******************************************************************************
2646 * EnumSystemLocalesEx (KERNEL32.@)
2648 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2650 struct enum_locale_ex_data data;
2652 if (reserved)
2654 SetLastError( ERROR_INVALID_PARAMETER );
2655 return FALSE;
2657 data.proc = proc;
2658 data.flags = flags;
2659 data.lparam = lparam;
2660 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2661 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2662 enum_locale_ex_proc, (LONG_PTR)&data );
2663 return TRUE;
2667 /***********************************************************************
2668 * VerLanguageNameA (KERNEL32.@)
2670 * Get the name of a language.
2672 * PARAMS
2673 * wLang [I] LANGID of the language
2674 * szLang [O] Destination for the language name
2676 * RETURNS
2677 * Success: The size of the language name. If szLang is non-NULL, it is filled
2678 * with the name.
2679 * Failure: 0. Use GetLastError() to determine the cause.
2682 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2684 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2688 /***********************************************************************
2689 * VerLanguageNameW (KERNEL32.@)
2691 * See VerLanguageNameA.
2693 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2695 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2699 /******************************************************************************
2700 * GetStringTypeW (KERNEL32.@)
2702 * See GetStringTypeA.
2704 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2706 static const unsigned char type2_map[16] =
2708 C2_NOTAPPLICABLE, /* unassigned */
2709 C2_LEFTTORIGHT, /* L */
2710 C2_RIGHTTOLEFT, /* R */
2711 C2_EUROPENUMBER, /* EN */
2712 C2_EUROPESEPARATOR, /* ES */
2713 C2_EUROPETERMINATOR, /* ET */
2714 C2_ARABICNUMBER, /* AN */
2715 C2_COMMONSEPARATOR, /* CS */
2716 C2_BLOCKSEPARATOR, /* B */
2717 C2_SEGMENTSEPARATOR, /* S */
2718 C2_WHITESPACE, /* WS */
2719 C2_OTHERNEUTRAL, /* ON */
2720 C2_RIGHTTOLEFT, /* AL */
2721 C2_NOTAPPLICABLE, /* NSM */
2722 C2_NOTAPPLICABLE, /* BN */
2723 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2726 if (count == -1) count = strlenW(src) + 1;
2727 switch(type)
2729 case CT_CTYPE1:
2730 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2731 break;
2732 case CT_CTYPE2:
2733 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2734 break;
2735 case CT_CTYPE3:
2737 WARN("CT_CTYPE3: semi-stub.\n");
2738 while (count--)
2740 int c = *src;
2741 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2743 type1 = get_char_typeW( *src++ ) & 0xfff;
2744 /* try to construct type3 from type1 */
2745 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2746 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2747 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2748 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2749 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2750 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2751 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2753 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
2754 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
2756 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2757 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2758 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2759 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2760 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2761 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2762 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2763 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2765 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2766 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2767 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2768 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2769 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2770 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2771 *chartype++ = type3;
2773 break;
2775 default:
2776 SetLastError( ERROR_INVALID_PARAMETER );
2777 return FALSE;
2779 return TRUE;
2783 /******************************************************************************
2784 * GetStringTypeExW (KERNEL32.@)
2786 * See GetStringTypeExA.
2788 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2790 /* locale is ignored for Unicode */
2791 return GetStringTypeW( type, src, count, chartype );
2795 /******************************************************************************
2796 * GetStringTypeA (KERNEL32.@)
2798 * Get characteristics of the characters making up a string.
2800 * PARAMS
2801 * locale [I] Locale Id for the string
2802 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2803 * src [I] String to analyse
2804 * count [I] Length of src in chars, or -1 if src is NUL terminated
2805 * chartype [O] Destination for the calculated characteristics
2807 * RETURNS
2808 * Success: TRUE. chartype is filled with the requested characteristics of each char
2809 * in src.
2810 * Failure: FALSE. Use GetLastError() to determine the cause.
2812 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2814 UINT cp;
2815 INT countW;
2816 LPWSTR srcW;
2817 BOOL ret = FALSE;
2819 if(count == -1) count = strlen(src) + 1;
2821 if (!(cp = get_lcid_codepage( locale )))
2823 FIXME("For locale %04x using current ANSI code page\n", locale);
2824 cp = GetACP();
2827 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2828 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2830 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2832 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2833 * string, with multibyte characters there maybe be more bytes in count
2834 * than character space in the buffer!
2836 ret = GetStringTypeW(type, srcW, countW, chartype);
2837 HeapFree(GetProcessHeap(), 0, srcW);
2839 return ret;
2842 /******************************************************************************
2843 * GetStringTypeExA (KERNEL32.@)
2845 * Get characteristics of the characters making up a string.
2847 * PARAMS
2848 * locale [I] Locale Id for the string
2849 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2850 * src [I] String to analyse
2851 * count [I] Length of src in chars, or -1 if src is NUL terminated
2852 * chartype [O] Destination for the calculated characteristics
2854 * RETURNS
2855 * Success: TRUE. chartype is filled with the requested characteristics of each char
2856 * in src.
2857 * Failure: FALSE. Use GetLastError() to determine the cause.
2859 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2861 return GetStringTypeA(locale, type, src, count, chartype);
2864 /*************************************************************************
2865 * LCMapStringEx (KERNEL32.@)
2867 * Map characters in a locale sensitive string.
2869 * PARAMS
2870 * name [I] Locale name for the conversion.
2871 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2872 * src [I] String to map
2873 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2874 * dst [O] Destination for mapped string
2875 * dstlen [I] Length of dst in characters
2876 * version [I] reserved, must be NULL
2877 * reserved [I] reserved, must be NULL
2878 * lparam [I] reserved, must be 0
2880 * RETURNS
2881 * Success: The length of the mapped string in dst, including the NUL terminator.
2882 * Failure: 0. Use GetLastError() to determine the cause.
2884 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2885 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2887 LPWSTR dst_ptr;
2889 if (version) FIXME("unsupported version structure %p\n", version);
2890 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2891 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2893 if (!src || !srclen || dstlen < 0)
2895 SetLastError(ERROR_INVALID_PARAMETER);
2896 return 0;
2899 /* mutually exclusive flags */
2900 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2901 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2902 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2903 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2905 SetLastError(ERROR_INVALID_FLAGS);
2906 return 0;
2909 if (!dstlen) dst = NULL;
2911 if (flags & LCMAP_SORTKEY)
2913 INT ret;
2914 if (src == dst)
2916 SetLastError(ERROR_INVALID_FLAGS);
2917 return 0;
2920 if (srclen < 0) srclen = strlenW(src);
2922 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2923 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2925 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2926 if (ret == 0)
2927 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2928 else
2929 ret++;
2930 return ret;
2933 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2934 if (flags & SORT_STRINGSORT)
2936 SetLastError(ERROR_INVALID_FLAGS);
2937 return 0;
2940 if (srclen < 0) srclen = strlenW(src) + 1;
2942 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2943 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2945 if (!dst) /* return required string length */
2947 INT len;
2949 for (len = 0; srclen; src++, srclen--)
2951 WCHAR wch = *src;
2952 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2953 * and skips white space and punctuation characters for
2954 * NORM_IGNORESYMBOLS.
2956 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2957 continue;
2958 len++;
2960 return len;
2963 if (flags & LCMAP_UPPERCASE)
2965 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2967 WCHAR wch = *src;
2968 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2969 continue;
2970 *dst_ptr++ = toupperW(wch);
2971 dstlen--;
2974 else if (flags & LCMAP_LOWERCASE)
2976 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2978 WCHAR wch = *src;
2979 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2980 continue;
2981 *dst_ptr++ = tolowerW(wch);
2982 dstlen--;
2985 else
2987 if (src == dst)
2989 SetLastError(ERROR_INVALID_FLAGS);
2990 return 0;
2992 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2994 WCHAR wch = *src;
2995 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2996 continue;
2997 *dst_ptr++ = wch;
2998 dstlen--;
3002 if (srclen)
3004 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3005 return 0;
3008 return dst_ptr - dst;
3011 /*************************************************************************
3012 * LCMapStringW (KERNEL32.@)
3014 * See LCMapStringA.
3016 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3017 LPWSTR dst, INT dstlen)
3019 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3020 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3022 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3025 /*************************************************************************
3026 * LCMapStringA (KERNEL32.@)
3028 * Map characters in a locale sensitive string.
3030 * PARAMS
3031 * lcid [I] LCID for the conversion.
3032 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3033 * src [I] String to map
3034 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3035 * dst [O] Destination for mapped string
3036 * dstlen [I] Length of dst in characters
3038 * RETURNS
3039 * Success: The length of the mapped string in dst, including the NUL terminator.
3040 * Failure: 0. Use GetLastError() to determine the cause.
3042 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3043 LPSTR dst, INT dstlen)
3045 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3046 LPWSTR srcW, dstW;
3047 INT ret = 0, srclenW, dstlenW;
3048 UINT locale_cp = CP_ACP;
3050 if (!src || !srclen || dstlen < 0)
3052 SetLastError(ERROR_INVALID_PARAMETER);
3053 return 0;
3056 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3058 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3059 if (srclenW)
3060 srcW = bufW;
3061 else
3063 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3064 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3065 if (!srcW)
3067 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3068 return 0;
3070 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3073 if (flags & LCMAP_SORTKEY)
3075 if (src == dst)
3077 SetLastError(ERROR_INVALID_FLAGS);
3078 goto map_string_exit;
3080 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3081 if (ret == 0)
3082 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3083 else
3084 ret++;
3085 goto map_string_exit;
3088 if (flags & SORT_STRINGSORT)
3090 SetLastError(ERROR_INVALID_FLAGS);
3091 goto map_string_exit;
3094 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3095 if (!dstlenW)
3096 goto map_string_exit;
3098 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3099 if (!dstW)
3101 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3102 goto map_string_exit;
3105 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3106 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3107 HeapFree(GetProcessHeap(), 0, dstW);
3109 map_string_exit:
3110 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3111 return ret;
3114 /*************************************************************************
3115 * FoldStringA (KERNEL32.@)
3117 * Map characters in a string.
3119 * PARAMS
3120 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3121 * src [I] String to map
3122 * srclen [I] Length of src, or -1 if src is NUL terminated
3123 * dst [O] Destination for mapped string
3124 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3126 * RETURNS
3127 * Success: The length of the string written to dst, including the terminating NUL. If
3128 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3129 * and dst may be NULL.
3130 * Failure: 0. Use GetLastError() to determine the cause.
3132 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3133 LPSTR dst, INT dstlen)
3135 INT ret = 0, srclenW = 0;
3136 WCHAR *srcW = NULL, *dstW = NULL;
3138 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3140 SetLastError(ERROR_INVALID_PARAMETER);
3141 return 0;
3144 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3145 src, srclen, NULL, 0);
3146 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3148 if (!srcW)
3150 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3151 goto FoldStringA_exit;
3154 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3155 src, srclen, srcW, srclenW);
3157 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3159 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3160 if (ret && dstlen)
3162 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3164 if (!dstW)
3166 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3167 goto FoldStringA_exit;
3170 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3171 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3173 ret = 0;
3174 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3178 HeapFree(GetProcessHeap(), 0, dstW);
3180 FoldStringA_exit:
3181 HeapFree(GetProcessHeap(), 0, srcW);
3182 return ret;
3185 /*************************************************************************
3186 * FoldStringW (KERNEL32.@)
3188 * See FoldStringA.
3190 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3191 LPWSTR dst, INT dstlen)
3193 int ret;
3195 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3197 case 0:
3198 if (dwFlags)
3199 break;
3200 /* Fall through for dwFlags == 0 */
3201 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3202 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3203 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3204 SetLastError(ERROR_INVALID_FLAGS);
3205 return 0;
3208 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3210 SetLastError(ERROR_INVALID_PARAMETER);
3211 return 0;
3214 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3215 if (!ret)
3216 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3217 return ret;
3220 /******************************************************************************
3221 * CompareStringW (KERNEL32.@)
3223 * See CompareStringA.
3225 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3226 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3228 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3231 /******************************************************************************
3232 * CompareStringEx (KERNEL32.@)
3234 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3235 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3237 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3238 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3239 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3240 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3241 INT ret;
3243 if (version) FIXME("unexpected version parameter\n");
3244 if (reserved) FIXME("unexpected reserved value\n");
3245 if (lParam) FIXME("unexpected lParam\n");
3247 if (!str1 || !str2)
3249 SetLastError(ERROR_INVALID_PARAMETER);
3250 return 0;
3253 if (flags & ~(supported_flags|semistub_flags))
3255 SetLastError(ERROR_INVALID_FLAGS);
3256 return 0;
3259 if (flags & semistub_flags)
3260 FIXME("semi-stub behavor for flag(s) 0x%x\n", flags & semistub_flags);
3262 if (len1 < 0) len1 = strlenW(str1);
3263 if (len2 < 0) len2 = strlenW(str2);
3265 ret = wine_compare_string(flags, str1, len1, str2, len2);
3267 if (ret) /* need to translate result */
3268 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3269 return CSTR_EQUAL;
3272 /******************************************************************************
3273 * CompareStringA (KERNEL32.@)
3275 * Compare two locale sensitive strings.
3277 * PARAMS
3278 * lcid [I] LCID for the comparison
3279 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3280 * str1 [I] First string to compare
3281 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3282 * str2 [I] Second string to compare
3283 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3285 * RETURNS
3286 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3287 * str1 is less than, equal to or greater than str2 respectively.
3288 * Failure: FALSE. Use GetLastError() to determine the cause.
3290 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3291 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3293 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3294 WCHAR *buf2W = buf1W + 130;
3295 LPWSTR str1W, str2W;
3296 INT len1W = 0, len2W = 0, ret;
3297 UINT locale_cp = CP_ACP;
3299 if (!str1 || !str2)
3301 SetLastError(ERROR_INVALID_PARAMETER);
3302 return 0;
3304 if (len1 < 0) len1 = strlen(str1);
3305 if (len2 < 0) len2 = strlen(str2);
3307 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3309 if (len1)
3311 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3312 if (len1W)
3313 str1W = buf1W;
3314 else
3316 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3317 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3318 if (!str1W)
3320 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3321 return 0;
3323 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3326 else
3328 len1W = 0;
3329 str1W = buf1W;
3332 if (len2)
3334 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3335 if (len2W)
3336 str2W = buf2W;
3337 else
3339 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3340 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3341 if (!str2W)
3343 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3344 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3345 return 0;
3347 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3350 else
3352 len2W = 0;
3353 str2W = buf2W;
3356 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3358 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3359 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3360 return ret;
3363 /******************************************************************************
3364 * CompareStringOrdinal (KERNEL32.@)
3366 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3368 int ret, len;
3370 if (!str1 || !str2)
3372 SetLastError(ERROR_INVALID_PARAMETER);
3373 return 0;
3375 if (len1 < 0) len1 = strlenW(str1);
3376 if (len2 < 0) len2 = strlenW(str2);
3378 len = min(len1, len2);
3379 if (ignore_case)
3381 ret = memicmpW(str1, str2, len);
3383 else
3385 ret = 0;
3386 for (; len > 0; len--)
3387 if ((ret = (*str1++ - *str2++))) break;
3389 if (!ret) ret = len1 - len2;
3391 if (ret < 0) return CSTR_LESS_THAN;
3392 if (ret > 0) return CSTR_GREATER_THAN;
3393 return CSTR_EQUAL;
3396 /*************************************************************************
3397 * lstrcmp (KERNEL32.@)
3398 * lstrcmpA (KERNEL32.@)
3400 * Compare two strings using the current thread locale.
3402 * PARAMS
3403 * str1 [I] First string to compare
3404 * str2 [I] Second string to compare
3406 * RETURNS
3407 * Success: A number less than, equal to or greater than 0 depending on whether
3408 * str1 is less than, equal to or greater than str2 respectively.
3409 * Failure: FALSE. Use GetLastError() to determine the cause.
3411 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3413 int ret;
3415 if ((str1 == NULL) && (str2 == NULL)) return 0;
3416 if (str1 == NULL) return -1;
3417 if (str2 == NULL) return 1;
3419 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3420 if (ret) ret -= 2;
3422 return ret;
3425 /*************************************************************************
3426 * lstrcmpi (KERNEL32.@)
3427 * lstrcmpiA (KERNEL32.@)
3429 * Compare two strings using the current thread locale, ignoring case.
3431 * PARAMS
3432 * str1 [I] First string to compare
3433 * str2 [I] Second string to compare
3435 * RETURNS
3436 * Success: A number less than, equal to or greater than 0 depending on whether
3437 * str2 is less than, equal to or greater than str1 respectively.
3438 * Failure: FALSE. Use GetLastError() to determine the cause.
3440 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3442 int ret;
3444 if ((str1 == NULL) && (str2 == NULL)) return 0;
3445 if (str1 == NULL) return -1;
3446 if (str2 == NULL) return 1;
3448 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3449 if (ret) ret -= 2;
3451 return ret;
3454 /*************************************************************************
3455 * lstrcmpW (KERNEL32.@)
3457 * See lstrcmpA.
3459 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3461 int ret;
3463 if ((str1 == NULL) && (str2 == NULL)) return 0;
3464 if (str1 == NULL) return -1;
3465 if (str2 == NULL) return 1;
3467 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3468 if (ret) ret -= 2;
3470 return ret;
3473 /*************************************************************************
3474 * lstrcmpiW (KERNEL32.@)
3476 * See lstrcmpiA.
3478 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3480 int ret;
3482 if ((str1 == NULL) && (str2 == NULL)) return 0;
3483 if (str1 == NULL) return -1;
3484 if (str2 == NULL) return 1;
3486 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3487 if (ret) ret -= 2;
3489 return ret;
3492 /******************************************************************************
3493 * LOCALE_Init
3495 void LOCALE_Init(void)
3497 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3498 const union cptable *unix_cp );
3500 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3502 #ifdef __APPLE__
3503 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3504 char user_locale[50];
3506 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3507 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3508 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3509 CFStringRef user_locale_string_ref;
3511 if (user_locale_country_ref)
3513 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3514 user_locale_lang_ref, user_locale_country_ref);
3516 else
3518 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3521 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3522 strcat(user_locale, ".UTF-8");
3524 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3525 setenv( "LANG", user_locale, 0 );
3526 TRACE( "setting locale to '%s'\n", user_locale );
3527 #endif /* __APPLE__ */
3529 setlocale( LC_ALL, "" );
3531 unix_cp = setup_unix_locales();
3532 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3534 #ifdef __APPLE__
3535 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3536 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3538 /* Retrieve the preferred language as chosen in System Preferences. */
3539 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3540 leave things be. */
3541 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3542 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3543 CFStringRef user_language_string_ref;
3544 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3545 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3546 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3547 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3549 struct locale_name locale_name;
3550 WCHAR buffer[128];
3551 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3552 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3553 parse_locale_name( buffer, &locale_name );
3554 lcid_LC_MESSAGES = locale_name.lcid;
3555 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3557 if (preferred_langs)
3558 CFRelease( preferred_langs );
3559 if (canonical_lang_string_ref)
3560 CFRelease( canonical_lang_string_ref );
3563 CFRelease( user_locale_ref );
3564 CFRelease( user_locale_string_ref );
3565 #endif
3567 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3568 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3569 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3571 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3572 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3573 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3574 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3575 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3576 if (!unix_cp)
3577 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3578 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3580 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3581 ansi_cptable = wine_cp_get_table( 1252 );
3582 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3583 oem_cptable = wine_cp_get_table( 437 );
3584 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3585 mac_cptable = wine_cp_get_table( 10000 );
3586 if (unix_cp != CP_UTF8)
3588 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3589 unix_cptable = wine_cp_get_table( 28591 );
3592 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3594 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3595 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3596 mac_cptable->info.codepage, unix_cp );
3598 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3601 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3603 UNICODE_STRING keyName;
3604 OBJECT_ATTRIBUTES attr;
3605 HANDLE hkey;
3607 RtlInitUnicodeString( &keyName, szKeyName );
3608 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3610 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3611 hkey = 0;
3613 return hkey;
3616 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3617 LPWSTR szValueName, ULONG valueNameSize,
3618 LPWSTR szValueData, ULONG valueDataSize)
3620 BYTE buffer[80];
3621 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3622 DWORD dwLen;
3624 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3625 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3626 info->NameLength > valueNameSize ||
3627 info->DataLength > valueDataSize)
3629 return FALSE;
3632 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3634 memcpy( szValueName, info->Name, info->NameLength);
3635 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3636 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3637 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3639 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3640 return TRUE;
3643 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3645 BYTE buffer[128];
3646 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3647 DWORD dwSize = sizeof(buffer);
3648 UNICODE_STRING valueName;
3650 RtlInitUnicodeString( &valueName, szValueName );
3652 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3653 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3654 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3655 info->DataLength == sizeof(DWORD))
3657 memcpy(lpVal, info->Data, sizeof(DWORD));
3658 return TRUE;
3661 return FALSE;
3664 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3666 LANGID langId;
3667 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3668 HRSRC hResource;
3669 BOOL bRet = FALSE;
3671 /* FIXME: Is it correct to use the system default langid? */
3672 langId = GetSystemDefaultLangID();
3674 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3675 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3677 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3679 if (hResource)
3681 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3683 if (hResDir)
3685 ULONG iResourceIndex = lgrpid & 0xf;
3686 LPCWSTR lpResEntry = LockResource( hResDir );
3687 ULONG i;
3689 for (i = 0; i < iResourceIndex; i++)
3690 lpResEntry += *lpResEntry + 1;
3692 if (*lpResEntry < nameSize)
3694 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3695 szName[*lpResEntry] = '\0';
3696 bRet = TRUE;
3700 FreeResource( hResource );
3702 return bRet;
3705 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3706 typedef struct
3708 LANGUAGEGROUP_ENUMPROCA procA;
3709 LANGUAGEGROUP_ENUMPROCW procW;
3710 DWORD dwFlags;
3711 LONG_PTR lParam;
3712 } ENUMLANGUAGEGROUP_CALLBACKS;
3714 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3715 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3717 WCHAR szNumber[10], szValue[4];
3718 HANDLE hKey;
3719 BOOL bContinue = TRUE;
3720 ULONG ulIndex = 0;
3722 if (!lpProcs)
3724 SetLastError(ERROR_INVALID_PARAMETER);
3725 return FALSE;
3728 switch (lpProcs->dwFlags)
3730 case 0:
3731 /* Default to LGRPID_INSTALLED */
3732 lpProcs->dwFlags = LGRPID_INSTALLED;
3733 /* Fall through... */
3734 case LGRPID_INSTALLED:
3735 case LGRPID_SUPPORTED:
3736 break;
3737 default:
3738 SetLastError(ERROR_INVALID_FLAGS);
3739 return FALSE;
3742 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3744 if (!hKey)
3745 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3747 while (bContinue)
3749 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3750 szValue, sizeof(szValue) ))
3752 BOOL bInstalled = szValue[0] == '1';
3753 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3755 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3756 bInstalled ? "" : "not ");
3758 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3760 WCHAR szGrpName[48];
3762 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3763 szGrpName[0] = '\0';
3765 if (lpProcs->procW)
3766 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3767 lpProcs->lParam );
3768 else
3770 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3771 char szGrpNameA[48];
3773 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3774 * or whether the language names are ever localised. Assume CP_ACP.
3777 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3778 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3780 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3781 lpProcs->lParam );
3785 ulIndex++;
3787 else
3788 bContinue = FALSE;
3790 if (!bContinue)
3791 break;
3794 if (hKey)
3795 NtClose( hKey );
3797 return TRUE;
3800 /******************************************************************************
3801 * EnumSystemLanguageGroupsA (KERNEL32.@)
3803 * Call a users function for each language group available on the system.
3805 * PARAMS
3806 * pLangGrpEnumProc [I] Callback function to call for each language group
3807 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3808 * lParam [I] User parameter to pass to pLangGrpEnumProc
3810 * RETURNS
3811 * Success: TRUE.
3812 * Failure: FALSE. Use GetLastError() to determine the cause.
3814 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3815 DWORD dwFlags, LONG_PTR lParam)
3817 ENUMLANGUAGEGROUP_CALLBACKS procs;
3819 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3821 procs.procA = pLangGrpEnumProc;
3822 procs.procW = NULL;
3823 procs.dwFlags = dwFlags;
3824 procs.lParam = lParam;
3826 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3829 /******************************************************************************
3830 * EnumSystemLanguageGroupsW (KERNEL32.@)
3832 * See EnumSystemLanguageGroupsA.
3834 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3835 DWORD dwFlags, LONG_PTR lParam)
3837 ENUMLANGUAGEGROUP_CALLBACKS procs;
3839 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3841 procs.procA = NULL;
3842 procs.procW = pLangGrpEnumProc;
3843 procs.dwFlags = dwFlags;
3844 procs.lParam = lParam;
3846 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3849 /******************************************************************************
3850 * IsValidLanguageGroup (KERNEL32.@)
3852 * Determine if a language group is supported and/or installed.
3854 * PARAMS
3855 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3856 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3858 * RETURNS
3859 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3860 * FALSE otherwise.
3862 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3864 static const WCHAR szFormat[] = { '%','x','\0' };
3865 WCHAR szValueName[16], szValue[2];
3866 BOOL bSupported = FALSE, bInstalled = FALSE;
3867 HANDLE hKey;
3870 switch (dwFlags)
3872 case LGRPID_INSTALLED:
3873 case LGRPID_SUPPORTED:
3875 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3877 sprintfW( szValueName, szFormat, lgrpid );
3879 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3881 bSupported = TRUE;
3883 if (szValue[0] == '1')
3884 bInstalled = TRUE;
3887 if (hKey)
3888 NtClose( hKey );
3890 break;
3893 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3894 (dwFlags == LGRPID_INSTALLED && bInstalled))
3895 return TRUE;
3897 return FALSE;
3900 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3901 typedef struct
3903 LANGGROUPLOCALE_ENUMPROCA procA;
3904 LANGGROUPLOCALE_ENUMPROCW procW;
3905 DWORD dwFlags;
3906 LGRPID lgrpid;
3907 LONG_PTR lParam;
3908 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3910 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3911 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3913 static const WCHAR szAlternateSortsKeyName[] = {
3914 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3916 WCHAR szNumber[10], szValue[4];
3917 HANDLE hKey;
3918 BOOL bContinue = TRUE, bAlternate = FALSE;
3919 LGRPID lgrpid;
3920 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3922 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3924 SetLastError(ERROR_INVALID_PARAMETER);
3925 return FALSE;
3928 if (lpProcs->dwFlags)
3930 SetLastError(ERROR_INVALID_FLAGS);
3931 return FALSE;
3934 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3936 if (!hKey)
3937 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3939 while (bContinue)
3941 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3942 szValue, sizeof(szValue) ))
3944 lgrpid = strtoulW( szValue, NULL, 16 );
3946 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3947 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3949 if (lgrpid == lpProcs->lgrpid)
3951 LCID lcid;
3953 lcid = strtoulW( szNumber, NULL, 16 );
3955 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3956 * '00000437 ;Georgian'
3957 * At present we only pass the LCID string.
3960 if (lpProcs->procW)
3961 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3962 else
3964 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3966 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3968 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3972 ulIndex++;
3974 else
3976 /* Finished enumerating this key */
3977 if (!bAlternate)
3979 /* Enumerate alternate sorts also */
3980 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3981 bAlternate = TRUE;
3982 ulIndex = 0;
3984 else
3985 bContinue = FALSE; /* Finished both keys */
3988 if (!bContinue)
3989 break;
3992 if (hKey)
3993 NtClose( hKey );
3995 return TRUE;
3998 /******************************************************************************
3999 * EnumLanguageGroupLocalesA (KERNEL32.@)
4001 * Call a users function for every locale in a language group available on the system.
4003 * PARAMS
4004 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4005 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4006 * dwFlags [I] Reserved, set to 0
4007 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4009 * RETURNS
4010 * Success: TRUE.
4011 * Failure: FALSE. Use GetLastError() to determine the cause.
4013 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4014 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4016 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4018 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4020 callbacks.procA = pLangGrpLcEnumProc;
4021 callbacks.procW = NULL;
4022 callbacks.dwFlags = dwFlags;
4023 callbacks.lgrpid = lgrpid;
4024 callbacks.lParam = lParam;
4026 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4029 /******************************************************************************
4030 * EnumLanguageGroupLocalesW (KERNEL32.@)
4032 * See EnumLanguageGroupLocalesA.
4034 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4035 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4037 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4039 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4041 callbacks.procA = NULL;
4042 callbacks.procW = pLangGrpLcEnumProc;
4043 callbacks.dwFlags = dwFlags;
4044 callbacks.lgrpid = lgrpid;
4045 callbacks.lParam = lParam;
4047 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4050 /******************************************************************************
4051 * InvalidateNLSCache (KERNEL32.@)
4053 * Invalidate the cache of NLS values.
4055 * PARAMS
4056 * None.
4058 * RETURNS
4059 * Success: TRUE.
4060 * Failure: FALSE.
4062 BOOL WINAPI InvalidateNLSCache(void)
4064 FIXME("() stub\n");
4065 return FALSE;
4068 /******************************************************************************
4069 * GetUserGeoID (KERNEL32.@)
4071 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4073 GEOID ret = GEOID_NOT_AVAILABLE;
4074 static const WCHAR geoW[] = {'G','e','o',0};
4075 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4076 WCHAR bufferW[40], *end;
4077 DWORD count;
4078 HANDLE hkey, hSubkey = 0;
4079 UNICODE_STRING keyW;
4080 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4081 RtlInitUnicodeString( &keyW, nationW );
4082 count = sizeof(bufferW);
4084 if(!(hkey = create_registry_key())) return ret;
4086 switch( GeoClass ){
4087 case GEOCLASS_NATION:
4088 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4090 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4091 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4092 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4094 break;
4095 case GEOCLASS_REGION:
4096 FIXME("GEOCLASS_REGION not handled yet\n");
4097 break;
4100 NtClose(hkey);
4101 if (hSubkey) NtClose(hSubkey);
4102 return ret;
4105 /******************************************************************************
4106 * SetUserGeoID (KERNEL32.@)
4108 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4110 static const WCHAR geoW[] = {'G','e','o',0};
4111 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4112 static const WCHAR formatW[] = {'%','i',0};
4113 UNICODE_STRING nameW,keyW;
4114 WCHAR bufferW[10];
4115 OBJECT_ATTRIBUTES attr;
4116 HANDLE hkey;
4118 if(!(hkey = create_registry_key())) return FALSE;
4120 attr.Length = sizeof(attr);
4121 attr.RootDirectory = hkey;
4122 attr.ObjectName = &nameW;
4123 attr.Attributes = 0;
4124 attr.SecurityDescriptor = NULL;
4125 attr.SecurityQualityOfService = NULL;
4126 RtlInitUnicodeString( &nameW, geoW );
4127 RtlInitUnicodeString( &keyW, nationW );
4129 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4132 NtClose(attr.RootDirectory);
4133 return FALSE;
4136 sprintfW(bufferW, formatW, GeoID);
4137 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4138 NtClose(attr.RootDirectory);
4139 NtClose(hkey);
4140 return TRUE;
4143 typedef struct
4145 union
4147 UILANGUAGE_ENUMPROCA procA;
4148 UILANGUAGE_ENUMPROCW procW;
4149 } u;
4150 DWORD flags;
4151 LONG_PTR param;
4152 } ENUM_UILANG_CALLBACK;
4154 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4155 LPCSTR name, WORD LangID, LONG_PTR lParam )
4157 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4158 char buf[20];
4160 sprintf(buf, "%08x", (UINT)LangID);
4161 return enum_uilang->u.procA( buf, enum_uilang->param );
4164 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4165 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4167 static const WCHAR formatW[] = {'%','0','8','x',0};
4168 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4169 WCHAR buf[20];
4171 sprintfW( buf, formatW, (UINT)LangID );
4172 return enum_uilang->u.procW( buf, enum_uilang->param );
4175 /******************************************************************************
4176 * EnumUILanguagesA (KERNEL32.@)
4178 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4180 ENUM_UILANG_CALLBACK enum_uilang;
4182 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4184 if(!pUILangEnumProc) {
4185 SetLastError(ERROR_INVALID_PARAMETER);
4186 return FALSE;
4188 if(dwFlags) {
4189 SetLastError(ERROR_INVALID_FLAGS);
4190 return FALSE;
4193 enum_uilang.u.procA = pUILangEnumProc;
4194 enum_uilang.flags = dwFlags;
4195 enum_uilang.param = lParam;
4197 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4198 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4199 (LONG_PTR)&enum_uilang);
4200 return TRUE;
4203 /******************************************************************************
4204 * EnumUILanguagesW (KERNEL32.@)
4206 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4208 ENUM_UILANG_CALLBACK enum_uilang;
4210 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4213 if(!pUILangEnumProc) {
4214 SetLastError(ERROR_INVALID_PARAMETER);
4215 return FALSE;
4217 if(dwFlags) {
4218 SetLastError(ERROR_INVALID_FLAGS);
4219 return FALSE;
4222 enum_uilang.u.procW = pUILangEnumProc;
4223 enum_uilang.flags = dwFlags;
4224 enum_uilang.param = lParam;
4226 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4227 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4228 (LONG_PTR)&enum_uilang);
4229 return TRUE;
4232 enum locationkind {
4233 LOCATION_NATION = 0,
4234 LOCATION_REGION,
4235 LOCATION_BOTH
4238 struct geoinfo_t {
4239 GEOID id;
4240 WCHAR iso2W[3];
4241 WCHAR iso3W[4];
4242 GEOID parent;
4243 INT uncode;
4244 enum locationkind kind;
4247 static const struct geoinfo_t geoinfodata[] = {
4248 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4249 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4250 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4251 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4252 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4253 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4254 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4255 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4256 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4257 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4258 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4259 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4260 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4261 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4262 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4263 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4264 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4265 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4266 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4267 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4268 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4269 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4270 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4271 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4272 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4273 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4274 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4275 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4276 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4277 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4278 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4279 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4280 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4281 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4282 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4283 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4284 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4285 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4286 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4287 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4288 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4289 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4290 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4291 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4292 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4293 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4294 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4295 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4296 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4297 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4298 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4299 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4300 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4301 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4302 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4303 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4304 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4305 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4306 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4307 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4308 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4309 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4310 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4311 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4312 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4313 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4314 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4315 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4316 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4317 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4318 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4319 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4320 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4321 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4322 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4323 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4324 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4325 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4326 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4327 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4328 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4329 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4330 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4331 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4332 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4333 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4334 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4335 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4336 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4337 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4338 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4339 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4340 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4341 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4342 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4343 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4344 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4345 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4346 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4347 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4348 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4349 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4350 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4351 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4352 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4353 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4354 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4355 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4356 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4357 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4358 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4359 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4360 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4361 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4362 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4363 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4364 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4365 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4366 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4367 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4368 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4369 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4370 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4371 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4372 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4373 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4374 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4375 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4376 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4377 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4378 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4379 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4380 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4381 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4382 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4383 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4384 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4385 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4386 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4387 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4388 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4389 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4390 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4391 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4392 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4393 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4394 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4395 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4396 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4397 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4398 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4399 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4400 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4401 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4402 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4403 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4404 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4405 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4406 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4407 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4408 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4409 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4410 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4411 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4412 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4413 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4414 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4415 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4416 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4417 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4418 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4419 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4420 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4421 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4422 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4423 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4424 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4425 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4426 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4427 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4428 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4429 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4430 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4431 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4432 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4433 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4434 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4435 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4436 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4437 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4438 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4439 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4440 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4441 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4442 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4443 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4444 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4445 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4446 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4447 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4448 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4449 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4450 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4451 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4452 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4453 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4454 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4455 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4456 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4457 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4458 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4459 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4460 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4461 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4462 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4463 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4464 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4465 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4466 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4467 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4468 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4469 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4470 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4471 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4472 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4473 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4474 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4475 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4476 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4477 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4478 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4479 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4480 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4481 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4482 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4483 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4484 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4485 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4486 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4487 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4488 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4489 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4490 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4491 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4492 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4493 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4494 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4495 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4496 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4497 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4498 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4499 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4500 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4501 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4502 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4503 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4504 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4505 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4506 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4507 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4508 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4509 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4510 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4511 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4512 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4513 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4514 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4515 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4516 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4517 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4518 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4519 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4520 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4521 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4522 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4523 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4524 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4525 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4526 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4527 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4528 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4529 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4530 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4531 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4532 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4533 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4534 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4535 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4536 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4537 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4538 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4539 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4540 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4541 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4542 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4543 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4544 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4545 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4546 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4549 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4551 int min, max;
4553 min = 0;
4554 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4556 while (min <= max) {
4557 const struct geoinfo_t *ptr;
4558 int n = (min+max)/2;
4560 ptr = &geoinfodata[n];
4561 if (geoid == ptr->id)
4562 /* we don't need empty entries */
4563 return *ptr->iso2W ? ptr : NULL;
4565 if (ptr->id > geoid)
4566 max = n-1;
4567 else
4568 min = n+1;
4571 return NULL;
4574 /******************************************************************************
4575 * GetGeoInfoW (KERNEL32.@)
4577 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4579 const struct geoinfo_t *ptr;
4580 const WCHAR *str = NULL;
4581 WCHAR buffW[12];
4582 LONG val = 0;
4583 INT len;
4585 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4587 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4588 SetLastError(ERROR_INVALID_PARAMETER);
4589 return 0;
4592 switch (geotype) {
4593 case GEO_NATION:
4594 val = geoid;
4595 break;
4596 case GEO_ISO_UN_NUMBER:
4597 val = ptr->uncode;
4598 break;
4599 case GEO_PARENT:
4600 val = ptr->parent;
4601 break;
4602 case GEO_ISO2:
4603 case GEO_ISO3:
4605 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4606 break;
4608 case GEO_RFC1766:
4609 case GEO_LCID:
4610 case GEO_FRIENDLYNAME:
4611 case GEO_OFFICIALNAME:
4612 case GEO_TIMEZONES:
4613 case GEO_OFFICIALLANGUAGES:
4614 case GEO_LATITUDE:
4615 case GEO_LONGITUDE:
4616 FIXME("type %d is not supported\n", geotype);
4617 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4618 return 0;
4619 default:
4620 WARN("unrecognized type %d\n", geotype);
4621 SetLastError(ERROR_INVALID_FLAGS);
4622 return 0;
4625 if (val) {
4626 static const WCHAR fmtW[] = {'%','d',0};
4627 sprintfW(buffW, fmtW, val);
4628 str = buffW;
4631 len = strlenW(str) + 1;
4632 if (!data || !data_len)
4633 return len;
4635 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4636 if (data_len < len)
4637 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4638 return data_len < len ? 0 : len;
4641 /******************************************************************************
4642 * GetGeoInfoA (KERNEL32.@)
4644 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4646 WCHAR *buffW;
4647 INT len;
4649 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4651 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4652 if (!len)
4653 return 0;
4655 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4656 if (!buffW)
4657 return 0;
4659 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4660 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4661 if (!data || !data_len) {
4662 HeapFree(GetProcessHeap(), 0, buffW);
4663 return len;
4666 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4667 HeapFree(GetProcessHeap(), 0, buffW);
4669 if (data_len < len)
4670 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4671 return data_len < len ? 0 : len;
4674 /******************************************************************************
4675 * EnumSystemGeoID (KERNEL32.@)
4677 * Call a users function for every location available on the system.
4679 * PARAMS
4680 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4681 * parent [I] GEOID for the parent
4682 * enumproc [I] Callback function to call for each location
4684 * RETURNS
4685 * Success: TRUE.
4686 * Failure: FALSE. Use GetLastError() to determine the cause.
4688 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4690 INT i;
4692 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4694 if (!enumproc) {
4695 SetLastError(ERROR_INVALID_PARAMETER);
4696 return FALSE;
4699 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4700 SetLastError(ERROR_INVALID_FLAGS);
4701 return FALSE;
4704 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4705 const struct geoinfo_t *ptr = &geoinfodata[i];
4707 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4708 continue;
4710 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4711 continue;
4713 if (parent && ptr->parent != parent)
4714 continue;
4716 if (!enumproc(ptr->id))
4717 return TRUE;
4720 return TRUE;
4723 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4725 LCID userlcid;
4727 TRACE("%p, %d\n", localename, buffersize);
4729 userlcid = GetUserDefaultLCID();
4730 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4733 /******************************************************************************
4734 * NormalizeString (KERNEL32.@)
4736 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4737 LPWSTR lpDstString, INT cwDstLength)
4739 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4740 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4741 return 0;
4744 /******************************************************************************
4745 * IsNormalizedString (KERNEL32.@)
4747 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4749 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4750 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4751 return FALSE;
4754 enum {
4755 BASE = 36,
4756 TMIN = 1,
4757 TMAX = 26,
4758 SKEW = 38,
4759 DAMP = 700,
4760 INIT_BIAS = 72,
4761 INIT_N = 128
4764 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4766 INT k;
4768 delta /= (firsttime ? DAMP : 2);
4769 delta += delta/numpoints;
4771 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4772 delta /= BASE-TMIN;
4773 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4776 /******************************************************************************
4777 * IdnToAscii (KERNEL32.@)
4778 * Implementation of Punycode based on RFC 3492.
4780 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4781 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4783 static const WCHAR prefixW[] = {'x','n','-','-'};
4785 WCHAR *norm_str;
4786 INT i, label_start, label_end, norm_len, out_label, out = 0;
4788 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4789 lpASCIICharStr, cchASCIIChar);
4791 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4792 if(!norm_len)
4793 return 0;
4794 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4795 if(!norm_str) {
4796 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4797 return 0;
4799 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4800 cchUnicodeChar, norm_str, norm_len);
4801 if(!norm_len) {
4802 HeapFree(GetProcessHeap(), 0, norm_str);
4803 return 0;
4806 for(label_start=0; label_start<norm_len;) {
4807 INT n = INIT_N, bias = INIT_BIAS;
4808 INT delta = 0, b = 0, h;
4810 out_label = out;
4811 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4812 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4813 if(norm_str[i] < 0x80)
4814 b++;
4815 label_end = i;
4817 if(b == label_end-label_start) {
4818 if(label_end < norm_len)
4819 b++;
4820 if(!lpASCIICharStr) {
4821 out += b;
4822 }else if(out+b <= cchASCIIChar) {
4823 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4824 out += b;
4825 }else {
4826 HeapFree(GetProcessHeap(), 0, norm_str);
4827 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4828 return 0;
4830 label_start = label_end+1;
4831 continue;
4834 if(!lpASCIICharStr) {
4835 out += 5+b; /* strlen(xn--...-) */
4836 }else if(out+5+b <= cchASCIIChar) {
4837 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4838 out += 4;
4839 for(i=label_start; i<label_end; i++)
4840 if(norm_str[i] < 0x80)
4841 lpASCIICharStr[out++] = norm_str[i];
4842 lpASCIICharStr[out++] = '-';
4843 }else {
4844 HeapFree(GetProcessHeap(), 0, norm_str);
4845 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4846 return 0;
4848 if(!b)
4849 out--;
4851 for(h=b; h<label_end-label_start;) {
4852 INT m = 0xffff, q, k;
4854 for(i=label_start; i<label_end; i++) {
4855 if(norm_str[i]>=n && m>norm_str[i])
4856 m = norm_str[i];
4858 delta += (m-n)*(h+1);
4859 n = m;
4861 for(i=label_start; i<label_end; i++) {
4862 if(norm_str[i] < n) {
4863 delta++;
4864 }else if(norm_str[i] == n) {
4865 for(q=delta, k=BASE; ; k+=BASE) {
4866 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4867 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4868 if(!lpASCIICharStr) {
4869 out++;
4870 }else if(out+1 <= cchASCIIChar) {
4871 lpASCIICharStr[out++] = disp<='z'-'a' ?
4872 'a'+disp : '0'+disp-'z'+'a'-1;
4873 }else {
4874 HeapFree(GetProcessHeap(), 0, norm_str);
4875 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4876 return 0;
4878 if(q < t)
4879 break;
4880 q = (q-t)/(BASE-t);
4882 bias = adapt(delta, h+1, h==b);
4883 delta = 0;
4884 h++;
4887 delta++;
4888 n++;
4891 if(out-out_label > 63) {
4892 HeapFree(GetProcessHeap(), 0, norm_str);
4893 SetLastError(ERROR_INVALID_NAME);
4894 return 0;
4897 if(label_end < norm_len) {
4898 if(!lpASCIICharStr) {
4899 out++;
4900 }else if(out+1 <= cchASCIIChar) {
4901 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4902 }else {
4903 HeapFree(GetProcessHeap(), 0, norm_str);
4904 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4905 return 0;
4908 label_start = label_end+1;
4911 HeapFree(GetProcessHeap(), 0, norm_str);
4912 return out;
4915 /******************************************************************************
4916 * IdnToNameprepUnicode (KERNEL32.@)
4918 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4919 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4921 enum {
4922 UNASSIGNED = 0x1,
4923 PROHIBITED = 0x2,
4924 BIDI_RAL = 0x4,
4925 BIDI_L = 0x8
4928 extern const unsigned short nameprep_char_type[];
4929 extern const WCHAR nameprep_mapping[];
4930 const WCHAR *ptr;
4931 WORD flags;
4932 WCHAR buf[64], *map_str, norm_str[64], ch;
4933 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4934 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4936 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4937 lpNameprepCharStr, cchNameprepChar);
4939 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4940 SetLastError(ERROR_INVALID_FLAGS);
4941 return 0;
4944 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4945 SetLastError(ERROR_INVALID_PARAMETER);
4946 return 0;
4949 if(cchUnicodeChar == -1)
4950 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4951 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4952 SetLastError(ERROR_INVALID_NAME);
4953 return 0;
4956 for(label_start=0; label_start<cchUnicodeChar;) {
4957 ascii_only = TRUE;
4958 for(i=label_start; i<cchUnicodeChar; i++) {
4959 ch = lpUnicodeCharStr[i];
4961 if(i!=cchUnicodeChar-1 && !ch) {
4962 SetLastError(ERROR_INVALID_NAME);
4963 return 0;
4965 /* check if ch is one of label separators defined in RFC3490 */
4966 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4967 break;
4969 if(ch > 0x7f) {
4970 ascii_only = FALSE;
4971 continue;
4974 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4975 continue;
4976 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4977 || (ch>='0' && ch<='9') || ch=='-')
4978 continue;
4980 SetLastError(ERROR_INVALID_NAME);
4981 return 0;
4983 label_end = i;
4984 /* last label may be empty */
4985 if(label_start==label_end && ch) {
4986 SetLastError(ERROR_INVALID_NAME);
4987 return 0;
4990 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4991 lpUnicodeCharStr[label_end-1]=='-')) {
4992 SetLastError(ERROR_INVALID_NAME);
4993 return 0;
4996 if(ascii_only) {
4997 /* maximal label length is 63 characters */
4998 if(label_end-label_start > 63) {
4999 SetLastError(ERROR_INVALID_NAME);
5000 return 0;
5002 if(label_end < cchUnicodeChar)
5003 label_end++;
5005 if(!lpNameprepCharStr) {
5006 out += label_end-label_start;
5007 }else if(out+label_end-label_start <= cchNameprepChar) {
5008 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5009 (label_end-label_start)*sizeof(WCHAR));
5010 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5011 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5012 out += label_end-label_start;
5013 }else {
5014 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5015 return 0;
5018 label_start = label_end;
5019 continue;
5022 map_len = 0;
5023 for(i=label_start; i<label_end; i++) {
5024 ch = lpUnicodeCharStr[i];
5025 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5026 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5028 if(!ptr[0]) map_len++;
5029 else if(!ptr[1]) map_len++;
5030 else if(!ptr[2]) map_len += 2;
5031 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5033 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5034 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5035 if(!map_str) {
5036 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5037 return 0;
5039 }else {
5040 map_str = buf;
5042 map_len = 0;
5043 for(i=label_start; i<label_end; i++) {
5044 ch = lpUnicodeCharStr[i];
5045 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5046 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5048 if(!ptr[0]) {
5049 map_str[map_len++] = ch;
5050 }else if(!ptr[1]) {
5051 map_str[map_len++] = ptr[0];
5052 }else if(!ptr[2]) {
5053 map_str[map_len++] = ptr[0];
5054 map_str[map_len++] = ptr[1];
5055 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5056 map_str[map_len++] = ptr[0];
5057 map_str[map_len++] = ptr[1];
5058 map_str[map_len++] = ptr[2];
5062 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5063 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5064 if(map_str != buf)
5065 HeapFree(GetProcessHeap(), 0, map_str);
5066 if(!norm_len) {
5067 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5068 SetLastError(ERROR_INVALID_NAME);
5069 return 0;
5072 if(label_end < cchUnicodeChar) {
5073 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5074 label_end++;
5077 if(!lpNameprepCharStr) {
5078 out += norm_len;
5079 }else if(out+norm_len <= cchNameprepChar) {
5080 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5081 out += norm_len;
5082 }else {
5083 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5084 return 0;
5087 have_bidi_ral = prohibit_bidi_ral = FALSE;
5088 mask = PROHIBITED;
5089 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5090 mask |= UNASSIGNED;
5091 for(i=0; i<norm_len; i++) {
5092 ch = norm_str[i];
5093 flags = get_table_entry( nameprep_char_type, ch );
5095 if(flags & mask) {
5096 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5097 : ERROR_NO_UNICODE_TRANSLATION);
5098 return 0;
5101 if(flags & BIDI_RAL)
5102 have_bidi_ral = TRUE;
5103 if(flags & BIDI_L)
5104 prohibit_bidi_ral = TRUE;
5107 if(have_bidi_ral) {
5108 ch = norm_str[0];
5109 flags = get_table_entry( nameprep_char_type, ch );
5110 if((flags & BIDI_RAL) == 0)
5111 prohibit_bidi_ral = TRUE;
5113 ch = norm_str[norm_len-1];
5114 flags = get_table_entry( nameprep_char_type, ch );
5115 if((flags & BIDI_RAL) == 0)
5116 prohibit_bidi_ral = TRUE;
5119 if(have_bidi_ral && prohibit_bidi_ral) {
5120 SetLastError(ERROR_INVALID_NAME);
5121 return 0;
5124 label_start = label_end;
5127 return out;
5130 /******************************************************************************
5131 * IdnToUnicode (KERNEL32.@)
5133 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5134 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5136 extern const unsigned short nameprep_char_type[];
5138 INT i, label_start, label_end, out_label, out = 0;
5139 WCHAR ch;
5141 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5142 lpUnicodeCharStr, cchUnicodeChar);
5144 for(label_start=0; label_start<cchASCIIChar;) {
5145 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5147 out_label = out;
5148 for(i=label_start; i<cchASCIIChar; i++) {
5149 ch = lpASCIICharStr[i];
5151 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5152 SetLastError(ERROR_INVALID_NAME);
5153 return 0;
5156 if(!ch || ch=='.')
5157 break;
5158 if(ch == '-')
5159 delim = i;
5161 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5162 continue;
5163 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5164 || (ch>='0' && ch<='9') || ch=='-')
5165 continue;
5167 SetLastError(ERROR_INVALID_NAME);
5168 return 0;
5170 label_end = i;
5171 /* last label may be empty */
5172 if(label_start==label_end && ch) {
5173 SetLastError(ERROR_INVALID_NAME);
5174 return 0;
5177 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5178 lpASCIICharStr[label_end-1]=='-')) {
5179 SetLastError(ERROR_INVALID_NAME);
5180 return 0;
5182 if(label_end-label_start > 63) {
5183 SetLastError(ERROR_INVALID_NAME);
5184 return 0;
5187 if(label_end-label_start<4 ||
5188 tolowerW(lpASCIICharStr[label_start])!='x' ||
5189 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5190 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5191 if(label_end < cchASCIIChar)
5192 label_end++;
5194 if(!lpUnicodeCharStr) {
5195 out += label_end-label_start;
5196 }else if(out+label_end-label_start <= cchUnicodeChar) {
5197 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5198 (label_end-label_start)*sizeof(WCHAR));
5199 out += label_end-label_start;
5200 }else {
5201 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5202 return 0;
5205 label_start = label_end;
5206 continue;
5209 if(delim == label_start+3)
5210 delim++;
5211 if(!lpUnicodeCharStr) {
5212 out += delim-label_start-4;
5213 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5214 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5215 (delim-label_start-4)*sizeof(WCHAR));
5216 out += delim-label_start-4;
5217 }else {
5218 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5219 return 0;
5221 if(out != out_label)
5222 delim++;
5224 for(i=delim; i<label_end;) {
5225 old_pos = pos;
5226 w = 1;
5227 for(k=BASE; ; k+=BASE) {
5228 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5229 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5230 SetLastError(ERROR_INVALID_NAME);
5231 return 0;
5233 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5234 pos += digit*w;
5235 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5236 if(digit < t)
5237 break;
5238 w *= BASE-t;
5240 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5241 n += pos/(out-out_label+1);
5242 pos %= out-out_label+1;
5244 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5245 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5246 SetLastError(ERROR_INVALID_NAME);
5247 return 0;
5249 if(!lpUnicodeCharStr) {
5250 out++;
5251 }else if(out+1 <= cchASCIIChar) {
5252 memmove(lpUnicodeCharStr+out_label+pos+1,
5253 lpUnicodeCharStr+out_label+pos,
5254 (out-out_label-pos)*sizeof(WCHAR));
5255 lpUnicodeCharStr[out_label+pos] = n;
5256 out++;
5257 }else {
5258 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5259 return 0;
5261 pos++;
5264 if(out-out_label > 63) {
5265 SetLastError(ERROR_INVALID_NAME);
5266 return 0;
5269 if(label_end < cchASCIIChar) {
5270 if(!lpUnicodeCharStr) {
5271 out++;
5272 }else if(out+1 <= cchUnicodeChar) {
5273 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5274 }else {
5275 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5276 return 0;
5279 label_start = label_end+1;
5282 return out;
5286 /******************************************************************************
5287 * GetUserPreferredUILanguages (KERNEL32.@)
5289 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5291 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5292 return FALSE;