oleacc: Added CAccPropServices stub implementation.
[wine/multimedia.git] / dlls / kernel32 / locale.c
blob1ae32d50458b38ac6f21e897ea21493ff70fc167
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) + 1;
1211 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1213 ret = -1;
1215 else
1217 SetLastError( RtlNtStatusToDosError(status) );
1218 ret = 0;
1220 HeapFree( GetProcessHeap(), 0, info );
1221 RtlLeaveCriticalSection( &cache_section );
1222 return ret;
1226 ret = lstrlenW( registry_value->cached_value ) + 1;
1228 if (buffer)
1230 if (ret > len)
1232 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1233 ret = 0;
1235 else
1237 lstrcpyW( buffer, registry_value->cached_value );
1241 RtlLeaveCriticalSection( &cache_section );
1243 return ret;
1247 /******************************************************************************
1248 * GetLocaleInfoA (KERNEL32.@)
1250 * Get information about an aspect of a locale.
1252 * PARAMS
1253 * lcid [I] LCID of the locale
1254 * lctype [I] LCTYPE_ flags from "winnls.h"
1255 * buffer [O] Destination for the information
1256 * len [I] Length of buffer in characters
1258 * RETURNS
1259 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1260 * with the information.
1261 * Failure: 0. Use GetLastError() to determine the cause.
1263 * NOTES
1264 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1265 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1266 * which is a bit string.
1268 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1270 WCHAR *bufferW;
1271 INT lenW, ret;
1273 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1275 if (len < 0 || (len && !buffer))
1277 SetLastError( ERROR_INVALID_PARAMETER );
1278 return 0;
1280 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1282 SetLastError( ERROR_INVALID_FLAGS );
1283 return 0;
1286 if (!len) buffer = NULL;
1288 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1290 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1292 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1293 return 0;
1295 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1297 if ((lctype & LOCALE_RETURN_NUMBER) ||
1298 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1300 /* it's not an ASCII string, just bytes */
1301 ret *= sizeof(WCHAR);
1302 if (buffer)
1304 if (ret <= len) memcpy( buffer, bufferW, ret );
1305 else
1307 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1308 ret = 0;
1312 else
1314 UINT codepage = CP_ACP;
1315 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1316 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1319 HeapFree( GetProcessHeap(), 0, bufferW );
1320 return ret;
1323 static int get_value_base_by_lctype( LCTYPE lctype )
1325 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1328 /******************************************************************************
1329 * GetLocaleInfoW (KERNEL32.@)
1331 * See GetLocaleInfoA.
1333 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1335 LANGID lang_id;
1336 HRSRC hrsrc;
1337 HGLOBAL hmem;
1338 INT ret;
1339 UINT lcflags;
1340 const WCHAR *p;
1341 unsigned int i;
1343 if (len < 0 || (len && !buffer))
1345 SetLastError( ERROR_INVALID_PARAMETER );
1346 return 0;
1348 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1349 !is_genitive_name_supported( lctype ))
1351 SetLastError( ERROR_INVALID_FLAGS );
1352 return 0;
1355 if (!len) buffer = NULL;
1357 lcid = convert_default_lcid( lcid, lctype );
1359 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1360 lctype &= 0xffff;
1362 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1364 /* first check for overrides in the registry */
1366 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1367 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1369 struct registry_value *value = get_locale_registry_value(lctype);
1371 if (value)
1373 if (lcflags & LOCALE_RETURN_NUMBER)
1375 WCHAR tmp[16];
1376 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1377 if (ret > 0)
1379 WCHAR *end;
1380 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1381 if (*end) /* invalid number */
1383 SetLastError( ERROR_INVALID_FLAGS );
1384 return 0;
1386 ret = sizeof(UINT)/sizeof(WCHAR);
1387 if (!buffer) return ret;
1388 if (ret > len)
1390 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1391 return 0;
1393 memcpy( buffer, &number, sizeof(number) );
1396 else ret = get_registry_locale_info( value, buffer, len );
1398 if (ret != -1) return ret;
1402 /* now load it from kernel resources */
1404 lang_id = LANGIDFROMLCID( lcid );
1406 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1407 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1408 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1410 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1411 ULongToPtr((lctype >> 4) + 1), lang_id )))
1413 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1414 return 0;
1416 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1417 return 0;
1419 p = LockResource( hmem );
1420 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1422 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1423 else if (is_genitive_name_supported( lctype ) && *p)
1425 /* genitive form's stored after a null separator from a nominative */
1426 for (i = 1; i <= *p; i++) if (!p[i]) break;
1428 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1430 ret = *p - i + 1;
1431 p += i;
1433 else ret = i;
1435 else
1436 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1438 if (!buffer) return ret;
1440 if (ret > len)
1442 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1443 return 0;
1446 if (lcflags & LOCALE_RETURN_NUMBER)
1448 UINT number;
1449 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1450 if (!tmp) return 0;
1451 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1452 tmp[*p] = 0;
1453 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1454 if (!*end)
1455 memcpy( buffer, &number, sizeof(number) );
1456 else /* invalid number */
1458 SetLastError( ERROR_INVALID_FLAGS );
1459 ret = 0;
1461 HeapFree( GetProcessHeap(), 0, tmp );
1463 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1464 lcid, lctype, buffer, len, number );
1466 else
1468 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1469 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1471 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1472 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1474 return ret;
1477 /******************************************************************************
1478 * GetLocaleInfoEx (KERNEL32.@)
1480 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1482 LCID lcid = LocaleNameToLCID(locale, 0);
1484 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1486 if (!lcid) return 0;
1488 /* special handling for neutral locale names */
1489 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1491 if (len && len < 3)
1493 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1494 return 0;
1497 if (len) strcpyW(buffer, locale);
1498 return 3;
1501 return GetLocaleInfoW(lcid, info, buffer, len);
1504 /******************************************************************************
1505 * SetLocaleInfoA [KERNEL32.@]
1507 * Set information about an aspect of a locale.
1509 * PARAMS
1510 * lcid [I] LCID of the locale
1511 * lctype [I] LCTYPE_ flags from "winnls.h"
1512 * data [I] Information to set
1514 * RETURNS
1515 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1516 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1517 * Failure: FALSE. Use GetLastError() to determine the cause.
1519 * NOTES
1520 * - Values are only be set for the current user locale; the system locale
1521 * settings cannot be changed.
1522 * - Any settings changed by this call are lost when the locale is changed by
1523 * the control panel (in Wine, this happens every time you change LANG).
1524 * - The native implementation of this function does not check that lcid matches
1525 * the current user locale, and simply sets the new values. Wine warns you in
1526 * this case, but behaves the same.
1528 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1530 UINT codepage = CP_ACP;
1531 WCHAR *strW;
1532 DWORD len;
1533 BOOL ret;
1535 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1537 if (!data)
1539 SetLastError( ERROR_INVALID_PARAMETER );
1540 return FALSE;
1542 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1543 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1545 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1546 return FALSE;
1548 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1549 ret = SetLocaleInfoW( lcid, lctype, strW );
1550 HeapFree( GetProcessHeap(), 0, strW );
1551 return ret;
1555 /******************************************************************************
1556 * SetLocaleInfoW (KERNEL32.@)
1558 * See SetLocaleInfoA.
1560 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1562 struct registry_value *value;
1563 static const WCHAR intlW[] = {'i','n','t','l',0 };
1564 UNICODE_STRING valueW;
1565 NTSTATUS status;
1566 HANDLE hkey;
1568 lctype &= 0xffff;
1569 value = get_locale_registry_value( lctype );
1571 if (!data || !value)
1573 SetLastError( ERROR_INVALID_PARAMETER );
1574 return FALSE;
1577 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1579 SetLastError( ERROR_INVALID_FLAGS );
1580 return FALSE;
1583 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1585 /* FIXME: should check that data to set is sane */
1587 /* FIXME: profile functions should map to registry */
1588 WriteProfileStringW( intlW, value->name, data );
1590 if (!(hkey = create_registry_key())) return FALSE;
1591 RtlInitUnicodeString( &valueW, value->name );
1592 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1594 RtlEnterCriticalSection( &cache_section );
1595 HeapFree( GetProcessHeap(), 0, value->cached_value );
1596 value->cached_value = NULL;
1597 RtlLeaveCriticalSection( &cache_section );
1599 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1601 /* Set I-value from S value */
1602 WCHAR *lpD, *lpM, *lpY;
1603 WCHAR szBuff[2];
1605 lpD = strrchrW(data, 'd');
1606 lpM = strrchrW(data, 'M');
1607 lpY = strrchrW(data, 'y');
1609 if (lpD <= lpM)
1611 szBuff[0] = '1'; /* D-M-Y */
1613 else
1615 if (lpY <= lpM)
1616 szBuff[0] = '2'; /* Y-M-D */
1617 else
1618 szBuff[0] = '0'; /* M-D-Y */
1621 szBuff[1] = '\0';
1623 if (lctype == LOCALE_SSHORTDATE)
1624 lctype = LOCALE_IDATE;
1625 else
1626 lctype = LOCALE_ILDATE;
1628 value = get_locale_registry_value( lctype );
1630 WriteProfileStringW( intlW, value->name, szBuff );
1632 RtlInitUnicodeString( &valueW, value->name );
1633 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1635 RtlEnterCriticalSection( &cache_section );
1636 HeapFree( GetProcessHeap(), 0, value->cached_value );
1637 value->cached_value = NULL;
1638 RtlLeaveCriticalSection( &cache_section );
1641 NtClose( hkey );
1643 if (status) SetLastError( RtlNtStatusToDosError(status) );
1644 return !status;
1648 /******************************************************************************
1649 * GetACP (KERNEL32.@)
1651 * Get the current Ansi code page Id for the system.
1653 * PARAMS
1654 * None.
1656 * RETURNS
1657 * The current Ansi code page identifier for the system.
1659 UINT WINAPI GetACP(void)
1661 assert( ansi_cptable );
1662 return ansi_cptable->info.codepage;
1666 /******************************************************************************
1667 * SetCPGlobal (KERNEL32.@)
1669 * Set the current Ansi code page Id for the system.
1671 * PARAMS
1672 * acp [I] code page ID to be the new ACP.
1674 * RETURNS
1675 * The previous ACP.
1677 UINT WINAPI SetCPGlobal( UINT acp )
1679 UINT ret = GetACP();
1680 const union cptable *new_cptable = wine_cp_get_table( acp );
1682 if (new_cptable) ansi_cptable = new_cptable;
1683 return ret;
1687 /***********************************************************************
1688 * GetOEMCP (KERNEL32.@)
1690 * Get the current OEM code page Id for the system.
1692 * PARAMS
1693 * None.
1695 * RETURNS
1696 * The current OEM code page identifier for the system.
1698 UINT WINAPI GetOEMCP(void)
1700 assert( oem_cptable );
1701 return oem_cptable->info.codepage;
1705 /***********************************************************************
1706 * IsValidCodePage (KERNEL32.@)
1708 * Determine if a given code page identifier is valid.
1710 * PARAMS
1711 * codepage [I] Code page Id to verify.
1713 * RETURNS
1714 * TRUE, If codepage is valid and available on the system,
1715 * FALSE otherwise.
1717 BOOL WINAPI IsValidCodePage( UINT codepage )
1719 switch(codepage) {
1720 case CP_UTF7:
1721 case CP_UTF8:
1722 return TRUE;
1723 default:
1724 return wine_cp_get_table( codepage ) != NULL;
1729 /***********************************************************************
1730 * IsDBCSLeadByteEx (KERNEL32.@)
1732 * Determine if a character is a lead byte in a given code page.
1734 * PARAMS
1735 * codepage [I] Code page for the test.
1736 * testchar [I] Character to test
1738 * RETURNS
1739 * TRUE, if testchar is a lead byte in codepage,
1740 * FALSE otherwise.
1742 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1744 const union cptable *table = get_codepage_table( codepage );
1745 return table && wine_is_dbcs_leadbyte( table, testchar );
1749 /***********************************************************************
1750 * IsDBCSLeadByte (KERNEL32.@)
1751 * IsDBCSLeadByte (KERNEL.207)
1753 * Determine if a character is a lead byte.
1755 * PARAMS
1756 * testchar [I] Character to test
1758 * RETURNS
1759 * TRUE, if testchar is a lead byte in the ANSI code page,
1760 * FALSE otherwise.
1762 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1764 if (!ansi_cptable) return FALSE;
1765 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1769 /***********************************************************************
1770 * GetCPInfo (KERNEL32.@)
1772 * Get information about a code page.
1774 * PARAMS
1775 * codepage [I] Code page number
1776 * cpinfo [O] Destination for code page information
1778 * RETURNS
1779 * Success: TRUE. cpinfo is updated with the information about codepage.
1780 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1782 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1784 const union cptable *table;
1786 if (!cpinfo)
1788 SetLastError( ERROR_INVALID_PARAMETER );
1789 return FALSE;
1792 if (!(table = get_codepage_table( codepage )))
1794 switch(codepage)
1796 case CP_UTF7:
1797 case CP_UTF8:
1798 cpinfo->DefaultChar[0] = 0x3f;
1799 cpinfo->DefaultChar[1] = 0;
1800 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1801 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1802 return TRUE;
1805 SetLastError( ERROR_INVALID_PARAMETER );
1806 return FALSE;
1808 if (table->info.def_char & 0xff00)
1810 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1811 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1813 else
1815 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1816 cpinfo->DefaultChar[1] = 0;
1818 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1819 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1820 else
1821 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1823 return TRUE;
1826 /***********************************************************************
1827 * GetCPInfoExA (KERNEL32.@)
1829 * Get extended information about a code page.
1831 * PARAMS
1832 * codepage [I] Code page number
1833 * dwFlags [I] Reserved, must to 0.
1834 * cpinfo [O] Destination for code page information
1836 * RETURNS
1837 * Success: TRUE. cpinfo is updated with the information about codepage.
1838 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1840 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1842 CPINFOEXW cpinfoW;
1844 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1845 return FALSE;
1847 /* the layout is the same except for CodePageName */
1848 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1849 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1850 return TRUE;
1853 /***********************************************************************
1854 * GetCPInfoExW (KERNEL32.@)
1856 * Unicode version of GetCPInfoExA.
1858 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1860 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1861 return FALSE;
1863 switch(codepage)
1865 case CP_UTF7:
1867 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1869 cpinfo->CodePage = CP_UTF7;
1870 cpinfo->UnicodeDefaultChar = 0x3f;
1871 strcpyW(cpinfo->CodePageName, utf7);
1872 break;
1875 case CP_UTF8:
1877 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1879 cpinfo->CodePage = CP_UTF8;
1880 cpinfo->UnicodeDefaultChar = 0x3f;
1881 strcpyW(cpinfo->CodePageName, utf8);
1882 break;
1885 default:
1887 const union cptable *table = get_codepage_table( codepage );
1889 cpinfo->CodePage = table->info.codepage;
1890 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1891 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1892 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1893 break;
1896 return TRUE;
1899 /***********************************************************************
1900 * EnumSystemCodePagesA (KERNEL32.@)
1902 * Call a user defined function for every code page installed on the system.
1904 * PARAMS
1905 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1906 * flags [I] Reserved, set to 0.
1908 * RETURNS
1909 * TRUE, If all code pages have been enumerated, or
1910 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1912 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1914 const union cptable *table;
1915 char buffer[10];
1916 int index = 0;
1918 for (;;)
1920 if (!(table = wine_cp_enum_table( index++ ))) break;
1921 sprintf( buffer, "%d", table->info.codepage );
1922 if (!lpfnCodePageEnum( buffer )) break;
1924 return TRUE;
1928 /***********************************************************************
1929 * EnumSystemCodePagesW (KERNEL32.@)
1931 * See EnumSystemCodePagesA.
1933 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1935 const union cptable *table;
1936 WCHAR buffer[10], *p;
1937 int page, index = 0;
1939 for (;;)
1941 if (!(table = wine_cp_enum_table( index++ ))) break;
1942 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1943 *--p = 0;
1944 page = table->info.codepage;
1947 *--p = '0' + (page % 10);
1948 page /= 10;
1949 } while( page );
1950 if (!lpfnCodePageEnum( p )) break;
1952 return TRUE;
1956 /***********************************************************************
1957 * MultiByteToWideChar (KERNEL32.@)
1959 * Convert a multibyte character string into a Unicode string.
1961 * PARAMS
1962 * page [I] Codepage character set to convert from
1963 * flags [I] Character mapping flags
1964 * src [I] Source string buffer
1965 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1966 * dst [O] Destination buffer
1967 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1969 * RETURNS
1970 * Success: If dstlen > 0, the number of characters written to dst.
1971 * If dstlen == 0, the number of characters needed to perform the
1972 * conversion. In both cases the count includes the terminating NUL.
1973 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1974 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1975 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1976 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1977 * possible for src.
1979 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1980 LPWSTR dst, INT dstlen )
1982 const union cptable *table;
1983 int ret;
1985 if (!src || !srclen || (!dst && dstlen))
1987 SetLastError( ERROR_INVALID_PARAMETER );
1988 return 0;
1991 if (srclen < 0) srclen = strlen(src) + 1;
1993 switch(page)
1995 case CP_SYMBOL:
1996 if (flags)
1998 SetLastError( ERROR_INVALID_FLAGS );
1999 return 0;
2001 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2002 break;
2003 case CP_UTF7:
2004 if (flags)
2006 SetLastError( ERROR_INVALID_FLAGS );
2007 return 0;
2009 FIXME("UTF-7 not supported\n");
2010 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2011 return 0;
2012 case CP_UNIXCP:
2013 if (unix_cptable)
2015 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2016 break;
2018 #ifdef __APPLE__
2019 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2020 #endif
2021 /* fall through */
2022 case CP_UTF8:
2023 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2024 break;
2025 default:
2026 if (!(table = get_codepage_table( page )))
2028 SetLastError( ERROR_INVALID_PARAMETER );
2029 return 0;
2031 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2032 break;
2035 if (ret < 0)
2037 switch(ret)
2039 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2040 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2042 ret = 0;
2044 TRACE("cp %d %s -> %s, ret = %d\n",
2045 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2046 return ret;
2050 /***********************************************************************
2051 * WideCharToMultiByte (KERNEL32.@)
2053 * Convert a Unicode character string into a multibyte string.
2055 * PARAMS
2056 * page [I] Code page character set to convert to
2057 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2058 * src [I] Source string buffer
2059 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2060 * dst [O] Destination buffer
2061 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2062 * defchar [I] Default character to use for conversion if no exact
2063 * conversion can be made
2064 * used [O] Set if default character was used in the conversion
2066 * RETURNS
2067 * Success: If dstlen > 0, the number of characters written to dst.
2068 * If dstlen == 0, number of characters needed to perform the
2069 * conversion. In both cases the count includes the terminating NUL.
2070 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2071 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2072 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2073 * parameter was given.
2075 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2076 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2078 const union cptable *table;
2079 int ret, used_tmp;
2081 if (!src || !srclen || (!dst && dstlen))
2083 SetLastError( ERROR_INVALID_PARAMETER );
2084 return 0;
2087 if (srclen < 0) srclen = strlenW(src) + 1;
2089 switch(page)
2091 case CP_SYMBOL:
2092 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2093 if (flags)
2095 SetLastError( ERROR_INVALID_FLAGS );
2096 return 0;
2098 if (defchar || used)
2100 SetLastError( ERROR_INVALID_PARAMETER );
2101 return 0;
2103 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2104 break;
2105 case CP_UTF7:
2106 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2107 if (defchar || used)
2109 SetLastError( ERROR_INVALID_PARAMETER );
2110 return 0;
2112 if (flags)
2114 SetLastError( ERROR_INVALID_FLAGS );
2115 return 0;
2117 FIXME("UTF-7 not supported\n");
2118 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2119 return 0;
2120 case CP_UNIXCP:
2121 if (unix_cptable)
2123 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2124 defchar, used ? &used_tmp : NULL );
2125 break;
2127 /* fall through */
2128 case CP_UTF8:
2129 if (defchar || used)
2131 SetLastError( ERROR_INVALID_PARAMETER );
2132 return 0;
2134 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2135 break;
2136 default:
2137 if (!(table = get_codepage_table( page )))
2139 SetLastError( ERROR_INVALID_PARAMETER );
2140 return 0;
2142 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2143 defchar, used ? &used_tmp : NULL );
2144 if (used) *used = used_tmp;
2145 break;
2148 if (ret < 0)
2150 switch(ret)
2152 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2153 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2155 ret = 0;
2157 TRACE("cp %d %s -> %s, ret = %d\n",
2158 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2159 return ret;
2163 /***********************************************************************
2164 * GetThreadLocale (KERNEL32.@)
2166 * Get the current threads locale.
2168 * PARAMS
2169 * None.
2171 * RETURNS
2172 * The LCID currently associated with the calling thread.
2174 LCID WINAPI GetThreadLocale(void)
2176 LCID ret = NtCurrentTeb()->CurrentLocale;
2177 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2178 return ret;
2181 /**********************************************************************
2182 * SetThreadLocale (KERNEL32.@)
2184 * Set the current threads locale.
2186 * PARAMS
2187 * lcid [I] LCID of the locale to set
2189 * RETURNS
2190 * Success: TRUE. The threads locale is set to lcid.
2191 * Failure: FALSE. Use GetLastError() to determine the cause.
2193 BOOL WINAPI SetThreadLocale( LCID lcid )
2195 TRACE("(0x%04X)\n", lcid);
2197 lcid = ConvertDefaultLocale(lcid);
2199 if (lcid != GetThreadLocale())
2201 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2203 SetLastError(ERROR_INVALID_PARAMETER);
2204 return FALSE;
2207 NtCurrentTeb()->CurrentLocale = lcid;
2209 return TRUE;
2212 /**********************************************************************
2213 * SetThreadUILanguage (KERNEL32.@)
2215 * Set the current threads UI language.
2217 * PARAMS
2218 * langid [I] LANGID of the language to set, or 0 to use
2219 * the available language which is best supported
2220 * for console applications
2222 * RETURNS
2223 * Success: The return value is the same as the input value.
2224 * Failure: The return value differs from the input value.
2225 * Use GetLastError() to determine the cause.
2227 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2229 TRACE("(0x%04x) stub - returning success\n", langid);
2230 return langid;
2233 /******************************************************************************
2234 * ConvertDefaultLocale (KERNEL32.@)
2236 * Convert a default locale identifier into a real identifier.
2238 * PARAMS
2239 * lcid [I] LCID identifier of the locale to convert
2241 * RETURNS
2242 * lcid unchanged, if not a default locale or its sublanguage is
2243 * not SUBLANG_NEUTRAL.
2244 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2245 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2246 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2248 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2250 LANGID langid;
2252 switch (lcid)
2254 case LOCALE_SYSTEM_DEFAULT:
2255 lcid = GetSystemDefaultLCID();
2256 break;
2257 case LOCALE_USER_DEFAULT:
2258 case LOCALE_NEUTRAL:
2259 lcid = GetUserDefaultLCID();
2260 break;
2261 default:
2262 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2263 langid = LANGIDFROMLCID(lcid);
2264 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2266 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2267 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2270 return lcid;
2274 /******************************************************************************
2275 * IsValidLocale (KERNEL32.@)
2277 * Determine if a locale is valid.
2279 * PARAMS
2280 * lcid [I] LCID of the locale to check
2281 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2283 * RETURNS
2284 * TRUE, if lcid is valid,
2285 * FALSE, otherwise.
2287 * NOTES
2288 * Wine does not currently make the distinction between supported and installed. All
2289 * languages supported are installed by default.
2291 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2293 /* check if language is registered in the kernel32 resources */
2294 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2295 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2298 /******************************************************************************
2299 * IsValidLocaleName (KERNEL32.@)
2301 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2303 struct locale_name locale_name;
2305 /* string parsing */
2306 parse_locale_name( locale, &locale_name );
2308 TRACE( "found lcid %x for %s, matches %d\n",
2309 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2311 return locale_name.matches > 0;
2314 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2315 LPCSTR name, WORD LangID, LONG_PTR lParam )
2317 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2318 char buf[20];
2320 sprintf(buf, "%08x", (UINT)LangID);
2321 return lpfnLocaleEnum( buf );
2324 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2325 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2327 static const WCHAR formatW[] = {'%','0','8','x',0};
2328 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2329 WCHAR buf[20];
2330 sprintfW( buf, formatW, (UINT)LangID );
2331 return lpfnLocaleEnum( buf );
2334 /******************************************************************************
2335 * EnumSystemLocalesA (KERNEL32.@)
2337 * Call a users function for each locale available on the system.
2339 * PARAMS
2340 * lpfnLocaleEnum [I] Callback function to call for each locale
2341 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2343 * RETURNS
2344 * Success: TRUE.
2345 * Failure: FALSE. Use GetLastError() to determine the cause.
2347 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2349 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2350 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2351 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2352 (LONG_PTR)lpfnLocaleEnum);
2353 return TRUE;
2357 /******************************************************************************
2358 * EnumSystemLocalesW (KERNEL32.@)
2360 * See EnumSystemLocalesA.
2362 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2364 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2365 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2366 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2367 (LONG_PTR)lpfnLocaleEnum);
2368 return TRUE;
2372 struct enum_locale_ex_data
2374 LOCALE_ENUMPROCEX proc;
2375 DWORD flags;
2376 LPARAM lparam;
2379 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2380 LPCWSTR name, WORD lang, LONG_PTR lparam )
2382 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2383 WCHAR buffer[256];
2384 DWORD neutral;
2385 unsigned int flags;
2387 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2388 buffer, sizeof(buffer) / sizeof(WCHAR) );
2389 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2390 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2391 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2392 neutral = 0;
2393 flags = LOCALE_WINDOWS;
2394 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2395 if (data->flags && !(data->flags & flags)) return TRUE;
2396 return data->proc( buffer, flags, data->lparam );
2399 /******************************************************************************
2400 * EnumSystemLocalesEx (KERNEL32.@)
2402 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2404 struct enum_locale_ex_data data;
2406 if (reserved)
2408 SetLastError( ERROR_INVALID_PARAMETER );
2409 return FALSE;
2411 data.proc = proc;
2412 data.flags = flags;
2413 data.lparam = lparam;
2414 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2415 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2416 enum_locale_ex_proc, (LONG_PTR)&data );
2417 return TRUE;
2421 /***********************************************************************
2422 * VerLanguageNameA (KERNEL32.@)
2424 * Get the name of a language.
2426 * PARAMS
2427 * wLang [I] LANGID of the language
2428 * szLang [O] Destination for the language name
2430 * RETURNS
2431 * Success: The size of the language name. If szLang is non-NULL, it is filled
2432 * with the name.
2433 * Failure: 0. Use GetLastError() to determine the cause.
2436 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2438 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2442 /***********************************************************************
2443 * VerLanguageNameW (KERNEL32.@)
2445 * See VerLanguageNameA.
2447 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2449 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2453 /******************************************************************************
2454 * GetStringTypeW (KERNEL32.@)
2456 * See GetStringTypeA.
2458 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2460 static const unsigned char type2_map[16] =
2462 C2_NOTAPPLICABLE, /* unassigned */
2463 C2_LEFTTORIGHT, /* L */
2464 C2_RIGHTTOLEFT, /* R */
2465 C2_EUROPENUMBER, /* EN */
2466 C2_EUROPESEPARATOR, /* ES */
2467 C2_EUROPETERMINATOR, /* ET */
2468 C2_ARABICNUMBER, /* AN */
2469 C2_COMMONSEPARATOR, /* CS */
2470 C2_BLOCKSEPARATOR, /* B */
2471 C2_SEGMENTSEPARATOR, /* S */
2472 C2_WHITESPACE, /* WS */
2473 C2_OTHERNEUTRAL, /* ON */
2474 C2_RIGHTTOLEFT, /* AL */
2475 C2_NOTAPPLICABLE, /* NSM */
2476 C2_NOTAPPLICABLE, /* BN */
2477 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2480 if (count == -1) count = strlenW(src) + 1;
2481 switch(type)
2483 case CT_CTYPE1:
2484 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2485 break;
2486 case CT_CTYPE2:
2487 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2488 break;
2489 case CT_CTYPE3:
2491 WARN("CT_CTYPE3: semi-stub.\n");
2492 while (count--)
2494 int c = *src;
2495 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2497 type1 = get_char_typeW( *src++ ) & 0xfff;
2498 /* try to construct type3 from type1 */
2499 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2500 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2501 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2502 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2503 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2504 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2505 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2507 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
2508 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
2510 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2511 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2512 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2513 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2514 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2515 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2516 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2517 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2519 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2520 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2521 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2522 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2523 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2524 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2525 *chartype++ = type3;
2527 break;
2529 default:
2530 SetLastError( ERROR_INVALID_PARAMETER );
2531 return FALSE;
2533 return TRUE;
2537 /******************************************************************************
2538 * GetStringTypeExW (KERNEL32.@)
2540 * See GetStringTypeExA.
2542 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2544 /* locale is ignored for Unicode */
2545 return GetStringTypeW( type, src, count, chartype );
2549 /******************************************************************************
2550 * GetStringTypeA (KERNEL32.@)
2552 * Get characteristics of the characters making up a string.
2554 * PARAMS
2555 * locale [I] Locale Id for the string
2556 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2557 * src [I] String to analyse
2558 * count [I] Length of src in chars, or -1 if src is NUL terminated
2559 * chartype [O] Destination for the calculated characteristics
2561 * RETURNS
2562 * Success: TRUE. chartype is filled with the requested characteristics of each char
2563 * in src.
2564 * Failure: FALSE. Use GetLastError() to determine the cause.
2566 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2568 UINT cp;
2569 INT countW;
2570 LPWSTR srcW;
2571 BOOL ret = FALSE;
2573 if(count == -1) count = strlen(src) + 1;
2575 if (!(cp = get_lcid_codepage( locale )))
2577 FIXME("For locale %04x using current ANSI code page\n", locale);
2578 cp = GetACP();
2581 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2582 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2584 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2586 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2587 * string, with multibyte characters there maybe be more bytes in count
2588 * than character space in the buffer!
2590 ret = GetStringTypeW(type, srcW, countW, chartype);
2591 HeapFree(GetProcessHeap(), 0, srcW);
2593 return ret;
2596 /******************************************************************************
2597 * GetStringTypeExA (KERNEL32.@)
2599 * Get characteristics of the characters making up a string.
2601 * PARAMS
2602 * locale [I] Locale Id for the string
2603 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2604 * src [I] String to analyse
2605 * count [I] Length of src in chars, or -1 if src is NUL terminated
2606 * chartype [O] Destination for the calculated characteristics
2608 * RETURNS
2609 * Success: TRUE. chartype is filled with the requested characteristics of each char
2610 * in src.
2611 * Failure: FALSE. Use GetLastError() to determine the cause.
2613 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2615 return GetStringTypeA(locale, type, src, count, chartype);
2618 /*************************************************************************
2619 * LCMapStringEx (KERNEL32.@)
2621 * Map characters in a locale sensitive string.
2623 * PARAMS
2624 * name [I] Locale name for the conversion.
2625 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2626 * src [I] String to map
2627 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2628 * dst [O] Destination for mapped string
2629 * dstlen [I] Length of dst in characters
2630 * version [I] reserved, must be NULL
2631 * reserved [I] reserved, must be NULL
2632 * lparam [I] reserved, must be 0
2634 * RETURNS
2635 * Success: The length of the mapped string in dst, including the NUL terminator.
2636 * Failure: 0. Use GetLastError() to determine the cause.
2638 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2639 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2641 LPWSTR dst_ptr;
2643 if (version) FIXME("unsupported version structure %p\n", version);
2644 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2645 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2647 if (!src || !srclen || dstlen < 0)
2649 SetLastError(ERROR_INVALID_PARAMETER);
2650 return 0;
2653 /* mutually exclusive flags */
2654 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2655 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2656 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2657 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2659 SetLastError(ERROR_INVALID_FLAGS);
2660 return 0;
2663 if (!dstlen) dst = NULL;
2665 if (flags & LCMAP_SORTKEY)
2667 INT ret;
2668 if (src == dst)
2670 SetLastError(ERROR_INVALID_FLAGS);
2671 return 0;
2674 if (srclen < 0) srclen = strlenW(src);
2676 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2677 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2679 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2680 if (ret == 0)
2681 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2682 else
2683 ret++;
2684 return ret;
2687 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2688 if (flags & SORT_STRINGSORT)
2690 SetLastError(ERROR_INVALID_FLAGS);
2691 return 0;
2694 if (srclen < 0) srclen = strlenW(src) + 1;
2696 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2697 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2699 if (!dst) /* return required string length */
2701 INT len;
2703 for (len = 0; srclen; src++, srclen--)
2705 WCHAR wch = *src;
2706 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2707 * and skips white space and punctuation characters for
2708 * NORM_IGNORESYMBOLS.
2710 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2711 continue;
2712 len++;
2714 return len;
2717 if (flags & LCMAP_UPPERCASE)
2719 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2721 WCHAR wch = *src;
2722 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2723 continue;
2724 *dst_ptr++ = toupperW(wch);
2725 dstlen--;
2728 else if (flags & LCMAP_LOWERCASE)
2730 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2732 WCHAR wch = *src;
2733 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2734 continue;
2735 *dst_ptr++ = tolowerW(wch);
2736 dstlen--;
2739 else
2741 if (src == dst)
2743 SetLastError(ERROR_INVALID_FLAGS);
2744 return 0;
2746 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2748 WCHAR wch = *src;
2749 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2750 continue;
2751 *dst_ptr++ = wch;
2752 dstlen--;
2756 if (srclen)
2758 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2759 return 0;
2762 return dst_ptr - dst;
2765 /*************************************************************************
2766 * LCMapStringW (KERNEL32.@)
2768 * See LCMapStringA.
2770 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2771 LPWSTR dst, INT dstlen)
2773 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2774 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2776 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2779 /*************************************************************************
2780 * LCMapStringA (KERNEL32.@)
2782 * Map characters in a locale sensitive string.
2784 * PARAMS
2785 * lcid [I] LCID for the conversion.
2786 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2787 * src [I] String to map
2788 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2789 * dst [O] Destination for mapped string
2790 * dstlen [I] Length of dst in characters
2792 * RETURNS
2793 * Success: The length of the mapped string in dst, including the NUL terminator.
2794 * Failure: 0. Use GetLastError() to determine the cause.
2796 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2797 LPSTR dst, INT dstlen)
2799 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2800 LPWSTR srcW, dstW;
2801 INT ret = 0, srclenW, dstlenW;
2802 UINT locale_cp = CP_ACP;
2804 if (!src || !srclen || dstlen < 0)
2806 SetLastError(ERROR_INVALID_PARAMETER);
2807 return 0;
2810 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2812 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2813 if (srclenW)
2814 srcW = bufW;
2815 else
2817 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2818 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2819 if (!srcW)
2821 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2822 return 0;
2824 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2827 if (flags & LCMAP_SORTKEY)
2829 if (src == dst)
2831 SetLastError(ERROR_INVALID_FLAGS);
2832 goto map_string_exit;
2834 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2835 if (ret == 0)
2836 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2837 else
2838 ret++;
2839 goto map_string_exit;
2842 if (flags & SORT_STRINGSORT)
2844 SetLastError(ERROR_INVALID_FLAGS);
2845 goto map_string_exit;
2848 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2849 if (!dstlenW)
2850 goto map_string_exit;
2852 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2853 if (!dstW)
2855 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2856 goto map_string_exit;
2859 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2860 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2861 HeapFree(GetProcessHeap(), 0, dstW);
2863 map_string_exit:
2864 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2865 return ret;
2868 /*************************************************************************
2869 * FoldStringA (KERNEL32.@)
2871 * Map characters in a string.
2873 * PARAMS
2874 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2875 * src [I] String to map
2876 * srclen [I] Length of src, or -1 if src is NUL terminated
2877 * dst [O] Destination for mapped string
2878 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2880 * RETURNS
2881 * Success: The length of the string written to dst, including the terminating NUL. If
2882 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2883 * and dst may be NULL.
2884 * Failure: 0. Use GetLastError() to determine the cause.
2886 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2887 LPSTR dst, INT dstlen)
2889 INT ret = 0, srclenW = 0;
2890 WCHAR *srcW = NULL, *dstW = NULL;
2892 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2894 SetLastError(ERROR_INVALID_PARAMETER);
2895 return 0;
2898 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2899 src, srclen, NULL, 0);
2900 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2902 if (!srcW)
2904 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2905 goto FoldStringA_exit;
2908 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2909 src, srclen, srcW, srclenW);
2911 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2913 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2914 if (ret && dstlen)
2916 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2918 if (!dstW)
2920 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2921 goto FoldStringA_exit;
2924 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2925 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2927 ret = 0;
2928 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2932 HeapFree(GetProcessHeap(), 0, dstW);
2934 FoldStringA_exit:
2935 HeapFree(GetProcessHeap(), 0, srcW);
2936 return ret;
2939 /*************************************************************************
2940 * FoldStringW (KERNEL32.@)
2942 * See FoldStringA.
2944 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2945 LPWSTR dst, INT dstlen)
2947 int ret;
2949 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2951 case 0:
2952 if (dwFlags)
2953 break;
2954 /* Fall through for dwFlags == 0 */
2955 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2956 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2957 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2958 SetLastError(ERROR_INVALID_FLAGS);
2959 return 0;
2962 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2964 SetLastError(ERROR_INVALID_PARAMETER);
2965 return 0;
2968 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2969 if (!ret)
2970 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2971 return ret;
2974 /******************************************************************************
2975 * CompareStringW (KERNEL32.@)
2977 * See CompareStringA.
2979 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
2980 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2982 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
2985 /******************************************************************************
2986 * CompareStringEx (KERNEL32.@)
2988 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
2989 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
2991 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
2992 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
2993 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
2994 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
2995 INT ret;
2997 if (version) FIXME("unexpected version parameter\n");
2998 if (reserved) FIXME("unexpected reserved value\n");
2999 if (lParam) FIXME("unexpected lParam\n");
3001 if (!str1 || !str2)
3003 SetLastError(ERROR_INVALID_PARAMETER);
3004 return 0;
3007 if (flags & ~(supported_flags|semistub_flags))
3009 SetLastError(ERROR_INVALID_FLAGS);
3010 return 0;
3013 if (flags & semistub_flags)
3014 FIXME("semi-stub behavor for flag(s) 0x%x\n", flags & semistub_flags);
3016 if (len1 < 0) len1 = strlenW(str1);
3017 if (len2 < 0) len2 = strlenW(str2);
3019 ret = wine_compare_string(flags, str1, len1, str2, len2);
3021 if (ret) /* need to translate result */
3022 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3023 return CSTR_EQUAL;
3026 /******************************************************************************
3027 * CompareStringA (KERNEL32.@)
3029 * Compare two locale sensitive strings.
3031 * PARAMS
3032 * lcid [I] LCID for the comparison
3033 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3034 * str1 [I] First string to compare
3035 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3036 * str2 [I] Second string to compare
3037 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3039 * RETURNS
3040 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3041 * str1 is less than, equal to or greater than str2 respectively.
3042 * Failure: FALSE. Use GetLastError() to determine the cause.
3044 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3045 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3047 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3048 WCHAR *buf2W = buf1W + 130;
3049 LPWSTR str1W, str2W;
3050 INT len1W = 0, len2W = 0, ret;
3051 UINT locale_cp = CP_ACP;
3053 if (!str1 || !str2)
3055 SetLastError(ERROR_INVALID_PARAMETER);
3056 return 0;
3058 if (len1 < 0) len1 = strlen(str1);
3059 if (len2 < 0) len2 = strlen(str2);
3061 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3063 if (len1)
3065 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3066 if (len1W)
3067 str1W = buf1W;
3068 else
3070 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3071 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3072 if (!str1W)
3074 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3075 return 0;
3077 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3080 else
3082 len1W = 0;
3083 str1W = buf1W;
3086 if (len2)
3088 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3089 if (len2W)
3090 str2W = buf2W;
3091 else
3093 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3094 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3095 if (!str2W)
3097 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3098 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3099 return 0;
3101 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3104 else
3106 len2W = 0;
3107 str2W = buf2W;
3110 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3112 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3113 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3114 return ret;
3117 /******************************************************************************
3118 * CompareStringOrdinal (KERNEL32.@)
3120 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3122 int ret, len;
3124 if (!str1 || !str2)
3126 SetLastError(ERROR_INVALID_PARAMETER);
3127 return 0;
3129 if (len1 < 0) len1 = strlenW(str1);
3130 if (len2 < 0) len2 = strlenW(str2);
3132 len = min(len1, len2);
3133 if (ignore_case)
3135 ret = memicmpW(str1, str2, len);
3137 else
3139 ret = 0;
3140 for (; len > 0; len--)
3141 if ((ret = (*str1++ - *str2++))) break;
3143 if (!ret) ret = len1 - len2;
3145 if (ret < 0) return CSTR_LESS_THAN;
3146 if (ret > 0) return CSTR_GREATER_THAN;
3147 return CSTR_EQUAL;
3150 /*************************************************************************
3151 * lstrcmp (KERNEL32.@)
3152 * lstrcmpA (KERNEL32.@)
3154 * Compare two strings using the current thread locale.
3156 * PARAMS
3157 * str1 [I] First string to compare
3158 * str2 [I] Second string to compare
3160 * RETURNS
3161 * Success: A number less than, equal to or greater than 0 depending on whether
3162 * str1 is less than, equal to or greater than str2 respectively.
3163 * Failure: FALSE. Use GetLastError() to determine the cause.
3165 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3167 int ret;
3169 if ((str1 == NULL) && (str2 == NULL)) return 0;
3170 if (str1 == NULL) return -1;
3171 if (str2 == NULL) return 1;
3173 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3174 if (ret) ret -= 2;
3176 return ret;
3179 /*************************************************************************
3180 * lstrcmpi (KERNEL32.@)
3181 * lstrcmpiA (KERNEL32.@)
3183 * Compare two strings using the current thread locale, ignoring case.
3185 * PARAMS
3186 * str1 [I] First string to compare
3187 * str2 [I] Second string to compare
3189 * RETURNS
3190 * Success: A number less than, equal to or greater than 0 depending on whether
3191 * str2 is less than, equal to or greater than str1 respectively.
3192 * Failure: FALSE. Use GetLastError() to determine the cause.
3194 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3196 int ret;
3198 if ((str1 == NULL) && (str2 == NULL)) return 0;
3199 if (str1 == NULL) return -1;
3200 if (str2 == NULL) return 1;
3202 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3203 if (ret) ret -= 2;
3205 return ret;
3208 /*************************************************************************
3209 * lstrcmpW (KERNEL32.@)
3211 * See lstrcmpA.
3213 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3215 int ret;
3217 if ((str1 == NULL) && (str2 == NULL)) return 0;
3218 if (str1 == NULL) return -1;
3219 if (str2 == NULL) return 1;
3221 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3222 if (ret) ret -= 2;
3224 return ret;
3227 /*************************************************************************
3228 * lstrcmpiW (KERNEL32.@)
3230 * See lstrcmpiA.
3232 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3234 int ret;
3236 if ((str1 == NULL) && (str2 == NULL)) return 0;
3237 if (str1 == NULL) return -1;
3238 if (str2 == NULL) return 1;
3240 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3241 if (ret) ret -= 2;
3243 return ret;
3246 /******************************************************************************
3247 * LOCALE_Init
3249 void LOCALE_Init(void)
3251 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3252 const union cptable *unix_cp );
3254 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3256 #ifdef __APPLE__
3257 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3258 char user_locale[50];
3260 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3261 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3262 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3263 CFStringRef user_locale_string_ref;
3265 if (user_locale_country_ref)
3267 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3268 user_locale_lang_ref, user_locale_country_ref);
3270 else
3272 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3275 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3276 strcat(user_locale, ".UTF-8");
3278 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3279 setenv( "LANG", user_locale, 0 );
3280 TRACE( "setting locale to '%s'\n", user_locale );
3281 #endif /* __APPLE__ */
3283 setlocale( LC_ALL, "" );
3285 unix_cp = setup_unix_locales();
3286 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3288 #ifdef __APPLE__
3289 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3290 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3292 /* Retrieve the preferred language as chosen in System Preferences. */
3293 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3294 leave things be. */
3295 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3296 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3297 CFStringRef user_language_string_ref;
3298 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3299 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3300 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3301 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3303 struct locale_name locale_name;
3304 WCHAR buffer[128];
3305 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3306 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3307 parse_locale_name( buffer, &locale_name );
3308 lcid_LC_MESSAGES = locale_name.lcid;
3309 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3311 if (preferred_langs)
3312 CFRelease( preferred_langs );
3313 if (canonical_lang_string_ref)
3314 CFRelease( canonical_lang_string_ref );
3317 CFRelease( user_locale_ref );
3318 CFRelease( user_locale_string_ref );
3319 #endif
3321 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3322 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3323 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3325 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3326 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3327 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3328 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3329 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3330 if (!unix_cp)
3331 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3332 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3334 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3335 ansi_cptable = wine_cp_get_table( 1252 );
3336 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3337 oem_cptable = wine_cp_get_table( 437 );
3338 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3339 mac_cptable = wine_cp_get_table( 10000 );
3340 if (unix_cp != CP_UTF8)
3342 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3343 unix_cptable = wine_cp_get_table( 28591 );
3346 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3348 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3349 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3350 mac_cptable->info.codepage, unix_cp );
3352 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3355 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3357 UNICODE_STRING keyName;
3358 OBJECT_ATTRIBUTES attr;
3359 HANDLE hkey;
3361 RtlInitUnicodeString( &keyName, szKeyName );
3362 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3364 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3365 hkey = 0;
3367 return hkey;
3370 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3371 LPWSTR szValueName, ULONG valueNameSize,
3372 LPWSTR szValueData, ULONG valueDataSize)
3374 BYTE buffer[80];
3375 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3376 DWORD dwLen;
3378 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3379 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3380 info->NameLength > valueNameSize ||
3381 info->DataLength > valueDataSize)
3383 return FALSE;
3386 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3388 memcpy( szValueName, info->Name, info->NameLength);
3389 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3390 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3391 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3393 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3394 return TRUE;
3397 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3399 BYTE buffer[128];
3400 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3401 DWORD dwSize = sizeof(buffer);
3402 UNICODE_STRING valueName;
3404 RtlInitUnicodeString( &valueName, szValueName );
3406 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3407 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3408 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3409 info->DataLength == sizeof(DWORD))
3411 memcpy(lpVal, info->Data, sizeof(DWORD));
3412 return TRUE;
3415 return FALSE;
3418 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3420 LANGID langId;
3421 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3422 HRSRC hResource;
3423 BOOL bRet = FALSE;
3425 /* FIXME: Is it correct to use the system default langid? */
3426 langId = GetSystemDefaultLangID();
3428 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3429 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3431 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3433 if (hResource)
3435 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3437 if (hResDir)
3439 ULONG iResourceIndex = lgrpid & 0xf;
3440 LPCWSTR lpResEntry = LockResource( hResDir );
3441 ULONG i;
3443 for (i = 0; i < iResourceIndex; i++)
3444 lpResEntry += *lpResEntry + 1;
3446 if (*lpResEntry < nameSize)
3448 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3449 szName[*lpResEntry] = '\0';
3450 bRet = TRUE;
3454 FreeResource( hResource );
3456 return bRet;
3459 /* Registry keys for NLS related information */
3461 static const WCHAR szCountryListName[] = {
3462 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3463 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3464 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3465 'T','e','l','e','p','h','o','n','y','\\',
3466 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3470 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3471 typedef struct
3473 LANGUAGEGROUP_ENUMPROCA procA;
3474 LANGUAGEGROUP_ENUMPROCW procW;
3475 DWORD dwFlags;
3476 LONG_PTR lParam;
3477 } ENUMLANGUAGEGROUP_CALLBACKS;
3479 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3480 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3482 WCHAR szNumber[10], szValue[4];
3483 HANDLE hKey;
3484 BOOL bContinue = TRUE;
3485 ULONG ulIndex = 0;
3487 if (!lpProcs)
3489 SetLastError(ERROR_INVALID_PARAMETER);
3490 return FALSE;
3493 switch (lpProcs->dwFlags)
3495 case 0:
3496 /* Default to LGRPID_INSTALLED */
3497 lpProcs->dwFlags = LGRPID_INSTALLED;
3498 /* Fall through... */
3499 case LGRPID_INSTALLED:
3500 case LGRPID_SUPPORTED:
3501 break;
3502 default:
3503 SetLastError(ERROR_INVALID_FLAGS);
3504 return FALSE;
3507 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3509 if (!hKey)
3510 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3512 while (bContinue)
3514 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3515 szValue, sizeof(szValue) ))
3517 BOOL bInstalled = szValue[0] == '1';
3518 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3520 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3521 bInstalled ? "" : "not ");
3523 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3525 WCHAR szGrpName[48];
3527 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3528 szGrpName[0] = '\0';
3530 if (lpProcs->procW)
3531 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3532 lpProcs->lParam );
3533 else
3535 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3536 char szGrpNameA[48];
3538 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3539 * or whether the language names are ever localised. Assume CP_ACP.
3542 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3543 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3545 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3546 lpProcs->lParam );
3550 ulIndex++;
3552 else
3553 bContinue = FALSE;
3555 if (!bContinue)
3556 break;
3559 if (hKey)
3560 NtClose( hKey );
3562 return TRUE;
3565 /******************************************************************************
3566 * EnumSystemLanguageGroupsA (KERNEL32.@)
3568 * Call a users function for each language group available on the system.
3570 * PARAMS
3571 * pLangGrpEnumProc [I] Callback function to call for each language group
3572 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3573 * lParam [I] User parameter to pass to pLangGrpEnumProc
3575 * RETURNS
3576 * Success: TRUE.
3577 * Failure: FALSE. Use GetLastError() to determine the cause.
3579 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3580 DWORD dwFlags, LONG_PTR lParam)
3582 ENUMLANGUAGEGROUP_CALLBACKS procs;
3584 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3586 procs.procA = pLangGrpEnumProc;
3587 procs.procW = NULL;
3588 procs.dwFlags = dwFlags;
3589 procs.lParam = lParam;
3591 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3594 /******************************************************************************
3595 * EnumSystemLanguageGroupsW (KERNEL32.@)
3597 * See EnumSystemLanguageGroupsA.
3599 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3600 DWORD dwFlags, LONG_PTR lParam)
3602 ENUMLANGUAGEGROUP_CALLBACKS procs;
3604 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3606 procs.procA = NULL;
3607 procs.procW = pLangGrpEnumProc;
3608 procs.dwFlags = dwFlags;
3609 procs.lParam = lParam;
3611 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3614 /******************************************************************************
3615 * IsValidLanguageGroup (KERNEL32.@)
3617 * Determine if a language group is supported and/or installed.
3619 * PARAMS
3620 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3621 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3623 * RETURNS
3624 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3625 * FALSE otherwise.
3627 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3629 static const WCHAR szFormat[] = { '%','x','\0' };
3630 WCHAR szValueName[16], szValue[2];
3631 BOOL bSupported = FALSE, bInstalled = FALSE;
3632 HANDLE hKey;
3635 switch (dwFlags)
3637 case LGRPID_INSTALLED:
3638 case LGRPID_SUPPORTED:
3640 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3642 sprintfW( szValueName, szFormat, lgrpid );
3644 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3646 bSupported = TRUE;
3648 if (szValue[0] == '1')
3649 bInstalled = TRUE;
3652 if (hKey)
3653 NtClose( hKey );
3655 break;
3658 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3659 (dwFlags == LGRPID_INSTALLED && bInstalled))
3660 return TRUE;
3662 return FALSE;
3665 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3666 typedef struct
3668 LANGGROUPLOCALE_ENUMPROCA procA;
3669 LANGGROUPLOCALE_ENUMPROCW procW;
3670 DWORD dwFlags;
3671 LGRPID lgrpid;
3672 LONG_PTR lParam;
3673 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3675 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3676 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3678 static const WCHAR szAlternateSortsKeyName[] = {
3679 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3681 WCHAR szNumber[10], szValue[4];
3682 HANDLE hKey;
3683 BOOL bContinue = TRUE, bAlternate = FALSE;
3684 LGRPID lgrpid;
3685 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3687 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3689 SetLastError(ERROR_INVALID_PARAMETER);
3690 return FALSE;
3693 if (lpProcs->dwFlags)
3695 SetLastError(ERROR_INVALID_FLAGS);
3696 return FALSE;
3699 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3701 if (!hKey)
3702 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3704 while (bContinue)
3706 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3707 szValue, sizeof(szValue) ))
3709 lgrpid = strtoulW( szValue, NULL, 16 );
3711 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3712 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3714 if (lgrpid == lpProcs->lgrpid)
3716 LCID lcid;
3718 lcid = strtoulW( szNumber, NULL, 16 );
3720 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3721 * '00000437 ;Georgian'
3722 * At present we only pass the LCID string.
3725 if (lpProcs->procW)
3726 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3727 else
3729 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3731 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3733 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3737 ulIndex++;
3739 else
3741 /* Finished enumerating this key */
3742 if (!bAlternate)
3744 /* Enumerate alternate sorts also */
3745 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3746 bAlternate = TRUE;
3747 ulIndex = 0;
3749 else
3750 bContinue = FALSE; /* Finished both keys */
3753 if (!bContinue)
3754 break;
3757 if (hKey)
3758 NtClose( hKey );
3760 return TRUE;
3763 /******************************************************************************
3764 * EnumLanguageGroupLocalesA (KERNEL32.@)
3766 * Call a users function for every locale in a language group available on the system.
3768 * PARAMS
3769 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3770 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3771 * dwFlags [I] Reserved, set to 0
3772 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3774 * RETURNS
3775 * Success: TRUE.
3776 * Failure: FALSE. Use GetLastError() to determine the cause.
3778 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3779 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3781 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3783 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3785 callbacks.procA = pLangGrpLcEnumProc;
3786 callbacks.procW = NULL;
3787 callbacks.dwFlags = dwFlags;
3788 callbacks.lgrpid = lgrpid;
3789 callbacks.lParam = lParam;
3791 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3794 /******************************************************************************
3795 * EnumLanguageGroupLocalesW (KERNEL32.@)
3797 * See EnumLanguageGroupLocalesA.
3799 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3800 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3802 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3804 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3806 callbacks.procA = NULL;
3807 callbacks.procW = pLangGrpLcEnumProc;
3808 callbacks.dwFlags = dwFlags;
3809 callbacks.lgrpid = lgrpid;
3810 callbacks.lParam = lParam;
3812 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3815 /******************************************************************************
3816 * InvalidateNLSCache (KERNEL32.@)
3818 * Invalidate the cache of NLS values.
3820 * PARAMS
3821 * None.
3823 * RETURNS
3824 * Success: TRUE.
3825 * Failure: FALSE.
3827 BOOL WINAPI InvalidateNLSCache(void)
3829 FIXME("() stub\n");
3830 return FALSE;
3833 /******************************************************************************
3834 * GetUserGeoID (KERNEL32.@)
3836 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3838 GEOID ret = GEOID_NOT_AVAILABLE;
3839 static const WCHAR geoW[] = {'G','e','o',0};
3840 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3841 WCHAR bufferW[40], *end;
3842 DWORD count;
3843 HANDLE hkey, hSubkey = 0;
3844 UNICODE_STRING keyW;
3845 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3846 RtlInitUnicodeString( &keyW, nationW );
3847 count = sizeof(bufferW);
3849 if(!(hkey = create_registry_key())) return ret;
3851 switch( GeoClass ){
3852 case GEOCLASS_NATION:
3853 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3855 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3856 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3857 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3859 break;
3860 case GEOCLASS_REGION:
3861 FIXME("GEOCLASS_REGION not handled yet\n");
3862 break;
3865 NtClose(hkey);
3866 if (hSubkey) NtClose(hSubkey);
3867 return ret;
3870 /******************************************************************************
3871 * SetUserGeoID (KERNEL32.@)
3873 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3875 static const WCHAR geoW[] = {'G','e','o',0};
3876 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3877 static const WCHAR formatW[] = {'%','i',0};
3878 UNICODE_STRING nameW,keyW;
3879 WCHAR bufferW[10];
3880 OBJECT_ATTRIBUTES attr;
3881 HANDLE hkey;
3883 if(!(hkey = create_registry_key())) return FALSE;
3885 attr.Length = sizeof(attr);
3886 attr.RootDirectory = hkey;
3887 attr.ObjectName = &nameW;
3888 attr.Attributes = 0;
3889 attr.SecurityDescriptor = NULL;
3890 attr.SecurityQualityOfService = NULL;
3891 RtlInitUnicodeString( &nameW, geoW );
3892 RtlInitUnicodeString( &keyW, nationW );
3894 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3897 NtClose(attr.RootDirectory);
3898 return FALSE;
3901 sprintfW(bufferW, formatW, GeoID);
3902 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3903 NtClose(attr.RootDirectory);
3904 NtClose(hkey);
3905 return TRUE;
3908 typedef struct
3910 union
3912 UILANGUAGE_ENUMPROCA procA;
3913 UILANGUAGE_ENUMPROCW procW;
3914 } u;
3915 DWORD flags;
3916 LONG_PTR param;
3917 } ENUM_UILANG_CALLBACK;
3919 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3920 LPCSTR name, WORD LangID, LONG_PTR lParam )
3922 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3923 char buf[20];
3925 sprintf(buf, "%08x", (UINT)LangID);
3926 return enum_uilang->u.procA( buf, enum_uilang->param );
3929 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3930 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3932 static const WCHAR formatW[] = {'%','0','8','x',0};
3933 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3934 WCHAR buf[20];
3936 sprintfW( buf, formatW, (UINT)LangID );
3937 return enum_uilang->u.procW( buf, enum_uilang->param );
3940 /******************************************************************************
3941 * EnumUILanguagesA (KERNEL32.@)
3943 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3945 ENUM_UILANG_CALLBACK enum_uilang;
3947 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3949 if(!pUILangEnumProc) {
3950 SetLastError(ERROR_INVALID_PARAMETER);
3951 return FALSE;
3953 if(dwFlags) {
3954 SetLastError(ERROR_INVALID_FLAGS);
3955 return FALSE;
3958 enum_uilang.u.procA = pUILangEnumProc;
3959 enum_uilang.flags = dwFlags;
3960 enum_uilang.param = lParam;
3962 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3963 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3964 (LONG_PTR)&enum_uilang);
3965 return TRUE;
3968 /******************************************************************************
3969 * EnumUILanguagesW (KERNEL32.@)
3971 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3973 ENUM_UILANG_CALLBACK enum_uilang;
3975 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3978 if(!pUILangEnumProc) {
3979 SetLastError(ERROR_INVALID_PARAMETER);
3980 return FALSE;
3982 if(dwFlags) {
3983 SetLastError(ERROR_INVALID_FLAGS);
3984 return FALSE;
3987 enum_uilang.u.procW = pUILangEnumProc;
3988 enum_uilang.flags = dwFlags;
3989 enum_uilang.param = lParam;
3991 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3992 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3993 (LONG_PTR)&enum_uilang);
3994 return TRUE;
3997 enum locationkind {
3998 LOCATION_NATION = 0,
3999 LOCATION_REGION,
4000 LOCATION_BOTH
4003 struct geoinfo_t {
4004 GEOID id;
4005 WCHAR iso2W[3];
4006 WCHAR iso3W[4];
4007 GEOID parent;
4008 INT uncode;
4009 enum locationkind kind;
4012 static const struct geoinfo_t geoinfodata[] = {
4013 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4014 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4015 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4016 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4017 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4018 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4019 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4020 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4021 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4022 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4023 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4024 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4025 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4026 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4027 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4028 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4029 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4030 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4031 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4032 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4033 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4034 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4035 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4036 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4037 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4038 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4039 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4040 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4041 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4042 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4043 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4044 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4045 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4046 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4047 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4048 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4049 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4050 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4051 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4052 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4053 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4054 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4055 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4056 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4057 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4058 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4059 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4060 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4061 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4062 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4063 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4064 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4065 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4066 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4067 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4068 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4069 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4070 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4071 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4072 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4073 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4074 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4075 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4076 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4077 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4078 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4079 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4080 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4081 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4082 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4083 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4084 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4085 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4086 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4087 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4088 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4089 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4090 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4091 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4092 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4093 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4094 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4095 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4096 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4097 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4098 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4099 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4100 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4101 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4102 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4103 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4104 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4105 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4106 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4107 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4108 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4109 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4110 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4111 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4112 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4113 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4114 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4115 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4116 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4117 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4118 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4119 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4120 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4121 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4122 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4123 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4124 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4125 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4126 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4127 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4128 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4129 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4130 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4131 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4132 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4133 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4134 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4135 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4136 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4137 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4138 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4139 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4140 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4141 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4142 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4143 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4144 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4145 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4146 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4147 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4148 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4149 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4150 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4151 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4152 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4153 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4154 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4155 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4156 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4157 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4158 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4159 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4160 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4161 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4162 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4163 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4164 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4165 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4166 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4167 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4168 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4169 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4170 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4171 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4172 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4173 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4174 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4175 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4176 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4177 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4178 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4179 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4180 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4181 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4182 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4183 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4184 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4185 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4186 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4187 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4188 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4189 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4190 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4191 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4192 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4193 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4194 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4195 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4196 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4197 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4198 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4199 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4200 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4201 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4202 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4203 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4204 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4205 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4206 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4207 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4208 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4209 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4210 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4211 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4212 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4213 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4214 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4215 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4216 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4217 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4218 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4219 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4220 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4221 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4222 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4223 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4224 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4225 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4226 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4227 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4228 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4229 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4230 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4231 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4232 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4233 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4234 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4235 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4236 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4237 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4238 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4239 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4240 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4241 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4242 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4243 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4244 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4245 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4246 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4247 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4248 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4249 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4250 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4251 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4252 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4253 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4254 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4255 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4256 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4257 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4258 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4259 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4260 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4261 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4262 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4263 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4264 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4265 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4266 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4267 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4268 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4269 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4270 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4271 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4272 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4273 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4274 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4275 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4276 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4277 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4278 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4279 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4280 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4281 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4282 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4283 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4284 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4285 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4286 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4287 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4288 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4289 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4290 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4291 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4292 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4293 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4294 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4295 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4296 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4297 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4298 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4299 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4300 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4301 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4302 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4303 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4304 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4305 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4306 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4307 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4308 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4309 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4310 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4311 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4314 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4316 int min, max;
4318 min = 0;
4319 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4321 while (min <= max) {
4322 const struct geoinfo_t *ptr;
4323 int n = (min+max)/2;
4325 ptr = &geoinfodata[n];
4326 if (geoid == ptr->id)
4327 /* we don't need empty entry */
4328 return *ptr->iso2W ? ptr : NULL;
4330 if (ptr->id > geoid)
4331 max = n-1;
4332 else
4333 min = n+1;
4336 return NULL;
4339 /******************************************************************************
4340 * GetGeoInfoW (KERNEL32.@)
4342 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4344 const struct geoinfo_t *ptr;
4345 const WCHAR *str = NULL;
4346 WCHAR buffW[12];
4347 LONG val = 0;
4348 INT len;
4350 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4352 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4353 SetLastError(ERROR_INVALID_PARAMETER);
4354 return 0;
4357 switch (geotype) {
4358 case GEO_NATION:
4359 val = geoid;
4360 break;
4361 case GEO_ISO_UN_NUMBER:
4362 val = ptr->uncode;
4363 break;
4364 case GEO_PARENT:
4365 val = ptr->parent;
4366 break;
4367 case GEO_ISO2:
4368 case GEO_ISO3:
4370 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4371 break;
4373 case GEO_RFC1766:
4374 case GEO_LCID:
4375 case GEO_FRIENDLYNAME:
4376 case GEO_OFFICIALNAME:
4377 case GEO_TIMEZONES:
4378 case GEO_OFFICIALLANGUAGES:
4379 case GEO_LATITUDE:
4380 case GEO_LONGITUDE:
4381 FIXME("type %d is not supported\n", geotype);
4382 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4383 return 0;
4384 default:
4385 WARN("unrecognized type %d\n", geotype);
4386 SetLastError(ERROR_INVALID_FLAGS);
4387 return 0;
4390 if (val) {
4391 static const WCHAR fmtW[] = {'%','d',0};
4392 sprintfW(buffW, fmtW, val);
4393 str = buffW;
4396 len = strlenW(str) + 1;
4397 if (!data || !data_len)
4398 return len;
4400 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4401 if (data_len < len)
4402 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4403 return data_len < len ? 0 : len;
4406 /******************************************************************************
4407 * GetGeoInfoA (KERNEL32.@)
4409 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4411 WCHAR *buffW;
4412 INT len;
4414 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4416 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4417 if (!len)
4418 return 0;
4420 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4421 if (!buffW)
4422 return 0;
4424 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4425 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4426 if (!data || !data_len) {
4427 HeapFree(GetProcessHeap(), 0, buffW);
4428 return len;
4431 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4432 HeapFree(GetProcessHeap(), 0, buffW);
4434 if (data_len < len)
4435 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4436 return data_len < len ? 0 : len;
4439 /******************************************************************************
4440 * EnumSystemGeoID (KERNEL32.@)
4442 * Call a users function for every location available on the system.
4444 * PARAMS
4445 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4446 * parent [I] GEOID for the parent
4447 * enumproc [I] Callback function to call for each location
4449 * RETURNS
4450 * Success: TRUE.
4451 * Failure: FALSE. Use GetLastError() to determine the cause.
4453 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4455 INT i;
4457 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4459 if (!enumproc) {
4460 SetLastError(ERROR_INVALID_PARAMETER);
4461 return FALSE;
4464 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4465 SetLastError(ERROR_INVALID_FLAGS);
4466 return FALSE;
4469 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4470 const struct geoinfo_t *ptr = &geoinfodata[i];
4472 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4473 continue;
4475 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4476 continue;
4478 if (parent && ptr->parent != parent)
4479 continue;
4481 if (!enumproc(ptr->id))
4482 return TRUE;
4485 return TRUE;
4488 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4490 LCID userlcid;
4492 TRACE("%p, %d\n", localename, buffersize);
4494 userlcid = GetUserDefaultLCID();
4495 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4498 /******************************************************************************
4499 * NormalizeString (KERNEL32.@)
4501 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4502 LPWSTR lpDstString, INT cwDstLength)
4504 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4505 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4506 return 0;
4509 /******************************************************************************
4510 * IsNormalizedString (KERNEL32.@)
4512 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4514 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4515 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4516 return FALSE;
4519 enum {
4520 BASE = 36,
4521 TMIN = 1,
4522 TMAX = 26,
4523 SKEW = 38,
4524 DAMP = 700,
4525 INIT_BIAS = 72,
4526 INIT_N = 128
4529 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4531 INT k;
4533 delta /= (firsttime ? DAMP : 2);
4534 delta += delta/numpoints;
4536 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4537 delta /= BASE-TMIN;
4538 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4541 /******************************************************************************
4542 * IdnToAscii (KERNEL32.@)
4543 * Implementation of Punycode based on RFC 3492.
4545 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4546 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4548 static const WCHAR prefixW[] = {'x','n','-','-'};
4550 WCHAR *norm_str;
4551 INT i, label_start, label_end, norm_len, out_label, out = 0;
4553 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4554 lpASCIICharStr, cchASCIIChar);
4556 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4557 if(!norm_len)
4558 return 0;
4559 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4560 if(!norm_str) {
4561 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4562 return 0;
4564 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4565 cchUnicodeChar, norm_str, norm_len);
4566 if(!norm_len) {
4567 HeapFree(GetProcessHeap(), 0, norm_str);
4568 return 0;
4571 for(label_start=0; label_start<norm_len;) {
4572 INT n = INIT_N, bias = INIT_BIAS;
4573 INT delta = 0, b = 0, h;
4575 out_label = out;
4576 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4577 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4578 if(norm_str[i] < 0x80)
4579 b++;
4580 label_end = i;
4582 if(b == label_end-label_start) {
4583 if(label_end < norm_len)
4584 b++;
4585 if(!lpASCIICharStr) {
4586 out += b;
4587 }else if(out+b <= cchASCIIChar) {
4588 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4589 out += b;
4590 }else {
4591 HeapFree(GetProcessHeap(), 0, norm_str);
4592 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4593 return 0;
4595 label_start = label_end+1;
4596 continue;
4599 if(!lpASCIICharStr) {
4600 out += 5+b; /* strlen(xn--...-) */
4601 }else if(out+5+b <= cchASCIIChar) {
4602 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4603 out += 4;
4604 for(i=label_start; i<label_end; i++)
4605 if(norm_str[i] < 0x80)
4606 lpASCIICharStr[out++] = norm_str[i];
4607 lpASCIICharStr[out++] = '-';
4608 }else {
4609 HeapFree(GetProcessHeap(), 0, norm_str);
4610 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4611 return 0;
4613 if(!b)
4614 out--;
4616 for(h=b; h<label_end-label_start;) {
4617 INT m = 0xffff, q, k;
4619 for(i=label_start; i<label_end; i++) {
4620 if(norm_str[i]>=n && m>norm_str[i])
4621 m = norm_str[i];
4623 delta += (m-n)*(h+1);
4624 n = m;
4626 for(i=label_start; i<label_end; i++) {
4627 if(norm_str[i] < n) {
4628 delta++;
4629 }else if(norm_str[i] == n) {
4630 for(q=delta, k=BASE; ; k+=BASE) {
4631 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4632 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4633 if(!lpASCIICharStr) {
4634 out++;
4635 }else if(out+1 <= cchASCIIChar) {
4636 lpASCIICharStr[out++] = disp<='z'-'a' ?
4637 'a'+disp : '0'+disp-'z'+'a'-1;
4638 }else {
4639 HeapFree(GetProcessHeap(), 0, norm_str);
4640 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4641 return 0;
4643 if(q < t)
4644 break;
4645 q = (q-t)/(BASE-t);
4647 bias = adapt(delta, h+1, h==b);
4648 delta = 0;
4649 h++;
4652 delta++;
4653 n++;
4656 if(out-out_label > 63) {
4657 HeapFree(GetProcessHeap(), 0, norm_str);
4658 SetLastError(ERROR_INVALID_NAME);
4659 return 0;
4662 if(label_end < norm_len) {
4663 if(!lpASCIICharStr) {
4664 out++;
4665 }else if(out+1 <= cchASCIIChar) {
4666 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4667 }else {
4668 HeapFree(GetProcessHeap(), 0, norm_str);
4669 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4670 return 0;
4673 label_start = label_end+1;
4676 HeapFree(GetProcessHeap(), 0, norm_str);
4677 return out;
4680 /******************************************************************************
4681 * IdnToNameprepUnicode (KERNEL32.@)
4683 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4684 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4686 enum {
4687 UNASSIGNED = 0x1,
4688 PROHIBITED = 0x2,
4689 BIDI_RAL = 0x4,
4690 BIDI_L = 0x8
4693 extern const unsigned short nameprep_char_type[];
4694 extern const WCHAR nameprep_mapping[];
4695 const WCHAR *ptr;
4696 WORD flags;
4697 WCHAR buf[64], *map_str, norm_str[64], ch;
4698 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4699 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4701 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4702 lpNameprepCharStr, cchNameprepChar);
4704 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4705 SetLastError(ERROR_INVALID_FLAGS);
4706 return 0;
4709 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4710 SetLastError(ERROR_INVALID_PARAMETER);
4711 return 0;
4714 if(cchUnicodeChar == -1)
4715 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4716 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4717 SetLastError(ERROR_INVALID_NAME);
4718 return 0;
4721 for(label_start=0; label_start<cchUnicodeChar;) {
4722 ascii_only = TRUE;
4723 for(i=label_start; i<cchUnicodeChar; i++) {
4724 ch = lpUnicodeCharStr[i];
4726 if(i!=cchUnicodeChar-1 && !ch) {
4727 SetLastError(ERROR_INVALID_NAME);
4728 return 0;
4730 /* check if ch is one of label separators defined in RFC3490 */
4731 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4732 break;
4734 if(ch > 0x7f) {
4735 ascii_only = FALSE;
4736 continue;
4739 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4740 continue;
4741 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4742 || (ch>='0' && ch<='9') || ch=='-')
4743 continue;
4745 SetLastError(ERROR_INVALID_NAME);
4746 return 0;
4748 label_end = i;
4749 /* last label may be empty */
4750 if(label_start==label_end && ch) {
4751 SetLastError(ERROR_INVALID_NAME);
4752 return 0;
4755 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4756 lpUnicodeCharStr[label_end-1]=='-')) {
4757 SetLastError(ERROR_INVALID_NAME);
4758 return 0;
4761 if(ascii_only) {
4762 /* maximal label length is 63 characters */
4763 if(label_end-label_start > 63) {
4764 SetLastError(ERROR_INVALID_NAME);
4765 return 0;
4767 if(label_end < cchUnicodeChar)
4768 label_end++;
4770 if(!lpNameprepCharStr) {
4771 out += label_end-label_start;
4772 }else if(out+label_end-label_start <= cchNameprepChar) {
4773 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4774 (label_end-label_start)*sizeof(WCHAR));
4775 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4776 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4777 out += label_end-label_start;
4778 }else {
4779 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4780 return 0;
4783 label_start = label_end;
4784 continue;
4787 map_len = 0;
4788 for(i=label_start; i<label_end; i++) {
4789 ch = lpUnicodeCharStr[i];
4790 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4791 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4793 if(!ptr[0]) map_len++;
4794 else if(!ptr[1]) map_len++;
4795 else if(!ptr[2]) map_len += 2;
4796 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4798 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4799 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4800 if(!map_str) {
4801 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4802 return 0;
4804 }else {
4805 map_str = buf;
4807 map_len = 0;
4808 for(i=label_start; i<label_end; i++) {
4809 ch = lpUnicodeCharStr[i];
4810 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4811 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4813 if(!ptr[0]) {
4814 map_str[map_len++] = ch;
4815 }else if(!ptr[1]) {
4816 map_str[map_len++] = ptr[0];
4817 }else if(!ptr[2]) {
4818 map_str[map_len++] = ptr[0];
4819 map_str[map_len++] = ptr[1];
4820 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4821 map_str[map_len++] = ptr[0];
4822 map_str[map_len++] = ptr[1];
4823 map_str[map_len++] = ptr[2];
4827 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4828 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4829 if(map_str != buf)
4830 HeapFree(GetProcessHeap(), 0, map_str);
4831 if(!norm_len) {
4832 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4833 SetLastError(ERROR_INVALID_NAME);
4834 return 0;
4837 if(label_end < cchUnicodeChar) {
4838 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4839 label_end++;
4842 if(!lpNameprepCharStr) {
4843 out += norm_len;
4844 }else if(out+norm_len <= cchNameprepChar) {
4845 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4846 out += norm_len;
4847 }else {
4848 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4849 return 0;
4852 have_bidi_ral = prohibit_bidi_ral = FALSE;
4853 mask = PROHIBITED;
4854 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4855 mask |= UNASSIGNED;
4856 for(i=0; i<norm_len; i++) {
4857 ch = norm_str[i];
4858 flags = get_table_entry( nameprep_char_type, ch );
4860 if(flags & mask) {
4861 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4862 : ERROR_NO_UNICODE_TRANSLATION);
4863 return 0;
4866 if(flags & BIDI_RAL)
4867 have_bidi_ral = TRUE;
4868 if(flags & BIDI_L)
4869 prohibit_bidi_ral = TRUE;
4872 if(have_bidi_ral) {
4873 ch = norm_str[0];
4874 flags = get_table_entry( nameprep_char_type, ch );
4875 if((flags & BIDI_RAL) == 0)
4876 prohibit_bidi_ral = TRUE;
4878 ch = norm_str[norm_len-1];
4879 flags = get_table_entry( nameprep_char_type, ch );
4880 if((flags & BIDI_RAL) == 0)
4881 prohibit_bidi_ral = TRUE;
4884 if(have_bidi_ral && prohibit_bidi_ral) {
4885 SetLastError(ERROR_INVALID_NAME);
4886 return 0;
4889 label_start = label_end;
4892 return out;
4895 /******************************************************************************
4896 * IdnToUnicode (KERNEL32.@)
4898 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4899 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4901 extern const unsigned short nameprep_char_type[];
4903 INT i, label_start, label_end, out_label, out = 0;
4904 WCHAR ch;
4906 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4907 lpUnicodeCharStr, cchUnicodeChar);
4909 for(label_start=0; label_start<cchASCIIChar;) {
4910 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4912 out_label = out;
4913 for(i=label_start; i<cchASCIIChar; i++) {
4914 ch = lpASCIICharStr[i];
4916 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4917 SetLastError(ERROR_INVALID_NAME);
4918 return 0;
4921 if(!ch || ch=='.')
4922 break;
4923 if(ch == '-')
4924 delim = i;
4926 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4927 continue;
4928 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4929 || (ch>='0' && ch<='9') || ch=='-')
4930 continue;
4932 SetLastError(ERROR_INVALID_NAME);
4933 return 0;
4935 label_end = i;
4936 /* last label may be empty */
4937 if(label_start==label_end && ch) {
4938 SetLastError(ERROR_INVALID_NAME);
4939 return 0;
4942 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
4943 lpASCIICharStr[label_end-1]=='-')) {
4944 SetLastError(ERROR_INVALID_NAME);
4945 return 0;
4947 if(label_end-label_start > 63) {
4948 SetLastError(ERROR_INVALID_NAME);
4949 return 0;
4952 if(label_end-label_start<4 ||
4953 tolowerW(lpASCIICharStr[label_start])!='x' ||
4954 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4955 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4956 if(label_end < cchASCIIChar)
4957 label_end++;
4959 if(!lpUnicodeCharStr) {
4960 out += label_end-label_start;
4961 }else if(out+label_end-label_start <= cchUnicodeChar) {
4962 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4963 (label_end-label_start)*sizeof(WCHAR));
4964 out += label_end-label_start;
4965 }else {
4966 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4967 return 0;
4970 label_start = label_end;
4971 continue;
4974 if(delim == label_start+3)
4975 delim++;
4976 if(!lpUnicodeCharStr) {
4977 out += delim-label_start-4;
4978 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4979 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4980 (delim-label_start-4)*sizeof(WCHAR));
4981 out += delim-label_start-4;
4982 }else {
4983 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4984 return 0;
4986 if(out != out_label)
4987 delim++;
4989 for(i=delim; i<label_end;) {
4990 old_pos = pos;
4991 w = 1;
4992 for(k=BASE; ; k+=BASE) {
4993 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4994 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4995 SetLastError(ERROR_INVALID_NAME);
4996 return 0;
4998 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4999 pos += digit*w;
5000 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5001 if(digit < t)
5002 break;
5003 w *= BASE-t;
5005 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5006 n += pos/(out-out_label+1);
5007 pos %= out-out_label+1;
5009 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5010 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5011 SetLastError(ERROR_INVALID_NAME);
5012 return 0;
5014 if(!lpUnicodeCharStr) {
5015 out++;
5016 }else if(out+1 <= cchASCIIChar) {
5017 memmove(lpUnicodeCharStr+out_label+pos+1,
5018 lpUnicodeCharStr+out_label+pos,
5019 (out-out_label-pos)*sizeof(WCHAR));
5020 lpUnicodeCharStr[out_label+pos] = n;
5021 out++;
5022 }else {
5023 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5024 return 0;
5026 pos++;
5029 if(out-out_label > 63) {
5030 SetLastError(ERROR_INVALID_NAME);
5031 return 0;
5034 if(label_end < cchASCIIChar) {
5035 if(!lpUnicodeCharStr) {
5036 out++;
5037 }else if(out+1 <= cchUnicodeChar) {
5038 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5039 }else {
5040 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5041 return 0;
5044 label_start = label_end+1;
5047 return out;