kernel32: Return proper char count in get_registry_locale_info.
[wine.git] / dlls / kernel32 / locale.c
blob1460f7a55b765758b7995482d180df48b39b5ccb
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 * MultiByteToWideChar (KERNEL32.@)
1961 * Convert a multibyte character string into a Unicode string.
1963 * PARAMS
1964 * page [I] Codepage character set to convert from
1965 * flags [I] Character mapping flags
1966 * src [I] Source string buffer
1967 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1968 * dst [O] Destination buffer
1969 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1971 * RETURNS
1972 * Success: If dstlen > 0, the number of characters written to dst.
1973 * If dstlen == 0, the number of characters needed to perform the
1974 * conversion. In both cases the count includes the terminating NUL.
1975 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1976 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1977 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1978 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1979 * possible for src.
1981 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1982 LPWSTR dst, INT dstlen )
1984 const union cptable *table;
1985 int ret;
1987 if (!src || !srclen || (!dst && dstlen))
1989 SetLastError( ERROR_INVALID_PARAMETER );
1990 return 0;
1993 if (srclen < 0) srclen = strlen(src) + 1;
1995 switch(page)
1997 case CP_SYMBOL:
1998 if (flags)
2000 SetLastError( ERROR_INVALID_FLAGS );
2001 return 0;
2003 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2004 break;
2005 case CP_UTF7:
2006 if (flags)
2008 SetLastError( ERROR_INVALID_FLAGS );
2009 return 0;
2011 FIXME("UTF-7 not supported\n");
2012 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2013 return 0;
2014 case CP_UNIXCP:
2015 if (unix_cptable)
2017 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2018 break;
2020 #ifdef __APPLE__
2021 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2022 #endif
2023 /* fall through */
2024 case CP_UTF8:
2025 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2026 break;
2027 default:
2028 if (!(table = get_codepage_table( page )))
2030 SetLastError( ERROR_INVALID_PARAMETER );
2031 return 0;
2033 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2034 break;
2037 if (ret < 0)
2039 switch(ret)
2041 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2042 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2044 ret = 0;
2046 TRACE("cp %d %s -> %s, ret = %d\n",
2047 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2048 return ret;
2052 /***********************************************************************
2053 * WideCharToMultiByte (KERNEL32.@)
2055 * Convert a Unicode character string into a multibyte string.
2057 * PARAMS
2058 * page [I] Code page character set to convert to
2059 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2060 * src [I] Source string buffer
2061 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2062 * dst [O] Destination buffer
2063 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2064 * defchar [I] Default character to use for conversion if no exact
2065 * conversion can be made
2066 * used [O] Set if default character was used in the conversion
2068 * RETURNS
2069 * Success: If dstlen > 0, the number of characters written to dst.
2070 * If dstlen == 0, number of characters needed to perform the
2071 * conversion. In both cases the count includes the terminating NUL.
2072 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2073 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2074 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2075 * parameter was given.
2077 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2078 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2080 const union cptable *table;
2081 int ret, used_tmp;
2083 if (!src || !srclen || (!dst && dstlen))
2085 SetLastError( ERROR_INVALID_PARAMETER );
2086 return 0;
2089 if (srclen < 0) srclen = strlenW(src) + 1;
2091 switch(page)
2093 case CP_SYMBOL:
2094 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2095 if (flags)
2097 SetLastError( ERROR_INVALID_FLAGS );
2098 return 0;
2100 if (defchar || used)
2102 SetLastError( ERROR_INVALID_PARAMETER );
2103 return 0;
2105 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2106 break;
2107 case CP_UTF7:
2108 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2109 if (defchar || used)
2111 SetLastError( ERROR_INVALID_PARAMETER );
2112 return 0;
2114 if (flags)
2116 SetLastError( ERROR_INVALID_FLAGS );
2117 return 0;
2119 FIXME("UTF-7 not supported\n");
2120 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2121 return 0;
2122 case CP_UNIXCP:
2123 if (unix_cptable)
2125 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2126 defchar, used ? &used_tmp : NULL );
2127 break;
2129 /* fall through */
2130 case CP_UTF8:
2131 if (defchar || used)
2133 SetLastError( ERROR_INVALID_PARAMETER );
2134 return 0;
2136 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2137 break;
2138 default:
2139 if (!(table = get_codepage_table( page )))
2141 SetLastError( ERROR_INVALID_PARAMETER );
2142 return 0;
2144 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2145 defchar, used ? &used_tmp : NULL );
2146 if (used) *used = used_tmp;
2147 break;
2150 if (ret < 0)
2152 switch(ret)
2154 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2155 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2157 ret = 0;
2159 TRACE("cp %d %s -> %s, ret = %d\n",
2160 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2161 return ret;
2165 /***********************************************************************
2166 * GetThreadLocale (KERNEL32.@)
2168 * Get the current threads locale.
2170 * PARAMS
2171 * None.
2173 * RETURNS
2174 * The LCID currently associated with the calling thread.
2176 LCID WINAPI GetThreadLocale(void)
2178 LCID ret = NtCurrentTeb()->CurrentLocale;
2179 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2180 return ret;
2183 /**********************************************************************
2184 * SetThreadLocale (KERNEL32.@)
2186 * Set the current threads locale.
2188 * PARAMS
2189 * lcid [I] LCID of the locale to set
2191 * RETURNS
2192 * Success: TRUE. The threads locale is set to lcid.
2193 * Failure: FALSE. Use GetLastError() to determine the cause.
2195 BOOL WINAPI SetThreadLocale( LCID lcid )
2197 TRACE("(0x%04X)\n", lcid);
2199 lcid = ConvertDefaultLocale(lcid);
2201 if (lcid != GetThreadLocale())
2203 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2205 SetLastError(ERROR_INVALID_PARAMETER);
2206 return FALSE;
2209 NtCurrentTeb()->CurrentLocale = lcid;
2211 return TRUE;
2214 /**********************************************************************
2215 * SetThreadUILanguage (KERNEL32.@)
2217 * Set the current threads UI language.
2219 * PARAMS
2220 * langid [I] LANGID of the language to set, or 0 to use
2221 * the available language which is best supported
2222 * for console applications
2224 * RETURNS
2225 * Success: The return value is the same as the input value.
2226 * Failure: The return value differs from the input value.
2227 * Use GetLastError() to determine the cause.
2229 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2231 TRACE("(0x%04x) stub - returning success\n", langid);
2232 return langid;
2235 /******************************************************************************
2236 * ConvertDefaultLocale (KERNEL32.@)
2238 * Convert a default locale identifier into a real identifier.
2240 * PARAMS
2241 * lcid [I] LCID identifier of the locale to convert
2243 * RETURNS
2244 * lcid unchanged, if not a default locale or its sublanguage is
2245 * not SUBLANG_NEUTRAL.
2246 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2247 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2248 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2250 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2252 LANGID langid;
2254 switch (lcid)
2256 case LOCALE_SYSTEM_DEFAULT:
2257 lcid = GetSystemDefaultLCID();
2258 break;
2259 case LOCALE_USER_DEFAULT:
2260 case LOCALE_NEUTRAL:
2261 lcid = GetUserDefaultLCID();
2262 break;
2263 default:
2264 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2265 langid = LANGIDFROMLCID(lcid);
2266 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2268 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2269 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2272 return lcid;
2276 /******************************************************************************
2277 * IsValidLocale (KERNEL32.@)
2279 * Determine if a locale is valid.
2281 * PARAMS
2282 * lcid [I] LCID of the locale to check
2283 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2285 * RETURNS
2286 * TRUE, if lcid is valid,
2287 * FALSE, otherwise.
2289 * NOTES
2290 * Wine does not currently make the distinction between supported and installed. All
2291 * languages supported are installed by default.
2293 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2295 /* check if language is registered in the kernel32 resources */
2296 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2297 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2300 /******************************************************************************
2301 * IsValidLocaleName (KERNEL32.@)
2303 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2305 struct locale_name locale_name;
2307 /* string parsing */
2308 parse_locale_name( locale, &locale_name );
2310 TRACE( "found lcid %x for %s, matches %d\n",
2311 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2313 return locale_name.matches > 0;
2316 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2317 LPCSTR name, WORD LangID, LONG_PTR lParam )
2319 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2320 char buf[20];
2322 sprintf(buf, "%08x", (UINT)LangID);
2323 return lpfnLocaleEnum( buf );
2326 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2327 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2329 static const WCHAR formatW[] = {'%','0','8','x',0};
2330 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2331 WCHAR buf[20];
2332 sprintfW( buf, formatW, (UINT)LangID );
2333 return lpfnLocaleEnum( buf );
2336 /******************************************************************************
2337 * EnumSystemLocalesA (KERNEL32.@)
2339 * Call a users function for each locale available on the system.
2341 * PARAMS
2342 * lpfnLocaleEnum [I] Callback function to call for each locale
2343 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2345 * RETURNS
2346 * Success: TRUE.
2347 * Failure: FALSE. Use GetLastError() to determine the cause.
2349 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2351 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2352 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2353 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2354 (LONG_PTR)lpfnLocaleEnum);
2355 return TRUE;
2359 /******************************************************************************
2360 * EnumSystemLocalesW (KERNEL32.@)
2362 * See EnumSystemLocalesA.
2364 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2366 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2367 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2368 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2369 (LONG_PTR)lpfnLocaleEnum);
2370 return TRUE;
2374 struct enum_locale_ex_data
2376 LOCALE_ENUMPROCEX proc;
2377 DWORD flags;
2378 LPARAM lparam;
2381 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2382 LPCWSTR name, WORD lang, LONG_PTR lparam )
2384 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2385 WCHAR buffer[256];
2386 DWORD neutral;
2387 unsigned int flags;
2389 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2390 buffer, sizeof(buffer) / sizeof(WCHAR) );
2391 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2392 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2393 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2394 neutral = 0;
2395 flags = LOCALE_WINDOWS;
2396 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2397 if (data->flags && !(data->flags & flags)) return TRUE;
2398 return data->proc( buffer, flags, data->lparam );
2401 /******************************************************************************
2402 * EnumSystemLocalesEx (KERNEL32.@)
2404 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2406 struct enum_locale_ex_data data;
2408 if (reserved)
2410 SetLastError( ERROR_INVALID_PARAMETER );
2411 return FALSE;
2413 data.proc = proc;
2414 data.flags = flags;
2415 data.lparam = lparam;
2416 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2417 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2418 enum_locale_ex_proc, (LONG_PTR)&data );
2419 return TRUE;
2423 /***********************************************************************
2424 * VerLanguageNameA (KERNEL32.@)
2426 * Get the name of a language.
2428 * PARAMS
2429 * wLang [I] LANGID of the language
2430 * szLang [O] Destination for the language name
2432 * RETURNS
2433 * Success: The size of the language name. If szLang is non-NULL, it is filled
2434 * with the name.
2435 * Failure: 0. Use GetLastError() to determine the cause.
2438 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2440 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2444 /***********************************************************************
2445 * VerLanguageNameW (KERNEL32.@)
2447 * See VerLanguageNameA.
2449 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2451 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2455 /******************************************************************************
2456 * GetStringTypeW (KERNEL32.@)
2458 * See GetStringTypeA.
2460 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2462 static const unsigned char type2_map[16] =
2464 C2_NOTAPPLICABLE, /* unassigned */
2465 C2_LEFTTORIGHT, /* L */
2466 C2_RIGHTTOLEFT, /* R */
2467 C2_EUROPENUMBER, /* EN */
2468 C2_EUROPESEPARATOR, /* ES */
2469 C2_EUROPETERMINATOR, /* ET */
2470 C2_ARABICNUMBER, /* AN */
2471 C2_COMMONSEPARATOR, /* CS */
2472 C2_BLOCKSEPARATOR, /* B */
2473 C2_SEGMENTSEPARATOR, /* S */
2474 C2_WHITESPACE, /* WS */
2475 C2_OTHERNEUTRAL, /* ON */
2476 C2_RIGHTTOLEFT, /* AL */
2477 C2_NOTAPPLICABLE, /* NSM */
2478 C2_NOTAPPLICABLE, /* BN */
2479 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2482 if (count == -1) count = strlenW(src) + 1;
2483 switch(type)
2485 case CT_CTYPE1:
2486 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2487 break;
2488 case CT_CTYPE2:
2489 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2490 break;
2491 case CT_CTYPE3:
2493 WARN("CT_CTYPE3: semi-stub.\n");
2494 while (count--)
2496 int c = *src;
2497 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2499 type1 = get_char_typeW( *src++ ) & 0xfff;
2500 /* try to construct type3 from type1 */
2501 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2502 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2503 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2504 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2505 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2506 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2507 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2509 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
2510 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
2512 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2513 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2514 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2515 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2516 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2517 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2518 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2519 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2521 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2522 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2523 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2524 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2525 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2526 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2527 *chartype++ = type3;
2529 break;
2531 default:
2532 SetLastError( ERROR_INVALID_PARAMETER );
2533 return FALSE;
2535 return TRUE;
2539 /******************************************************************************
2540 * GetStringTypeExW (KERNEL32.@)
2542 * See GetStringTypeExA.
2544 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2546 /* locale is ignored for Unicode */
2547 return GetStringTypeW( type, src, count, chartype );
2551 /******************************************************************************
2552 * GetStringTypeA (KERNEL32.@)
2554 * Get characteristics of the characters making up a string.
2556 * PARAMS
2557 * locale [I] Locale Id for the string
2558 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2559 * src [I] String to analyse
2560 * count [I] Length of src in chars, or -1 if src is NUL terminated
2561 * chartype [O] Destination for the calculated characteristics
2563 * RETURNS
2564 * Success: TRUE. chartype is filled with the requested characteristics of each char
2565 * in src.
2566 * Failure: FALSE. Use GetLastError() to determine the cause.
2568 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2570 UINT cp;
2571 INT countW;
2572 LPWSTR srcW;
2573 BOOL ret = FALSE;
2575 if(count == -1) count = strlen(src) + 1;
2577 if (!(cp = get_lcid_codepage( locale )))
2579 FIXME("For locale %04x using current ANSI code page\n", locale);
2580 cp = GetACP();
2583 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2584 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2586 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2588 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2589 * string, with multibyte characters there maybe be more bytes in count
2590 * than character space in the buffer!
2592 ret = GetStringTypeW(type, srcW, countW, chartype);
2593 HeapFree(GetProcessHeap(), 0, srcW);
2595 return ret;
2598 /******************************************************************************
2599 * GetStringTypeExA (KERNEL32.@)
2601 * Get characteristics of the characters making up a string.
2603 * PARAMS
2604 * locale [I] Locale Id for the string
2605 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2606 * src [I] String to analyse
2607 * count [I] Length of src in chars, or -1 if src is NUL terminated
2608 * chartype [O] Destination for the calculated characteristics
2610 * RETURNS
2611 * Success: TRUE. chartype is filled with the requested characteristics of each char
2612 * in src.
2613 * Failure: FALSE. Use GetLastError() to determine the cause.
2615 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2617 return GetStringTypeA(locale, type, src, count, chartype);
2620 /*************************************************************************
2621 * LCMapStringEx (KERNEL32.@)
2623 * Map characters in a locale sensitive string.
2625 * PARAMS
2626 * name [I] Locale name for the conversion.
2627 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2628 * src [I] String to map
2629 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2630 * dst [O] Destination for mapped string
2631 * dstlen [I] Length of dst in characters
2632 * version [I] reserved, must be NULL
2633 * reserved [I] reserved, must be NULL
2634 * lparam [I] reserved, must be 0
2636 * RETURNS
2637 * Success: The length of the mapped string in dst, including the NUL terminator.
2638 * Failure: 0. Use GetLastError() to determine the cause.
2640 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2641 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2643 LPWSTR dst_ptr;
2645 if (version) FIXME("unsupported version structure %p\n", version);
2646 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2647 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2649 if (!src || !srclen || dstlen < 0)
2651 SetLastError(ERROR_INVALID_PARAMETER);
2652 return 0;
2655 /* mutually exclusive flags */
2656 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2657 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2658 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2659 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2661 SetLastError(ERROR_INVALID_FLAGS);
2662 return 0;
2665 if (!dstlen) dst = NULL;
2667 if (flags & LCMAP_SORTKEY)
2669 INT ret;
2670 if (src == dst)
2672 SetLastError(ERROR_INVALID_FLAGS);
2673 return 0;
2676 if (srclen < 0) srclen = strlenW(src);
2678 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2679 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2681 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2682 if (ret == 0)
2683 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2684 else
2685 ret++;
2686 return ret;
2689 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2690 if (flags & SORT_STRINGSORT)
2692 SetLastError(ERROR_INVALID_FLAGS);
2693 return 0;
2696 if (srclen < 0) srclen = strlenW(src) + 1;
2698 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2699 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2701 if (!dst) /* return required string length */
2703 INT len;
2705 for (len = 0; srclen; src++, srclen--)
2707 WCHAR wch = *src;
2708 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2709 * and skips white space and punctuation characters for
2710 * NORM_IGNORESYMBOLS.
2712 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2713 continue;
2714 len++;
2716 return len;
2719 if (flags & LCMAP_UPPERCASE)
2721 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2723 WCHAR wch = *src;
2724 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2725 continue;
2726 *dst_ptr++ = toupperW(wch);
2727 dstlen--;
2730 else if (flags & LCMAP_LOWERCASE)
2732 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2734 WCHAR wch = *src;
2735 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2736 continue;
2737 *dst_ptr++ = tolowerW(wch);
2738 dstlen--;
2741 else
2743 if (src == dst)
2745 SetLastError(ERROR_INVALID_FLAGS);
2746 return 0;
2748 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2750 WCHAR wch = *src;
2751 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2752 continue;
2753 *dst_ptr++ = wch;
2754 dstlen--;
2758 if (srclen)
2760 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2761 return 0;
2764 return dst_ptr - dst;
2767 /*************************************************************************
2768 * LCMapStringW (KERNEL32.@)
2770 * See LCMapStringA.
2772 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2773 LPWSTR dst, INT dstlen)
2775 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2776 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2778 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2781 /*************************************************************************
2782 * LCMapStringA (KERNEL32.@)
2784 * Map characters in a locale sensitive string.
2786 * PARAMS
2787 * lcid [I] LCID for the conversion.
2788 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2789 * src [I] String to map
2790 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2791 * dst [O] Destination for mapped string
2792 * dstlen [I] Length of dst in characters
2794 * RETURNS
2795 * Success: The length of the mapped string in dst, including the NUL terminator.
2796 * Failure: 0. Use GetLastError() to determine the cause.
2798 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2799 LPSTR dst, INT dstlen)
2801 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2802 LPWSTR srcW, dstW;
2803 INT ret = 0, srclenW, dstlenW;
2804 UINT locale_cp = CP_ACP;
2806 if (!src || !srclen || dstlen < 0)
2808 SetLastError(ERROR_INVALID_PARAMETER);
2809 return 0;
2812 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2814 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2815 if (srclenW)
2816 srcW = bufW;
2817 else
2819 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2820 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2821 if (!srcW)
2823 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2824 return 0;
2826 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2829 if (flags & LCMAP_SORTKEY)
2831 if (src == dst)
2833 SetLastError(ERROR_INVALID_FLAGS);
2834 goto map_string_exit;
2836 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2837 if (ret == 0)
2838 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2839 else
2840 ret++;
2841 goto map_string_exit;
2844 if (flags & SORT_STRINGSORT)
2846 SetLastError(ERROR_INVALID_FLAGS);
2847 goto map_string_exit;
2850 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2851 if (!dstlenW)
2852 goto map_string_exit;
2854 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2855 if (!dstW)
2857 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2858 goto map_string_exit;
2861 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2862 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2863 HeapFree(GetProcessHeap(), 0, dstW);
2865 map_string_exit:
2866 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2867 return ret;
2870 /*************************************************************************
2871 * FoldStringA (KERNEL32.@)
2873 * Map characters in a string.
2875 * PARAMS
2876 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2877 * src [I] String to map
2878 * srclen [I] Length of src, or -1 if src is NUL terminated
2879 * dst [O] Destination for mapped string
2880 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2882 * RETURNS
2883 * Success: The length of the string written to dst, including the terminating NUL. If
2884 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2885 * and dst may be NULL.
2886 * Failure: 0. Use GetLastError() to determine the cause.
2888 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2889 LPSTR dst, INT dstlen)
2891 INT ret = 0, srclenW = 0;
2892 WCHAR *srcW = NULL, *dstW = NULL;
2894 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2896 SetLastError(ERROR_INVALID_PARAMETER);
2897 return 0;
2900 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2901 src, srclen, NULL, 0);
2902 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2904 if (!srcW)
2906 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2907 goto FoldStringA_exit;
2910 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2911 src, srclen, srcW, srclenW);
2913 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2915 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2916 if (ret && dstlen)
2918 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2920 if (!dstW)
2922 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2923 goto FoldStringA_exit;
2926 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2927 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2929 ret = 0;
2930 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2934 HeapFree(GetProcessHeap(), 0, dstW);
2936 FoldStringA_exit:
2937 HeapFree(GetProcessHeap(), 0, srcW);
2938 return ret;
2941 /*************************************************************************
2942 * FoldStringW (KERNEL32.@)
2944 * See FoldStringA.
2946 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2947 LPWSTR dst, INT dstlen)
2949 int ret;
2951 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2953 case 0:
2954 if (dwFlags)
2955 break;
2956 /* Fall through for dwFlags == 0 */
2957 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2958 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2959 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2960 SetLastError(ERROR_INVALID_FLAGS);
2961 return 0;
2964 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2966 SetLastError(ERROR_INVALID_PARAMETER);
2967 return 0;
2970 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2971 if (!ret)
2972 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2973 return ret;
2976 /******************************************************************************
2977 * CompareStringW (KERNEL32.@)
2979 * See CompareStringA.
2981 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
2982 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2984 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
2987 /******************************************************************************
2988 * CompareStringEx (KERNEL32.@)
2990 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
2991 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
2993 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
2994 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
2995 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
2996 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
2997 INT ret;
2999 if (version) FIXME("unexpected version parameter\n");
3000 if (reserved) FIXME("unexpected reserved value\n");
3001 if (lParam) FIXME("unexpected lParam\n");
3003 if (!str1 || !str2)
3005 SetLastError(ERROR_INVALID_PARAMETER);
3006 return 0;
3009 if (flags & ~(supported_flags|semistub_flags))
3011 SetLastError(ERROR_INVALID_FLAGS);
3012 return 0;
3015 if (flags & semistub_flags)
3016 FIXME("semi-stub behavor for flag(s) 0x%x\n", flags & semistub_flags);
3018 if (len1 < 0) len1 = strlenW(str1);
3019 if (len2 < 0) len2 = strlenW(str2);
3021 ret = wine_compare_string(flags, str1, len1, str2, len2);
3023 if (ret) /* need to translate result */
3024 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3025 return CSTR_EQUAL;
3028 /******************************************************************************
3029 * CompareStringA (KERNEL32.@)
3031 * Compare two locale sensitive strings.
3033 * PARAMS
3034 * lcid [I] LCID for the comparison
3035 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3036 * str1 [I] First string to compare
3037 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3038 * str2 [I] Second string to compare
3039 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3041 * RETURNS
3042 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3043 * str1 is less than, equal to or greater than str2 respectively.
3044 * Failure: FALSE. Use GetLastError() to determine the cause.
3046 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3047 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3049 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3050 WCHAR *buf2W = buf1W + 130;
3051 LPWSTR str1W, str2W;
3052 INT len1W = 0, len2W = 0, ret;
3053 UINT locale_cp = CP_ACP;
3055 if (!str1 || !str2)
3057 SetLastError(ERROR_INVALID_PARAMETER);
3058 return 0;
3060 if (len1 < 0) len1 = strlen(str1);
3061 if (len2 < 0) len2 = strlen(str2);
3063 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3065 if (len1)
3067 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3068 if (len1W)
3069 str1W = buf1W;
3070 else
3072 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3073 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3074 if (!str1W)
3076 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3077 return 0;
3079 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3082 else
3084 len1W = 0;
3085 str1W = buf1W;
3088 if (len2)
3090 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3091 if (len2W)
3092 str2W = buf2W;
3093 else
3095 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3096 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3097 if (!str2W)
3099 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3100 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3101 return 0;
3103 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3106 else
3108 len2W = 0;
3109 str2W = buf2W;
3112 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3114 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3115 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3116 return ret;
3119 /******************************************************************************
3120 * CompareStringOrdinal (KERNEL32.@)
3122 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3124 int ret, len;
3126 if (!str1 || !str2)
3128 SetLastError(ERROR_INVALID_PARAMETER);
3129 return 0;
3131 if (len1 < 0) len1 = strlenW(str1);
3132 if (len2 < 0) len2 = strlenW(str2);
3134 len = min(len1, len2);
3135 if (ignore_case)
3137 ret = memicmpW(str1, str2, len);
3139 else
3141 ret = 0;
3142 for (; len > 0; len--)
3143 if ((ret = (*str1++ - *str2++))) break;
3145 if (!ret) ret = len1 - len2;
3147 if (ret < 0) return CSTR_LESS_THAN;
3148 if (ret > 0) return CSTR_GREATER_THAN;
3149 return CSTR_EQUAL;
3152 /*************************************************************************
3153 * lstrcmp (KERNEL32.@)
3154 * lstrcmpA (KERNEL32.@)
3156 * Compare two strings using the current thread locale.
3158 * PARAMS
3159 * str1 [I] First string to compare
3160 * str2 [I] Second string to compare
3162 * RETURNS
3163 * Success: A number less than, equal to or greater than 0 depending on whether
3164 * str1 is less than, equal to or greater than str2 respectively.
3165 * Failure: FALSE. Use GetLastError() to determine the cause.
3167 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3169 int ret;
3171 if ((str1 == NULL) && (str2 == NULL)) return 0;
3172 if (str1 == NULL) return -1;
3173 if (str2 == NULL) return 1;
3175 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3176 if (ret) ret -= 2;
3178 return ret;
3181 /*************************************************************************
3182 * lstrcmpi (KERNEL32.@)
3183 * lstrcmpiA (KERNEL32.@)
3185 * Compare two strings using the current thread locale, ignoring case.
3187 * PARAMS
3188 * str1 [I] First string to compare
3189 * str2 [I] Second string to compare
3191 * RETURNS
3192 * Success: A number less than, equal to or greater than 0 depending on whether
3193 * str2 is less than, equal to or greater than str1 respectively.
3194 * Failure: FALSE. Use GetLastError() to determine the cause.
3196 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3198 int ret;
3200 if ((str1 == NULL) && (str2 == NULL)) return 0;
3201 if (str1 == NULL) return -1;
3202 if (str2 == NULL) return 1;
3204 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3205 if (ret) ret -= 2;
3207 return ret;
3210 /*************************************************************************
3211 * lstrcmpW (KERNEL32.@)
3213 * See lstrcmpA.
3215 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3217 int ret;
3219 if ((str1 == NULL) && (str2 == NULL)) return 0;
3220 if (str1 == NULL) return -1;
3221 if (str2 == NULL) return 1;
3223 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3224 if (ret) ret -= 2;
3226 return ret;
3229 /*************************************************************************
3230 * lstrcmpiW (KERNEL32.@)
3232 * See lstrcmpiA.
3234 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3236 int ret;
3238 if ((str1 == NULL) && (str2 == NULL)) return 0;
3239 if (str1 == NULL) return -1;
3240 if (str2 == NULL) return 1;
3242 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3243 if (ret) ret -= 2;
3245 return ret;
3248 /******************************************************************************
3249 * LOCALE_Init
3251 void LOCALE_Init(void)
3253 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3254 const union cptable *unix_cp );
3256 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3258 #ifdef __APPLE__
3259 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3260 char user_locale[50];
3262 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3263 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3264 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3265 CFStringRef user_locale_string_ref;
3267 if (user_locale_country_ref)
3269 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3270 user_locale_lang_ref, user_locale_country_ref);
3272 else
3274 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3277 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3278 strcat(user_locale, ".UTF-8");
3280 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3281 setenv( "LANG", user_locale, 0 );
3282 TRACE( "setting locale to '%s'\n", user_locale );
3283 #endif /* __APPLE__ */
3285 setlocale( LC_ALL, "" );
3287 unix_cp = setup_unix_locales();
3288 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3290 #ifdef __APPLE__
3291 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3292 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3294 /* Retrieve the preferred language as chosen in System Preferences. */
3295 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3296 leave things be. */
3297 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3298 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3299 CFStringRef user_language_string_ref;
3300 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3301 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3302 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3303 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3305 struct locale_name locale_name;
3306 WCHAR buffer[128];
3307 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3308 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3309 parse_locale_name( buffer, &locale_name );
3310 lcid_LC_MESSAGES = locale_name.lcid;
3311 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3313 if (preferred_langs)
3314 CFRelease( preferred_langs );
3315 if (canonical_lang_string_ref)
3316 CFRelease( canonical_lang_string_ref );
3319 CFRelease( user_locale_ref );
3320 CFRelease( user_locale_string_ref );
3321 #endif
3323 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3324 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3325 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3327 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3328 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3329 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3330 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3331 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3332 if (!unix_cp)
3333 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3334 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3336 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3337 ansi_cptable = wine_cp_get_table( 1252 );
3338 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3339 oem_cptable = wine_cp_get_table( 437 );
3340 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3341 mac_cptable = wine_cp_get_table( 10000 );
3342 if (unix_cp != CP_UTF8)
3344 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3345 unix_cptable = wine_cp_get_table( 28591 );
3348 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3350 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3351 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3352 mac_cptable->info.codepage, unix_cp );
3354 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3357 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3359 UNICODE_STRING keyName;
3360 OBJECT_ATTRIBUTES attr;
3361 HANDLE hkey;
3363 RtlInitUnicodeString( &keyName, szKeyName );
3364 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3366 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3367 hkey = 0;
3369 return hkey;
3372 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3373 LPWSTR szValueName, ULONG valueNameSize,
3374 LPWSTR szValueData, ULONG valueDataSize)
3376 BYTE buffer[80];
3377 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3378 DWORD dwLen;
3380 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3381 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3382 info->NameLength > valueNameSize ||
3383 info->DataLength > valueDataSize)
3385 return FALSE;
3388 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3390 memcpy( szValueName, info->Name, info->NameLength);
3391 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3392 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3393 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3395 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3396 return TRUE;
3399 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3401 BYTE buffer[128];
3402 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3403 DWORD dwSize = sizeof(buffer);
3404 UNICODE_STRING valueName;
3406 RtlInitUnicodeString( &valueName, szValueName );
3408 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3409 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3410 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3411 info->DataLength == sizeof(DWORD))
3413 memcpy(lpVal, info->Data, sizeof(DWORD));
3414 return TRUE;
3417 return FALSE;
3420 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3422 LANGID langId;
3423 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3424 HRSRC hResource;
3425 BOOL bRet = FALSE;
3427 /* FIXME: Is it correct to use the system default langid? */
3428 langId = GetSystemDefaultLangID();
3430 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3431 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3433 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3435 if (hResource)
3437 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3439 if (hResDir)
3441 ULONG iResourceIndex = lgrpid & 0xf;
3442 LPCWSTR lpResEntry = LockResource( hResDir );
3443 ULONG i;
3445 for (i = 0; i < iResourceIndex; i++)
3446 lpResEntry += *lpResEntry + 1;
3448 if (*lpResEntry < nameSize)
3450 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3451 szName[*lpResEntry] = '\0';
3452 bRet = TRUE;
3456 FreeResource( hResource );
3458 return bRet;
3461 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3462 typedef struct
3464 LANGUAGEGROUP_ENUMPROCA procA;
3465 LANGUAGEGROUP_ENUMPROCW procW;
3466 DWORD dwFlags;
3467 LONG_PTR lParam;
3468 } ENUMLANGUAGEGROUP_CALLBACKS;
3470 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3471 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3473 WCHAR szNumber[10], szValue[4];
3474 HANDLE hKey;
3475 BOOL bContinue = TRUE;
3476 ULONG ulIndex = 0;
3478 if (!lpProcs)
3480 SetLastError(ERROR_INVALID_PARAMETER);
3481 return FALSE;
3484 switch (lpProcs->dwFlags)
3486 case 0:
3487 /* Default to LGRPID_INSTALLED */
3488 lpProcs->dwFlags = LGRPID_INSTALLED;
3489 /* Fall through... */
3490 case LGRPID_INSTALLED:
3491 case LGRPID_SUPPORTED:
3492 break;
3493 default:
3494 SetLastError(ERROR_INVALID_FLAGS);
3495 return FALSE;
3498 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3500 if (!hKey)
3501 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3503 while (bContinue)
3505 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3506 szValue, sizeof(szValue) ))
3508 BOOL bInstalled = szValue[0] == '1';
3509 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3511 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3512 bInstalled ? "" : "not ");
3514 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3516 WCHAR szGrpName[48];
3518 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3519 szGrpName[0] = '\0';
3521 if (lpProcs->procW)
3522 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3523 lpProcs->lParam );
3524 else
3526 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3527 char szGrpNameA[48];
3529 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3530 * or whether the language names are ever localised. Assume CP_ACP.
3533 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3534 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3536 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3537 lpProcs->lParam );
3541 ulIndex++;
3543 else
3544 bContinue = FALSE;
3546 if (!bContinue)
3547 break;
3550 if (hKey)
3551 NtClose( hKey );
3553 return TRUE;
3556 /******************************************************************************
3557 * EnumSystemLanguageGroupsA (KERNEL32.@)
3559 * Call a users function for each language group available on the system.
3561 * PARAMS
3562 * pLangGrpEnumProc [I] Callback function to call for each language group
3563 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3564 * lParam [I] User parameter to pass to pLangGrpEnumProc
3566 * RETURNS
3567 * Success: TRUE.
3568 * Failure: FALSE. Use GetLastError() to determine the cause.
3570 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3571 DWORD dwFlags, LONG_PTR lParam)
3573 ENUMLANGUAGEGROUP_CALLBACKS procs;
3575 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3577 procs.procA = pLangGrpEnumProc;
3578 procs.procW = NULL;
3579 procs.dwFlags = dwFlags;
3580 procs.lParam = lParam;
3582 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3585 /******************************************************************************
3586 * EnumSystemLanguageGroupsW (KERNEL32.@)
3588 * See EnumSystemLanguageGroupsA.
3590 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3591 DWORD dwFlags, LONG_PTR lParam)
3593 ENUMLANGUAGEGROUP_CALLBACKS procs;
3595 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3597 procs.procA = NULL;
3598 procs.procW = pLangGrpEnumProc;
3599 procs.dwFlags = dwFlags;
3600 procs.lParam = lParam;
3602 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3605 /******************************************************************************
3606 * IsValidLanguageGroup (KERNEL32.@)
3608 * Determine if a language group is supported and/or installed.
3610 * PARAMS
3611 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3612 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3614 * RETURNS
3615 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3616 * FALSE otherwise.
3618 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3620 static const WCHAR szFormat[] = { '%','x','\0' };
3621 WCHAR szValueName[16], szValue[2];
3622 BOOL bSupported = FALSE, bInstalled = FALSE;
3623 HANDLE hKey;
3626 switch (dwFlags)
3628 case LGRPID_INSTALLED:
3629 case LGRPID_SUPPORTED:
3631 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3633 sprintfW( szValueName, szFormat, lgrpid );
3635 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3637 bSupported = TRUE;
3639 if (szValue[0] == '1')
3640 bInstalled = TRUE;
3643 if (hKey)
3644 NtClose( hKey );
3646 break;
3649 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3650 (dwFlags == LGRPID_INSTALLED && bInstalled))
3651 return TRUE;
3653 return FALSE;
3656 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3657 typedef struct
3659 LANGGROUPLOCALE_ENUMPROCA procA;
3660 LANGGROUPLOCALE_ENUMPROCW procW;
3661 DWORD dwFlags;
3662 LGRPID lgrpid;
3663 LONG_PTR lParam;
3664 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3666 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3667 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3669 static const WCHAR szAlternateSortsKeyName[] = {
3670 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3672 WCHAR szNumber[10], szValue[4];
3673 HANDLE hKey;
3674 BOOL bContinue = TRUE, bAlternate = FALSE;
3675 LGRPID lgrpid;
3676 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3678 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3680 SetLastError(ERROR_INVALID_PARAMETER);
3681 return FALSE;
3684 if (lpProcs->dwFlags)
3686 SetLastError(ERROR_INVALID_FLAGS);
3687 return FALSE;
3690 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3692 if (!hKey)
3693 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3695 while (bContinue)
3697 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3698 szValue, sizeof(szValue) ))
3700 lgrpid = strtoulW( szValue, NULL, 16 );
3702 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3703 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3705 if (lgrpid == lpProcs->lgrpid)
3707 LCID lcid;
3709 lcid = strtoulW( szNumber, NULL, 16 );
3711 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3712 * '00000437 ;Georgian'
3713 * At present we only pass the LCID string.
3716 if (lpProcs->procW)
3717 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3718 else
3720 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3722 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3724 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3728 ulIndex++;
3730 else
3732 /* Finished enumerating this key */
3733 if (!bAlternate)
3735 /* Enumerate alternate sorts also */
3736 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3737 bAlternate = TRUE;
3738 ulIndex = 0;
3740 else
3741 bContinue = FALSE; /* Finished both keys */
3744 if (!bContinue)
3745 break;
3748 if (hKey)
3749 NtClose( hKey );
3751 return TRUE;
3754 /******************************************************************************
3755 * EnumLanguageGroupLocalesA (KERNEL32.@)
3757 * Call a users function for every locale in a language group available on the system.
3759 * PARAMS
3760 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3761 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3762 * dwFlags [I] Reserved, set to 0
3763 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3765 * RETURNS
3766 * Success: TRUE.
3767 * Failure: FALSE. Use GetLastError() to determine the cause.
3769 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3770 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3772 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3774 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3776 callbacks.procA = pLangGrpLcEnumProc;
3777 callbacks.procW = NULL;
3778 callbacks.dwFlags = dwFlags;
3779 callbacks.lgrpid = lgrpid;
3780 callbacks.lParam = lParam;
3782 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3785 /******************************************************************************
3786 * EnumLanguageGroupLocalesW (KERNEL32.@)
3788 * See EnumLanguageGroupLocalesA.
3790 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3791 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3793 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3795 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3797 callbacks.procA = NULL;
3798 callbacks.procW = pLangGrpLcEnumProc;
3799 callbacks.dwFlags = dwFlags;
3800 callbacks.lgrpid = lgrpid;
3801 callbacks.lParam = lParam;
3803 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3806 /******************************************************************************
3807 * InvalidateNLSCache (KERNEL32.@)
3809 * Invalidate the cache of NLS values.
3811 * PARAMS
3812 * None.
3814 * RETURNS
3815 * Success: TRUE.
3816 * Failure: FALSE.
3818 BOOL WINAPI InvalidateNLSCache(void)
3820 FIXME("() stub\n");
3821 return FALSE;
3824 /******************************************************************************
3825 * GetUserGeoID (KERNEL32.@)
3827 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3829 GEOID ret = GEOID_NOT_AVAILABLE;
3830 static const WCHAR geoW[] = {'G','e','o',0};
3831 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3832 WCHAR bufferW[40], *end;
3833 DWORD count;
3834 HANDLE hkey, hSubkey = 0;
3835 UNICODE_STRING keyW;
3836 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3837 RtlInitUnicodeString( &keyW, nationW );
3838 count = sizeof(bufferW);
3840 if(!(hkey = create_registry_key())) return ret;
3842 switch( GeoClass ){
3843 case GEOCLASS_NATION:
3844 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3846 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3847 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3848 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3850 break;
3851 case GEOCLASS_REGION:
3852 FIXME("GEOCLASS_REGION not handled yet\n");
3853 break;
3856 NtClose(hkey);
3857 if (hSubkey) NtClose(hSubkey);
3858 return ret;
3861 /******************************************************************************
3862 * SetUserGeoID (KERNEL32.@)
3864 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3866 static const WCHAR geoW[] = {'G','e','o',0};
3867 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3868 static const WCHAR formatW[] = {'%','i',0};
3869 UNICODE_STRING nameW,keyW;
3870 WCHAR bufferW[10];
3871 OBJECT_ATTRIBUTES attr;
3872 HANDLE hkey;
3874 if(!(hkey = create_registry_key())) return FALSE;
3876 attr.Length = sizeof(attr);
3877 attr.RootDirectory = hkey;
3878 attr.ObjectName = &nameW;
3879 attr.Attributes = 0;
3880 attr.SecurityDescriptor = NULL;
3881 attr.SecurityQualityOfService = NULL;
3882 RtlInitUnicodeString( &nameW, geoW );
3883 RtlInitUnicodeString( &keyW, nationW );
3885 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3888 NtClose(attr.RootDirectory);
3889 return FALSE;
3892 sprintfW(bufferW, formatW, GeoID);
3893 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3894 NtClose(attr.RootDirectory);
3895 NtClose(hkey);
3896 return TRUE;
3899 typedef struct
3901 union
3903 UILANGUAGE_ENUMPROCA procA;
3904 UILANGUAGE_ENUMPROCW procW;
3905 } u;
3906 DWORD flags;
3907 LONG_PTR param;
3908 } ENUM_UILANG_CALLBACK;
3910 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3911 LPCSTR name, WORD LangID, LONG_PTR lParam )
3913 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3914 char buf[20];
3916 sprintf(buf, "%08x", (UINT)LangID);
3917 return enum_uilang->u.procA( buf, enum_uilang->param );
3920 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3921 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3923 static const WCHAR formatW[] = {'%','0','8','x',0};
3924 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3925 WCHAR buf[20];
3927 sprintfW( buf, formatW, (UINT)LangID );
3928 return enum_uilang->u.procW( buf, enum_uilang->param );
3931 /******************************************************************************
3932 * EnumUILanguagesA (KERNEL32.@)
3934 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3936 ENUM_UILANG_CALLBACK enum_uilang;
3938 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3940 if(!pUILangEnumProc) {
3941 SetLastError(ERROR_INVALID_PARAMETER);
3942 return FALSE;
3944 if(dwFlags) {
3945 SetLastError(ERROR_INVALID_FLAGS);
3946 return FALSE;
3949 enum_uilang.u.procA = pUILangEnumProc;
3950 enum_uilang.flags = dwFlags;
3951 enum_uilang.param = lParam;
3953 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3954 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3955 (LONG_PTR)&enum_uilang);
3956 return TRUE;
3959 /******************************************************************************
3960 * EnumUILanguagesW (KERNEL32.@)
3962 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3964 ENUM_UILANG_CALLBACK enum_uilang;
3966 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3969 if(!pUILangEnumProc) {
3970 SetLastError(ERROR_INVALID_PARAMETER);
3971 return FALSE;
3973 if(dwFlags) {
3974 SetLastError(ERROR_INVALID_FLAGS);
3975 return FALSE;
3978 enum_uilang.u.procW = pUILangEnumProc;
3979 enum_uilang.flags = dwFlags;
3980 enum_uilang.param = lParam;
3982 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3983 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3984 (LONG_PTR)&enum_uilang);
3985 return TRUE;
3988 enum locationkind {
3989 LOCATION_NATION = 0,
3990 LOCATION_REGION,
3991 LOCATION_BOTH
3994 struct geoinfo_t {
3995 GEOID id;
3996 WCHAR iso2W[3];
3997 WCHAR iso3W[4];
3998 GEOID parent;
3999 INT uncode;
4000 enum locationkind kind;
4003 static const struct geoinfo_t geoinfodata[] = {
4004 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4005 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4006 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4007 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4008 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4009 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4010 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4011 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4012 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4013 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4014 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4015 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4016 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4017 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4018 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4019 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4020 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4021 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4022 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4023 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4024 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4025 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4026 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4027 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4028 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4029 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4030 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4031 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4032 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4033 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4034 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4035 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4036 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4037 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4038 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4039 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4040 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4041 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4042 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4043 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4044 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4045 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4046 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4047 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4048 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4049 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4050 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4051 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4052 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4053 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4054 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4055 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4056 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4057 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4058 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4059 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4060 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4061 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4062 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4063 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4064 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4065 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4066 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4067 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4068 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4069 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4070 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4071 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4072 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4073 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4074 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4075 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4076 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4077 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4078 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4079 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4080 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4081 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4082 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4083 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4084 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4085 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4086 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4087 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4088 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4089 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4090 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4091 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4092 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4093 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4094 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4095 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4096 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4097 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4098 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4099 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4100 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4101 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4102 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4103 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4104 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4105 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4106 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4107 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4108 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4109 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4110 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4111 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4112 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4113 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4114 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4115 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4116 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4117 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4118 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4119 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4120 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4121 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4122 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4123 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4124 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4125 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4126 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4127 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4128 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4129 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4130 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4131 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4132 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4133 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4134 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4135 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4136 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4137 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4138 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4139 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4140 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4141 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4142 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4143 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4144 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4145 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4146 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4147 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4148 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4149 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4150 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4151 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4152 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4153 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4154 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4155 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4156 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4157 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4158 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4159 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4160 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4161 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4162 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4163 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4164 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4165 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4166 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4167 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4168 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4169 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4170 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4171 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4172 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4173 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4174 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4175 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4176 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4177 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4178 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4179 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4180 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4181 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4182 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4183 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4184 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4185 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4186 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4187 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4188 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4189 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4190 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4191 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4192 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4193 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4194 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4195 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4196 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4197 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4198 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4199 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4200 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4201 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4202 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4203 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4204 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4205 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4206 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4207 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4208 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4209 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4210 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4211 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4212 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4213 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4214 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4215 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4216 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4217 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4218 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4219 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4220 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4221 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4222 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4223 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4224 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4225 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4226 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4227 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4228 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4229 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4230 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4231 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4232 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4233 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4234 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4235 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4236 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4237 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4238 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4239 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4240 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4241 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4242 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4243 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4244 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4245 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4246 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4247 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4248 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4249 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4250 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4251 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4252 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4253 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4254 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4255 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4256 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4257 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4258 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4259 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4260 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4261 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4262 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4263 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4264 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4265 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4266 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4267 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4268 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4269 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4270 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4271 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4272 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4273 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4274 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4275 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4276 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4277 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4278 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4279 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4280 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4281 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4282 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4283 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4284 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4285 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4286 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4287 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4288 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4289 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4290 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4291 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4292 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4293 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4294 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4295 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4296 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4297 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4298 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4299 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4300 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4301 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4302 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4305 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4307 int min, max;
4309 min = 0;
4310 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4312 while (min <= max) {
4313 const struct geoinfo_t *ptr;
4314 int n = (min+max)/2;
4316 ptr = &geoinfodata[n];
4317 if (geoid == ptr->id)
4318 /* we don't need empty entries */
4319 return *ptr->iso2W ? ptr : NULL;
4321 if (ptr->id > geoid)
4322 max = n-1;
4323 else
4324 min = n+1;
4327 return NULL;
4330 /******************************************************************************
4331 * GetGeoInfoW (KERNEL32.@)
4333 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4335 const struct geoinfo_t *ptr;
4336 const WCHAR *str = NULL;
4337 WCHAR buffW[12];
4338 LONG val = 0;
4339 INT len;
4341 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4343 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4344 SetLastError(ERROR_INVALID_PARAMETER);
4345 return 0;
4348 switch (geotype) {
4349 case GEO_NATION:
4350 val = geoid;
4351 break;
4352 case GEO_ISO_UN_NUMBER:
4353 val = ptr->uncode;
4354 break;
4355 case GEO_PARENT:
4356 val = ptr->parent;
4357 break;
4358 case GEO_ISO2:
4359 case GEO_ISO3:
4361 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4362 break;
4364 case GEO_RFC1766:
4365 case GEO_LCID:
4366 case GEO_FRIENDLYNAME:
4367 case GEO_OFFICIALNAME:
4368 case GEO_TIMEZONES:
4369 case GEO_OFFICIALLANGUAGES:
4370 case GEO_LATITUDE:
4371 case GEO_LONGITUDE:
4372 FIXME("type %d is not supported\n", geotype);
4373 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4374 return 0;
4375 default:
4376 WARN("unrecognized type %d\n", geotype);
4377 SetLastError(ERROR_INVALID_FLAGS);
4378 return 0;
4381 if (val) {
4382 static const WCHAR fmtW[] = {'%','d',0};
4383 sprintfW(buffW, fmtW, val);
4384 str = buffW;
4387 len = strlenW(str) + 1;
4388 if (!data || !data_len)
4389 return len;
4391 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4392 if (data_len < len)
4393 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4394 return data_len < len ? 0 : len;
4397 /******************************************************************************
4398 * GetGeoInfoA (KERNEL32.@)
4400 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4402 WCHAR *buffW;
4403 INT len;
4405 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4407 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4408 if (!len)
4409 return 0;
4411 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4412 if (!buffW)
4413 return 0;
4415 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4416 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4417 if (!data || !data_len) {
4418 HeapFree(GetProcessHeap(), 0, buffW);
4419 return len;
4422 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4423 HeapFree(GetProcessHeap(), 0, buffW);
4425 if (data_len < len)
4426 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4427 return data_len < len ? 0 : len;
4430 /******************************************************************************
4431 * EnumSystemGeoID (KERNEL32.@)
4433 * Call a users function for every location available on the system.
4435 * PARAMS
4436 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4437 * parent [I] GEOID for the parent
4438 * enumproc [I] Callback function to call for each location
4440 * RETURNS
4441 * Success: TRUE.
4442 * Failure: FALSE. Use GetLastError() to determine the cause.
4444 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4446 INT i;
4448 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4450 if (!enumproc) {
4451 SetLastError(ERROR_INVALID_PARAMETER);
4452 return FALSE;
4455 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4456 SetLastError(ERROR_INVALID_FLAGS);
4457 return FALSE;
4460 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4461 const struct geoinfo_t *ptr = &geoinfodata[i];
4463 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4464 continue;
4466 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4467 continue;
4469 if (parent && ptr->parent != parent)
4470 continue;
4472 if (!enumproc(ptr->id))
4473 return TRUE;
4476 return TRUE;
4479 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4481 LCID userlcid;
4483 TRACE("%p, %d\n", localename, buffersize);
4485 userlcid = GetUserDefaultLCID();
4486 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4489 /******************************************************************************
4490 * NormalizeString (KERNEL32.@)
4492 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4493 LPWSTR lpDstString, INT cwDstLength)
4495 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4496 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4497 return 0;
4500 /******************************************************************************
4501 * IsNormalizedString (KERNEL32.@)
4503 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4505 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4506 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4507 return FALSE;
4510 enum {
4511 BASE = 36,
4512 TMIN = 1,
4513 TMAX = 26,
4514 SKEW = 38,
4515 DAMP = 700,
4516 INIT_BIAS = 72,
4517 INIT_N = 128
4520 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4522 INT k;
4524 delta /= (firsttime ? DAMP : 2);
4525 delta += delta/numpoints;
4527 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4528 delta /= BASE-TMIN;
4529 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4532 /******************************************************************************
4533 * IdnToAscii (KERNEL32.@)
4534 * Implementation of Punycode based on RFC 3492.
4536 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4537 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4539 static const WCHAR prefixW[] = {'x','n','-','-'};
4541 WCHAR *norm_str;
4542 INT i, label_start, label_end, norm_len, out_label, out = 0;
4544 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4545 lpASCIICharStr, cchASCIIChar);
4547 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4548 if(!norm_len)
4549 return 0;
4550 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4551 if(!norm_str) {
4552 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4553 return 0;
4555 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4556 cchUnicodeChar, norm_str, norm_len);
4557 if(!norm_len) {
4558 HeapFree(GetProcessHeap(), 0, norm_str);
4559 return 0;
4562 for(label_start=0; label_start<norm_len;) {
4563 INT n = INIT_N, bias = INIT_BIAS;
4564 INT delta = 0, b = 0, h;
4566 out_label = out;
4567 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4568 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4569 if(norm_str[i] < 0x80)
4570 b++;
4571 label_end = i;
4573 if(b == label_end-label_start) {
4574 if(label_end < norm_len)
4575 b++;
4576 if(!lpASCIICharStr) {
4577 out += b;
4578 }else if(out+b <= cchASCIIChar) {
4579 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4580 out += b;
4581 }else {
4582 HeapFree(GetProcessHeap(), 0, norm_str);
4583 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4584 return 0;
4586 label_start = label_end+1;
4587 continue;
4590 if(!lpASCIICharStr) {
4591 out += 5+b; /* strlen(xn--...-) */
4592 }else if(out+5+b <= cchASCIIChar) {
4593 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4594 out += 4;
4595 for(i=label_start; i<label_end; i++)
4596 if(norm_str[i] < 0x80)
4597 lpASCIICharStr[out++] = norm_str[i];
4598 lpASCIICharStr[out++] = '-';
4599 }else {
4600 HeapFree(GetProcessHeap(), 0, norm_str);
4601 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4602 return 0;
4604 if(!b)
4605 out--;
4607 for(h=b; h<label_end-label_start;) {
4608 INT m = 0xffff, q, k;
4610 for(i=label_start; i<label_end; i++) {
4611 if(norm_str[i]>=n && m>norm_str[i])
4612 m = norm_str[i];
4614 delta += (m-n)*(h+1);
4615 n = m;
4617 for(i=label_start; i<label_end; i++) {
4618 if(norm_str[i] < n) {
4619 delta++;
4620 }else if(norm_str[i] == n) {
4621 for(q=delta, k=BASE; ; k+=BASE) {
4622 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4623 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4624 if(!lpASCIICharStr) {
4625 out++;
4626 }else if(out+1 <= cchASCIIChar) {
4627 lpASCIICharStr[out++] = disp<='z'-'a' ?
4628 'a'+disp : '0'+disp-'z'+'a'-1;
4629 }else {
4630 HeapFree(GetProcessHeap(), 0, norm_str);
4631 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4632 return 0;
4634 if(q < t)
4635 break;
4636 q = (q-t)/(BASE-t);
4638 bias = adapt(delta, h+1, h==b);
4639 delta = 0;
4640 h++;
4643 delta++;
4644 n++;
4647 if(out-out_label > 63) {
4648 HeapFree(GetProcessHeap(), 0, norm_str);
4649 SetLastError(ERROR_INVALID_NAME);
4650 return 0;
4653 if(label_end < norm_len) {
4654 if(!lpASCIICharStr) {
4655 out++;
4656 }else if(out+1 <= cchASCIIChar) {
4657 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4658 }else {
4659 HeapFree(GetProcessHeap(), 0, norm_str);
4660 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4661 return 0;
4664 label_start = label_end+1;
4667 HeapFree(GetProcessHeap(), 0, norm_str);
4668 return out;
4671 /******************************************************************************
4672 * IdnToNameprepUnicode (KERNEL32.@)
4674 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4675 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4677 enum {
4678 UNASSIGNED = 0x1,
4679 PROHIBITED = 0x2,
4680 BIDI_RAL = 0x4,
4681 BIDI_L = 0x8
4684 extern const unsigned short nameprep_char_type[];
4685 extern const WCHAR nameprep_mapping[];
4686 const WCHAR *ptr;
4687 WORD flags;
4688 WCHAR buf[64], *map_str, norm_str[64], ch;
4689 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4690 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4692 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4693 lpNameprepCharStr, cchNameprepChar);
4695 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4696 SetLastError(ERROR_INVALID_FLAGS);
4697 return 0;
4700 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4701 SetLastError(ERROR_INVALID_PARAMETER);
4702 return 0;
4705 if(cchUnicodeChar == -1)
4706 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4707 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4708 SetLastError(ERROR_INVALID_NAME);
4709 return 0;
4712 for(label_start=0; label_start<cchUnicodeChar;) {
4713 ascii_only = TRUE;
4714 for(i=label_start; i<cchUnicodeChar; i++) {
4715 ch = lpUnicodeCharStr[i];
4717 if(i!=cchUnicodeChar-1 && !ch) {
4718 SetLastError(ERROR_INVALID_NAME);
4719 return 0;
4721 /* check if ch is one of label separators defined in RFC3490 */
4722 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4723 break;
4725 if(ch > 0x7f) {
4726 ascii_only = FALSE;
4727 continue;
4730 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4731 continue;
4732 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4733 || (ch>='0' && ch<='9') || ch=='-')
4734 continue;
4736 SetLastError(ERROR_INVALID_NAME);
4737 return 0;
4739 label_end = i;
4740 /* last label may be empty */
4741 if(label_start==label_end && ch) {
4742 SetLastError(ERROR_INVALID_NAME);
4743 return 0;
4746 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4747 lpUnicodeCharStr[label_end-1]=='-')) {
4748 SetLastError(ERROR_INVALID_NAME);
4749 return 0;
4752 if(ascii_only) {
4753 /* maximal label length is 63 characters */
4754 if(label_end-label_start > 63) {
4755 SetLastError(ERROR_INVALID_NAME);
4756 return 0;
4758 if(label_end < cchUnicodeChar)
4759 label_end++;
4761 if(!lpNameprepCharStr) {
4762 out += label_end-label_start;
4763 }else if(out+label_end-label_start <= cchNameprepChar) {
4764 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4765 (label_end-label_start)*sizeof(WCHAR));
4766 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4767 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4768 out += label_end-label_start;
4769 }else {
4770 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4771 return 0;
4774 label_start = label_end;
4775 continue;
4778 map_len = 0;
4779 for(i=label_start; i<label_end; i++) {
4780 ch = lpUnicodeCharStr[i];
4781 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4782 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4784 if(!ptr[0]) map_len++;
4785 else if(!ptr[1]) map_len++;
4786 else if(!ptr[2]) map_len += 2;
4787 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4789 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4790 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4791 if(!map_str) {
4792 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4793 return 0;
4795 }else {
4796 map_str = buf;
4798 map_len = 0;
4799 for(i=label_start; i<label_end; i++) {
4800 ch = lpUnicodeCharStr[i];
4801 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4802 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4804 if(!ptr[0]) {
4805 map_str[map_len++] = ch;
4806 }else if(!ptr[1]) {
4807 map_str[map_len++] = ptr[0];
4808 }else if(!ptr[2]) {
4809 map_str[map_len++] = ptr[0];
4810 map_str[map_len++] = ptr[1];
4811 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4812 map_str[map_len++] = ptr[0];
4813 map_str[map_len++] = ptr[1];
4814 map_str[map_len++] = ptr[2];
4818 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4819 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4820 if(map_str != buf)
4821 HeapFree(GetProcessHeap(), 0, map_str);
4822 if(!norm_len) {
4823 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4824 SetLastError(ERROR_INVALID_NAME);
4825 return 0;
4828 if(label_end < cchUnicodeChar) {
4829 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4830 label_end++;
4833 if(!lpNameprepCharStr) {
4834 out += norm_len;
4835 }else if(out+norm_len <= cchNameprepChar) {
4836 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4837 out += norm_len;
4838 }else {
4839 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4840 return 0;
4843 have_bidi_ral = prohibit_bidi_ral = FALSE;
4844 mask = PROHIBITED;
4845 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4846 mask |= UNASSIGNED;
4847 for(i=0; i<norm_len; i++) {
4848 ch = norm_str[i];
4849 flags = get_table_entry( nameprep_char_type, ch );
4851 if(flags & mask) {
4852 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4853 : ERROR_NO_UNICODE_TRANSLATION);
4854 return 0;
4857 if(flags & BIDI_RAL)
4858 have_bidi_ral = TRUE;
4859 if(flags & BIDI_L)
4860 prohibit_bidi_ral = TRUE;
4863 if(have_bidi_ral) {
4864 ch = norm_str[0];
4865 flags = get_table_entry( nameprep_char_type, ch );
4866 if((flags & BIDI_RAL) == 0)
4867 prohibit_bidi_ral = TRUE;
4869 ch = norm_str[norm_len-1];
4870 flags = get_table_entry( nameprep_char_type, ch );
4871 if((flags & BIDI_RAL) == 0)
4872 prohibit_bidi_ral = TRUE;
4875 if(have_bidi_ral && prohibit_bidi_ral) {
4876 SetLastError(ERROR_INVALID_NAME);
4877 return 0;
4880 label_start = label_end;
4883 return out;
4886 /******************************************************************************
4887 * IdnToUnicode (KERNEL32.@)
4889 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4890 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4892 extern const unsigned short nameprep_char_type[];
4894 INT i, label_start, label_end, out_label, out = 0;
4895 WCHAR ch;
4897 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4898 lpUnicodeCharStr, cchUnicodeChar);
4900 for(label_start=0; label_start<cchASCIIChar;) {
4901 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4903 out_label = out;
4904 for(i=label_start; i<cchASCIIChar; i++) {
4905 ch = lpASCIICharStr[i];
4907 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4908 SetLastError(ERROR_INVALID_NAME);
4909 return 0;
4912 if(!ch || ch=='.')
4913 break;
4914 if(ch == '-')
4915 delim = i;
4917 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4918 continue;
4919 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4920 || (ch>='0' && ch<='9') || ch=='-')
4921 continue;
4923 SetLastError(ERROR_INVALID_NAME);
4924 return 0;
4926 label_end = i;
4927 /* last label may be empty */
4928 if(label_start==label_end && ch) {
4929 SetLastError(ERROR_INVALID_NAME);
4930 return 0;
4933 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
4934 lpASCIICharStr[label_end-1]=='-')) {
4935 SetLastError(ERROR_INVALID_NAME);
4936 return 0;
4938 if(label_end-label_start > 63) {
4939 SetLastError(ERROR_INVALID_NAME);
4940 return 0;
4943 if(label_end-label_start<4 ||
4944 tolowerW(lpASCIICharStr[label_start])!='x' ||
4945 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4946 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4947 if(label_end < cchASCIIChar)
4948 label_end++;
4950 if(!lpUnicodeCharStr) {
4951 out += label_end-label_start;
4952 }else if(out+label_end-label_start <= cchUnicodeChar) {
4953 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4954 (label_end-label_start)*sizeof(WCHAR));
4955 out += label_end-label_start;
4956 }else {
4957 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4958 return 0;
4961 label_start = label_end;
4962 continue;
4965 if(delim == label_start+3)
4966 delim++;
4967 if(!lpUnicodeCharStr) {
4968 out += delim-label_start-4;
4969 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4970 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4971 (delim-label_start-4)*sizeof(WCHAR));
4972 out += delim-label_start-4;
4973 }else {
4974 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4975 return 0;
4977 if(out != out_label)
4978 delim++;
4980 for(i=delim; i<label_end;) {
4981 old_pos = pos;
4982 w = 1;
4983 for(k=BASE; ; k+=BASE) {
4984 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4985 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4986 SetLastError(ERROR_INVALID_NAME);
4987 return 0;
4989 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4990 pos += digit*w;
4991 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4992 if(digit < t)
4993 break;
4994 w *= BASE-t;
4996 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4997 n += pos/(out-out_label+1);
4998 pos %= out-out_label+1;
5000 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5001 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5002 SetLastError(ERROR_INVALID_NAME);
5003 return 0;
5005 if(!lpUnicodeCharStr) {
5006 out++;
5007 }else if(out+1 <= cchASCIIChar) {
5008 memmove(lpUnicodeCharStr+out_label+pos+1,
5009 lpUnicodeCharStr+out_label+pos,
5010 (out-out_label-pos)*sizeof(WCHAR));
5011 lpUnicodeCharStr[out_label+pos] = n;
5012 out++;
5013 }else {
5014 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5015 return 0;
5017 pos++;
5020 if(out-out_label > 63) {
5021 SetLastError(ERROR_INVALID_NAME);
5022 return 0;
5025 if(label_end < cchASCIIChar) {
5026 if(!lpUnicodeCharStr) {
5027 out++;
5028 }else if(out+1 <= cchUnicodeChar) {
5029 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5030 }else {
5031 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5032 return 0;
5035 label_start = label_end+1;
5038 return out;
5042 /******************************************************************************
5043 * GetUserPreferredUILanguages (KERNEL32.@)
5045 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5047 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5048 return FALSE;