ddraw/tests: Test d3d2 and 3 drawing with non-standard viewports.
[wine.git] / dlls / kernel32 / locale.c
blob8c5736b4f8c51c74ad7a01dd195b72b3093fbeb3
1 /*
2 * Locale support
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2002 Alexandre Julliard for CodeWeavers
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFLocale.h>
37 # include <CoreFoundation/CFString.h>
38 #endif
40 #include "ntstatus.h"
41 #define WIN32_NO_STATUS
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h" /* for RT_STRINGW */
45 #include "winternl.h"
46 #include "wine/unicode.h"
47 #include "winnls.h"
48 #include "winerror.h"
49 #include "winver.h"
50 #include "kernel_private.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(nls);
55 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
56 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
58 /* current code pages */
59 static const union cptable *ansi_cptable;
60 static const union cptable *oem_cptable;
61 static const union cptable *mac_cptable;
62 static const union cptable *unix_cptable; /* NULL if UTF8 */
64 static const WCHAR szLocaleKeyName[] = {
65 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
66 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
67 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
70 static const WCHAR szLangGroupsKeyName[] = {
71 '\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
72 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
73 'C','o','n','t','r','o','l','\\','N','l','s','\\',
74 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
77 /* Charset to codepage map, sorted by name. */
78 static const struct charset_entry
80 const char *charset_name;
81 UINT codepage;
82 } charset_names[] =
84 { "BIG5", 950 },
85 { "CP1250", 1250 },
86 { "CP1251", 1251 },
87 { "CP1252", 1252 },
88 { "CP1253", 1253 },
89 { "CP1254", 1254 },
90 { "CP1255", 1255 },
91 { "CP1256", 1256 },
92 { "CP1257", 1257 },
93 { "CP1258", 1258 },
94 { "CP932", 932 },
95 { "CP936", 936 },
96 { "CP949", 949 },
97 { "CP950", 950 },
98 { "EUCJP", 20932 },
99 { "GB2312", 936 },
100 { "IBM037", 37 },
101 { "IBM1026", 1026 },
102 { "IBM424", 424 },
103 { "IBM437", 437 },
104 { "IBM500", 500 },
105 { "IBM850", 850 },
106 { "IBM852", 852 },
107 { "IBM855", 855 },
108 { "IBM857", 857 },
109 { "IBM860", 860 },
110 { "IBM861", 861 },
111 { "IBM862", 862 },
112 { "IBM863", 863 },
113 { "IBM864", 864 },
114 { "IBM865", 865 },
115 { "IBM866", 866 },
116 { "IBM869", 869 },
117 { "IBM874", 874 },
118 { "IBM875", 875 },
119 { "ISO88591", 28591 },
120 { "ISO885910", 28600 },
121 { "ISO885913", 28603 },
122 { "ISO885914", 28604 },
123 { "ISO885915", 28605 },
124 { "ISO885916", 28606 },
125 { "ISO88592", 28592 },
126 { "ISO88593", 28593 },
127 { "ISO88594", 28594 },
128 { "ISO88595", 28595 },
129 { "ISO88596", 28596 },
130 { "ISO88597", 28597 },
131 { "ISO88598", 28598 },
132 { "ISO88599", 28599 },
133 { "KOI8R", 20866 },
134 { "KOI8U", 21866 },
135 { "UTF8", CP_UTF8 }
139 struct locale_name
141 WCHAR win_name[128]; /* Windows name ("en-US") */
142 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
143 WCHAR *country; /* country ("US") */
144 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
145 WCHAR *script; /* script ("Latn") for Windows format only */
146 WCHAR *modifier; /* modifier or sort order */
147 LCID lcid; /* corresponding LCID */
148 int matches; /* number of elements matching LCID (0..4) */
149 UINT codepage; /* codepage corresponding to charset */
152 /* locale ids corresponding to the various Unix locale parameters */
153 static LCID lcid_LC_COLLATE;
154 static LCID lcid_LC_CTYPE;
155 static LCID lcid_LC_MESSAGES;
156 static LCID lcid_LC_MONETARY;
157 static LCID lcid_LC_NUMERIC;
158 static LCID lcid_LC_TIME;
159 static LCID lcid_LC_PAPER;
160 static LCID lcid_LC_MEASUREMENT;
161 static LCID lcid_LC_TELEPHONE;
163 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
164 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
165 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
166 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
167 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
168 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
169 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
170 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
171 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
172 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
173 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
174 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
175 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
176 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
177 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
178 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
179 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
180 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
181 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
182 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
183 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
184 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
185 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
186 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
187 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
188 static const WCHAR sListW[] = {'s','L','i','s','t',0};
189 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
190 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
191 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
192 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
193 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
194 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
195 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
196 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
197 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
198 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
199 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
200 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
201 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
203 static struct registry_value
205 DWORD lctype;
206 const WCHAR *name;
207 WCHAR *cached_value;
208 } registry_values[] =
210 { LOCALE_ICALENDARTYPE, iCalendarTypeW },
211 { LOCALE_ICURRDIGITS, iCurrDigitsW },
212 { LOCALE_ICURRENCY, iCurrencyW },
213 { LOCALE_IDIGITS, iDigitsW },
214 { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
215 { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
216 { LOCALE_ILZERO, iLZeroW },
217 { LOCALE_IMEASURE, iMeasureW },
218 { LOCALE_INEGCURR, iNegCurrW },
219 { LOCALE_INEGNUMBER, iNegNumberW },
220 { LOCALE_IPAPERSIZE, iPaperSizeW },
221 { LOCALE_ITIME, iTimeW },
222 { LOCALE_S1159, s1159W },
223 { LOCALE_S2359, s2359W },
224 { LOCALE_SCURRENCY, sCurrencyW },
225 { LOCALE_SDATE, sDateW },
226 { LOCALE_SDECIMAL, sDecimalW },
227 { LOCALE_SGROUPING, sGroupingW },
228 { LOCALE_SLIST, sListW },
229 { LOCALE_SLONGDATE, sLongDateW },
230 { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
231 { LOCALE_SMONGROUPING, sMonGroupingW },
232 { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
233 { LOCALE_SNEGATIVESIGN, sNegativeSignW },
234 { LOCALE_SPOSITIVESIGN, sPositiveSignW },
235 { LOCALE_SSHORTDATE, sShortDateW },
236 { LOCALE_STHOUSAND, sThousandW },
237 { LOCALE_STIME, sTimeW },
238 { LOCALE_STIMEFORMAT, sTimeFormatW },
239 { LOCALE_SYEARMONTH, sYearMonthW },
240 /* The following are not listed under MSDN as supported,
241 * but seem to be used and also stored in the registry.
243 { LOCALE_ICOUNTRY, iCountryW },
244 { LOCALE_IDATE, iDateW },
245 { LOCALE_ILDATE, iLDateW },
246 { LOCALE_ITLZERO, iTLZeroW },
247 { LOCALE_SCOUNTRY, sCountryW },
248 { LOCALE_SABBREVLANGNAME, sLanguageW },
249 /* The following are used in XP and later */
250 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
251 { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
252 { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
255 static CRITICAL_SECTION cache_section;
256 static CRITICAL_SECTION_DEBUG critsect_debug =
258 0, 0, &cache_section,
259 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
260 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
262 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
264 /* Copy Ascii string to Unicode without using codepages */
265 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
267 while (n > 1 && *src)
269 *dst++ = (unsigned char)*src++;
270 n--;
272 if (n) *dst = 0;
275 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
277 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
280 /***********************************************************************
281 * get_lcid_codepage
283 * Retrieve the ANSI codepage for a given locale.
285 static inline UINT get_lcid_codepage( LCID lcid )
287 UINT ret;
288 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
289 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
290 return ret;
294 /***********************************************************************
295 * get_codepage_table
297 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
299 static const union cptable *get_codepage_table( unsigned int codepage )
301 const union cptable *ret = NULL;
303 assert( ansi_cptable ); /* init must have been done already */
305 switch(codepage)
307 case CP_ACP:
308 return ansi_cptable;
309 case CP_OEMCP:
310 return oem_cptable;
311 case CP_MACCP:
312 return mac_cptable;
313 case CP_UTF7:
314 case CP_UTF8:
315 break;
316 case CP_THREAD_ACP:
317 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
318 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
319 if (!codepage) return ansi_cptable;
320 /* fall through */
321 default:
322 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
323 if (codepage == oem_cptable->info.codepage) return oem_cptable;
324 if (codepage == mac_cptable->info.codepage) return mac_cptable;
325 ret = wine_cp_get_table( codepage );
326 break;
328 return ret;
332 /***********************************************************************
333 * charset_cmp (internal)
335 static int charset_cmp( const void *name, const void *entry )
337 const struct charset_entry *charset = entry;
338 return strcasecmp( name, charset->charset_name );
341 /***********************************************************************
342 * find_charset
344 static UINT find_charset( const WCHAR *name )
346 const struct charset_entry *entry;
347 char charset_name[16];
348 size_t i, j;
350 /* remove punctuation characters from charset name */
351 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
352 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
353 charset_name[j] = 0;
355 entry = bsearch( charset_name, charset_names,
356 sizeof(charset_names)/sizeof(charset_names[0]),
357 sizeof(charset_names[0]), charset_cmp );
358 if (entry) return entry->codepage;
359 return 0;
363 /***********************************************************************
364 * find_locale_id_callback
366 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
367 LPCWSTR name, WORD LangID, LPARAM lParam )
369 struct locale_name *data = (struct locale_name *)lParam;
370 WCHAR buffer[128];
371 int matches = 0;
372 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
374 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
376 /* first check exact name */
377 if (data->win_name[0] &&
378 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
379 buffer, sizeof(buffer)/sizeof(WCHAR) ))
381 if (!strcmpW( data->win_name, buffer ))
383 matches = 4; /* everything matches */
384 goto done;
388 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
389 buffer, sizeof(buffer)/sizeof(WCHAR) ))
390 return TRUE;
391 if (strcmpW( buffer, data->lang )) return TRUE;
392 matches++; /* language name matched */
394 if (data->country)
396 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
397 buffer, sizeof(buffer)/sizeof(WCHAR) ))
399 if (strcmpW( buffer, data->country )) goto done;
400 matches++; /* country name matched */
403 else /* match default language */
405 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
408 if (data->codepage)
410 UINT unix_cp;
411 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
412 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
414 if (unix_cp == data->codepage) matches++;
418 /* FIXME: check sort order */
420 done:
421 if (matches > data->matches)
423 data->lcid = lcid;
424 data->matches = matches;
426 return (data->matches < 4); /* no need to continue for perfect match */
430 /***********************************************************************
431 * parse_locale_name
433 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
434 * Unix format is: lang[_country][.charset][@modifier]
435 * Windows format is: lang[-script][-country][_modifier]
437 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
439 static const WCHAR sepW[] = {'-','_','.','@',0};
440 static const WCHAR winsepW[] = {'-','_',0};
441 static const WCHAR posixW[] = {'P','O','S','I','X',0};
442 static const WCHAR cW[] = {'C',0};
443 static const WCHAR latinW[] = {'l','a','t','i','n',0};
444 static const WCHAR latnW[] = {'-','L','a','t','n',0};
445 WCHAR *p;
447 TRACE("%s\n", debugstr_w(str));
449 name->country = name->charset = name->script = name->modifier = NULL;
450 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
451 name->matches = 0;
452 name->codepage = 0;
453 name->win_name[0] = 0;
454 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
456 if (!*name->lang)
458 name->lcid = LOCALE_INVARIANT;
459 name->matches = 4;
460 return;
463 if (!(p = strpbrkW( name->lang, sepW )))
465 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
467 name->matches = 4; /* perfect match for default English lcid */
468 return;
470 strcpyW( name->win_name, name->lang );
472 else if (*p == '-') /* Windows format */
474 strcpyW( name->win_name, name->lang );
475 *p++ = 0;
476 name->country = p;
477 if (!(p = strpbrkW( p, winsepW ))) goto done;
478 if (*p == '-')
480 *p++ = 0;
481 name->script = name->country;
482 name->country = p;
483 if (!(p = strpbrkW( p, winsepW ))) goto done;
485 *p++ = 0;
486 name->modifier = p;
488 else /* Unix format */
490 if (*p == '_')
492 *p++ = 0;
493 name->country = p;
494 p = strpbrkW( p, sepW + 2 );
496 if (p && *p == '.')
498 *p++ = 0;
499 name->charset = p;
500 p = strchrW( p, '@' );
502 if (p)
504 *p++ = 0;
505 name->modifier = p;
508 if (name->charset)
509 name->codepage = find_charset( name->charset );
511 /* rebuild a Windows name if possible */
513 if (name->charset) goto done; /* can't specify charset in Windows format */
514 if (name->modifier && strcmpW( name->modifier, latinW ))
515 goto done; /* only Latn script supported for now */
516 strcpyW( name->win_name, name->lang );
517 if (name->modifier) strcatW( name->win_name, latnW );
518 if (name->country)
520 p = name->win_name + strlenW(name->win_name);
521 *p++ = '-';
522 strcpyW( p, name->country );
525 done:
526 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
527 find_locale_id_callback, (LPARAM)name );
531 /***********************************************************************
532 * convert_default_lcid
534 * Get the default LCID to use for a given lctype in GetLocaleInfo.
536 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
538 if (lcid == LOCALE_SYSTEM_DEFAULT ||
539 lcid == LOCALE_USER_DEFAULT ||
540 lcid == LOCALE_NEUTRAL)
542 LCID default_id = 0;
544 switch(lctype & 0xffff)
546 case LOCALE_SSORTNAME:
547 default_id = lcid_LC_COLLATE;
548 break;
550 case LOCALE_FONTSIGNATURE:
551 case LOCALE_IDEFAULTANSICODEPAGE:
552 case LOCALE_IDEFAULTCODEPAGE:
553 case LOCALE_IDEFAULTEBCDICCODEPAGE:
554 case LOCALE_IDEFAULTMACCODEPAGE:
555 case LOCALE_IDEFAULTUNIXCODEPAGE:
556 default_id = lcid_LC_CTYPE;
557 break;
559 case LOCALE_ICURRDIGITS:
560 case LOCALE_ICURRENCY:
561 case LOCALE_IINTLCURRDIGITS:
562 case LOCALE_INEGCURR:
563 case LOCALE_INEGSEPBYSPACE:
564 case LOCALE_INEGSIGNPOSN:
565 case LOCALE_INEGSYMPRECEDES:
566 case LOCALE_IPOSSEPBYSPACE:
567 case LOCALE_IPOSSIGNPOSN:
568 case LOCALE_IPOSSYMPRECEDES:
569 case LOCALE_SCURRENCY:
570 case LOCALE_SINTLSYMBOL:
571 case LOCALE_SMONDECIMALSEP:
572 case LOCALE_SMONGROUPING:
573 case LOCALE_SMONTHOUSANDSEP:
574 case LOCALE_SNATIVECURRNAME:
575 default_id = lcid_LC_MONETARY;
576 break;
578 case LOCALE_IDIGITS:
579 case LOCALE_IDIGITSUBSTITUTION:
580 case LOCALE_ILZERO:
581 case LOCALE_INEGNUMBER:
582 case LOCALE_SDECIMAL:
583 case LOCALE_SGROUPING:
584 case LOCALE_SNAN:
585 case LOCALE_SNATIVEDIGITS:
586 case LOCALE_SNEGATIVESIGN:
587 case LOCALE_SNEGINFINITY:
588 case LOCALE_SPOSINFINITY:
589 case LOCALE_SPOSITIVESIGN:
590 case LOCALE_STHOUSAND:
591 default_id = lcid_LC_NUMERIC;
592 break;
594 case LOCALE_ICALENDARTYPE:
595 case LOCALE_ICENTURY:
596 case LOCALE_IDATE:
597 case LOCALE_IDAYLZERO:
598 case LOCALE_IFIRSTDAYOFWEEK:
599 case LOCALE_IFIRSTWEEKOFYEAR:
600 case LOCALE_ILDATE:
601 case LOCALE_IMONLZERO:
602 case LOCALE_IOPTIONALCALENDAR:
603 case LOCALE_ITIME:
604 case LOCALE_ITIMEMARKPOSN:
605 case LOCALE_ITLZERO:
606 case LOCALE_S1159:
607 case LOCALE_S2359:
608 case LOCALE_SABBREVDAYNAME1:
609 case LOCALE_SABBREVDAYNAME2:
610 case LOCALE_SABBREVDAYNAME3:
611 case LOCALE_SABBREVDAYNAME4:
612 case LOCALE_SABBREVDAYNAME5:
613 case LOCALE_SABBREVDAYNAME6:
614 case LOCALE_SABBREVDAYNAME7:
615 case LOCALE_SABBREVMONTHNAME1:
616 case LOCALE_SABBREVMONTHNAME2:
617 case LOCALE_SABBREVMONTHNAME3:
618 case LOCALE_SABBREVMONTHNAME4:
619 case LOCALE_SABBREVMONTHNAME5:
620 case LOCALE_SABBREVMONTHNAME6:
621 case LOCALE_SABBREVMONTHNAME7:
622 case LOCALE_SABBREVMONTHNAME8:
623 case LOCALE_SABBREVMONTHNAME9:
624 case LOCALE_SABBREVMONTHNAME10:
625 case LOCALE_SABBREVMONTHNAME11:
626 case LOCALE_SABBREVMONTHNAME12:
627 case LOCALE_SABBREVMONTHNAME13:
628 case LOCALE_SDATE:
629 case LOCALE_SDAYNAME1:
630 case LOCALE_SDAYNAME2:
631 case LOCALE_SDAYNAME3:
632 case LOCALE_SDAYNAME4:
633 case LOCALE_SDAYNAME5:
634 case LOCALE_SDAYNAME6:
635 case LOCALE_SDAYNAME7:
636 case LOCALE_SDURATION:
637 case LOCALE_SLONGDATE:
638 case LOCALE_SMONTHNAME1:
639 case LOCALE_SMONTHNAME2:
640 case LOCALE_SMONTHNAME3:
641 case LOCALE_SMONTHNAME4:
642 case LOCALE_SMONTHNAME5:
643 case LOCALE_SMONTHNAME6:
644 case LOCALE_SMONTHNAME7:
645 case LOCALE_SMONTHNAME8:
646 case LOCALE_SMONTHNAME9:
647 case LOCALE_SMONTHNAME10:
648 case LOCALE_SMONTHNAME11:
649 case LOCALE_SMONTHNAME12:
650 case LOCALE_SMONTHNAME13:
651 case LOCALE_SSHORTDATE:
652 case LOCALE_SSHORTESTDAYNAME1:
653 case LOCALE_SSHORTESTDAYNAME2:
654 case LOCALE_SSHORTESTDAYNAME3:
655 case LOCALE_SSHORTESTDAYNAME4:
656 case LOCALE_SSHORTESTDAYNAME5:
657 case LOCALE_SSHORTESTDAYNAME6:
658 case LOCALE_SSHORTESTDAYNAME7:
659 case LOCALE_STIME:
660 case LOCALE_STIMEFORMAT:
661 case LOCALE_SYEARMONTH:
662 default_id = lcid_LC_TIME;
663 break;
665 case LOCALE_IPAPERSIZE:
666 default_id = lcid_LC_PAPER;
667 break;
669 case LOCALE_IMEASURE:
670 default_id = lcid_LC_MEASUREMENT;
671 break;
673 case LOCALE_ICOUNTRY:
674 default_id = lcid_LC_TELEPHONE;
675 break;
677 if (default_id) lcid = default_id;
679 return ConvertDefaultLocale( lcid );
682 /***********************************************************************
683 * is_genitive_name_supported
685 * Determine could LCTYPE basically support genitive name form or not.
687 static BOOL is_genitive_name_supported( LCTYPE lctype )
689 switch(lctype & 0xffff)
691 case LOCALE_SMONTHNAME1:
692 case LOCALE_SMONTHNAME2:
693 case LOCALE_SMONTHNAME3:
694 case LOCALE_SMONTHNAME4:
695 case LOCALE_SMONTHNAME5:
696 case LOCALE_SMONTHNAME6:
697 case LOCALE_SMONTHNAME7:
698 case LOCALE_SMONTHNAME8:
699 case LOCALE_SMONTHNAME9:
700 case LOCALE_SMONTHNAME10:
701 case LOCALE_SMONTHNAME11:
702 case LOCALE_SMONTHNAME12:
703 case LOCALE_SMONTHNAME13:
704 return TRUE;
705 default:
706 return FALSE;
710 /***********************************************************************
711 * create_registry_key
713 * Create the Control Panel\\International registry key.
715 static inline HANDLE create_registry_key(void)
717 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
718 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
719 OBJECT_ATTRIBUTES attr;
720 UNICODE_STRING nameW;
721 HANDLE cpl_key, hkey = 0;
723 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
725 attr.Length = sizeof(attr);
726 attr.RootDirectory = hkey;
727 attr.ObjectName = &nameW;
728 attr.Attributes = 0;
729 attr.SecurityDescriptor = NULL;
730 attr.SecurityQualityOfService = NULL;
731 RtlInitUnicodeString( &nameW, cplW );
733 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
735 NtClose( attr.RootDirectory );
736 attr.RootDirectory = cpl_key;
737 RtlInitUnicodeString( &nameW, intlW );
738 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
740 NtClose( attr.RootDirectory );
741 return hkey;
745 /* update the registry settings for a given locale parameter */
746 /* return TRUE if an update was needed */
747 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
748 const LCTYPE *values, UINT nb_values )
750 static const WCHAR formatW[] = { '%','0','8','x',0 };
751 WCHAR bufferW[40];
752 UNICODE_STRING nameW;
753 DWORD count, i;
755 RtlInitUnicodeString( &nameW, name );
756 count = sizeof(bufferW);
757 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
759 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
760 LPCWSTR text = (LPCWSTR)info->Data;
762 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
763 TRACE( "updating registry, locale %s changed %s -> %08x\n",
764 debugstr_w(name), debugstr_w(text), lcid );
766 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
767 sprintfW( bufferW, formatW, lcid );
768 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
770 for (i = 0; i < nb_values; i++)
772 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
773 sizeof(bufferW)/sizeof(WCHAR) );
774 SetLocaleInfoW( lcid, values[i], bufferW );
776 return TRUE;
780 /***********************************************************************
781 * LOCALE_InitRegistry
783 * Update registry contents on startup if the user locale has changed.
784 * This simulates the action of the Windows control panel.
786 void LOCALE_InitRegistry(void)
788 static const WCHAR acpW[] = {'A','C','P',0};
789 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
790 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
791 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
792 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
793 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
794 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
795 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
796 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
797 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
798 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
799 static const struct
801 LPCWSTR name;
802 USHORT value;
803 } update_cp_values[] = {
804 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
805 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
806 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
808 static const LCTYPE lc_messages_values[] = {
809 LOCALE_SABBREVLANGNAME,
810 LOCALE_SCOUNTRY,
811 LOCALE_SLIST };
812 static const LCTYPE lc_monetary_values[] = {
813 LOCALE_SCURRENCY,
814 LOCALE_ICURRENCY,
815 LOCALE_INEGCURR,
816 LOCALE_ICURRDIGITS,
817 LOCALE_ILZERO,
818 LOCALE_SMONDECIMALSEP,
819 LOCALE_SMONGROUPING,
820 LOCALE_SMONTHOUSANDSEP };
821 static const LCTYPE lc_numeric_values[] = {
822 LOCALE_SDECIMAL,
823 LOCALE_STHOUSAND,
824 LOCALE_IDIGITS,
825 LOCALE_IDIGITSUBSTITUTION,
826 LOCALE_SNATIVEDIGITS,
827 LOCALE_INEGNUMBER,
828 LOCALE_SNEGATIVESIGN,
829 LOCALE_SPOSITIVESIGN,
830 LOCALE_SGROUPING };
831 static const LCTYPE lc_time_values[] = {
832 LOCALE_S1159,
833 LOCALE_S2359,
834 LOCALE_STIME,
835 LOCALE_ITIME,
836 LOCALE_ITLZERO,
837 LOCALE_SSHORTDATE,
838 LOCALE_SLONGDATE,
839 LOCALE_SDATE,
840 LOCALE_ITIMEMARKPOSN,
841 LOCALE_ICALENDARTYPE,
842 LOCALE_IFIRSTDAYOFWEEK,
843 LOCALE_IFIRSTWEEKOFYEAR,
844 LOCALE_STIMEFORMAT,
845 LOCALE_SYEARMONTH,
846 LOCALE_IDATE };
847 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
848 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
849 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
851 UNICODE_STRING nameW;
852 WCHAR bufferW[80];
853 DWORD count, i;
854 HANDLE hkey;
855 LCID lcid = GetUserDefaultLCID();
857 if (!(hkey = create_registry_key()))
858 return; /* don't do anything if we can't create the registry key */
860 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
861 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
862 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
863 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
864 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
865 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
866 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
867 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
868 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
869 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
870 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
871 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
872 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
873 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
875 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
877 static const WCHAR codepageW[] =
878 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
879 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
880 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
882 OBJECT_ATTRIBUTES attr;
883 HANDLE nls_key;
884 DWORD len = 14;
886 RtlInitUnicodeString( &nameW, codepageW );
887 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
888 while (codepageW[len])
890 nameW.Length = len * sizeof(WCHAR);
891 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
892 NtClose( nls_key );
893 len++;
894 while (codepageW[len] && codepageW[len] != '\\') len++;
896 nameW.Length = len * sizeof(WCHAR);
897 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
899 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
901 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
902 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
903 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
904 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
906 NtClose( nls_key );
910 NtClose( hkey );
914 #ifdef __APPLE__
915 /***********************************************************************
916 * get_mac_locale
918 * Return a locale identifier string reflecting the Mac locale, in a form
919 * that parse_locale_name() will understand. So, strip out unusual
920 * things like script, variant, etc. Or, rather, just construct it as
921 * <lang>[_<country>].UTF-8.
923 static const char* get_mac_locale(void)
925 static char mac_locale[50];
927 if (!mac_locale[0])
929 CFLocaleRef locale = CFLocaleCopyCurrent();
930 CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
931 CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
932 CFStringRef locale_string;
934 if (country)
935 locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
936 else
937 locale_string = CFStringCreateCopy(NULL, lang);
939 CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
940 strcat(mac_locale, ".UTF-8");
942 CFRelease(locale);
943 CFRelease(locale_string);
946 return mac_locale;
950 /***********************************************************************
951 * has_env
953 static BOOL has_env(const char* name)
955 const char* value = getenv( name );
956 return value && value[0];
958 #endif
961 /***********************************************************************
962 * get_locale
964 * Get the locale identifier for a given category. On most platforms,
965 * this is just a thin wrapper around setlocale(). On OS X, though, it
966 * is common for the Mac locale settings to not be supported by the C
967 * library. So, we sometimes override the result with the Mac locale.
969 static const char* get_locale(int category, const char* category_name)
971 const char* ret = setlocale(category, NULL);
973 #ifdef __APPLE__
974 /* If LC_ALL is set, respect it as a user override.
975 If LC_* is set, respect it as a user override, except if it's LC_CTYPE
976 and equal to UTF-8. That's because, when the Mac locale isn't supported
977 by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
978 parse_locale_name() doesn't handle that properly, so we override that
979 with the Mac locale (which uses UTF-8 for the charset, anyway).
980 Otherwise:
981 For LC_MESSAGES, we override the C library because the user language
982 setting is separate from the locale setting on which LANG was based.
983 If the C library didn't get anything better from LANG than C or POSIX,
984 override that. That probably means the Mac locale isn't supported by
985 the C library. */
986 if (!has_env( "LC_ALL" ) &&
987 ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
988 (!has_env( category_name ) &&
989 (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
991 const char* override = get_mac_locale();
993 if (category == LC_MESSAGES)
995 /* Retrieve the preferred language as chosen in System Preferences. */
996 static char messages_locale[50];
998 if (!messages_locale[0])
1000 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1001 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1003 CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1004 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1005 if (components)
1007 CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1008 CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1009 CFLocaleRef locale = NULL;
1010 CFStringRef locale_string;
1012 if (!country)
1014 locale = CFLocaleCopyCurrent();
1015 country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1018 if (country)
1019 locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1020 else
1021 locale_string = CFStringCreateCopy( NULL, lang );
1022 CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1023 strcat( messages_locale, ".UTF-8" );
1025 CFRelease( locale_string );
1026 if (locale) CFRelease( locale );
1027 CFRelease( components );
1030 if (preferred_langs)
1031 CFRelease( preferred_langs );
1034 if (messages_locale[0])
1035 override = messages_locale;
1038 TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1039 ret = override;
1041 #endif
1043 return ret;
1047 /***********************************************************************
1048 * setup_unix_locales
1050 static UINT setup_unix_locales(void)
1052 struct locale_name locale_name;
1053 WCHAR buffer[128], ctype_buff[128];
1054 const char *locale;
1055 UINT unix_cp = 0;
1057 if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1059 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
1060 parse_locale_name( ctype_buff, &locale_name );
1061 lcid_LC_CTYPE = locale_name.lcid;
1062 unix_cp = locale_name.codepage;
1064 if (!lcid_LC_CTYPE) /* this one needs a default value */
1065 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
1067 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1068 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
1070 #define GET_UNIX_LOCALE(cat) do \
1071 if ((locale = get_locale( cat, #cat ))) \
1073 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
1074 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1075 else { \
1076 parse_locale_name( buffer, &locale_name ); \
1077 lcid_##cat = locale_name.lcid; \
1078 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
1079 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1081 } while (0)
1083 GET_UNIX_LOCALE( LC_COLLATE );
1084 GET_UNIX_LOCALE( LC_MESSAGES );
1085 GET_UNIX_LOCALE( LC_MONETARY );
1086 GET_UNIX_LOCALE( LC_NUMERIC );
1087 GET_UNIX_LOCALE( LC_TIME );
1088 #ifdef LC_PAPER
1089 GET_UNIX_LOCALE( LC_PAPER );
1090 #endif
1091 #ifdef LC_MEASUREMENT
1092 GET_UNIX_LOCALE( LC_MEASUREMENT );
1093 #endif
1094 #ifdef LC_TELEPHONE
1095 GET_UNIX_LOCALE( LC_TELEPHONE );
1096 #endif
1098 #undef GET_UNIX_LOCALE
1100 return unix_cp;
1104 /***********************************************************************
1105 * GetUserDefaultLangID (KERNEL32.@)
1107 * Get the default language Id for the current user.
1109 * PARAMS
1110 * None.
1112 * RETURNS
1113 * The current LANGID of the default language for the current user.
1115 LANGID WINAPI GetUserDefaultLangID(void)
1117 return LANGIDFROMLCID(GetUserDefaultLCID());
1121 /***********************************************************************
1122 * GetSystemDefaultLangID (KERNEL32.@)
1124 * Get the default language Id for the system.
1126 * PARAMS
1127 * None.
1129 * RETURNS
1130 * The current LANGID of the default language for the system.
1132 LANGID WINAPI GetSystemDefaultLangID(void)
1134 return LANGIDFROMLCID(GetSystemDefaultLCID());
1138 /***********************************************************************
1139 * GetUserDefaultLCID (KERNEL32.@)
1141 * Get the default locale Id for the current user.
1143 * PARAMS
1144 * None.
1146 * RETURNS
1147 * The current LCID of the default locale for the current user.
1149 LCID WINAPI GetUserDefaultLCID(void)
1151 LCID lcid;
1152 NtQueryDefaultLocale( TRUE, &lcid );
1153 return lcid;
1157 /***********************************************************************
1158 * GetSystemDefaultLCID (KERNEL32.@)
1160 * Get the default locale Id for the system.
1162 * PARAMS
1163 * None.
1165 * RETURNS
1166 * The current LCID of the default locale for the system.
1168 LCID WINAPI GetSystemDefaultLCID(void)
1170 LCID lcid;
1171 NtQueryDefaultLocale( FALSE, &lcid );
1172 return lcid;
1175 /***********************************************************************
1176 * GetSystemDefaultLocaleName (KERNEL32.@)
1178 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1180 LCID lcid = GetSystemDefaultLCID();
1181 return LCIDToLocaleName(lcid, localename, len, 0);
1184 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1186 LCTYPE type;
1187 int lsize;
1189 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1191 if (flags & MUI_LANGUAGE_ID)
1192 type = LOCALE_ILANGUAGE;
1193 else
1194 type = LOCALE_SNAME;
1196 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1197 if (!lsize)
1199 /* keep last error from callee */
1200 return FALSE;
1202 lsize++;
1203 if (!*size)
1205 *size = lsize;
1206 *count = 1;
1207 return TRUE;
1210 if (lsize > *size)
1212 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1213 return FALSE;
1216 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1218 /* keep last error from callee */
1219 return FALSE;
1222 buffer[lsize-1] = 0;
1223 *size = lsize;
1224 *count = 1;
1225 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1226 return TRUE;
1230 /***********************************************************************
1231 * GetSystemPreferredUILanguages (KERNEL32.@)
1233 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1235 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1237 SetLastError(ERROR_INVALID_PARAMETER);
1238 return FALSE;
1240 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1242 SetLastError(ERROR_INVALID_PARAMETER);
1243 return FALSE;
1245 if (*size && !buffer)
1247 SetLastError(ERROR_INVALID_PARAMETER);
1248 return FALSE;
1251 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1254 /***********************************************************************
1255 * SetThreadPreferredUILanguages (KERNEL32.@)
1257 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1259 FIXME( "%u, %p, %p\n", flags, buffer, count );
1260 return TRUE;
1263 /***********************************************************************
1264 * GetThreadPreferredUILanguages (KERNEL32.@)
1266 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1268 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1269 return get_dummy_preferred_ui_language( flags, count, buf, size );
1272 /***********************************************************************
1273 * GetUserDefaultUILanguage (KERNEL32.@)
1275 * Get the default user interface language Id for the current user.
1277 * PARAMS
1278 * None.
1280 * RETURNS
1281 * The current LANGID of the default UI language for the current user.
1283 LANGID WINAPI GetUserDefaultUILanguage(void)
1285 LANGID lang;
1286 NtQueryDefaultUILanguage( &lang );
1287 return lang;
1291 /***********************************************************************
1292 * GetSystemDefaultUILanguage (KERNEL32.@)
1294 * Get the default user interface language Id for the system.
1296 * PARAMS
1297 * None.
1299 * RETURNS
1300 * The current LANGID of the default UI language for the system. This is
1301 * typically the same language used during the installation process.
1303 LANGID WINAPI GetSystemDefaultUILanguage(void)
1305 LANGID lang;
1306 NtQueryInstallUILanguage( &lang );
1307 return lang;
1311 /***********************************************************************
1312 * LocaleNameToLCID (KERNEL32.@)
1314 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1316 struct locale_name locale_name;
1318 if (flags) FIXME( "unsupported flags %x\n", flags );
1320 if (name == LOCALE_NAME_USER_DEFAULT)
1321 return GetUserDefaultLCID();
1323 /* string parsing */
1324 parse_locale_name( name, &locale_name );
1326 TRACE( "found lcid %x for %s, matches %d\n",
1327 locale_name.lcid, debugstr_w(name), locale_name.matches );
1329 if (!locale_name.matches)
1331 SetLastError(ERROR_INVALID_PARAMETER);
1332 return 0;
1335 if (locale_name.matches == 1)
1336 WARN( "locale %s not recognized, defaulting to %s\n",
1337 debugstr_w(name), debugstr_w(locale_name.lang) );
1339 return locale_name.lcid;
1343 /***********************************************************************
1344 * LCIDToLocaleName (KERNEL32.@)
1346 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1348 if (flags) FIXME( "unsupported flags %x\n", flags );
1350 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1354 /******************************************************************************
1355 * get_locale_registry_value
1357 * Gets the registry value name and cache for a given lctype.
1359 static struct registry_value *get_locale_registry_value( DWORD lctype )
1361 int i;
1362 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1363 if (registry_values[i].lctype == lctype)
1364 return &registry_values[i];
1365 return NULL;
1369 /******************************************************************************
1370 * get_registry_locale_info
1372 * Retrieve user-modified locale info from the registry.
1373 * Return length, 0 on error, -1 if not found.
1375 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1377 DWORD size;
1378 INT ret;
1379 HANDLE hkey;
1380 NTSTATUS status;
1381 UNICODE_STRING nameW;
1382 KEY_VALUE_PARTIAL_INFORMATION *info;
1383 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1385 RtlEnterCriticalSection( &cache_section );
1387 if (!registry_value->cached_value)
1389 if (!(hkey = create_registry_key()))
1391 RtlLeaveCriticalSection( &cache_section );
1392 return -1;
1395 RtlInitUnicodeString( &nameW, registry_value->name );
1396 size = info_size + len * sizeof(WCHAR);
1398 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1400 NtClose( hkey );
1401 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1402 RtlLeaveCriticalSection( &cache_section );
1403 return 0;
1406 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1408 /* try again with a bigger buffer when we have to return the correct size */
1409 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1411 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1412 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1414 info = new_info;
1415 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1419 NtClose( hkey );
1421 if (!status)
1423 INT length = (size - info_size) / sizeof(WCHAR);
1424 LPWSTR cached_value;
1426 if (!length || ((WCHAR *)&info->Data)[length-1])
1427 length++;
1429 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1431 if (!cached_value)
1433 HeapFree( GetProcessHeap(), 0, info );
1434 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1435 RtlLeaveCriticalSection( &cache_section );
1436 return 0;
1439 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1440 cached_value[length-1] = 0;
1441 HeapFree( GetProcessHeap(), 0, info );
1442 registry_value->cached_value = cached_value;
1444 else
1446 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1448 ret = (size - info_size) / sizeof(WCHAR);
1450 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1452 ret = -1;
1454 else
1456 SetLastError( RtlNtStatusToDosError(status) );
1457 ret = 0;
1459 HeapFree( GetProcessHeap(), 0, info );
1460 RtlLeaveCriticalSection( &cache_section );
1461 return ret;
1465 ret = lstrlenW( registry_value->cached_value ) + 1;
1467 if (buffer)
1469 if (ret > len)
1471 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1472 ret = 0;
1474 else
1476 lstrcpyW( buffer, registry_value->cached_value );
1480 RtlLeaveCriticalSection( &cache_section );
1482 return ret;
1486 /******************************************************************************
1487 * GetLocaleInfoA (KERNEL32.@)
1489 * Get information about an aspect of a locale.
1491 * PARAMS
1492 * lcid [I] LCID of the locale
1493 * lctype [I] LCTYPE_ flags from "winnls.h"
1494 * buffer [O] Destination for the information
1495 * len [I] Length of buffer in characters
1497 * RETURNS
1498 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1499 * with the information.
1500 * Failure: 0. Use GetLastError() to determine the cause.
1502 * NOTES
1503 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1504 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1505 * which is a bit string.
1507 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1509 WCHAR *bufferW;
1510 INT lenW, ret;
1512 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1514 if (len < 0 || (len && !buffer))
1516 SetLastError( ERROR_INVALID_PARAMETER );
1517 return 0;
1519 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1520 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1522 SetLastError( ERROR_INVALID_FLAGS );
1523 return 0;
1526 if (!len) buffer = NULL;
1528 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1530 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1532 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1533 return 0;
1535 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1537 if ((lctype & LOCALE_RETURN_NUMBER) ||
1538 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1540 /* it's not an ASCII string, just bytes */
1541 ret *= sizeof(WCHAR);
1542 if (buffer)
1544 if (ret <= len) memcpy( buffer, bufferW, ret );
1545 else
1547 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1548 ret = 0;
1552 else
1554 UINT codepage = CP_ACP;
1555 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1556 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1559 HeapFree( GetProcessHeap(), 0, bufferW );
1560 return ret;
1563 static int get_value_base_by_lctype( LCTYPE lctype )
1565 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1568 /******************************************************************************
1569 * GetLocaleInfoW (KERNEL32.@)
1571 * See GetLocaleInfoA.
1573 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1575 LANGID lang_id;
1576 HRSRC hrsrc;
1577 HGLOBAL hmem;
1578 INT ret;
1579 UINT lcflags;
1580 const WCHAR *p;
1581 unsigned int i;
1583 if (len < 0 || (len && !buffer))
1585 SetLastError( ERROR_INVALID_PARAMETER );
1586 return 0;
1588 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1589 !is_genitive_name_supported( lctype ))
1591 SetLastError( ERROR_INVALID_FLAGS );
1592 return 0;
1595 if (!len) buffer = NULL;
1597 lcid = convert_default_lcid( lcid, lctype );
1599 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1600 lctype &= 0xffff;
1602 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1604 /* first check for overrides in the registry */
1606 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1607 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1609 struct registry_value *value = get_locale_registry_value(lctype);
1611 if (value)
1613 if (lcflags & LOCALE_RETURN_NUMBER)
1615 WCHAR tmp[16];
1616 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1617 if (ret > 0)
1619 WCHAR *end;
1620 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1621 if (*end) /* invalid number */
1623 SetLastError( ERROR_INVALID_FLAGS );
1624 return 0;
1626 ret = sizeof(UINT)/sizeof(WCHAR);
1627 if (!buffer) return ret;
1628 if (ret > len)
1630 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1631 return 0;
1633 memcpy( buffer, &number, sizeof(number) );
1636 else ret = get_registry_locale_info( value, buffer, len );
1638 if (ret != -1) return ret;
1642 /* now load it from kernel resources */
1644 lang_id = LANGIDFROMLCID( lcid );
1646 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1647 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1648 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1650 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1651 ULongToPtr((lctype >> 4) + 1), lang_id )))
1653 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1654 return 0;
1656 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1657 return 0;
1659 p = LockResource( hmem );
1660 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1662 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1663 else if (is_genitive_name_supported( lctype ) && *p)
1665 /* genitive form's stored after a null separator from a nominative */
1666 for (i = 1; i <= *p; i++) if (!p[i]) break;
1668 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1670 ret = *p - i + 1;
1671 p += i;
1673 else ret = i;
1675 else
1676 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1678 if (!buffer) return ret;
1680 if (ret > len)
1682 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1683 return 0;
1686 if (lcflags & LOCALE_RETURN_NUMBER)
1688 UINT number;
1689 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1690 if (!tmp) return 0;
1691 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1692 tmp[*p] = 0;
1693 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1694 if (!*end)
1695 memcpy( buffer, &number, sizeof(number) );
1696 else /* invalid number */
1698 SetLastError( ERROR_INVALID_FLAGS );
1699 ret = 0;
1701 HeapFree( GetProcessHeap(), 0, tmp );
1703 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1704 lcid, lctype, buffer, len, number );
1706 else
1708 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1709 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1711 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1712 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1714 return ret;
1717 /******************************************************************************
1718 * GetLocaleInfoEx (KERNEL32.@)
1720 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1722 LCID lcid = LocaleNameToLCID(locale, 0);
1724 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1726 if (!lcid) return 0;
1728 /* special handling for neutral locale names */
1729 if (info == LOCALE_SNAME && locale && strlenW(locale) == 2)
1731 if (len && len < 3)
1733 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1734 return 0;
1737 if (len) strcpyW(buffer, locale);
1738 return 3;
1741 return GetLocaleInfoW(lcid, info, buffer, len);
1744 /******************************************************************************
1745 * SetLocaleInfoA [KERNEL32.@]
1747 * Set information about an aspect of a locale.
1749 * PARAMS
1750 * lcid [I] LCID of the locale
1751 * lctype [I] LCTYPE_ flags from "winnls.h"
1752 * data [I] Information to set
1754 * RETURNS
1755 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1756 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1757 * Failure: FALSE. Use GetLastError() to determine the cause.
1759 * NOTES
1760 * - Values are only be set for the current user locale; the system locale
1761 * settings cannot be changed.
1762 * - Any settings changed by this call are lost when the locale is changed by
1763 * the control panel (in Wine, this happens every time you change LANG).
1764 * - The native implementation of this function does not check that lcid matches
1765 * the current user locale, and simply sets the new values. Wine warns you in
1766 * this case, but behaves the same.
1768 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1770 UINT codepage = CP_ACP;
1771 WCHAR *strW;
1772 DWORD len;
1773 BOOL ret;
1775 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1777 if (!data)
1779 SetLastError( ERROR_INVALID_PARAMETER );
1780 return FALSE;
1782 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1783 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1785 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1786 return FALSE;
1788 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1789 ret = SetLocaleInfoW( lcid, lctype, strW );
1790 HeapFree( GetProcessHeap(), 0, strW );
1791 return ret;
1795 /******************************************************************************
1796 * SetLocaleInfoW (KERNEL32.@)
1798 * See SetLocaleInfoA.
1800 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1802 struct registry_value *value;
1803 static const WCHAR intlW[] = {'i','n','t','l',0 };
1804 UNICODE_STRING valueW;
1805 NTSTATUS status;
1806 HANDLE hkey;
1808 lctype &= 0xffff;
1809 value = get_locale_registry_value( lctype );
1811 if (!data || !value)
1813 SetLastError( ERROR_INVALID_PARAMETER );
1814 return FALSE;
1817 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1819 SetLastError( ERROR_INVALID_FLAGS );
1820 return FALSE;
1823 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1825 /* FIXME: should check that data to set is sane */
1827 /* FIXME: profile functions should map to registry */
1828 WriteProfileStringW( intlW, value->name, data );
1830 if (!(hkey = create_registry_key())) return FALSE;
1831 RtlInitUnicodeString( &valueW, value->name );
1832 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1834 RtlEnterCriticalSection( &cache_section );
1835 HeapFree( GetProcessHeap(), 0, value->cached_value );
1836 value->cached_value = NULL;
1837 RtlLeaveCriticalSection( &cache_section );
1839 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1841 /* Set I-value from S value */
1842 WCHAR *lpD, *lpM, *lpY;
1843 WCHAR szBuff[2];
1845 lpD = strrchrW(data, 'd');
1846 lpM = strrchrW(data, 'M');
1847 lpY = strrchrW(data, 'y');
1849 if (lpD <= lpM)
1851 szBuff[0] = '1'; /* D-M-Y */
1853 else
1855 if (lpY <= lpM)
1856 szBuff[0] = '2'; /* Y-M-D */
1857 else
1858 szBuff[0] = '0'; /* M-D-Y */
1861 szBuff[1] = '\0';
1863 if (lctype == LOCALE_SSHORTDATE)
1864 lctype = LOCALE_IDATE;
1865 else
1866 lctype = LOCALE_ILDATE;
1868 value = get_locale_registry_value( lctype );
1870 WriteProfileStringW( intlW, value->name, szBuff );
1872 RtlInitUnicodeString( &valueW, value->name );
1873 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1875 RtlEnterCriticalSection( &cache_section );
1876 HeapFree( GetProcessHeap(), 0, value->cached_value );
1877 value->cached_value = NULL;
1878 RtlLeaveCriticalSection( &cache_section );
1881 NtClose( hkey );
1883 if (status) SetLastError( RtlNtStatusToDosError(status) );
1884 return !status;
1888 /******************************************************************************
1889 * GetACP (KERNEL32.@)
1891 * Get the current Ansi code page Id for the system.
1893 * PARAMS
1894 * None.
1896 * RETURNS
1897 * The current Ansi code page identifier for the system.
1899 UINT WINAPI GetACP(void)
1901 assert( ansi_cptable );
1902 return ansi_cptable->info.codepage;
1906 /******************************************************************************
1907 * SetCPGlobal (KERNEL32.@)
1909 * Set the current Ansi code page Id for the system.
1911 * PARAMS
1912 * acp [I] code page ID to be the new ACP.
1914 * RETURNS
1915 * The previous ACP.
1917 UINT WINAPI SetCPGlobal( UINT acp )
1919 UINT ret = GetACP();
1920 const union cptable *new_cptable = wine_cp_get_table( acp );
1922 if (new_cptable) ansi_cptable = new_cptable;
1923 return ret;
1927 /***********************************************************************
1928 * GetOEMCP (KERNEL32.@)
1930 * Get the current OEM code page Id for the system.
1932 * PARAMS
1933 * None.
1935 * RETURNS
1936 * The current OEM code page identifier for the system.
1938 UINT WINAPI GetOEMCP(void)
1940 assert( oem_cptable );
1941 return oem_cptable->info.codepage;
1945 /***********************************************************************
1946 * IsValidCodePage (KERNEL32.@)
1948 * Determine if a given code page identifier is valid.
1950 * PARAMS
1951 * codepage [I] Code page Id to verify.
1953 * RETURNS
1954 * TRUE, If codepage is valid and available on the system,
1955 * FALSE otherwise.
1957 BOOL WINAPI IsValidCodePage( UINT codepage )
1959 switch(codepage) {
1960 case CP_UTF7:
1961 case CP_UTF8:
1962 return TRUE;
1963 default:
1964 return wine_cp_get_table( codepage ) != NULL;
1969 /***********************************************************************
1970 * IsDBCSLeadByteEx (KERNEL32.@)
1972 * Determine if a character is a lead byte in a given code page.
1974 * PARAMS
1975 * codepage [I] Code page for the test.
1976 * testchar [I] Character to test
1978 * RETURNS
1979 * TRUE, if testchar is a lead byte in codepage,
1980 * FALSE otherwise.
1982 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1984 const union cptable *table = get_codepage_table( codepage );
1985 return table && wine_is_dbcs_leadbyte( table, testchar );
1989 /***********************************************************************
1990 * IsDBCSLeadByte (KERNEL32.@)
1991 * IsDBCSLeadByte (KERNEL.207)
1993 * Determine if a character is a lead byte.
1995 * PARAMS
1996 * testchar [I] Character to test
1998 * RETURNS
1999 * TRUE, if testchar is a lead byte in the ANSI code page,
2000 * FALSE otherwise.
2002 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2004 if (!ansi_cptable) return FALSE;
2005 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2009 /***********************************************************************
2010 * GetCPInfo (KERNEL32.@)
2012 * Get information about a code page.
2014 * PARAMS
2015 * codepage [I] Code page number
2016 * cpinfo [O] Destination for code page information
2018 * RETURNS
2019 * Success: TRUE. cpinfo is updated with the information about codepage.
2020 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2022 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2024 const union cptable *table;
2026 if (!cpinfo)
2028 SetLastError( ERROR_INVALID_PARAMETER );
2029 return FALSE;
2032 if (!(table = get_codepage_table( codepage )))
2034 switch(codepage)
2036 case CP_UTF7:
2037 case CP_UTF8:
2038 cpinfo->DefaultChar[0] = 0x3f;
2039 cpinfo->DefaultChar[1] = 0;
2040 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2041 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2042 return TRUE;
2045 SetLastError( ERROR_INVALID_PARAMETER );
2046 return FALSE;
2048 if (table->info.def_char & 0xff00)
2050 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2051 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2053 else
2055 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2056 cpinfo->DefaultChar[1] = 0;
2058 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2059 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2060 else
2061 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2063 return TRUE;
2066 /***********************************************************************
2067 * GetCPInfoExA (KERNEL32.@)
2069 * Get extended information about a code page.
2071 * PARAMS
2072 * codepage [I] Code page number
2073 * dwFlags [I] Reserved, must to 0.
2074 * cpinfo [O] Destination for code page information
2076 * RETURNS
2077 * Success: TRUE. cpinfo is updated with the information about codepage.
2078 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2080 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2082 CPINFOEXW cpinfoW;
2084 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2085 return FALSE;
2087 /* the layout is the same except for CodePageName */
2088 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2089 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2090 return TRUE;
2093 /***********************************************************************
2094 * GetCPInfoExW (KERNEL32.@)
2096 * Unicode version of GetCPInfoExA.
2098 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2100 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2101 return FALSE;
2103 switch(codepage)
2105 case CP_UTF7:
2107 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2109 cpinfo->CodePage = CP_UTF7;
2110 cpinfo->UnicodeDefaultChar = 0x3f;
2111 strcpyW(cpinfo->CodePageName, utf7);
2112 break;
2115 case CP_UTF8:
2117 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2119 cpinfo->CodePage = CP_UTF8;
2120 cpinfo->UnicodeDefaultChar = 0x3f;
2121 strcpyW(cpinfo->CodePageName, utf8);
2122 break;
2125 default:
2127 const union cptable *table = get_codepage_table( codepage );
2129 cpinfo->CodePage = table->info.codepage;
2130 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2131 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2132 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2133 break;
2136 return TRUE;
2139 /***********************************************************************
2140 * EnumSystemCodePagesA (KERNEL32.@)
2142 * Call a user defined function for every code page installed on the system.
2144 * PARAMS
2145 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2146 * flags [I] Reserved, set to 0.
2148 * RETURNS
2149 * TRUE, If all code pages have been enumerated, or
2150 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2152 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2154 const union cptable *table;
2155 char buffer[10];
2156 int index = 0;
2158 for (;;)
2160 if (!(table = wine_cp_enum_table( index++ ))) break;
2161 sprintf( buffer, "%d", table->info.codepage );
2162 if (!lpfnCodePageEnum( buffer )) break;
2164 return TRUE;
2168 /***********************************************************************
2169 * EnumSystemCodePagesW (KERNEL32.@)
2171 * See EnumSystemCodePagesA.
2173 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2175 const union cptable *table;
2176 WCHAR buffer[10], *p;
2177 int page, index = 0;
2179 for (;;)
2181 if (!(table = wine_cp_enum_table( index++ ))) break;
2182 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2183 *--p = 0;
2184 page = table->info.codepage;
2187 *--p = '0' + (page % 10);
2188 page /= 10;
2189 } while( page );
2190 if (!lpfnCodePageEnum( p )) break;
2192 return TRUE;
2196 /***********************************************************************
2197 * utf7_write_w
2199 * Helper for utf7_mbstowcs
2201 * RETURNS
2202 * TRUE on success, FALSE on error
2204 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2206 if (dstlen > 0)
2208 if (*index >= dstlen)
2209 return FALSE;
2211 dst[*index] = character;
2214 (*index)++;
2216 return TRUE;
2219 /***********************************************************************
2220 * utf7_mbstowcs
2222 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2224 * RETURNS
2225 * On success, the number of characters written
2226 * On dst buffer overflow, -1
2228 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2230 static const signed char base64_decoding_table[] =
2232 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2233 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2234 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2235 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2236 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2237 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2238 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2239 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2242 const char *source_end = src + srclen;
2243 int dest_index = 0;
2245 DWORD byte_pair = 0;
2246 short offset = 0;
2248 while (src < source_end)
2250 if (*src == '+')
2252 src++;
2253 if (src >= source_end)
2254 break;
2256 if (*src == '-')
2258 /* just a plus sign escaped as +- */
2259 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2260 return -1;
2261 src++;
2262 continue;
2267 signed char sextet = *src;
2268 if (sextet == '-')
2270 /* skip over the dash and end base64 decoding
2271 * the current, unfinished byte pair is discarded */
2272 src++;
2273 offset = 0;
2274 break;
2276 if (sextet < 0)
2278 /* the next character of src is < 0 and therefore not part of a base64 sequence
2279 * the current, unfinished byte pair is NOT discarded in this case
2280 * this is probably a bug in Windows */
2281 break;
2284 sextet = base64_decoding_table[sextet];
2285 if (sextet == -1)
2287 /* -1 means that the next character of src is not part of a base64 sequence
2288 * in other words, all sextets in this base64 sequence have been processed
2289 * the current, unfinished byte pair is discarded */
2290 offset = 0;
2291 break;
2294 byte_pair = (byte_pair << 6) | sextet;
2295 offset += 6;
2297 if (offset >= 16)
2299 /* this byte pair is done */
2300 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2301 return -1;
2302 offset -= 16;
2305 src++;
2307 while (src < source_end);
2309 else
2311 /* we have to convert to unsigned char in case *src < 0 */
2312 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2313 return -1;
2314 src++;
2318 return dest_index;
2321 /***********************************************************************
2322 * MultiByteToWideChar (KERNEL32.@)
2324 * Convert a multibyte character string into a Unicode string.
2326 * PARAMS
2327 * page [I] Codepage character set to convert from
2328 * flags [I] Character mapping flags
2329 * src [I] Source string buffer
2330 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2331 * dst [O] Destination buffer
2332 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2334 * RETURNS
2335 * Success: If dstlen > 0, the number of characters written to dst.
2336 * If dstlen == 0, the number of characters needed to perform the
2337 * conversion. In both cases the count includes the terminating NUL.
2338 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2339 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2340 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2341 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2342 * possible for src.
2344 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2345 LPWSTR dst, INT dstlen )
2347 const union cptable *table;
2348 int ret;
2350 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2352 SetLastError( ERROR_INVALID_PARAMETER );
2353 return 0;
2356 if (srclen < 0) srclen = strlen(src) + 1;
2358 switch(page)
2360 case CP_SYMBOL:
2361 if (flags)
2363 SetLastError( ERROR_INVALID_FLAGS );
2364 return 0;
2366 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2367 break;
2368 case CP_UTF7:
2369 if (flags)
2371 SetLastError( ERROR_INVALID_FLAGS );
2372 return 0;
2374 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2375 break;
2376 case CP_UNIXCP:
2377 if (unix_cptable)
2379 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2380 break;
2382 #ifdef __APPLE__
2383 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2384 #endif
2385 /* fall through */
2386 case CP_UTF8:
2387 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2388 break;
2389 default:
2390 if (!(table = get_codepage_table( page )))
2392 SetLastError( ERROR_INVALID_PARAMETER );
2393 return 0;
2395 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2396 break;
2399 if (ret < 0)
2401 switch(ret)
2403 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2404 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2406 ret = 0;
2408 TRACE("cp %d %s -> %s, ret = %d\n",
2409 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2410 return ret;
2414 /***********************************************************************
2415 * utf7_can_directly_encode
2417 * Helper for utf7_wcstombs
2419 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2421 static const BOOL directly_encodable_table[] =
2423 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2425 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2426 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2427 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2428 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2429 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2430 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2433 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2436 /***********************************************************************
2437 * utf7_write_c
2439 * Helper for utf7_wcstombs
2441 * RETURNS
2442 * TRUE on success, FALSE on error
2444 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2446 if (dstlen > 0)
2448 if (*index >= dstlen)
2449 return FALSE;
2451 dst[*index] = character;
2454 (*index)++;
2456 return TRUE;
2459 /***********************************************************************
2460 * utf7_wcstombs
2462 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2464 * RETURNS
2465 * On success, the number of characters written
2466 * On dst buffer overflow, -1
2468 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2470 static const char base64_encoding_table[] =
2471 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2473 const WCHAR *source_end = src + srclen;
2474 int dest_index = 0;
2476 while (src < source_end)
2478 if (*src == '+')
2480 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2481 return -1;
2482 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2483 return -1;
2484 src++;
2486 else if (utf7_can_directly_encode(*src))
2488 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2489 return -1;
2490 src++;
2492 else
2494 unsigned int offset = 0;
2495 DWORD byte_pair = 0;
2497 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2498 return -1;
2500 while (src < source_end && !utf7_can_directly_encode(*src))
2502 byte_pair = (byte_pair << 16) | *src;
2503 offset += 16;
2504 while (offset >= 6)
2506 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2507 return -1;
2508 offset -= 6;
2510 src++;
2513 if (offset)
2515 /* Windows won't create a padded base64 character if there's no room for the - sign
2516 * as well ; this is probably a bug in Windows */
2517 if (dstlen > 0 && dest_index + 1 >= dstlen)
2518 return -1;
2520 byte_pair <<= (6 - offset);
2521 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2522 return -1;
2525 /* Windows always explicitly terminates the base64 sequence
2526 even though RFC 2152 (page 3, rule 2) does not require this */
2527 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2528 return -1;
2532 return dest_index;
2535 /***********************************************************************
2536 * WideCharToMultiByte (KERNEL32.@)
2538 * Convert a Unicode character string into a multibyte string.
2540 * PARAMS
2541 * page [I] Code page character set to convert to
2542 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2543 * src [I] Source string buffer
2544 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2545 * dst [O] Destination buffer
2546 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2547 * defchar [I] Default character to use for conversion if no exact
2548 * conversion can be made
2549 * used [O] Set if default character was used in the conversion
2551 * RETURNS
2552 * Success: If dstlen > 0, the number of characters written to dst.
2553 * If dstlen == 0, number of characters needed to perform the
2554 * conversion. In both cases the count includes the terminating NUL.
2555 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2556 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2557 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2558 * parameter was given.
2560 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2561 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2563 const union cptable *table;
2564 int ret, used_tmp;
2566 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2568 SetLastError( ERROR_INVALID_PARAMETER );
2569 return 0;
2572 if (srclen < 0) srclen = strlenW(src) + 1;
2574 switch(page)
2576 case CP_SYMBOL:
2577 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2578 if (flags)
2580 SetLastError( ERROR_INVALID_FLAGS );
2581 return 0;
2583 if (defchar || used)
2585 SetLastError( ERROR_INVALID_PARAMETER );
2586 return 0;
2588 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2589 break;
2590 case CP_UTF7:
2591 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2592 if (defchar || used)
2594 SetLastError( ERROR_INVALID_PARAMETER );
2595 return 0;
2597 if (flags)
2599 SetLastError( ERROR_INVALID_FLAGS );
2600 return 0;
2602 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2603 break;
2604 case CP_UNIXCP:
2605 if (unix_cptable)
2607 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2608 defchar, used ? &used_tmp : NULL );
2609 break;
2611 /* fall through */
2612 case CP_UTF8:
2613 if (defchar || used)
2615 SetLastError( ERROR_INVALID_PARAMETER );
2616 return 0;
2618 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2619 break;
2620 default:
2621 if (!(table = get_codepage_table( page )))
2623 SetLastError( ERROR_INVALID_PARAMETER );
2624 return 0;
2626 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2627 defchar, used ? &used_tmp : NULL );
2628 if (used) *used = used_tmp;
2629 break;
2632 if (ret < 0)
2634 switch(ret)
2636 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2637 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2639 ret = 0;
2641 TRACE("cp %d %s -> %s, ret = %d\n",
2642 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2643 return ret;
2647 /***********************************************************************
2648 * GetThreadLocale (KERNEL32.@)
2650 * Get the current threads locale.
2652 * PARAMS
2653 * None.
2655 * RETURNS
2656 * The LCID currently associated with the calling thread.
2658 LCID WINAPI GetThreadLocale(void)
2660 LCID ret = NtCurrentTeb()->CurrentLocale;
2661 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2662 return ret;
2665 /**********************************************************************
2666 * SetThreadLocale (KERNEL32.@)
2668 * Set the current threads locale.
2670 * PARAMS
2671 * lcid [I] LCID of the locale to set
2673 * RETURNS
2674 * Success: TRUE. The threads locale is set to lcid.
2675 * Failure: FALSE. Use GetLastError() to determine the cause.
2677 BOOL WINAPI SetThreadLocale( LCID lcid )
2679 TRACE("(0x%04X)\n", lcid);
2681 lcid = ConvertDefaultLocale(lcid);
2683 if (lcid != GetThreadLocale())
2685 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2687 SetLastError(ERROR_INVALID_PARAMETER);
2688 return FALSE;
2691 NtCurrentTeb()->CurrentLocale = lcid;
2693 return TRUE;
2696 /**********************************************************************
2697 * SetThreadUILanguage (KERNEL32.@)
2699 * Set the current threads UI language.
2701 * PARAMS
2702 * langid [I] LANGID of the language to set, or 0 to use
2703 * the available language which is best supported
2704 * for console applications
2706 * RETURNS
2707 * Success: The return value is the same as the input value.
2708 * Failure: The return value differs from the input value.
2709 * Use GetLastError() to determine the cause.
2711 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2713 TRACE("(0x%04x) stub - returning success\n", langid);
2714 return langid;
2717 /******************************************************************************
2718 * ConvertDefaultLocale (KERNEL32.@)
2720 * Convert a default locale identifier into a real identifier.
2722 * PARAMS
2723 * lcid [I] LCID identifier of the locale to convert
2725 * RETURNS
2726 * lcid unchanged, if not a default locale or its sublanguage is
2727 * not SUBLANG_NEUTRAL.
2728 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2729 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2730 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2732 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2734 LANGID langid;
2736 switch (lcid)
2738 case LOCALE_INVARIANT:
2739 /* keep as-is */
2740 break;
2741 case LOCALE_SYSTEM_DEFAULT:
2742 lcid = GetSystemDefaultLCID();
2743 break;
2744 case LOCALE_USER_DEFAULT:
2745 case LOCALE_NEUTRAL:
2746 lcid = GetUserDefaultLCID();
2747 break;
2748 default:
2749 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2750 langid = LANGIDFROMLCID(lcid);
2751 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2753 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2754 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2757 return lcid;
2761 /******************************************************************************
2762 * IsValidLocale (KERNEL32.@)
2764 * Determine if a locale is valid.
2766 * PARAMS
2767 * lcid [I] LCID of the locale to check
2768 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2770 * RETURNS
2771 * TRUE, if lcid is valid,
2772 * FALSE, otherwise.
2774 * NOTES
2775 * Wine does not currently make the distinction between supported and installed. All
2776 * languages supported are installed by default.
2778 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2780 /* check if language is registered in the kernel32 resources */
2781 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2782 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2785 /******************************************************************************
2786 * IsValidLocaleName (KERNEL32.@)
2788 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2790 struct locale_name locale_name;
2792 if (!locale)
2793 return FALSE;
2795 /* string parsing */
2796 parse_locale_name( locale, &locale_name );
2798 TRACE( "found lcid %x for %s, matches %d\n",
2799 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2801 return locale_name.matches > 0;
2804 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2805 LPCSTR name, WORD LangID, LONG_PTR lParam )
2807 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2808 char buf[20];
2810 sprintf(buf, "%08x", (UINT)LangID);
2811 return lpfnLocaleEnum( buf );
2814 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2815 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2817 static const WCHAR formatW[] = {'%','0','8','x',0};
2818 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2819 WCHAR buf[20];
2820 sprintfW( buf, formatW, (UINT)LangID );
2821 return lpfnLocaleEnum( buf );
2824 /******************************************************************************
2825 * EnumSystemLocalesA (KERNEL32.@)
2827 * Call a users function for each locale available on the system.
2829 * PARAMS
2830 * lpfnLocaleEnum [I] Callback function to call for each locale
2831 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2833 * RETURNS
2834 * Success: TRUE.
2835 * Failure: FALSE. Use GetLastError() to determine the cause.
2837 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2839 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2840 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2841 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2842 (LONG_PTR)lpfnLocaleEnum);
2843 return TRUE;
2847 /******************************************************************************
2848 * EnumSystemLocalesW (KERNEL32.@)
2850 * See EnumSystemLocalesA.
2852 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2854 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2855 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2856 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2857 (LONG_PTR)lpfnLocaleEnum);
2858 return TRUE;
2862 struct enum_locale_ex_data
2864 LOCALE_ENUMPROCEX proc;
2865 DWORD flags;
2866 LPARAM lparam;
2869 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2870 LPCWSTR name, WORD lang, LONG_PTR lparam )
2872 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2873 WCHAR buffer[256];
2874 DWORD neutral;
2875 unsigned int flags;
2877 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2878 buffer, sizeof(buffer) / sizeof(WCHAR) );
2879 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2880 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2881 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2882 neutral = 0;
2883 flags = LOCALE_WINDOWS;
2884 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2885 if (data->flags && !(data->flags & flags)) return TRUE;
2886 return data->proc( buffer, flags, data->lparam );
2889 /******************************************************************************
2890 * EnumSystemLocalesEx (KERNEL32.@)
2892 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2894 struct enum_locale_ex_data data;
2896 if (reserved)
2898 SetLastError( ERROR_INVALID_PARAMETER );
2899 return FALSE;
2901 data.proc = proc;
2902 data.flags = flags;
2903 data.lparam = lparam;
2904 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2905 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2906 enum_locale_ex_proc, (LONG_PTR)&data );
2907 return TRUE;
2911 /***********************************************************************
2912 * VerLanguageNameA (KERNEL32.@)
2914 * Get the name of a language.
2916 * PARAMS
2917 * wLang [I] LANGID of the language
2918 * szLang [O] Destination for the language name
2920 * RETURNS
2921 * Success: The size of the language name. If szLang is non-NULL, it is filled
2922 * with the name.
2923 * Failure: 0. Use GetLastError() to determine the cause.
2926 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2928 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2932 /***********************************************************************
2933 * VerLanguageNameW (KERNEL32.@)
2935 * See VerLanguageNameA.
2937 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2939 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2943 /******************************************************************************
2944 * GetStringTypeW (KERNEL32.@)
2946 * See GetStringTypeA.
2948 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2950 static const unsigned char type2_map[16] =
2952 C2_NOTAPPLICABLE, /* unassigned */
2953 C2_LEFTTORIGHT, /* L */
2954 C2_RIGHTTOLEFT, /* R */
2955 C2_EUROPENUMBER, /* EN */
2956 C2_EUROPESEPARATOR, /* ES */
2957 C2_EUROPETERMINATOR, /* ET */
2958 C2_ARABICNUMBER, /* AN */
2959 C2_COMMONSEPARATOR, /* CS */
2960 C2_BLOCKSEPARATOR, /* B */
2961 C2_SEGMENTSEPARATOR, /* S */
2962 C2_WHITESPACE, /* WS */
2963 C2_OTHERNEUTRAL, /* ON */
2964 C2_RIGHTTOLEFT, /* AL */
2965 C2_NOTAPPLICABLE, /* NSM */
2966 C2_NOTAPPLICABLE, /* BN */
2967 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2970 if (!src)
2972 SetLastError( ERROR_INVALID_PARAMETER );
2973 return FALSE;
2976 if (count == -1) count = strlenW(src) + 1;
2977 switch(type)
2979 case CT_CTYPE1:
2980 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2981 break;
2982 case CT_CTYPE2:
2983 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2984 break;
2985 case CT_CTYPE3:
2987 WARN("CT_CTYPE3: semi-stub.\n");
2988 while (count--)
2990 int c = *src;
2991 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2993 type1 = get_char_typeW( *src++ ) & 0xfff;
2994 /* try to construct type3 from type1 */
2995 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2996 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2997 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2998 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2999 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3000 if (c == 0x0640) type3 |= C3_KASHIDA;
3001 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3003 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3004 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3006 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3007 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3008 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3009 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3010 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3011 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3012 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3013 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3015 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3016 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3017 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3018 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3019 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3020 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3021 *chartype++ = type3;
3023 break;
3025 default:
3026 SetLastError( ERROR_INVALID_PARAMETER );
3027 return FALSE;
3029 return TRUE;
3033 /******************************************************************************
3034 * GetStringTypeExW (KERNEL32.@)
3036 * See GetStringTypeExA.
3038 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3040 /* locale is ignored for Unicode */
3041 return GetStringTypeW( type, src, count, chartype );
3045 /******************************************************************************
3046 * GetStringTypeA (KERNEL32.@)
3048 * Get characteristics of the characters making up a string.
3050 * PARAMS
3051 * locale [I] Locale Id for the string
3052 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3053 * src [I] String to analyse
3054 * count [I] Length of src in chars, or -1 if src is NUL terminated
3055 * chartype [O] Destination for the calculated characteristics
3057 * RETURNS
3058 * Success: TRUE. chartype is filled with the requested characteristics of each char
3059 * in src.
3060 * Failure: FALSE. Use GetLastError() to determine the cause.
3062 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3064 UINT cp;
3065 INT countW;
3066 LPWSTR srcW;
3067 BOOL ret = FALSE;
3069 if(count == -1) count = strlen(src) + 1;
3071 if (!(cp = get_lcid_codepage( locale )))
3073 FIXME("For locale %04x using current ANSI code page\n", locale);
3074 cp = GetACP();
3077 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3078 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3080 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3082 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3083 * string, with multibyte characters there maybe be more bytes in count
3084 * than character space in the buffer!
3086 ret = GetStringTypeW(type, srcW, countW, chartype);
3087 HeapFree(GetProcessHeap(), 0, srcW);
3089 return ret;
3092 /******************************************************************************
3093 * GetStringTypeExA (KERNEL32.@)
3095 * Get characteristics of the characters making up a string.
3097 * PARAMS
3098 * locale [I] Locale Id for the string
3099 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3100 * src [I] String to analyse
3101 * count [I] Length of src in chars, or -1 if src is NUL terminated
3102 * chartype [O] Destination for the calculated characteristics
3104 * RETURNS
3105 * Success: TRUE. chartype is filled with the requested characteristics of each char
3106 * in src.
3107 * Failure: FALSE. Use GetLastError() to determine the cause.
3109 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3111 return GetStringTypeA(locale, type, src, count, chartype);
3114 /* compose a full-width katakana. return consumed source characters. */
3115 static INT compose_katakana( LPCWSTR src, INT srclen, LPWSTR dst )
3117 const static BYTE katakana_map[] = {
3118 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
3119 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
3120 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
3121 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
3122 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
3123 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
3124 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
3125 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
3127 WCHAR before, dummy;
3129 if (!dst)
3130 dst = &dummy;
3132 switch (*src)
3134 case 0x309b: case 0x309c:
3135 *dst = *src - 2;
3136 return 1;
3137 case 0x30f0: case 0x30f1: case 0x30fd:
3138 *dst = *src;
3139 break;
3140 default:
3142 int shift = *src - 0xff61;
3143 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]) )
3144 return 0;
3145 else
3146 *dst = katakana_map[shift] | 0x3000;
3150 if (srclen <= 1)
3151 return 1;
3153 before = *dst;
3155 /* datakuten (voiced sound) */
3156 if (*(src + 1) == 0xff9e)
3158 if ((*src >= 0xff76 && *src <= 0xff84) ||
3159 (*src >= 0xff8a && *src <= 0xff8e) ||
3160 *src == 0x30fd)
3161 *dst += 1;
3162 else if (*src == 0xff73)
3163 *dst = 0x30f4; /* KATAKANA LETTER VU */
3164 else if (*src == 0xff9c)
3165 *dst = 0x30f7; /* KATAKANA LETTER VA */
3166 else if (*src == 0x30f0)
3167 *dst = 0x30f8; /* KATAKANA LETTER VI */
3168 else if (*src == 0x30f1)
3169 *dst = 0x30f9; /* KATAKANA LETTER VE */
3170 else if (*src == 0xff66)
3171 *dst = 0x30fa; /* KATAKANA LETTER VO */
3174 /* handakuten (semi-voiced sound) */
3175 if (*(src + 1) == 0xff9f)
3176 if (*src >= 0xff8a && *src <= 0xff8e)
3177 *dst += 2;
3179 return (*dst != before) ? 2 : 1;
3182 /* map one or two half-width characters to one full-width character */
3183 static INT map_to_fullwidth( LPCWSTR src, INT srclen, LPWSTR dst )
3185 INT n;
3187 if (*src <= '~' && *src > ' ' && *src != '\\')
3188 *dst = *src - 0x20 + 0xff00;
3189 else if (*src == ' ')
3190 *dst = 0x3000;
3191 else if (*src <= 0x00af && *src >= 0x00a2)
3193 const static BYTE misc_symbols_table[] = {
3194 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3195 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3197 if (misc_symbols_table[*src - 0x00a2])
3198 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3199 else
3200 *dst = *src;
3202 else if (*src == 0x20a9) /* WON SIGN */
3203 *dst = 0xffe6;
3204 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3205 return n;
3206 else if (*src >= 0xffa0 && *src <= 0xffdc)
3208 const static BYTE hangul_mapping_table[] = {
3209 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3210 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3211 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3212 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3213 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3214 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3215 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3216 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3219 if (hangul_mapping_table[*src - 0xffa0])
3220 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3221 else
3222 *dst = *src;
3224 else
3225 *dst = *src;
3227 return 1;
3230 /* decompose a full-width katakana character into one or two half-width characters. */
3231 static INT decompose_katakana( WCHAR c, LPWSTR dst, INT dstlen )
3233 const static BYTE katakana_map[] = {
3234 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3235 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3236 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3237 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3238 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3239 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3240 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3241 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3242 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3243 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3244 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3245 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3246 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3248 INT len = 0, shift = c - 0x3099;
3249 BYTE k;
3251 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]))
3252 return 0;
3254 k = katakana_map[shift];
3256 if (!k)
3258 if (dstlen > 0)
3259 *dst = c;
3260 len++;
3262 else if (k > 0x60)
3264 if (dstlen > 0)
3265 *dst = k | 0xff00;
3266 len++;
3268 else
3270 if (dstlen >= 2)
3272 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3273 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3275 len += 2;
3277 return len;
3280 /* map single full-width character to single or double half-width characters. */
3281 static INT map_to_halfwidth(WCHAR c, LPWSTR dst, INT dstlen)
3283 INT n = decompose_katakana(c, dst, dstlen);
3284 if (n > 0)
3285 return n;
3287 if (c == 0x3000)
3288 *dst = ' ';
3289 else if (c == 0x3001)
3290 *dst = 0xff64;
3291 else if (c == 0x3002)
3292 *dst = 0xff61;
3293 else if (c == 0x300c || c == 0x300d)
3294 *dst = (c - 0x300c) + 0xff62;
3295 else if (c >= 0x3131 && c <= 0x3163)
3297 *dst = c - 0x3131 + 0xffa1;
3298 if (*dst >= 0xffbf) *dst += 3;
3299 if (*dst >= 0xffc8) *dst += 2;
3300 if (*dst >= 0xffd0) *dst += 2;
3301 if (*dst >= 0xffd8) *dst += 2;
3303 else if (c == 0x3164)
3304 *dst = 0xffa0;
3305 else if (c == 0x2019)
3306 *dst = '\'';
3307 else if (c == 0x201d)
3308 *dst = '"';
3309 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3310 *dst = c - 0xff00 + 0x20;
3311 else if (c >= 0xffe0 && c <= 0xffe6)
3313 const static WCHAR misc_symbol_map[] = {
3314 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9
3316 *dst = misc_symbol_map[c - 0xffe0];
3318 else
3319 *dst = c;
3321 return 1;
3324 /*************************************************************************
3325 * LCMapStringEx (KERNEL32.@)
3327 * Map characters in a locale sensitive string.
3329 * PARAMS
3330 * name [I] Locale name for the conversion.
3331 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3332 * src [I] String to map
3333 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3334 * dst [O] Destination for mapped string
3335 * dstlen [I] Length of dst in characters
3336 * version [I] reserved, must be NULL
3337 * reserved [I] reserved, must be NULL
3338 * lparam [I] reserved, must be 0
3340 * RETURNS
3341 * Success: The length of the mapped string in dst, including the NUL terminator.
3342 * Failure: 0. Use GetLastError() to determine the cause.
3344 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3345 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3347 LPWSTR dst_ptr;
3348 INT len;
3350 if (version) FIXME("unsupported version structure %p\n", version);
3351 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3352 if (lparam)
3354 static int once;
3355 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3358 if (!src || !srclen || dstlen < 0)
3360 SetLastError(ERROR_INVALID_PARAMETER);
3361 return 0;
3364 /* mutually exclusive flags */
3365 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3366 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3367 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3368 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
3369 !flags)
3371 SetLastError(ERROR_INVALID_FLAGS);
3372 return 0;
3375 if (!dstlen) dst = NULL;
3377 if (flags & LCMAP_SORTKEY)
3379 INT ret;
3380 if (src == dst)
3382 SetLastError(ERROR_INVALID_FLAGS);
3383 return 0;
3386 if (srclen < 0) srclen = strlenW(src);
3388 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3389 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3391 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3392 if (ret == 0)
3393 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3394 else
3395 ret++;
3396 return ret;
3399 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3400 if (flags & SORT_STRINGSORT)
3402 SetLastError(ERROR_INVALID_FLAGS);
3403 return 0;
3405 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
3406 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
3407 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
3408 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
3410 SetLastError(ERROR_INVALID_FLAGS);
3411 return 0;
3414 if (srclen < 0) srclen = strlenW(src) + 1;
3416 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3417 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3419 if (!dst) /* return required string length */
3421 if (flags & NORM_IGNORESYMBOLS)
3423 for (len = 0; srclen; src++, srclen--)
3425 WCHAR wch = *src;
3426 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3427 * and skips white space and punctuation characters for
3428 * NORM_IGNORESYMBOLS.
3430 if (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE))
3431 continue;
3432 len++;
3435 else if (flags & LCMAP_FULLWIDTH)
3437 for (len = 0; srclen; src++, srclen--, len++)
3439 if (compose_katakana(src, srclen, NULL) == 2)
3441 src++;
3442 srclen--;
3446 else if (flags & LCMAP_HALFWIDTH)
3448 for (len = 0; srclen; src++, srclen--, len++)
3449 if (decompose_katakana(*src, NULL, 0) == 2)
3450 len++;
3452 else
3453 len = srclen;
3454 return len;
3457 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3459 SetLastError(ERROR_INVALID_FLAGS);
3460 return 0;
3463 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
3465 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3467 WCHAR wch = *src;
3468 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3469 continue;
3470 *dst_ptr++ = wch;
3471 len--;
3473 goto done;
3476 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
3478 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
3480 WCHAR wch;
3481 if (flags & LCMAP_FULLWIDTH)
3483 /* map half-width character to full-width one,
3484 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
3485 if (map_to_fullwidth(src, srclen, &wch) == 2)
3487 src++;
3488 srclen--;
3491 else
3492 wch = *src;
3494 if (flags & LCMAP_KATAKANA)
3496 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
3497 we can't use C3_HIRAGANA as some characters can't map to katakana */
3498 if ((wch >= 0x3041 && wch <= 0x3096) ||
3499 wch == 0x309D || wch == 0x309E)
3500 wch += 0x60;
3502 else if (flags & LCMAP_HIRAGANA)
3504 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
3505 we can't use C3_KATAKANA as some characters can't map to hiragana */
3506 if ((wch >= 0x30A1 && wch <= 0x30F6) ||
3507 wch == 0x30FD || wch == 0x30FE)
3508 wch -= 0x60;
3511 if (flags & LCMAP_HALFWIDTH)
3513 /* map full-width character to half-width one,
3514 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
3515 if (map_to_halfwidth(wch, dst_ptr, dstlen) == 2)
3517 dstlen--;
3518 dst_ptr++;
3519 if (!dstlen)
3521 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3522 return 0;
3526 else
3527 *dst_ptr = wch;
3529 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)))
3530 goto done;
3532 srclen = dst_ptr - dst;
3533 src = dst;
3536 if (flags & LCMAP_UPPERCASE)
3538 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3540 *dst_ptr++ = toupperW(*src);
3541 len--;
3544 else if (flags & LCMAP_LOWERCASE)
3546 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3548 *dst_ptr++ = tolowerW(*src);
3549 len--;
3552 else
3554 len = min(srclen, dstlen);
3555 memcpy(dst, src, len * sizeof(WCHAR));
3556 dst_ptr = dst + len;
3557 srclen -= len;
3560 done:
3561 if (srclen)
3563 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3564 return 0;
3567 return dst_ptr - dst;
3570 /*************************************************************************
3571 * LCMapStringW (KERNEL32.@)
3573 * See LCMapStringA.
3575 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3576 LPWSTR dst, INT dstlen)
3578 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3579 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3581 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3584 /*************************************************************************
3585 * LCMapStringA (KERNEL32.@)
3587 * Map characters in a locale sensitive string.
3589 * PARAMS
3590 * lcid [I] LCID for the conversion.
3591 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3592 * src [I] String to map
3593 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3594 * dst [O] Destination for mapped string
3595 * dstlen [I] Length of dst in characters
3597 * RETURNS
3598 * Success: The length of the mapped string in dst, including the NUL terminator.
3599 * Failure: 0. Use GetLastError() to determine the cause.
3601 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3602 LPSTR dst, INT dstlen)
3604 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3605 LPWSTR srcW, dstW;
3606 INT ret = 0, srclenW, dstlenW;
3607 UINT locale_cp = CP_ACP;
3609 if (!src || !srclen || dstlen < 0)
3611 SetLastError(ERROR_INVALID_PARAMETER);
3612 return 0;
3615 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3617 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3618 if (srclenW)
3619 srcW = bufW;
3620 else
3622 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3623 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3624 if (!srcW)
3626 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3627 return 0;
3629 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3632 if (flags & LCMAP_SORTKEY)
3634 if (src == dst)
3636 SetLastError(ERROR_INVALID_FLAGS);
3637 goto map_string_exit;
3639 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3640 if (ret == 0)
3641 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3642 else
3643 ret++;
3644 goto map_string_exit;
3647 if (flags & SORT_STRINGSORT)
3649 SetLastError(ERROR_INVALID_FLAGS);
3650 goto map_string_exit;
3653 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3654 if (!dstlenW)
3655 goto map_string_exit;
3657 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3658 if (!dstW)
3660 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3661 goto map_string_exit;
3664 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3665 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3666 HeapFree(GetProcessHeap(), 0, dstW);
3668 map_string_exit:
3669 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3670 return ret;
3673 /*************************************************************************
3674 * FoldStringA (KERNEL32.@)
3676 * Map characters in a string.
3678 * PARAMS
3679 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3680 * src [I] String to map
3681 * srclen [I] Length of src, or -1 if src is NUL terminated
3682 * dst [O] Destination for mapped string
3683 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3685 * RETURNS
3686 * Success: The length of the string written to dst, including the terminating NUL. If
3687 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3688 * and dst may be NULL.
3689 * Failure: 0. Use GetLastError() to determine the cause.
3691 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3692 LPSTR dst, INT dstlen)
3694 INT ret = 0, srclenW = 0;
3695 WCHAR *srcW = NULL, *dstW = NULL;
3697 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3699 SetLastError(ERROR_INVALID_PARAMETER);
3700 return 0;
3703 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3704 src, srclen, NULL, 0);
3705 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3707 if (!srcW)
3709 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3710 goto FoldStringA_exit;
3713 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3714 src, srclen, srcW, srclenW);
3716 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3718 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3719 if (ret && dstlen)
3721 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3723 if (!dstW)
3725 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3726 goto FoldStringA_exit;
3729 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3730 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3732 ret = 0;
3733 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3737 HeapFree(GetProcessHeap(), 0, dstW);
3739 FoldStringA_exit:
3740 HeapFree(GetProcessHeap(), 0, srcW);
3741 return ret;
3744 /*************************************************************************
3745 * FoldStringW (KERNEL32.@)
3747 * See FoldStringA.
3749 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3750 LPWSTR dst, INT dstlen)
3752 int ret;
3754 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3756 case 0:
3757 if (dwFlags)
3758 break;
3759 /* Fall through for dwFlags == 0 */
3760 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3761 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3762 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3763 SetLastError(ERROR_INVALID_FLAGS);
3764 return 0;
3767 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3769 SetLastError(ERROR_INVALID_PARAMETER);
3770 return 0;
3773 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3774 if (!ret)
3775 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3776 return ret;
3779 /******************************************************************************
3780 * CompareStringW (KERNEL32.@)
3782 * See CompareStringA.
3784 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3785 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3787 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3790 /******************************************************************************
3791 * CompareStringEx (KERNEL32.@)
3793 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3794 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3796 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3797 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3798 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3799 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3800 INT ret;
3801 static int once;
3803 if (version) FIXME("unexpected version parameter\n");
3804 if (reserved) FIXME("unexpected reserved value\n");
3805 if (lParam) FIXME("unexpected lParam\n");
3807 if (!str1 || !str2)
3809 SetLastError(ERROR_INVALID_PARAMETER);
3810 return 0;
3813 if (flags & ~(supported_flags|semistub_flags))
3815 SetLastError(ERROR_INVALID_FLAGS);
3816 return 0;
3819 if (flags & semistub_flags)
3821 if (!once++)
3822 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3825 if (len1 < 0) len1 = strlenW(str1);
3826 if (len2 < 0) len2 = strlenW(str2);
3828 ret = wine_compare_string(flags, str1, len1, str2, len2);
3830 if (ret) /* need to translate result */
3831 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3832 return CSTR_EQUAL;
3835 /******************************************************************************
3836 * CompareStringA (KERNEL32.@)
3838 * Compare two locale sensitive strings.
3840 * PARAMS
3841 * lcid [I] LCID for the comparison
3842 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3843 * str1 [I] First string to compare
3844 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3845 * str2 [I] Second string to compare
3846 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3848 * RETURNS
3849 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3850 * str1 is less than, equal to or greater than str2 respectively.
3851 * Failure: FALSE. Use GetLastError() to determine the cause.
3853 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3854 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3856 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3857 WCHAR *buf2W = buf1W + 130;
3858 LPWSTR str1W, str2W;
3859 INT len1W = 0, len2W = 0, ret;
3860 UINT locale_cp = CP_ACP;
3862 if (!str1 || !str2)
3864 SetLastError(ERROR_INVALID_PARAMETER);
3865 return 0;
3867 if (len1 < 0) len1 = strlen(str1);
3868 if (len2 < 0) len2 = strlen(str2);
3870 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3872 if (len1)
3874 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3875 if (len1W)
3876 str1W = buf1W;
3877 else
3879 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3880 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3881 if (!str1W)
3883 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3884 return 0;
3886 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3889 else
3891 len1W = 0;
3892 str1W = buf1W;
3895 if (len2)
3897 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3898 if (len2W)
3899 str2W = buf2W;
3900 else
3902 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3903 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3904 if (!str2W)
3906 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3907 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3908 return 0;
3910 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3913 else
3915 len2W = 0;
3916 str2W = buf2W;
3919 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3921 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3922 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3923 return ret;
3926 /******************************************************************************
3927 * CompareStringOrdinal (KERNEL32.@)
3929 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3931 int ret;
3933 if (!str1 || !str2)
3935 SetLastError(ERROR_INVALID_PARAMETER);
3936 return 0;
3938 if (len1 < 0) len1 = strlenW(str1);
3939 if (len2 < 0) len2 = strlenW(str2);
3941 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
3942 if (ret < 0) return CSTR_LESS_THAN;
3943 if (ret > 0) return CSTR_GREATER_THAN;
3944 return CSTR_EQUAL;
3947 /*************************************************************************
3948 * lstrcmp (KERNEL32.@)
3949 * lstrcmpA (KERNEL32.@)
3951 * Compare two strings using the current thread locale.
3953 * PARAMS
3954 * str1 [I] First string to compare
3955 * str2 [I] Second string to compare
3957 * RETURNS
3958 * Success: A number less than, equal to or greater than 0 depending on whether
3959 * str1 is less than, equal to or greater than str2 respectively.
3960 * Failure: FALSE. Use GetLastError() to determine the cause.
3962 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3964 int ret;
3966 if ((str1 == NULL) && (str2 == NULL)) return 0;
3967 if (str1 == NULL) return -1;
3968 if (str2 == NULL) return 1;
3970 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3971 if (ret) ret -= 2;
3973 return ret;
3976 /*************************************************************************
3977 * lstrcmpi (KERNEL32.@)
3978 * lstrcmpiA (KERNEL32.@)
3980 * Compare two strings using the current thread locale, ignoring case.
3982 * PARAMS
3983 * str1 [I] First string to compare
3984 * str2 [I] Second string to compare
3986 * RETURNS
3987 * Success: A number less than, equal to or greater than 0 depending on whether
3988 * str2 is less than, equal to or greater than str1 respectively.
3989 * Failure: FALSE. Use GetLastError() to determine the cause.
3991 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3993 int ret;
3995 if ((str1 == NULL) && (str2 == NULL)) return 0;
3996 if (str1 == NULL) return -1;
3997 if (str2 == NULL) return 1;
3999 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4000 if (ret) ret -= 2;
4002 return ret;
4005 /*************************************************************************
4006 * lstrcmpW (KERNEL32.@)
4008 * See lstrcmpA.
4010 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
4012 int ret;
4014 if ((str1 == NULL) && (str2 == NULL)) return 0;
4015 if (str1 == NULL) return -1;
4016 if (str2 == NULL) return 1;
4018 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
4019 if (ret) ret -= 2;
4021 return ret;
4024 /*************************************************************************
4025 * lstrcmpiW (KERNEL32.@)
4027 * See lstrcmpiA.
4029 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
4031 int ret;
4033 if ((str1 == NULL) && (str2 == NULL)) return 0;
4034 if (str1 == NULL) return -1;
4035 if (str2 == NULL) return 1;
4037 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
4038 if (ret) ret -= 2;
4040 return ret;
4043 /******************************************************************************
4044 * LOCALE_Init
4046 void LOCALE_Init(void)
4048 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
4049 const union cptable *unix_cp );
4051 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
4053 setlocale( LC_ALL, "" );
4055 #ifdef __APPLE__
4056 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
4057 if (!has_env("LANG"))
4059 const char* mac_locale = get_mac_locale();
4061 setenv( "LANG", mac_locale, 1 );
4062 if (setlocale( LC_ALL, "" ))
4063 TRACE( "setting LANG to '%s'\n", mac_locale );
4064 else
4066 /* no C library locale matching Mac locale; don't pass garbage to children */
4067 unsetenv("LANG");
4068 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
4071 #endif /* __APPLE__ */
4073 unix_cp = setup_unix_locales();
4074 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
4076 #ifdef __APPLE__
4077 if (!unix_cp)
4078 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
4079 #endif
4081 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
4082 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
4083 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
4085 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
4086 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
4087 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
4088 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
4089 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
4090 if (!unix_cp)
4091 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
4092 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
4094 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
4095 ansi_cptable = wine_cp_get_table( 1252 );
4096 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
4097 oem_cptable = wine_cp_get_table( 437 );
4098 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
4099 mac_cptable = wine_cp_get_table( 10000 );
4100 if (unix_cp != CP_UTF8)
4102 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
4103 unix_cptable = wine_cp_get_table( 28591 );
4106 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
4108 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
4109 ansi_cptable->info.codepage, oem_cptable->info.codepage,
4110 mac_cptable->info.codepage, unix_cp );
4112 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
4115 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4117 UNICODE_STRING keyName;
4118 OBJECT_ATTRIBUTES attr;
4119 HANDLE hkey;
4121 RtlInitUnicodeString( &keyName, szKeyName );
4122 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
4124 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
4125 hkey = 0;
4127 return hkey;
4130 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4131 LPWSTR szValueName, ULONG valueNameSize,
4132 LPWSTR szValueData, ULONG valueDataSize)
4134 BYTE buffer[80];
4135 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
4136 DWORD dwLen;
4138 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
4139 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
4140 info->NameLength > valueNameSize ||
4141 info->DataLength > valueDataSize)
4143 return FALSE;
4146 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
4148 memcpy( szValueName, info->Name, info->NameLength);
4149 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
4150 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
4151 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
4153 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
4154 return TRUE;
4157 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
4159 BYTE buffer[128];
4160 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
4161 DWORD dwSize = sizeof(buffer);
4162 UNICODE_STRING valueName;
4164 RtlInitUnicodeString( &valueName, szValueName );
4166 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
4167 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
4168 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
4169 info->DataLength == sizeof(DWORD))
4171 memcpy(lpVal, info->Data, sizeof(DWORD));
4172 return TRUE;
4175 return FALSE;
4178 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
4180 LANGID langId;
4181 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
4182 HRSRC hResource;
4183 BOOL bRet = FALSE;
4185 /* FIXME: Is it correct to use the system default langid? */
4186 langId = GetSystemDefaultLangID();
4188 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
4189 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
4191 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
4193 if (hResource)
4195 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
4197 if (hResDir)
4199 ULONG iResourceIndex = lgrpid & 0xf;
4200 LPCWSTR lpResEntry = LockResource( hResDir );
4201 ULONG i;
4203 for (i = 0; i < iResourceIndex; i++)
4204 lpResEntry += *lpResEntry + 1;
4206 if (*lpResEntry < nameSize)
4208 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
4209 szName[*lpResEntry] = '\0';
4210 bRet = TRUE;
4214 FreeResource( hResource );
4216 return bRet;
4219 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
4220 typedef struct
4222 LANGUAGEGROUP_ENUMPROCA procA;
4223 LANGUAGEGROUP_ENUMPROCW procW;
4224 DWORD dwFlags;
4225 LONG_PTR lParam;
4226 } ENUMLANGUAGEGROUP_CALLBACKS;
4228 /* Internal implementation of EnumSystemLanguageGroupsA/W */
4229 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
4231 WCHAR szNumber[10], szValue[4];
4232 HANDLE hKey;
4233 BOOL bContinue = TRUE;
4234 ULONG ulIndex = 0;
4236 if (!lpProcs)
4238 SetLastError(ERROR_INVALID_PARAMETER);
4239 return FALSE;
4242 switch (lpProcs->dwFlags)
4244 case 0:
4245 /* Default to LGRPID_INSTALLED */
4246 lpProcs->dwFlags = LGRPID_INSTALLED;
4247 /* Fall through... */
4248 case LGRPID_INSTALLED:
4249 case LGRPID_SUPPORTED:
4250 break;
4251 default:
4252 SetLastError(ERROR_INVALID_FLAGS);
4253 return FALSE;
4256 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4258 if (!hKey)
4259 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4261 while (bContinue)
4263 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4264 szValue, sizeof(szValue) ))
4266 BOOL bInstalled = szValue[0] == '1';
4267 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4269 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4270 bInstalled ? "" : "not ");
4272 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4274 WCHAR szGrpName[48];
4276 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
4277 szGrpName[0] = '\0';
4279 if (lpProcs->procW)
4280 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4281 lpProcs->lParam );
4282 else
4284 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4285 char szGrpNameA[48];
4287 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4288 * or whether the language names are ever localised. Assume CP_ACP.
4291 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4292 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4294 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4295 lpProcs->lParam );
4299 ulIndex++;
4301 else
4302 bContinue = FALSE;
4304 if (!bContinue)
4305 break;
4308 if (hKey)
4309 NtClose( hKey );
4311 return TRUE;
4314 /******************************************************************************
4315 * EnumSystemLanguageGroupsA (KERNEL32.@)
4317 * Call a users function for each language group available on the system.
4319 * PARAMS
4320 * pLangGrpEnumProc [I] Callback function to call for each language group
4321 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4322 * lParam [I] User parameter to pass to pLangGrpEnumProc
4324 * RETURNS
4325 * Success: TRUE.
4326 * Failure: FALSE. Use GetLastError() to determine the cause.
4328 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4329 DWORD dwFlags, LONG_PTR lParam)
4331 ENUMLANGUAGEGROUP_CALLBACKS procs;
4333 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4335 procs.procA = pLangGrpEnumProc;
4336 procs.procW = NULL;
4337 procs.dwFlags = dwFlags;
4338 procs.lParam = lParam;
4340 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4343 /******************************************************************************
4344 * EnumSystemLanguageGroupsW (KERNEL32.@)
4346 * See EnumSystemLanguageGroupsA.
4348 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4349 DWORD dwFlags, LONG_PTR lParam)
4351 ENUMLANGUAGEGROUP_CALLBACKS procs;
4353 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4355 procs.procA = NULL;
4356 procs.procW = pLangGrpEnumProc;
4357 procs.dwFlags = dwFlags;
4358 procs.lParam = lParam;
4360 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4363 /******************************************************************************
4364 * IsValidLanguageGroup (KERNEL32.@)
4366 * Determine if a language group is supported and/or installed.
4368 * PARAMS
4369 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4370 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4372 * RETURNS
4373 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4374 * FALSE otherwise.
4376 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4378 static const WCHAR szFormat[] = { '%','x','\0' };
4379 WCHAR szValueName[16], szValue[2];
4380 BOOL bSupported = FALSE, bInstalled = FALSE;
4381 HANDLE hKey;
4384 switch (dwFlags)
4386 case LGRPID_INSTALLED:
4387 case LGRPID_SUPPORTED:
4389 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4391 sprintfW( szValueName, szFormat, lgrpid );
4393 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4395 bSupported = TRUE;
4397 if (szValue[0] == '1')
4398 bInstalled = TRUE;
4401 if (hKey)
4402 NtClose( hKey );
4404 break;
4407 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4408 (dwFlags == LGRPID_INSTALLED && bInstalled))
4409 return TRUE;
4411 return FALSE;
4414 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4415 typedef struct
4417 LANGGROUPLOCALE_ENUMPROCA procA;
4418 LANGGROUPLOCALE_ENUMPROCW procW;
4419 DWORD dwFlags;
4420 LGRPID lgrpid;
4421 LONG_PTR lParam;
4422 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4424 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4425 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4427 static const WCHAR szAlternateSortsKeyName[] = {
4428 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4430 WCHAR szNumber[10], szValue[4];
4431 HANDLE hKey;
4432 BOOL bContinue = TRUE, bAlternate = FALSE;
4433 LGRPID lgrpid;
4434 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4436 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4438 SetLastError(ERROR_INVALID_PARAMETER);
4439 return FALSE;
4442 if (lpProcs->dwFlags)
4444 SetLastError(ERROR_INVALID_FLAGS);
4445 return FALSE;
4448 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4450 if (!hKey)
4451 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4453 while (bContinue)
4455 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4456 szValue, sizeof(szValue) ))
4458 lgrpid = strtoulW( szValue, NULL, 16 );
4460 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4461 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4463 if (lgrpid == lpProcs->lgrpid)
4465 LCID lcid;
4467 lcid = strtoulW( szNumber, NULL, 16 );
4469 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4470 * '00000437 ;Georgian'
4471 * At present we only pass the LCID string.
4474 if (lpProcs->procW)
4475 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4476 else
4478 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4480 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4482 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4486 ulIndex++;
4488 else
4490 /* Finished enumerating this key */
4491 if (!bAlternate)
4493 /* Enumerate alternate sorts also */
4494 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4495 bAlternate = TRUE;
4496 ulIndex = 0;
4498 else
4499 bContinue = FALSE; /* Finished both keys */
4502 if (!bContinue)
4503 break;
4506 if (hKey)
4507 NtClose( hKey );
4509 return TRUE;
4512 /******************************************************************************
4513 * EnumLanguageGroupLocalesA (KERNEL32.@)
4515 * Call a users function for every locale in a language group available on the system.
4517 * PARAMS
4518 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4519 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4520 * dwFlags [I] Reserved, set to 0
4521 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4523 * RETURNS
4524 * Success: TRUE.
4525 * Failure: FALSE. Use GetLastError() to determine the cause.
4527 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4528 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4530 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4532 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4534 callbacks.procA = pLangGrpLcEnumProc;
4535 callbacks.procW = NULL;
4536 callbacks.dwFlags = dwFlags;
4537 callbacks.lgrpid = lgrpid;
4538 callbacks.lParam = lParam;
4540 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4543 /******************************************************************************
4544 * EnumLanguageGroupLocalesW (KERNEL32.@)
4546 * See EnumLanguageGroupLocalesA.
4548 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4549 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4551 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4553 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4555 callbacks.procA = NULL;
4556 callbacks.procW = pLangGrpLcEnumProc;
4557 callbacks.dwFlags = dwFlags;
4558 callbacks.lgrpid = lgrpid;
4559 callbacks.lParam = lParam;
4561 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4564 /******************************************************************************
4565 * InvalidateNLSCache (KERNEL32.@)
4567 * Invalidate the cache of NLS values.
4569 * PARAMS
4570 * None.
4572 * RETURNS
4573 * Success: TRUE.
4574 * Failure: FALSE.
4576 BOOL WINAPI InvalidateNLSCache(void)
4578 FIXME("() stub\n");
4579 return FALSE;
4582 /******************************************************************************
4583 * GetUserGeoID (KERNEL32.@)
4585 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4587 GEOID ret = GEOID_NOT_AVAILABLE;
4588 static const WCHAR geoW[] = {'G','e','o',0};
4589 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4590 WCHAR bufferW[40], *end;
4591 DWORD count;
4592 HANDLE hkey, hSubkey = 0;
4593 UNICODE_STRING keyW;
4594 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4595 RtlInitUnicodeString( &keyW, nationW );
4596 count = sizeof(bufferW);
4598 if(!(hkey = create_registry_key())) return ret;
4600 switch( GeoClass ){
4601 case GEOCLASS_NATION:
4602 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4604 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4605 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4606 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4608 break;
4609 case GEOCLASS_REGION:
4610 FIXME("GEOCLASS_REGION not handled yet\n");
4611 break;
4614 NtClose(hkey);
4615 if (hSubkey) NtClose(hSubkey);
4616 return ret;
4619 /******************************************************************************
4620 * SetUserGeoID (KERNEL32.@)
4622 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4624 static const WCHAR geoW[] = {'G','e','o',0};
4625 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4626 static const WCHAR formatW[] = {'%','i',0};
4627 UNICODE_STRING nameW,keyW;
4628 WCHAR bufferW[10];
4629 OBJECT_ATTRIBUTES attr;
4630 HANDLE hkey;
4632 if(!(hkey = create_registry_key())) return FALSE;
4634 attr.Length = sizeof(attr);
4635 attr.RootDirectory = hkey;
4636 attr.ObjectName = &nameW;
4637 attr.Attributes = 0;
4638 attr.SecurityDescriptor = NULL;
4639 attr.SecurityQualityOfService = NULL;
4640 RtlInitUnicodeString( &nameW, geoW );
4641 RtlInitUnicodeString( &keyW, nationW );
4643 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4646 NtClose(attr.RootDirectory);
4647 return FALSE;
4650 sprintfW(bufferW, formatW, GeoID);
4651 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4652 NtClose(attr.RootDirectory);
4653 NtClose(hkey);
4654 return TRUE;
4657 typedef struct
4659 union
4661 UILANGUAGE_ENUMPROCA procA;
4662 UILANGUAGE_ENUMPROCW procW;
4663 } u;
4664 DWORD flags;
4665 LONG_PTR param;
4666 } ENUM_UILANG_CALLBACK;
4668 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4669 LPCSTR name, WORD LangID, LONG_PTR lParam )
4671 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4672 char buf[20];
4674 sprintf(buf, "%08x", (UINT)LangID);
4675 return enum_uilang->u.procA( buf, enum_uilang->param );
4678 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4679 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4681 static const WCHAR formatW[] = {'%','0','8','x',0};
4682 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4683 WCHAR buf[20];
4685 sprintfW( buf, formatW, (UINT)LangID );
4686 return enum_uilang->u.procW( buf, enum_uilang->param );
4689 /******************************************************************************
4690 * EnumUILanguagesA (KERNEL32.@)
4692 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4694 ENUM_UILANG_CALLBACK enum_uilang;
4696 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4698 if(!pUILangEnumProc) {
4699 SetLastError(ERROR_INVALID_PARAMETER);
4700 return FALSE;
4702 if(dwFlags) {
4703 SetLastError(ERROR_INVALID_FLAGS);
4704 return FALSE;
4707 enum_uilang.u.procA = pUILangEnumProc;
4708 enum_uilang.flags = dwFlags;
4709 enum_uilang.param = lParam;
4711 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4712 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4713 (LONG_PTR)&enum_uilang);
4714 return TRUE;
4717 /******************************************************************************
4718 * EnumUILanguagesW (KERNEL32.@)
4720 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4722 ENUM_UILANG_CALLBACK enum_uilang;
4724 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4727 if(!pUILangEnumProc) {
4728 SetLastError(ERROR_INVALID_PARAMETER);
4729 return FALSE;
4731 if(dwFlags) {
4732 SetLastError(ERROR_INVALID_FLAGS);
4733 return FALSE;
4736 enum_uilang.u.procW = pUILangEnumProc;
4737 enum_uilang.flags = dwFlags;
4738 enum_uilang.param = lParam;
4740 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4741 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4742 (LONG_PTR)&enum_uilang);
4743 return TRUE;
4746 enum locationkind {
4747 LOCATION_NATION = 0,
4748 LOCATION_REGION,
4749 LOCATION_BOTH
4752 struct geoinfo_t {
4753 GEOID id;
4754 WCHAR iso2W[3];
4755 WCHAR iso3W[4];
4756 GEOID parent;
4757 INT uncode;
4758 enum locationkind kind;
4761 static const struct geoinfo_t geoinfodata[] = {
4762 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4763 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4764 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4765 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4766 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4767 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4768 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4769 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4770 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4771 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4772 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4773 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4774 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4775 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4776 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4777 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4778 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4779 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4780 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4781 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4782 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4783 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4784 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4785 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4786 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4787 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4788 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4789 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4790 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4791 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4792 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4793 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4794 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4795 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4796 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4797 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4798 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4799 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4800 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4801 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4802 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4803 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4804 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4805 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4806 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4807 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4808 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4809 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4810 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4811 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4812 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4813 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4814 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4815 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4816 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4817 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4818 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4819 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4820 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4821 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4822 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4823 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4824 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4825 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4826 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4827 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4828 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4829 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4830 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4831 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4832 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4833 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4834 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4835 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4836 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4837 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4838 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4839 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4840 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4841 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4842 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4843 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4844 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4845 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4846 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4847 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4848 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4849 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4850 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4851 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4852 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4853 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4854 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4855 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4856 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4857 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4858 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4859 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4860 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4861 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4862 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4863 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4864 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4865 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4866 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4867 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4868 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4869 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4870 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4871 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4872 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4873 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4874 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4875 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4876 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4877 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4878 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4879 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4880 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4881 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4882 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4883 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4884 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4885 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4886 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4887 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4888 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4889 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4890 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4891 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4892 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4893 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4894 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4895 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4896 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4897 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4898 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4899 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4900 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4901 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4902 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4903 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4904 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4905 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4906 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4907 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4908 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4909 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4910 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4911 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4912 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4913 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4914 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4915 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4916 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4917 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4918 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4919 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4920 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4921 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4922 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4923 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4924 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4925 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4926 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4927 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4928 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4929 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4930 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4931 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4932 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4933 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4934 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4935 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4936 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4937 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4938 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4939 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4940 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4941 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4942 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4943 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4944 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4945 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4946 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4947 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4948 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4949 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4950 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4951 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4952 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4953 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4954 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4955 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4956 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4957 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4958 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4959 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4960 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4961 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4962 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4963 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4964 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4965 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4966 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4967 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4968 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4969 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4970 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4971 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4972 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4973 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4974 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4975 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4976 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4977 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4978 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4979 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4980 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4981 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4982 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4983 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4984 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4985 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4986 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4987 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4988 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4989 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4990 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4991 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4992 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4993 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4994 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4995 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4996 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4997 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4998 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4999 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
5000 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
5001 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
5002 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
5003 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
5004 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
5005 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
5006 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
5007 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
5008 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
5009 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
5010 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
5011 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
5012 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
5013 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
5014 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
5015 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
5016 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
5017 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
5018 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
5019 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
5020 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
5021 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
5022 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
5023 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
5024 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
5025 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
5026 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
5027 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
5028 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
5029 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
5030 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
5031 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
5032 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
5033 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
5034 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
5035 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
5036 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
5037 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
5038 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
5039 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
5040 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
5041 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
5042 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
5043 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
5044 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
5045 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
5046 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
5047 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
5048 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
5049 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
5050 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
5051 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
5052 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
5053 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
5054 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
5055 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
5056 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
5057 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
5058 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
5059 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
5060 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
5063 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
5065 int min, max;
5067 min = 0;
5068 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
5070 while (min <= max) {
5071 const struct geoinfo_t *ptr;
5072 int n = (min+max)/2;
5074 ptr = &geoinfodata[n];
5075 if (geoid == ptr->id)
5076 /* we don't need empty entries */
5077 return *ptr->iso2W ? ptr : NULL;
5079 if (ptr->id > geoid)
5080 max = n-1;
5081 else
5082 min = n+1;
5085 return NULL;
5088 /******************************************************************************
5089 * GetGeoInfoW (KERNEL32.@)
5091 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
5093 const struct geoinfo_t *ptr;
5094 const WCHAR *str = NULL;
5095 WCHAR buffW[12];
5096 LONG val = 0;
5097 INT len;
5099 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5101 if (!(ptr = get_geoinfo_dataptr(geoid))) {
5102 SetLastError(ERROR_INVALID_PARAMETER);
5103 return 0;
5106 switch (geotype) {
5107 case GEO_NATION:
5108 val = geoid;
5109 break;
5110 case GEO_ISO_UN_NUMBER:
5111 val = ptr->uncode;
5112 break;
5113 case GEO_PARENT:
5114 val = ptr->parent;
5115 break;
5116 case GEO_ISO2:
5117 case GEO_ISO3:
5119 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
5120 break;
5122 case GEO_RFC1766:
5123 case GEO_LCID:
5124 case GEO_FRIENDLYNAME:
5125 case GEO_OFFICIALNAME:
5126 case GEO_TIMEZONES:
5127 case GEO_OFFICIALLANGUAGES:
5128 case GEO_LATITUDE:
5129 case GEO_LONGITUDE:
5130 FIXME("type %d is not supported\n", geotype);
5131 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5132 return 0;
5133 default:
5134 WARN("unrecognized type %d\n", geotype);
5135 SetLastError(ERROR_INVALID_FLAGS);
5136 return 0;
5139 if (val) {
5140 static const WCHAR fmtW[] = {'%','d',0};
5141 sprintfW(buffW, fmtW, val);
5142 str = buffW;
5145 len = strlenW(str) + 1;
5146 if (!data || !data_len)
5147 return len;
5149 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
5150 if (data_len < len)
5151 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5152 return data_len < len ? 0 : len;
5155 /******************************************************************************
5156 * GetGeoInfoA (KERNEL32.@)
5158 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
5160 WCHAR *buffW;
5161 INT len;
5163 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5165 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
5166 if (!len)
5167 return 0;
5169 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
5170 if (!buffW)
5171 return 0;
5173 GetGeoInfoW(geoid, geotype, buffW, len, lang);
5174 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
5175 if (!data || !data_len) {
5176 HeapFree(GetProcessHeap(), 0, buffW);
5177 return len;
5180 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
5181 HeapFree(GetProcessHeap(), 0, buffW);
5183 if (data_len < len)
5184 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5185 return data_len < len ? 0 : len;
5188 /******************************************************************************
5189 * EnumSystemGeoID (KERNEL32.@)
5191 * Call a users function for every location available on the system.
5193 * PARAMS
5194 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
5195 * parent [I] GEOID for the parent
5196 * enumproc [I] Callback function to call for each location
5198 * RETURNS
5199 * Success: TRUE.
5200 * Failure: FALSE. Use GetLastError() to determine the cause.
5202 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
5204 INT i;
5206 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
5208 if (!enumproc) {
5209 SetLastError(ERROR_INVALID_PARAMETER);
5210 return FALSE;
5213 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
5214 SetLastError(ERROR_INVALID_FLAGS);
5215 return FALSE;
5218 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
5219 const struct geoinfo_t *ptr = &geoinfodata[i];
5221 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
5222 continue;
5224 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
5225 continue;
5227 if (parent && ptr->parent != parent)
5228 continue;
5230 if (!enumproc(ptr->id))
5231 return TRUE;
5234 return TRUE;
5237 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
5239 LCID userlcid;
5241 TRACE("%p, %d\n", localename, buffersize);
5243 userlcid = GetUserDefaultLCID();
5244 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
5247 /******************************************************************************
5248 * NormalizeString (KERNEL32.@)
5250 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
5251 LPWSTR lpDstString, INT cwDstLength)
5253 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
5254 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5255 return 0;
5258 /******************************************************************************
5259 * IsNormalizedString (KERNEL32.@)
5261 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
5263 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5264 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5265 return FALSE;
5268 enum {
5269 BASE = 36,
5270 TMIN = 1,
5271 TMAX = 26,
5272 SKEW = 38,
5273 DAMP = 700,
5274 INIT_BIAS = 72,
5275 INIT_N = 128
5278 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5280 INT k;
5282 delta /= (firsttime ? DAMP : 2);
5283 delta += delta/numpoints;
5285 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5286 delta /= BASE-TMIN;
5287 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5290 /******************************************************************************
5291 * IdnToAscii (KERNEL32.@)
5292 * Implementation of Punycode based on RFC 3492.
5294 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5295 LPWSTR lpASCIICharStr, INT cchASCIIChar)
5297 static const WCHAR prefixW[] = {'x','n','-','-'};
5299 WCHAR *norm_str;
5300 INT i, label_start, label_end, norm_len, out_label, out = 0;
5302 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5303 lpASCIICharStr, cchASCIIChar);
5305 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5306 if(!norm_len)
5307 return 0;
5308 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5309 if(!norm_str) {
5310 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5311 return 0;
5313 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5314 cchUnicodeChar, norm_str, norm_len);
5315 if(!norm_len) {
5316 HeapFree(GetProcessHeap(), 0, norm_str);
5317 return 0;
5320 for(label_start=0; label_start<norm_len;) {
5321 INT n = INIT_N, bias = INIT_BIAS;
5322 INT delta = 0, b = 0, h;
5324 out_label = out;
5325 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5326 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5327 if(norm_str[i] < 0x80)
5328 b++;
5329 label_end = i;
5331 if(b == label_end-label_start) {
5332 if(label_end < norm_len)
5333 b++;
5334 if(!lpASCIICharStr) {
5335 out += b;
5336 }else if(out+b <= cchASCIIChar) {
5337 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5338 out += b;
5339 }else {
5340 HeapFree(GetProcessHeap(), 0, norm_str);
5341 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5342 return 0;
5344 label_start = label_end+1;
5345 continue;
5348 if(!lpASCIICharStr) {
5349 out += 5+b; /* strlen(xn--...-) */
5350 }else if(out+5+b <= cchASCIIChar) {
5351 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5352 out += 4;
5353 for(i=label_start; i<label_end; i++)
5354 if(norm_str[i] < 0x80)
5355 lpASCIICharStr[out++] = norm_str[i];
5356 lpASCIICharStr[out++] = '-';
5357 }else {
5358 HeapFree(GetProcessHeap(), 0, norm_str);
5359 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5360 return 0;
5362 if(!b)
5363 out--;
5365 for(h=b; h<label_end-label_start;) {
5366 INT m = 0xffff, q, k;
5368 for(i=label_start; i<label_end; i++) {
5369 if(norm_str[i]>=n && m>norm_str[i])
5370 m = norm_str[i];
5372 delta += (m-n)*(h+1);
5373 n = m;
5375 for(i=label_start; i<label_end; i++) {
5376 if(norm_str[i] < n) {
5377 delta++;
5378 }else if(norm_str[i] == n) {
5379 for(q=delta, k=BASE; ; k+=BASE) {
5380 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5381 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5382 if(!lpASCIICharStr) {
5383 out++;
5384 }else if(out+1 <= cchASCIIChar) {
5385 lpASCIICharStr[out++] = disp<='z'-'a' ?
5386 'a'+disp : '0'+disp-'z'+'a'-1;
5387 }else {
5388 HeapFree(GetProcessHeap(), 0, norm_str);
5389 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5390 return 0;
5392 if(q < t)
5393 break;
5394 q = (q-t)/(BASE-t);
5396 bias = adapt(delta, h+1, h==b);
5397 delta = 0;
5398 h++;
5401 delta++;
5402 n++;
5405 if(out-out_label > 63) {
5406 HeapFree(GetProcessHeap(), 0, norm_str);
5407 SetLastError(ERROR_INVALID_NAME);
5408 return 0;
5411 if(label_end < norm_len) {
5412 if(!lpASCIICharStr) {
5413 out++;
5414 }else if(out+1 <= cchASCIIChar) {
5415 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5416 }else {
5417 HeapFree(GetProcessHeap(), 0, norm_str);
5418 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5419 return 0;
5422 label_start = label_end+1;
5425 HeapFree(GetProcessHeap(), 0, norm_str);
5426 return out;
5429 /******************************************************************************
5430 * IdnToNameprepUnicode (KERNEL32.@)
5432 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5433 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5435 enum {
5436 UNASSIGNED = 0x1,
5437 PROHIBITED = 0x2,
5438 BIDI_RAL = 0x4,
5439 BIDI_L = 0x8
5442 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5443 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5444 const WCHAR *ptr;
5445 WORD flags;
5446 WCHAR buf[64], *map_str, norm_str[64], ch;
5447 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5448 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5450 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5451 lpNameprepCharStr, cchNameprepChar);
5453 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5454 SetLastError(ERROR_INVALID_FLAGS);
5455 return 0;
5458 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5459 SetLastError(ERROR_INVALID_PARAMETER);
5460 return 0;
5463 if(cchUnicodeChar == -1)
5464 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5465 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5466 SetLastError(ERROR_INVALID_NAME);
5467 return 0;
5470 for(label_start=0; label_start<cchUnicodeChar;) {
5471 ascii_only = TRUE;
5472 for(i=label_start; i<cchUnicodeChar; i++) {
5473 ch = lpUnicodeCharStr[i];
5475 if(i!=cchUnicodeChar-1 && !ch) {
5476 SetLastError(ERROR_INVALID_NAME);
5477 return 0;
5479 /* check if ch is one of label separators defined in RFC3490 */
5480 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5481 break;
5483 if(ch > 0x7f) {
5484 ascii_only = FALSE;
5485 continue;
5488 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5489 continue;
5490 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5491 || (ch>='0' && ch<='9') || ch=='-')
5492 continue;
5494 SetLastError(ERROR_INVALID_NAME);
5495 return 0;
5497 label_end = i;
5498 /* last label may be empty */
5499 if(label_start==label_end && ch) {
5500 SetLastError(ERROR_INVALID_NAME);
5501 return 0;
5504 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5505 lpUnicodeCharStr[label_end-1]=='-')) {
5506 SetLastError(ERROR_INVALID_NAME);
5507 return 0;
5510 if(ascii_only) {
5511 /* maximal label length is 63 characters */
5512 if(label_end-label_start > 63) {
5513 SetLastError(ERROR_INVALID_NAME);
5514 return 0;
5516 if(label_end < cchUnicodeChar)
5517 label_end++;
5519 if(!lpNameprepCharStr) {
5520 out += label_end-label_start;
5521 }else if(out+label_end-label_start <= cchNameprepChar) {
5522 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5523 (label_end-label_start)*sizeof(WCHAR));
5524 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5525 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5526 out += label_end-label_start;
5527 }else {
5528 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5529 return 0;
5532 label_start = label_end;
5533 continue;
5536 map_len = 0;
5537 for(i=label_start; i<label_end; i++) {
5538 ch = lpUnicodeCharStr[i];
5539 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5540 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5542 if(!ptr[0]) map_len++;
5543 else if(!ptr[1]) map_len++;
5544 else if(!ptr[2]) map_len += 2;
5545 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5547 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5548 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5549 if(!map_str) {
5550 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5551 return 0;
5553 }else {
5554 map_str = buf;
5556 map_len = 0;
5557 for(i=label_start; i<label_end; i++) {
5558 ch = lpUnicodeCharStr[i];
5559 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5560 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5562 if(!ptr[0]) {
5563 map_str[map_len++] = ch;
5564 }else if(!ptr[1]) {
5565 map_str[map_len++] = ptr[0];
5566 }else if(!ptr[2]) {
5567 map_str[map_len++] = ptr[0];
5568 map_str[map_len++] = ptr[1];
5569 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5570 map_str[map_len++] = ptr[0];
5571 map_str[map_len++] = ptr[1];
5572 map_str[map_len++] = ptr[2];
5576 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5577 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5578 if(map_str != buf)
5579 HeapFree(GetProcessHeap(), 0, map_str);
5580 if(!norm_len) {
5581 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5582 SetLastError(ERROR_INVALID_NAME);
5583 return 0;
5586 if(label_end < cchUnicodeChar) {
5587 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5588 label_end++;
5591 if(!lpNameprepCharStr) {
5592 out += norm_len;
5593 }else if(out+norm_len <= cchNameprepChar) {
5594 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5595 out += norm_len;
5596 }else {
5597 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5598 return 0;
5601 have_bidi_ral = prohibit_bidi_ral = FALSE;
5602 mask = PROHIBITED;
5603 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5604 mask |= UNASSIGNED;
5605 for(i=0; i<norm_len; i++) {
5606 ch = norm_str[i];
5607 flags = get_table_entry( nameprep_char_type, ch );
5609 if(flags & mask) {
5610 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5611 : ERROR_NO_UNICODE_TRANSLATION);
5612 return 0;
5615 if(flags & BIDI_RAL)
5616 have_bidi_ral = TRUE;
5617 if(flags & BIDI_L)
5618 prohibit_bidi_ral = TRUE;
5621 if(have_bidi_ral) {
5622 ch = norm_str[0];
5623 flags = get_table_entry( nameprep_char_type, ch );
5624 if((flags & BIDI_RAL) == 0)
5625 prohibit_bidi_ral = TRUE;
5627 ch = norm_str[norm_len-1];
5628 flags = get_table_entry( nameprep_char_type, ch );
5629 if((flags & BIDI_RAL) == 0)
5630 prohibit_bidi_ral = TRUE;
5633 if(have_bidi_ral && prohibit_bidi_ral) {
5634 SetLastError(ERROR_INVALID_NAME);
5635 return 0;
5638 label_start = label_end;
5641 return out;
5644 /******************************************************************************
5645 * IdnToUnicode (KERNEL32.@)
5647 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5648 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5650 extern const unsigned short nameprep_char_type[];
5652 INT i, label_start, label_end, out_label, out = 0;
5653 WCHAR ch;
5655 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5656 lpUnicodeCharStr, cchUnicodeChar);
5658 for(label_start=0; label_start<cchASCIIChar;) {
5659 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5661 out_label = out;
5662 for(i=label_start; i<cchASCIIChar; i++) {
5663 ch = lpASCIICharStr[i];
5665 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5666 SetLastError(ERROR_INVALID_NAME);
5667 return 0;
5670 if(!ch || ch=='.')
5671 break;
5672 if(ch == '-')
5673 delim = i;
5675 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5676 continue;
5677 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5678 || (ch>='0' && ch<='9') || ch=='-')
5679 continue;
5681 SetLastError(ERROR_INVALID_NAME);
5682 return 0;
5684 label_end = i;
5685 /* last label may be empty */
5686 if(label_start==label_end && ch) {
5687 SetLastError(ERROR_INVALID_NAME);
5688 return 0;
5691 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5692 lpASCIICharStr[label_end-1]=='-')) {
5693 SetLastError(ERROR_INVALID_NAME);
5694 return 0;
5696 if(label_end-label_start > 63) {
5697 SetLastError(ERROR_INVALID_NAME);
5698 return 0;
5701 if(label_end-label_start<4 ||
5702 tolowerW(lpASCIICharStr[label_start])!='x' ||
5703 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5704 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5705 if(label_end < cchASCIIChar)
5706 label_end++;
5708 if(!lpUnicodeCharStr) {
5709 out += label_end-label_start;
5710 }else if(out+label_end-label_start <= cchUnicodeChar) {
5711 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5712 (label_end-label_start)*sizeof(WCHAR));
5713 out += label_end-label_start;
5714 }else {
5715 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5716 return 0;
5719 label_start = label_end;
5720 continue;
5723 if(delim == label_start+3)
5724 delim++;
5725 if(!lpUnicodeCharStr) {
5726 out += delim-label_start-4;
5727 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5728 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5729 (delim-label_start-4)*sizeof(WCHAR));
5730 out += delim-label_start-4;
5731 }else {
5732 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5733 return 0;
5735 if(out != out_label)
5736 delim++;
5738 for(i=delim; i<label_end;) {
5739 old_pos = pos;
5740 w = 1;
5741 for(k=BASE; ; k+=BASE) {
5742 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5743 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5744 SetLastError(ERROR_INVALID_NAME);
5745 return 0;
5747 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5748 pos += digit*w;
5749 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5750 if(digit < t)
5751 break;
5752 w *= BASE-t;
5754 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5755 n += pos/(out-out_label+1);
5756 pos %= out-out_label+1;
5758 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5759 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5760 SetLastError(ERROR_INVALID_NAME);
5761 return 0;
5763 if(!lpUnicodeCharStr) {
5764 out++;
5765 }else if(out+1 <= cchASCIIChar) {
5766 memmove(lpUnicodeCharStr+out_label+pos+1,
5767 lpUnicodeCharStr+out_label+pos,
5768 (out-out_label-pos)*sizeof(WCHAR));
5769 lpUnicodeCharStr[out_label+pos] = n;
5770 out++;
5771 }else {
5772 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5773 return 0;
5775 pos++;
5778 if(out-out_label > 63) {
5779 SetLastError(ERROR_INVALID_NAME);
5780 return 0;
5783 if(label_end < cchASCIIChar) {
5784 if(!lpUnicodeCharStr) {
5785 out++;
5786 }else if(out+1 <= cchUnicodeChar) {
5787 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5788 }else {
5789 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5790 return 0;
5793 label_start = label_end+1;
5796 return out;
5800 /******************************************************************************
5801 * GetUserPreferredUILanguages (KERNEL32.@)
5803 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5805 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5806 return FALSE;
5809 /******************************************************************************
5810 * GetFileMUIPath (KERNEL32.@)
5813 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5814 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5816 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5817 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5819 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5821 return FALSE;
5824 /******************************************************************************
5825 * GetFileMUIInfo (KERNEL32.@)
5828 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5830 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5832 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5833 return FALSE;