kernel32: Simplify Linux implementation of GlobalMemoryStatusEx.
[wine.git] / dlls / kernel32 / locale.c
blobca5975137f19e660b1b5a74b2c417536ce05f8b8
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;
362 static WORD get_default_sublang(LCID lang)
364 switch (PRIMARYLANGID(lang))
366 case LANG_SPANISH:
367 return SUBLANG_SPANISH_MODERN;
368 case LANG_CHINESE:
369 return SUBLANG_CHINESE_SIMPLIFIED;
370 default:
371 return SUBLANG_DEFAULT;
375 /***********************************************************************
376 * find_locale_id_callback
378 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
379 LPCWSTR name, WORD LangID, LPARAM lParam )
381 struct locale_name *data = (struct locale_name *)lParam;
382 WCHAR buffer[128];
383 int matches = 0;
384 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
386 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
388 /* first check exact name */
389 if (data->win_name[0] &&
390 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
391 buffer, sizeof(buffer)/sizeof(WCHAR) ))
393 if (!strcmpiW( data->win_name, buffer ))
395 matches = 4; /* everything matches */
396 goto done;
400 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
401 buffer, sizeof(buffer)/sizeof(WCHAR) ))
402 return TRUE;
403 if (strcmpiW( buffer, data->lang )) return TRUE;
404 matches++; /* language name matched */
406 if (data->country)
408 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
409 buffer, sizeof(buffer)/sizeof(WCHAR) ))
411 if (strcmpiW( buffer, data->country )) goto done;
412 matches++; /* country name matched */
415 else /* match default language */
417 if (SUBLANGID(LangID) == get_default_sublang( LangID )) matches++;
420 if (data->codepage)
422 UINT unix_cp;
423 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
424 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
426 if (unix_cp == data->codepage) matches++;
430 /* FIXME: check sort order */
432 done:
433 if (matches > data->matches)
435 data->lcid = lcid;
436 data->matches = matches;
438 return (data->matches < 4); /* no need to continue for perfect match */
442 /***********************************************************************
443 * parse_locale_name
445 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
446 * Unix format is: lang[_country][.charset][@modifier]
447 * Windows format is: lang[-script][-country][_modifier]
449 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
451 static const WCHAR sepW[] = {'-','_','.','@',0};
452 static const WCHAR winsepW[] = {'-','_',0};
453 static const WCHAR posixW[] = {'P','O','S','I','X',0};
454 static const WCHAR cW[] = {'C',0};
455 static const WCHAR latinW[] = {'l','a','t','i','n',0};
456 static const WCHAR latnW[] = {'-','L','a','t','n',0};
457 WCHAR *p;
459 TRACE("%s\n", debugstr_w(str));
461 name->country = name->charset = name->script = name->modifier = NULL;
462 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
463 name->matches = 0;
464 name->codepage = 0;
465 name->win_name[0] = 0;
466 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
468 if (!*name->lang)
470 name->lcid = LOCALE_INVARIANT;
471 name->matches = 4;
472 return;
475 if (!(p = strpbrkW( name->lang, sepW )))
477 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
479 name->matches = 4; /* perfect match for default English lcid */
480 return;
482 strcpyW( name->win_name, name->lang );
484 else if (*p == '-') /* Windows format */
486 strcpyW( name->win_name, name->lang );
487 *p++ = 0;
488 name->country = p;
489 if (!(p = strpbrkW( p, winsepW ))) goto done;
490 if (*p == '-')
492 *p++ = 0;
493 name->script = name->country;
494 name->country = p;
495 if (!(p = strpbrkW( p, winsepW ))) goto done;
497 *p++ = 0;
498 name->modifier = p;
500 else /* Unix format */
502 if (*p == '_')
504 *p++ = 0;
505 name->country = p;
506 p = strpbrkW( p, sepW + 2 );
508 if (p && *p == '.')
510 *p++ = 0;
511 name->charset = p;
512 p = strchrW( p, '@' );
514 if (p)
516 *p++ = 0;
517 name->modifier = p;
520 if (name->charset)
521 name->codepage = find_charset( name->charset );
523 /* rebuild a Windows name if possible */
525 if (name->charset) goto done; /* can't specify charset in Windows format */
526 if (name->modifier && strcmpW( name->modifier, latinW ))
527 goto done; /* only Latn script supported for now */
528 strcpyW( name->win_name, name->lang );
529 if (name->modifier) strcatW( name->win_name, latnW );
530 if (name->country)
532 p = name->win_name + strlenW(name->win_name);
533 *p++ = '-';
534 strcpyW( p, name->country );
537 done:
538 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
539 find_locale_id_callback, (LPARAM)name );
543 /***********************************************************************
544 * convert_default_lcid
546 * Get the default LCID to use for a given lctype in GetLocaleInfo.
548 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
550 if (lcid == LOCALE_SYSTEM_DEFAULT ||
551 lcid == LOCALE_USER_DEFAULT ||
552 lcid == LOCALE_NEUTRAL)
554 LCID default_id = 0;
556 switch(lctype & 0xffff)
558 case LOCALE_SSORTNAME:
559 default_id = lcid_LC_COLLATE;
560 break;
562 case LOCALE_FONTSIGNATURE:
563 case LOCALE_IDEFAULTANSICODEPAGE:
564 case LOCALE_IDEFAULTCODEPAGE:
565 case LOCALE_IDEFAULTEBCDICCODEPAGE:
566 case LOCALE_IDEFAULTMACCODEPAGE:
567 case LOCALE_IDEFAULTUNIXCODEPAGE:
568 default_id = lcid_LC_CTYPE;
569 break;
571 case LOCALE_ICURRDIGITS:
572 case LOCALE_ICURRENCY:
573 case LOCALE_IINTLCURRDIGITS:
574 case LOCALE_INEGCURR:
575 case LOCALE_INEGSEPBYSPACE:
576 case LOCALE_INEGSIGNPOSN:
577 case LOCALE_INEGSYMPRECEDES:
578 case LOCALE_IPOSSEPBYSPACE:
579 case LOCALE_IPOSSIGNPOSN:
580 case LOCALE_IPOSSYMPRECEDES:
581 case LOCALE_SCURRENCY:
582 case LOCALE_SINTLSYMBOL:
583 case LOCALE_SMONDECIMALSEP:
584 case LOCALE_SMONGROUPING:
585 case LOCALE_SMONTHOUSANDSEP:
586 case LOCALE_SNATIVECURRNAME:
587 default_id = lcid_LC_MONETARY;
588 break;
590 case LOCALE_IDIGITS:
591 case LOCALE_IDIGITSUBSTITUTION:
592 case LOCALE_ILZERO:
593 case LOCALE_INEGNUMBER:
594 case LOCALE_SDECIMAL:
595 case LOCALE_SGROUPING:
596 case LOCALE_SNAN:
597 case LOCALE_SNATIVEDIGITS:
598 case LOCALE_SNEGATIVESIGN:
599 case LOCALE_SNEGINFINITY:
600 case LOCALE_SPOSINFINITY:
601 case LOCALE_SPOSITIVESIGN:
602 case LOCALE_STHOUSAND:
603 default_id = lcid_LC_NUMERIC;
604 break;
606 case LOCALE_ICALENDARTYPE:
607 case LOCALE_ICENTURY:
608 case LOCALE_IDATE:
609 case LOCALE_IDAYLZERO:
610 case LOCALE_IFIRSTDAYOFWEEK:
611 case LOCALE_IFIRSTWEEKOFYEAR:
612 case LOCALE_ILDATE:
613 case LOCALE_IMONLZERO:
614 case LOCALE_IOPTIONALCALENDAR:
615 case LOCALE_ITIME:
616 case LOCALE_ITIMEMARKPOSN:
617 case LOCALE_ITLZERO:
618 case LOCALE_S1159:
619 case LOCALE_S2359:
620 case LOCALE_SABBREVDAYNAME1:
621 case LOCALE_SABBREVDAYNAME2:
622 case LOCALE_SABBREVDAYNAME3:
623 case LOCALE_SABBREVDAYNAME4:
624 case LOCALE_SABBREVDAYNAME5:
625 case LOCALE_SABBREVDAYNAME6:
626 case LOCALE_SABBREVDAYNAME7:
627 case LOCALE_SABBREVMONTHNAME1:
628 case LOCALE_SABBREVMONTHNAME2:
629 case LOCALE_SABBREVMONTHNAME3:
630 case LOCALE_SABBREVMONTHNAME4:
631 case LOCALE_SABBREVMONTHNAME5:
632 case LOCALE_SABBREVMONTHNAME6:
633 case LOCALE_SABBREVMONTHNAME7:
634 case LOCALE_SABBREVMONTHNAME8:
635 case LOCALE_SABBREVMONTHNAME9:
636 case LOCALE_SABBREVMONTHNAME10:
637 case LOCALE_SABBREVMONTHNAME11:
638 case LOCALE_SABBREVMONTHNAME12:
639 case LOCALE_SABBREVMONTHNAME13:
640 case LOCALE_SDATE:
641 case LOCALE_SDAYNAME1:
642 case LOCALE_SDAYNAME2:
643 case LOCALE_SDAYNAME3:
644 case LOCALE_SDAYNAME4:
645 case LOCALE_SDAYNAME5:
646 case LOCALE_SDAYNAME6:
647 case LOCALE_SDAYNAME7:
648 case LOCALE_SDURATION:
649 case LOCALE_SLONGDATE:
650 case LOCALE_SMONTHNAME1:
651 case LOCALE_SMONTHNAME2:
652 case LOCALE_SMONTHNAME3:
653 case LOCALE_SMONTHNAME4:
654 case LOCALE_SMONTHNAME5:
655 case LOCALE_SMONTHNAME6:
656 case LOCALE_SMONTHNAME7:
657 case LOCALE_SMONTHNAME8:
658 case LOCALE_SMONTHNAME9:
659 case LOCALE_SMONTHNAME10:
660 case LOCALE_SMONTHNAME11:
661 case LOCALE_SMONTHNAME12:
662 case LOCALE_SMONTHNAME13:
663 case LOCALE_SSHORTDATE:
664 case LOCALE_SSHORTESTDAYNAME1:
665 case LOCALE_SSHORTESTDAYNAME2:
666 case LOCALE_SSHORTESTDAYNAME3:
667 case LOCALE_SSHORTESTDAYNAME4:
668 case LOCALE_SSHORTESTDAYNAME5:
669 case LOCALE_SSHORTESTDAYNAME6:
670 case LOCALE_SSHORTESTDAYNAME7:
671 case LOCALE_STIME:
672 case LOCALE_STIMEFORMAT:
673 case LOCALE_SYEARMONTH:
674 default_id = lcid_LC_TIME;
675 break;
677 case LOCALE_IPAPERSIZE:
678 default_id = lcid_LC_PAPER;
679 break;
681 case LOCALE_IMEASURE:
682 default_id = lcid_LC_MEASUREMENT;
683 break;
685 case LOCALE_ICOUNTRY:
686 default_id = lcid_LC_TELEPHONE;
687 break;
689 if (default_id) lcid = default_id;
691 return ConvertDefaultLocale( lcid );
694 /***********************************************************************
695 * is_genitive_name_supported
697 * Determine could LCTYPE basically support genitive name form or not.
699 static BOOL is_genitive_name_supported( LCTYPE lctype )
701 switch(lctype & 0xffff)
703 case LOCALE_SMONTHNAME1:
704 case LOCALE_SMONTHNAME2:
705 case LOCALE_SMONTHNAME3:
706 case LOCALE_SMONTHNAME4:
707 case LOCALE_SMONTHNAME5:
708 case LOCALE_SMONTHNAME6:
709 case LOCALE_SMONTHNAME7:
710 case LOCALE_SMONTHNAME8:
711 case LOCALE_SMONTHNAME9:
712 case LOCALE_SMONTHNAME10:
713 case LOCALE_SMONTHNAME11:
714 case LOCALE_SMONTHNAME12:
715 case LOCALE_SMONTHNAME13:
716 return TRUE;
717 default:
718 return FALSE;
722 /***********************************************************************
723 * create_registry_key
725 * Create the Control Panel\\International registry key.
727 static inline HANDLE create_registry_key(void)
729 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
730 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
731 OBJECT_ATTRIBUTES attr;
732 UNICODE_STRING nameW;
733 HANDLE cpl_key, hkey = 0;
735 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
737 attr.Length = sizeof(attr);
738 attr.RootDirectory = hkey;
739 attr.ObjectName = &nameW;
740 attr.Attributes = 0;
741 attr.SecurityDescriptor = NULL;
742 attr.SecurityQualityOfService = NULL;
743 RtlInitUnicodeString( &nameW, cplW );
745 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
747 NtClose( attr.RootDirectory );
748 attr.RootDirectory = cpl_key;
749 RtlInitUnicodeString( &nameW, intlW );
750 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
752 NtClose( attr.RootDirectory );
753 return hkey;
757 /* update the registry settings for a given locale parameter */
758 /* return TRUE if an update was needed */
759 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
760 const LCTYPE *values, UINT nb_values )
762 static const WCHAR formatW[] = { '%','0','8','x',0 };
763 WCHAR bufferW[40];
764 UNICODE_STRING nameW;
765 DWORD count, i;
767 RtlInitUnicodeString( &nameW, name );
768 count = sizeof(bufferW);
769 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
771 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
772 LPCWSTR text = (LPCWSTR)info->Data;
774 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
775 TRACE( "updating registry, locale %s changed %s -> %08x\n",
776 debugstr_w(name), debugstr_w(text), lcid );
778 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
779 sprintfW( bufferW, formatW, lcid );
780 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
782 for (i = 0; i < nb_values; i++)
784 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
785 sizeof(bufferW)/sizeof(WCHAR) );
786 SetLocaleInfoW( lcid, values[i], bufferW );
788 return TRUE;
792 /***********************************************************************
793 * LOCALE_InitRegistry
795 * Update registry contents on startup if the user locale has changed.
796 * This simulates the action of the Windows control panel.
798 void LOCALE_InitRegistry(void)
800 static const WCHAR acpW[] = {'A','C','P',0};
801 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
802 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
803 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
804 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
805 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
806 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
807 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
808 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
809 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
810 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
811 static const struct
813 LPCWSTR name;
814 USHORT value;
815 } update_cp_values[] = {
816 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
817 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
818 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
820 static const LCTYPE lc_messages_values[] = {
821 LOCALE_SABBREVLANGNAME,
822 LOCALE_SCOUNTRY,
823 LOCALE_SLIST };
824 static const LCTYPE lc_monetary_values[] = {
825 LOCALE_SCURRENCY,
826 LOCALE_ICURRENCY,
827 LOCALE_INEGCURR,
828 LOCALE_ICURRDIGITS,
829 LOCALE_ILZERO,
830 LOCALE_SMONDECIMALSEP,
831 LOCALE_SMONGROUPING,
832 LOCALE_SMONTHOUSANDSEP };
833 static const LCTYPE lc_numeric_values[] = {
834 LOCALE_SDECIMAL,
835 LOCALE_STHOUSAND,
836 LOCALE_IDIGITS,
837 LOCALE_IDIGITSUBSTITUTION,
838 LOCALE_SNATIVEDIGITS,
839 LOCALE_INEGNUMBER,
840 LOCALE_SNEGATIVESIGN,
841 LOCALE_SPOSITIVESIGN,
842 LOCALE_SGROUPING };
843 static const LCTYPE lc_time_values[] = {
844 LOCALE_S1159,
845 LOCALE_S2359,
846 LOCALE_STIME,
847 LOCALE_ITIME,
848 LOCALE_ITLZERO,
849 LOCALE_SSHORTDATE,
850 LOCALE_SLONGDATE,
851 LOCALE_SDATE,
852 LOCALE_ITIMEMARKPOSN,
853 LOCALE_ICALENDARTYPE,
854 LOCALE_IFIRSTDAYOFWEEK,
855 LOCALE_IFIRSTWEEKOFYEAR,
856 LOCALE_STIMEFORMAT,
857 LOCALE_SYEARMONTH,
858 LOCALE_IDATE };
859 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
860 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
861 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
863 UNICODE_STRING nameW;
864 WCHAR bufferW[80];
865 DWORD count, i;
866 HANDLE hkey;
867 LCID lcid = GetUserDefaultLCID();
869 if (!(hkey = create_registry_key()))
870 return; /* don't do anything if we can't create the registry key */
872 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
873 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
874 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
875 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
876 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
877 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
878 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
879 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
880 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
881 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
882 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
883 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
884 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
885 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
887 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
889 static const WCHAR codepageW[] =
890 {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
891 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
892 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
894 OBJECT_ATTRIBUTES attr;
895 HANDLE nls_key;
896 DWORD len = 14;
898 RtlInitUnicodeString( &nameW, codepageW );
899 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
900 while (codepageW[len])
902 nameW.Length = len * sizeof(WCHAR);
903 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
904 NtClose( nls_key );
905 len++;
906 while (codepageW[len] && codepageW[len] != '\\') len++;
908 nameW.Length = len * sizeof(WCHAR);
909 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
911 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
913 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
914 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
915 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
916 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
918 NtClose( nls_key );
922 NtClose( hkey );
926 #ifdef __APPLE__
927 /***********************************************************************
928 * get_mac_locale
930 * Return a locale identifier string reflecting the Mac locale, in a form
931 * that parse_locale_name() will understand. So, strip out unusual
932 * things like script, variant, etc. Or, rather, just construct it as
933 * <lang>[_<country>].UTF-8.
935 static const char* get_mac_locale(void)
937 static char mac_locale[50];
939 if (!mac_locale[0])
941 CFLocaleRef locale = CFLocaleCopyCurrent();
942 CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
943 CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
944 CFStringRef locale_string;
946 if (country)
947 locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
948 else
949 locale_string = CFStringCreateCopy(NULL, lang);
951 CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
952 strcat(mac_locale, ".UTF-8");
954 CFRelease(locale);
955 CFRelease(locale_string);
958 return mac_locale;
962 /***********************************************************************
963 * has_env
965 static BOOL has_env(const char* name)
967 const char* value = getenv( name );
968 return value && value[0];
970 #endif
973 /***********************************************************************
974 * get_locale
976 * Get the locale identifier for a given category. On most platforms,
977 * this is just a thin wrapper around setlocale(). On OS X, though, it
978 * is common for the Mac locale settings to not be supported by the C
979 * library. So, we sometimes override the result with the Mac locale.
981 static const char* get_locale(int category, const char* category_name)
983 const char* ret = setlocale(category, NULL);
985 #ifdef __APPLE__
986 /* If LC_ALL is set, respect it as a user override.
987 If LC_* is set, respect it as a user override, except if it's LC_CTYPE
988 and equal to UTF-8. That's because, when the Mac locale isn't supported
989 by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
990 parse_locale_name() doesn't handle that properly, so we override that
991 with the Mac locale (which uses UTF-8 for the charset, anyway).
992 Otherwise:
993 For LC_MESSAGES, we override the C library because the user language
994 setting is separate from the locale setting on which LANG was based.
995 If the C library didn't get anything better from LANG than C or POSIX,
996 override that. That probably means the Mac locale isn't supported by
997 the C library. */
998 if (!has_env( "LC_ALL" ) &&
999 ((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
1000 (!has_env( category_name ) &&
1001 (category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
1003 const char* override = get_mac_locale();
1005 if (category == LC_MESSAGES)
1007 /* Retrieve the preferred language as chosen in System Preferences. */
1008 static char messages_locale[50];
1010 if (!messages_locale[0])
1012 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
1013 if (preferred_langs && CFArrayGetCount( preferred_langs ))
1015 CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
1016 CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
1017 if (components)
1019 CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
1020 CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
1021 CFLocaleRef locale = NULL;
1022 CFStringRef locale_string;
1024 if (!country)
1026 locale = CFLocaleCopyCurrent();
1027 country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
1030 if (country)
1031 locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
1032 else
1033 locale_string = CFStringCreateCopy( NULL, lang );
1034 CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
1035 strcat( messages_locale, ".UTF-8" );
1037 CFRelease( locale_string );
1038 if (locale) CFRelease( locale );
1039 CFRelease( components );
1042 if (preferred_langs)
1043 CFRelease( preferred_langs );
1046 if (messages_locale[0])
1047 override = messages_locale;
1050 TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
1051 ret = override;
1053 #endif
1055 return ret;
1059 /***********************************************************************
1060 * setup_unix_locales
1062 static UINT setup_unix_locales(void)
1064 struct locale_name locale_name;
1065 WCHAR buffer[128], ctype_buff[128];
1066 const char *locale;
1067 UINT unix_cp = 0;
1069 if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
1071 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
1072 parse_locale_name( ctype_buff, &locale_name );
1073 lcid_LC_CTYPE = locale_name.lcid;
1074 unix_cp = locale_name.codepage;
1076 if (!lcid_LC_CTYPE) /* this one needs a default value */
1077 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
1079 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
1080 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
1082 #define GET_UNIX_LOCALE(cat) do \
1083 if ((locale = get_locale( cat, #cat ))) \
1085 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
1086 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
1087 else { \
1088 parse_locale_name( buffer, &locale_name ); \
1089 lcid_##cat = locale_name.lcid; \
1090 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
1091 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
1093 } while (0)
1095 GET_UNIX_LOCALE( LC_COLLATE );
1096 GET_UNIX_LOCALE( LC_MESSAGES );
1097 GET_UNIX_LOCALE( LC_MONETARY );
1098 GET_UNIX_LOCALE( LC_NUMERIC );
1099 GET_UNIX_LOCALE( LC_TIME );
1100 #ifdef LC_PAPER
1101 GET_UNIX_LOCALE( LC_PAPER );
1102 #endif
1103 #ifdef LC_MEASUREMENT
1104 GET_UNIX_LOCALE( LC_MEASUREMENT );
1105 #endif
1106 #ifdef LC_TELEPHONE
1107 GET_UNIX_LOCALE( LC_TELEPHONE );
1108 #endif
1110 #undef GET_UNIX_LOCALE
1112 return unix_cp;
1116 /***********************************************************************
1117 * GetUserDefaultLangID (KERNEL32.@)
1119 * Get the default language Id for the current user.
1121 * PARAMS
1122 * None.
1124 * RETURNS
1125 * The current LANGID of the default language for the current user.
1127 LANGID WINAPI GetUserDefaultLangID(void)
1129 return LANGIDFROMLCID(GetUserDefaultLCID());
1133 /***********************************************************************
1134 * GetSystemDefaultLangID (KERNEL32.@)
1136 * Get the default language Id for the system.
1138 * PARAMS
1139 * None.
1141 * RETURNS
1142 * The current LANGID of the default language for the system.
1144 LANGID WINAPI GetSystemDefaultLangID(void)
1146 return LANGIDFROMLCID(GetSystemDefaultLCID());
1150 /***********************************************************************
1151 * GetUserDefaultLCID (KERNEL32.@)
1153 * Get the default locale Id for the current user.
1155 * PARAMS
1156 * None.
1158 * RETURNS
1159 * The current LCID of the default locale for the current user.
1161 LCID WINAPI GetUserDefaultLCID(void)
1163 LCID lcid;
1164 NtQueryDefaultLocale( TRUE, &lcid );
1165 return lcid;
1169 /***********************************************************************
1170 * GetSystemDefaultLCID (KERNEL32.@)
1172 * Get the default locale Id for the system.
1174 * PARAMS
1175 * None.
1177 * RETURNS
1178 * The current LCID of the default locale for the system.
1180 LCID WINAPI GetSystemDefaultLCID(void)
1182 LCID lcid;
1183 NtQueryDefaultLocale( FALSE, &lcid );
1184 return lcid;
1187 /***********************************************************************
1188 * GetSystemDefaultLocaleName (KERNEL32.@)
1190 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1192 LCID lcid = GetSystemDefaultLCID();
1193 return LCIDToLocaleName(lcid, localename, len, 0);
1196 static BOOL get_dummy_preferred_ui_language( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1198 LCTYPE type;
1199 int lsize;
1201 FIXME("(0x%x %p %p %p) returning a dummy value (current locale)\n", flags, count, buffer, size);
1203 if (flags & MUI_LANGUAGE_ID)
1204 type = LOCALE_ILANGUAGE;
1205 else
1206 type = LOCALE_SNAME;
1208 lsize = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, NULL, 0);
1209 if (!lsize)
1211 /* keep last error from callee */
1212 return FALSE;
1214 lsize++;
1215 if (!*size)
1217 *size = lsize;
1218 *count = 1;
1219 return TRUE;
1222 if (lsize > *size)
1224 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1225 return FALSE;
1228 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, type, buffer, *size))
1230 /* keep last error from callee */
1231 return FALSE;
1234 buffer[lsize-1] = 0;
1235 *size = lsize;
1236 *count = 1;
1237 TRACE("returned variable content: %d, \"%s\", %d\n", *count, debugstr_w(buffer), *size);
1238 return TRUE;
1242 /***********************************************************************
1243 * GetSystemPreferredUILanguages (KERNEL32.@)
1245 BOOL WINAPI GetSystemPreferredUILanguages(DWORD flags, ULONG* count, WCHAR* buffer, ULONG* size)
1247 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS))
1249 SetLastError(ERROR_INVALID_PARAMETER);
1250 return FALSE;
1252 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1254 SetLastError(ERROR_INVALID_PARAMETER);
1255 return FALSE;
1257 if (*size && !buffer)
1259 SetLastError(ERROR_INVALID_PARAMETER);
1260 return FALSE;
1263 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1266 /***********************************************************************
1267 * SetThreadPreferredUILanguages (KERNEL32.@)
1269 BOOL WINAPI SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, PULONG count )
1271 FIXME( "%u, %p, %p\n", flags, buffer, count );
1272 return TRUE;
1275 /***********************************************************************
1276 * GetThreadPreferredUILanguages (KERNEL32.@)
1278 BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buf, ULONG *size )
1280 FIXME( "%08x, %p, %p %p\n", flags, count, buf, size );
1281 return get_dummy_preferred_ui_language( flags, count, buf, size );
1284 /******************************************************************************
1285 * GetUserPreferredUILanguages (KERNEL32.@)
1287 BOOL WINAPI GetUserPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
1289 TRACE( "%u %p %p %p\n", flags, count, buffer, size );
1291 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID))
1293 SetLastError(ERROR_INVALID_PARAMETER);
1294 return FALSE;
1296 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID))
1298 SetLastError(ERROR_INVALID_PARAMETER);
1299 return FALSE;
1301 if (*size && !buffer)
1303 SetLastError(ERROR_INVALID_PARAMETER);
1304 return FALSE;
1307 return get_dummy_preferred_ui_language( flags, count, buffer, size );
1310 /***********************************************************************
1311 * GetUserDefaultUILanguage (KERNEL32.@)
1313 * Get the default user interface language Id for the current user.
1315 * PARAMS
1316 * None.
1318 * RETURNS
1319 * The current LANGID of the default UI language for the current user.
1321 LANGID WINAPI GetUserDefaultUILanguage(void)
1323 LANGID lang;
1324 NtQueryDefaultUILanguage( &lang );
1325 return lang;
1329 /***********************************************************************
1330 * GetSystemDefaultUILanguage (KERNEL32.@)
1332 * Get the default user interface language Id for the system.
1334 * PARAMS
1335 * None.
1337 * RETURNS
1338 * The current LANGID of the default UI language for the system. This is
1339 * typically the same language used during the installation process.
1341 LANGID WINAPI GetSystemDefaultUILanguage(void)
1343 LANGID lang;
1344 NtQueryInstallUILanguage( &lang );
1345 return lang;
1349 /***********************************************************************
1350 * LocaleNameToLCID (KERNEL32.@)
1352 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1354 struct locale_name locale_name;
1356 if (flags) FIXME( "unsupported flags %x\n", flags );
1358 if (name == LOCALE_NAME_USER_DEFAULT)
1359 return GetUserDefaultLCID();
1361 /* string parsing */
1362 parse_locale_name( name, &locale_name );
1364 TRACE( "found lcid %x for %s, matches %d\n",
1365 locale_name.lcid, debugstr_w(name), locale_name.matches );
1367 if (!locale_name.matches)
1369 SetLastError(ERROR_INVALID_PARAMETER);
1370 return 0;
1373 if (locale_name.matches == 1)
1374 WARN( "locale %s not recognized, defaulting to %s\n",
1375 debugstr_w(name), debugstr_w(locale_name.lang) );
1377 return locale_name.lcid;
1381 /***********************************************************************
1382 * LCIDToLocaleName (KERNEL32.@)
1384 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1386 if (flags) FIXME( "unsupported flags %x\n", flags );
1388 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1392 /******************************************************************************
1393 * get_locale_registry_value
1395 * Gets the registry value name and cache for a given lctype.
1397 static struct registry_value *get_locale_registry_value( DWORD lctype )
1399 int i;
1400 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1401 if (registry_values[i].lctype == lctype)
1402 return &registry_values[i];
1403 return NULL;
1407 /******************************************************************************
1408 * get_registry_locale_info
1410 * Retrieve user-modified locale info from the registry.
1411 * Return length, 0 on error, -1 if not found.
1413 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1415 DWORD size;
1416 INT ret;
1417 HANDLE hkey;
1418 NTSTATUS status;
1419 UNICODE_STRING nameW;
1420 KEY_VALUE_PARTIAL_INFORMATION *info;
1421 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1423 RtlEnterCriticalSection( &cache_section );
1425 if (!registry_value->cached_value)
1427 if (!(hkey = create_registry_key()))
1429 RtlLeaveCriticalSection( &cache_section );
1430 return -1;
1433 RtlInitUnicodeString( &nameW, registry_value->name );
1434 size = info_size + len * sizeof(WCHAR);
1436 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1438 NtClose( hkey );
1439 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1440 RtlLeaveCriticalSection( &cache_section );
1441 return 0;
1444 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1446 /* try again with a bigger buffer when we have to return the correct size */
1447 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1449 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1450 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1452 info = new_info;
1453 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1457 NtClose( hkey );
1459 if (!status)
1461 INT length = (size - info_size) / sizeof(WCHAR);
1462 LPWSTR cached_value;
1464 if (!length || ((WCHAR *)&info->Data)[length-1])
1465 length++;
1467 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1469 if (!cached_value)
1471 HeapFree( GetProcessHeap(), 0, info );
1472 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1473 RtlLeaveCriticalSection( &cache_section );
1474 return 0;
1477 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1478 cached_value[length-1] = 0;
1479 HeapFree( GetProcessHeap(), 0, info );
1480 registry_value->cached_value = cached_value;
1482 else
1484 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1486 ret = (size - info_size) / sizeof(WCHAR);
1488 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1490 ret = -1;
1492 else
1494 SetLastError( RtlNtStatusToDosError(status) );
1495 ret = 0;
1497 HeapFree( GetProcessHeap(), 0, info );
1498 RtlLeaveCriticalSection( &cache_section );
1499 return ret;
1503 ret = lstrlenW( registry_value->cached_value ) + 1;
1505 if (buffer)
1507 if (ret > len)
1509 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1510 ret = 0;
1512 else
1514 lstrcpyW( buffer, registry_value->cached_value );
1518 RtlLeaveCriticalSection( &cache_section );
1520 return ret;
1524 /******************************************************************************
1525 * GetLocaleInfoA (KERNEL32.@)
1527 * Get information about an aspect of a locale.
1529 * PARAMS
1530 * lcid [I] LCID of the locale
1531 * lctype [I] LCTYPE_ flags from "winnls.h"
1532 * buffer [O] Destination for the information
1533 * len [I] Length of buffer in characters
1535 * RETURNS
1536 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1537 * with the information.
1538 * Failure: 0. Use GetLastError() to determine the cause.
1540 * NOTES
1541 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1542 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1543 * which is a bit string.
1545 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1547 WCHAR *bufferW;
1548 INT lenW, ret;
1550 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1552 if (len < 0 || (len && !buffer))
1554 SetLastError( ERROR_INVALID_PARAMETER );
1555 return 0;
1557 if (((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_SSHORTTIME) ||
1558 (lctype & LOCALE_RETURN_GENITIVE_NAMES))
1560 SetLastError( ERROR_INVALID_FLAGS );
1561 return 0;
1564 if (!len) buffer = NULL;
1566 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1568 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1570 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1571 return 0;
1573 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1575 if ((lctype & LOCALE_RETURN_NUMBER) ||
1576 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1578 /* it's not an ASCII string, just bytes */
1579 ret *= sizeof(WCHAR);
1580 if (buffer)
1582 if (ret <= len) memcpy( buffer, bufferW, ret );
1583 else
1585 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1586 ret = 0;
1590 else
1592 UINT codepage = CP_ACP;
1593 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1594 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1597 HeapFree( GetProcessHeap(), 0, bufferW );
1598 return ret;
1601 static int get_value_base_by_lctype( LCTYPE lctype )
1603 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1606 /******************************************************************************
1607 * GetLocaleInfoW (KERNEL32.@)
1609 * See GetLocaleInfoA.
1611 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1613 LANGID lang_id;
1614 HRSRC hrsrc;
1615 HGLOBAL hmem;
1616 INT ret;
1617 UINT lcflags;
1618 const WCHAR *p;
1619 unsigned int i;
1621 if (len < 0 || (len && !buffer))
1623 SetLastError( ERROR_INVALID_PARAMETER );
1624 return 0;
1626 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1627 !is_genitive_name_supported( lctype ))
1629 SetLastError( ERROR_INVALID_FLAGS );
1630 return 0;
1633 if (!len) buffer = NULL;
1635 lcid = convert_default_lcid( lcid, lctype );
1637 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1638 lctype &= 0xffff;
1640 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1642 /* first check for overrides in the registry */
1644 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1645 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1647 struct registry_value *value = get_locale_registry_value(lctype);
1649 if (value)
1651 if (lcflags & LOCALE_RETURN_NUMBER)
1653 WCHAR tmp[16];
1654 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1655 if (ret > 0)
1657 WCHAR *end;
1658 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1659 if (*end) /* invalid number */
1661 SetLastError( ERROR_INVALID_FLAGS );
1662 return 0;
1664 ret = sizeof(UINT)/sizeof(WCHAR);
1665 if (!buffer) return ret;
1666 if (ret > len)
1668 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1669 return 0;
1671 memcpy( buffer, &number, sizeof(number) );
1674 else ret = get_registry_locale_info( value, buffer, len );
1676 if (ret != -1) return ret;
1680 /* now load it from kernel resources */
1682 lang_id = LANGIDFROMLCID( lcid );
1684 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1685 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1686 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), get_default_sublang( lang_id ));
1688 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1689 ULongToPtr((lctype >> 4) + 1), lang_id )))
1691 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1692 return 0;
1694 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1695 return 0;
1697 p = LockResource( hmem );
1698 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1700 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1701 else if (is_genitive_name_supported( lctype ) && *p)
1703 /* genitive form's stored after a null separator from a nominative */
1704 for (i = 1; i <= *p; i++) if (!p[i]) break;
1706 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1708 ret = *p - i + 1;
1709 p += i;
1711 else ret = i;
1713 else
1714 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1716 if (!buffer) return ret;
1718 if (ret > len)
1720 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1721 return 0;
1724 if (lcflags & LOCALE_RETURN_NUMBER)
1726 UINT number;
1727 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1728 if (!tmp) return 0;
1729 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1730 tmp[*p] = 0;
1731 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1732 if (!*end)
1733 memcpy( buffer, &number, sizeof(number) );
1734 else /* invalid number */
1736 SetLastError( ERROR_INVALID_FLAGS );
1737 ret = 0;
1739 HeapFree( GetProcessHeap(), 0, tmp );
1741 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1742 lcid, lctype, buffer, len, number );
1744 else
1746 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1747 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1749 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1750 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1752 return ret;
1755 /******************************************************************************
1756 * GetLocaleInfoEx (KERNEL32.@)
1758 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1760 LCID lcid = LocaleNameToLCID(locale, 0);
1762 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1764 if (!lcid) return 0;
1766 /* special handling for neutral locale names */
1767 if (info == LOCALE_SNAME && locale && strlenW(locale) == 2)
1769 if (len && len < 3)
1771 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1772 return 0;
1775 if (len) strcpyW(buffer, locale);
1776 return 3;
1779 return GetLocaleInfoW(lcid, info, buffer, len);
1782 /******************************************************************************
1783 * SetLocaleInfoA [KERNEL32.@]
1785 * Set information about an aspect of a locale.
1787 * PARAMS
1788 * lcid [I] LCID of the locale
1789 * lctype [I] LCTYPE_ flags from "winnls.h"
1790 * data [I] Information to set
1792 * RETURNS
1793 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1794 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1795 * Failure: FALSE. Use GetLastError() to determine the cause.
1797 * NOTES
1798 * - Values are only be set for the current user locale; the system locale
1799 * settings cannot be changed.
1800 * - Any settings changed by this call are lost when the locale is changed by
1801 * the control panel (in Wine, this happens every time you change LANG).
1802 * - The native implementation of this function does not check that lcid matches
1803 * the current user locale, and simply sets the new values. Wine warns you in
1804 * this case, but behaves the same.
1806 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1808 UINT codepage = CP_ACP;
1809 WCHAR *strW;
1810 DWORD len;
1811 BOOL ret;
1813 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1815 if (!data)
1817 SetLastError( ERROR_INVALID_PARAMETER );
1818 return FALSE;
1820 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1821 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1823 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1824 return FALSE;
1826 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1827 ret = SetLocaleInfoW( lcid, lctype, strW );
1828 HeapFree( GetProcessHeap(), 0, strW );
1829 return ret;
1833 /******************************************************************************
1834 * SetLocaleInfoW (KERNEL32.@)
1836 * See SetLocaleInfoA.
1838 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1840 struct registry_value *value;
1841 static const WCHAR intlW[] = {'i','n','t','l',0 };
1842 UNICODE_STRING valueW;
1843 NTSTATUS status;
1844 HANDLE hkey;
1846 lctype &= 0xffff;
1847 value = get_locale_registry_value( lctype );
1849 if (!data || !value)
1851 SetLastError( ERROR_INVALID_PARAMETER );
1852 return FALSE;
1855 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1857 SetLastError( ERROR_INVALID_FLAGS );
1858 return FALSE;
1861 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1863 /* FIXME: should check that data to set is sane */
1865 /* FIXME: profile functions should map to registry */
1866 WriteProfileStringW( intlW, value->name, data );
1868 if (!(hkey = create_registry_key())) return FALSE;
1869 RtlInitUnicodeString( &valueW, value->name );
1870 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1872 RtlEnterCriticalSection( &cache_section );
1873 HeapFree( GetProcessHeap(), 0, value->cached_value );
1874 value->cached_value = NULL;
1875 RtlLeaveCriticalSection( &cache_section );
1877 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1879 /* Set I-value from S value */
1880 WCHAR *lpD, *lpM, *lpY;
1881 WCHAR szBuff[2];
1883 lpD = strrchrW(data, 'd');
1884 lpM = strrchrW(data, 'M');
1885 lpY = strrchrW(data, 'y');
1887 if (lpD <= lpM)
1889 szBuff[0] = '1'; /* D-M-Y */
1891 else
1893 if (lpY <= lpM)
1894 szBuff[0] = '2'; /* Y-M-D */
1895 else
1896 szBuff[0] = '0'; /* M-D-Y */
1899 szBuff[1] = '\0';
1901 if (lctype == LOCALE_SSHORTDATE)
1902 lctype = LOCALE_IDATE;
1903 else
1904 lctype = LOCALE_ILDATE;
1906 value = get_locale_registry_value( lctype );
1908 WriteProfileStringW( intlW, value->name, szBuff );
1910 RtlInitUnicodeString( &valueW, value->name );
1911 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1913 RtlEnterCriticalSection( &cache_section );
1914 HeapFree( GetProcessHeap(), 0, value->cached_value );
1915 value->cached_value = NULL;
1916 RtlLeaveCriticalSection( &cache_section );
1919 NtClose( hkey );
1921 if (status) SetLastError( RtlNtStatusToDosError(status) );
1922 return !status;
1926 /******************************************************************************
1927 * GetACP (KERNEL32.@)
1929 * Get the current Ansi code page Id for the system.
1931 * PARAMS
1932 * None.
1934 * RETURNS
1935 * The current Ansi code page identifier for the system.
1937 UINT WINAPI GetACP(void)
1939 assert( ansi_cptable );
1940 return ansi_cptable->info.codepage;
1944 /******************************************************************************
1945 * SetCPGlobal (KERNEL32.@)
1947 * Set the current Ansi code page Id for the system.
1949 * PARAMS
1950 * acp [I] code page ID to be the new ACP.
1952 * RETURNS
1953 * The previous ACP.
1955 UINT WINAPI SetCPGlobal( UINT acp )
1957 UINT ret = GetACP();
1958 const union cptable *new_cptable = wine_cp_get_table( acp );
1960 if (new_cptable) ansi_cptable = new_cptable;
1961 return ret;
1965 /***********************************************************************
1966 * GetOEMCP (KERNEL32.@)
1968 * Get the current OEM code page Id for the system.
1970 * PARAMS
1971 * None.
1973 * RETURNS
1974 * The current OEM code page identifier for the system.
1976 UINT WINAPI GetOEMCP(void)
1978 assert( oem_cptable );
1979 return oem_cptable->info.codepage;
1983 /***********************************************************************
1984 * IsValidCodePage (KERNEL32.@)
1986 * Determine if a given code page identifier is valid.
1988 * PARAMS
1989 * codepage [I] Code page Id to verify.
1991 * RETURNS
1992 * TRUE, If codepage is valid and available on the system,
1993 * FALSE otherwise.
1995 BOOL WINAPI IsValidCodePage( UINT codepage )
1997 switch(codepage) {
1998 case CP_UTF7:
1999 case CP_UTF8:
2000 return TRUE;
2001 default:
2002 return wine_cp_get_table( codepage ) != NULL;
2007 /***********************************************************************
2008 * IsDBCSLeadByteEx (KERNEL32.@)
2010 * Determine if a character is a lead byte in a given code page.
2012 * PARAMS
2013 * codepage [I] Code page for the test.
2014 * testchar [I] Character to test
2016 * RETURNS
2017 * TRUE, if testchar is a lead byte in codepage,
2018 * FALSE otherwise.
2020 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
2022 const union cptable *table = get_codepage_table( codepage );
2023 return table && wine_is_dbcs_leadbyte( table, testchar );
2027 /***********************************************************************
2028 * IsDBCSLeadByte (KERNEL32.@)
2029 * IsDBCSLeadByte (KERNEL.207)
2031 * Determine if a character is a lead byte.
2033 * PARAMS
2034 * testchar [I] Character to test
2036 * RETURNS
2037 * TRUE, if testchar is a lead byte in the ANSI code page,
2038 * FALSE otherwise.
2040 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
2042 if (!ansi_cptable) return FALSE;
2043 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
2047 /***********************************************************************
2048 * GetCPInfo (KERNEL32.@)
2050 * Get information about a code page.
2052 * PARAMS
2053 * codepage [I] Code page number
2054 * cpinfo [O] Destination for code page information
2056 * RETURNS
2057 * Success: TRUE. cpinfo is updated with the information about codepage.
2058 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2060 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
2062 const union cptable *table;
2064 if (!cpinfo)
2066 SetLastError( ERROR_INVALID_PARAMETER );
2067 return FALSE;
2070 if (!(table = get_codepage_table( codepage )))
2072 switch(codepage)
2074 case CP_UTF7:
2075 case CP_UTF8:
2076 cpinfo->DefaultChar[0] = 0x3f;
2077 cpinfo->DefaultChar[1] = 0;
2078 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2079 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
2080 return TRUE;
2083 SetLastError( ERROR_INVALID_PARAMETER );
2084 return FALSE;
2086 if (table->info.def_char & 0xff00)
2088 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
2089 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
2091 else
2093 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
2094 cpinfo->DefaultChar[1] = 0;
2096 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
2097 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
2098 else
2099 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
2101 return TRUE;
2104 /***********************************************************************
2105 * GetCPInfoExA (KERNEL32.@)
2107 * Get extended information about a code page.
2109 * PARAMS
2110 * codepage [I] Code page number
2111 * dwFlags [I] Reserved, must to 0.
2112 * cpinfo [O] Destination for code page information
2114 * RETURNS
2115 * Success: TRUE. cpinfo is updated with the information about codepage.
2116 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
2118 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
2120 CPINFOEXW cpinfoW;
2122 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
2123 return FALSE;
2125 /* the layout is the same except for CodePageName */
2126 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
2127 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
2128 return TRUE;
2131 /***********************************************************************
2132 * GetCPInfoExW (KERNEL32.@)
2134 * Unicode version of GetCPInfoExA.
2136 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
2138 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
2139 return FALSE;
2141 switch(codepage)
2143 case CP_UTF7:
2145 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
2147 cpinfo->CodePage = CP_UTF7;
2148 cpinfo->UnicodeDefaultChar = 0x3f;
2149 strcpyW(cpinfo->CodePageName, utf7);
2150 break;
2153 case CP_UTF8:
2155 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
2157 cpinfo->CodePage = CP_UTF8;
2158 cpinfo->UnicodeDefaultChar = 0x3f;
2159 strcpyW(cpinfo->CodePageName, utf8);
2160 break;
2163 default:
2165 const union cptable *table = get_codepage_table( codepage );
2167 cpinfo->CodePage = table->info.codepage;
2168 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
2169 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
2170 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
2171 break;
2174 return TRUE;
2177 /***********************************************************************
2178 * EnumSystemCodePagesA (KERNEL32.@)
2180 * Call a user defined function for every code page installed on the system.
2182 * PARAMS
2183 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
2184 * flags [I] Reserved, set to 0.
2186 * RETURNS
2187 * TRUE, If all code pages have been enumerated, or
2188 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
2190 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
2192 const union cptable *table;
2193 char buffer[10];
2194 int index = 0;
2196 for (;;)
2198 if (!(table = wine_cp_enum_table( index++ ))) break;
2199 sprintf( buffer, "%d", table->info.codepage );
2200 if (!lpfnCodePageEnum( buffer )) break;
2202 return TRUE;
2206 /***********************************************************************
2207 * EnumSystemCodePagesW (KERNEL32.@)
2209 * See EnumSystemCodePagesA.
2211 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
2213 const union cptable *table;
2214 WCHAR buffer[10], *p;
2215 int page, index = 0;
2217 for (;;)
2219 if (!(table = wine_cp_enum_table( index++ ))) break;
2220 p = buffer + sizeof(buffer)/sizeof(WCHAR);
2221 *--p = 0;
2222 page = table->info.codepage;
2225 *--p = '0' + (page % 10);
2226 page /= 10;
2227 } while( page );
2228 if (!lpfnCodePageEnum( p )) break;
2230 return TRUE;
2234 /***********************************************************************
2235 * utf7_write_w
2237 * Helper for utf7_mbstowcs
2239 * RETURNS
2240 * TRUE on success, FALSE on error
2242 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
2244 if (dstlen > 0)
2246 if (*index >= dstlen)
2247 return FALSE;
2249 dst[*index] = character;
2252 (*index)++;
2254 return TRUE;
2257 /***********************************************************************
2258 * utf7_mbstowcs
2260 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2262 * RETURNS
2263 * On success, the number of characters written
2264 * On dst buffer overflow, -1
2266 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2268 static const signed char base64_decoding_table[] =
2270 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2271 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2272 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2273 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2274 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2275 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2276 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2277 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2280 const char *source_end = src + srclen;
2281 int dest_index = 0;
2283 DWORD byte_pair = 0;
2284 short offset = 0;
2286 while (src < source_end)
2288 if (*src == '+')
2290 src++;
2291 if (src >= source_end)
2292 break;
2294 if (*src == '-')
2296 /* just a plus sign escaped as +- */
2297 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2298 return -1;
2299 src++;
2300 continue;
2305 signed char sextet = *src;
2306 if (sextet == '-')
2308 /* skip over the dash and end base64 decoding
2309 * the current, unfinished byte pair is discarded */
2310 src++;
2311 offset = 0;
2312 break;
2314 if (sextet < 0)
2316 /* the next character of src is < 0 and therefore not part of a base64 sequence
2317 * the current, unfinished byte pair is NOT discarded in this case
2318 * this is probably a bug in Windows */
2319 break;
2322 sextet = base64_decoding_table[sextet];
2323 if (sextet == -1)
2325 /* -1 means that the next character of src is not part of a base64 sequence
2326 * in other words, all sextets in this base64 sequence have been processed
2327 * the current, unfinished byte pair is discarded */
2328 offset = 0;
2329 break;
2332 byte_pair = (byte_pair << 6) | sextet;
2333 offset += 6;
2335 if (offset >= 16)
2337 /* this byte pair is done */
2338 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2339 return -1;
2340 offset -= 16;
2343 src++;
2345 while (src < source_end);
2347 else
2349 /* we have to convert to unsigned char in case *src < 0 */
2350 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2351 return -1;
2352 src++;
2356 return dest_index;
2359 /***********************************************************************
2360 * MultiByteToWideChar (KERNEL32.@)
2362 * Convert a multibyte character string into a Unicode string.
2364 * PARAMS
2365 * page [I] Codepage character set to convert from
2366 * flags [I] Character mapping flags
2367 * src [I] Source string buffer
2368 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2369 * dst [O] Destination buffer
2370 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2372 * RETURNS
2373 * Success: If dstlen > 0, the number of characters written to dst.
2374 * If dstlen == 0, the number of characters needed to perform the
2375 * conversion. In both cases the count includes the terminating NUL.
2376 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2377 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2378 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2379 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2380 * possible for src.
2382 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2383 LPWSTR dst, INT dstlen )
2385 const union cptable *table;
2386 int ret;
2388 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2390 SetLastError( ERROR_INVALID_PARAMETER );
2391 return 0;
2394 if (srclen < 0) srclen = strlen(src) + 1;
2396 switch(page)
2398 case CP_SYMBOL:
2399 if (flags)
2401 SetLastError( ERROR_INVALID_FLAGS );
2402 return 0;
2404 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2405 break;
2406 case CP_UTF7:
2407 if (flags)
2409 SetLastError( ERROR_INVALID_FLAGS );
2410 return 0;
2412 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2413 break;
2414 case CP_UNIXCP:
2415 if (unix_cptable)
2417 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2418 break;
2420 #ifdef __APPLE__
2421 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2422 #endif
2423 /* fall through */
2424 case CP_UTF8:
2425 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2426 break;
2427 default:
2428 if (!(table = get_codepage_table( page )))
2430 SetLastError( ERROR_INVALID_PARAMETER );
2431 return 0;
2433 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2434 break;
2437 if (ret < 0)
2439 switch(ret)
2441 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2442 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2444 ret = 0;
2446 TRACE("cp %d %s -> %s, ret = %d\n",
2447 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2448 return ret;
2452 /***********************************************************************
2453 * utf7_can_directly_encode
2455 * Helper for utf7_wcstombs
2457 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2459 static const BOOL directly_encodable_table[] =
2461 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2462 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2463 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2464 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2465 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2466 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2467 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2468 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2471 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2474 /***********************************************************************
2475 * utf7_write_c
2477 * Helper for utf7_wcstombs
2479 * RETURNS
2480 * TRUE on success, FALSE on error
2482 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2484 if (dstlen > 0)
2486 if (*index >= dstlen)
2487 return FALSE;
2489 dst[*index] = character;
2492 (*index)++;
2494 return TRUE;
2497 /***********************************************************************
2498 * utf7_wcstombs
2500 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2502 * RETURNS
2503 * On success, the number of characters written
2504 * On dst buffer overflow, -1
2506 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2508 static const char base64_encoding_table[] =
2509 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2511 const WCHAR *source_end = src + srclen;
2512 int dest_index = 0;
2514 while (src < source_end)
2516 if (*src == '+')
2518 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2519 return -1;
2520 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2521 return -1;
2522 src++;
2524 else if (utf7_can_directly_encode(*src))
2526 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2527 return -1;
2528 src++;
2530 else
2532 unsigned int offset = 0;
2533 DWORD byte_pair = 0;
2535 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2536 return -1;
2538 while (src < source_end && !utf7_can_directly_encode(*src))
2540 byte_pair = (byte_pair << 16) | *src;
2541 offset += 16;
2542 while (offset >= 6)
2544 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2545 return -1;
2546 offset -= 6;
2548 src++;
2551 if (offset)
2553 /* Windows won't create a padded base64 character if there's no room for the - sign
2554 * as well ; this is probably a bug in Windows */
2555 if (dstlen > 0 && dest_index + 1 >= dstlen)
2556 return -1;
2558 byte_pair <<= (6 - offset);
2559 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2560 return -1;
2563 /* Windows always explicitly terminates the base64 sequence
2564 even though RFC 2152 (page 3, rule 2) does not require this */
2565 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2566 return -1;
2570 return dest_index;
2573 /***********************************************************************
2574 * WideCharToMultiByte (KERNEL32.@)
2576 * Convert a Unicode character string into a multibyte string.
2578 * PARAMS
2579 * page [I] Code page character set to convert to
2580 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2581 * src [I] Source string buffer
2582 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2583 * dst [O] Destination buffer
2584 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2585 * defchar [I] Default character to use for conversion if no exact
2586 * conversion can be made
2587 * used [O] Set if default character was used in the conversion
2589 * RETURNS
2590 * Success: If dstlen > 0, the number of characters written to dst.
2591 * If dstlen == 0, number of characters needed to perform the
2592 * conversion. In both cases the count includes the terminating NUL.
2593 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2594 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2595 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2596 * parameter was given.
2598 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2599 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2601 const union cptable *table;
2602 int ret, used_tmp;
2604 if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
2606 SetLastError( ERROR_INVALID_PARAMETER );
2607 return 0;
2610 if (srclen < 0) srclen = strlenW(src) + 1;
2612 switch(page)
2614 case CP_SYMBOL:
2615 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2616 if (flags)
2618 SetLastError( ERROR_INVALID_FLAGS );
2619 return 0;
2621 if (defchar || used)
2623 SetLastError( ERROR_INVALID_PARAMETER );
2624 return 0;
2626 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2627 break;
2628 case CP_UTF7:
2629 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2630 if (defchar || used)
2632 SetLastError( ERROR_INVALID_PARAMETER );
2633 return 0;
2635 if (flags)
2637 SetLastError( ERROR_INVALID_FLAGS );
2638 return 0;
2640 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2641 break;
2642 case CP_UNIXCP:
2643 if (unix_cptable)
2645 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2646 defchar, used ? &used_tmp : NULL );
2647 break;
2649 /* fall through */
2650 case CP_UTF8:
2651 if (defchar || used)
2653 SetLastError( ERROR_INVALID_PARAMETER );
2654 return 0;
2656 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2657 break;
2658 default:
2659 if (!(table = get_codepage_table( page )))
2661 SetLastError( ERROR_INVALID_PARAMETER );
2662 return 0;
2664 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2665 defchar, used ? &used_tmp : NULL );
2666 if (used) *used = used_tmp;
2667 break;
2670 if (ret < 0)
2672 switch(ret)
2674 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2675 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2677 ret = 0;
2679 TRACE("cp %d %s -> %s, ret = %d\n",
2680 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2681 return ret;
2685 /***********************************************************************
2686 * GetThreadLocale (KERNEL32.@)
2688 * Get the current threads locale.
2690 * PARAMS
2691 * None.
2693 * RETURNS
2694 * The LCID currently associated with the calling thread.
2696 LCID WINAPI GetThreadLocale(void)
2698 LCID ret = NtCurrentTeb()->CurrentLocale;
2699 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2700 return ret;
2703 /**********************************************************************
2704 * SetThreadLocale (KERNEL32.@)
2706 * Set the current threads locale.
2708 * PARAMS
2709 * lcid [I] LCID of the locale to set
2711 * RETURNS
2712 * Success: TRUE. The threads locale is set to lcid.
2713 * Failure: FALSE. Use GetLastError() to determine the cause.
2715 BOOL WINAPI SetThreadLocale( LCID lcid )
2717 TRACE("(0x%04X)\n", lcid);
2719 lcid = ConvertDefaultLocale(lcid);
2721 if (lcid != GetThreadLocale())
2723 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2725 SetLastError(ERROR_INVALID_PARAMETER);
2726 return FALSE;
2729 NtCurrentTeb()->CurrentLocale = lcid;
2731 return TRUE;
2734 /**********************************************************************
2735 * SetThreadUILanguage (KERNEL32.@)
2737 * Set the current threads UI language.
2739 * PARAMS
2740 * langid [I] LANGID of the language to set, or 0 to use
2741 * the available language which is best supported
2742 * for console applications
2744 * RETURNS
2745 * Success: The return value is the same as the input value.
2746 * Failure: The return value differs from the input value.
2747 * Use GetLastError() to determine the cause.
2749 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2751 TRACE("(0x%04x) stub - returning success\n", langid);
2752 return langid;
2755 /******************************************************************************
2756 * ConvertDefaultLocale (KERNEL32.@)
2758 * Convert a default locale identifier into a real identifier.
2760 * PARAMS
2761 * lcid [I] LCID identifier of the locale to convert
2763 * RETURNS
2764 * lcid unchanged, if not a default locale or its sublanguage is
2765 * not SUBLANG_NEUTRAL.
2766 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2767 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2768 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2770 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2772 LANGID langid;
2774 switch (lcid)
2776 case LOCALE_INVARIANT:
2777 /* keep as-is */
2778 break;
2779 case LOCALE_SYSTEM_DEFAULT:
2780 lcid = GetSystemDefaultLCID();
2781 break;
2782 case LOCALE_USER_DEFAULT:
2783 case LOCALE_NEUTRAL:
2784 lcid = GetUserDefaultLCID();
2785 break;
2786 default:
2787 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2788 langid = LANGIDFROMLCID(lcid);
2789 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2791 langid = MAKELANGID(PRIMARYLANGID(langid), get_default_sublang( langid ));
2792 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2795 return lcid;
2799 /******************************************************************************
2800 * IsValidLocale (KERNEL32.@)
2802 * Determine if a locale is valid.
2804 * PARAMS
2805 * lcid [I] LCID of the locale to check
2806 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2808 * RETURNS
2809 * TRUE, if lcid is valid,
2810 * FALSE, otherwise.
2812 * NOTES
2813 * Wine does not currently make the distinction between supported and installed. All
2814 * languages supported are installed by default.
2816 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2818 /* check if language is registered in the kernel32 resources */
2819 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2820 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2823 /******************************************************************************
2824 * IsValidLocaleName (KERNEL32.@)
2826 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2828 struct locale_name locale_name;
2830 if (!locale)
2831 return FALSE;
2833 /* string parsing */
2834 parse_locale_name( locale, &locale_name );
2836 TRACE( "found lcid %x for %s, matches %d\n",
2837 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2839 return locale_name.matches > 0;
2842 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2843 LPCSTR name, WORD LangID, LONG_PTR lParam )
2845 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2846 char buf[20];
2848 sprintf(buf, "%08x", (UINT)LangID);
2849 return lpfnLocaleEnum( buf );
2852 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2853 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2855 static const WCHAR formatW[] = {'%','0','8','x',0};
2856 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2857 WCHAR buf[20];
2858 sprintfW( buf, formatW, (UINT)LangID );
2859 return lpfnLocaleEnum( buf );
2862 /******************************************************************************
2863 * EnumSystemLocalesA (KERNEL32.@)
2865 * Call a users function for each locale available on the system.
2867 * PARAMS
2868 * lpfnLocaleEnum [I] Callback function to call for each locale
2869 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2871 * RETURNS
2872 * Success: TRUE.
2873 * Failure: FALSE. Use GetLastError() to determine the cause.
2875 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2877 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2878 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2879 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2880 (LONG_PTR)lpfnLocaleEnum);
2881 return TRUE;
2885 /******************************************************************************
2886 * EnumSystemLocalesW (KERNEL32.@)
2888 * See EnumSystemLocalesA.
2890 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2892 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2893 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2894 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2895 (LONG_PTR)lpfnLocaleEnum);
2896 return TRUE;
2900 struct enum_locale_ex_data
2902 LOCALE_ENUMPROCEX proc;
2903 DWORD flags;
2904 LPARAM lparam;
2907 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2908 LPCWSTR name, WORD lang, LONG_PTR lparam )
2910 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2911 WCHAR buffer[256];
2912 DWORD neutral;
2913 unsigned int flags;
2915 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2916 buffer, sizeof(buffer) / sizeof(WCHAR) );
2917 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2918 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2919 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2920 neutral = 0;
2921 flags = LOCALE_WINDOWS;
2922 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2923 if (data->flags && !(data->flags & flags)) return TRUE;
2924 return data->proc( buffer, flags, data->lparam );
2927 /******************************************************************************
2928 * EnumSystemLocalesEx (KERNEL32.@)
2930 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2932 struct enum_locale_ex_data data;
2934 if (reserved)
2936 SetLastError( ERROR_INVALID_PARAMETER );
2937 return FALSE;
2939 data.proc = proc;
2940 data.flags = flags;
2941 data.lparam = lparam;
2942 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2943 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2944 enum_locale_ex_proc, (LONG_PTR)&data );
2945 return TRUE;
2949 /***********************************************************************
2950 * VerLanguageNameA (KERNEL32.@)
2952 * Get the name of a language.
2954 * PARAMS
2955 * wLang [I] LANGID of the language
2956 * szLang [O] Destination for the language name
2958 * RETURNS
2959 * Success: The size of the language name. If szLang is non-NULL, it is filled
2960 * with the name.
2961 * Failure: 0. Use GetLastError() to determine the cause.
2964 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2966 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2970 /***********************************************************************
2971 * VerLanguageNameW (KERNEL32.@)
2973 * See VerLanguageNameA.
2975 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2977 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2981 /******************************************************************************
2982 * GetStringTypeW (KERNEL32.@)
2984 * See GetStringTypeA.
2986 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2988 static const unsigned char type2_map[16] =
2990 C2_NOTAPPLICABLE, /* unassigned */
2991 C2_LEFTTORIGHT, /* L */
2992 C2_RIGHTTOLEFT, /* R */
2993 C2_EUROPENUMBER, /* EN */
2994 C2_EUROPESEPARATOR, /* ES */
2995 C2_EUROPETERMINATOR, /* ET */
2996 C2_ARABICNUMBER, /* AN */
2997 C2_COMMONSEPARATOR, /* CS */
2998 C2_BLOCKSEPARATOR, /* B */
2999 C2_SEGMENTSEPARATOR, /* S */
3000 C2_WHITESPACE, /* WS */
3001 C2_OTHERNEUTRAL, /* ON */
3002 C2_RIGHTTOLEFT, /* AL */
3003 C2_NOTAPPLICABLE, /* NSM */
3004 C2_NOTAPPLICABLE, /* BN */
3005 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
3008 if (!src)
3010 SetLastError( ERROR_INVALID_PARAMETER );
3011 return FALSE;
3014 if (count == -1) count = strlenW(src) + 1;
3015 switch(type)
3017 case CT_CTYPE1:
3018 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
3019 break;
3020 case CT_CTYPE2:
3021 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
3022 break;
3023 case CT_CTYPE3:
3025 WARN("CT_CTYPE3: semi-stub.\n");
3026 while (count--)
3028 int c = *src;
3029 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
3031 type1 = get_char_typeW( *src++ ) & 0xfff;
3032 /* try to construct type3 from type1 */
3033 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
3034 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
3035 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
3036 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
3037 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
3038 if (c == 0x0640) type3 |= C3_KASHIDA;
3039 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
3041 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
3042 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
3044 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
3045 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
3046 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
3047 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
3048 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
3049 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
3050 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
3051 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
3053 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
3054 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
3055 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
3056 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
3057 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
3058 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
3059 *chartype++ = type3;
3061 break;
3063 default:
3064 SetLastError( ERROR_INVALID_PARAMETER );
3065 return FALSE;
3067 return TRUE;
3071 /******************************************************************************
3072 * GetStringTypeExW (KERNEL32.@)
3074 * See GetStringTypeExA.
3076 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
3078 /* locale is ignored for Unicode */
3079 return GetStringTypeW( type, src, count, chartype );
3083 /******************************************************************************
3084 * GetStringTypeA (KERNEL32.@)
3086 * Get characteristics of the characters making up a string.
3088 * PARAMS
3089 * locale [I] Locale Id for the string
3090 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3091 * src [I] String to analyse
3092 * count [I] Length of src in chars, or -1 if src is NUL terminated
3093 * chartype [O] Destination for the calculated characteristics
3095 * RETURNS
3096 * Success: TRUE. chartype is filled with the requested characteristics of each char
3097 * in src.
3098 * Failure: FALSE. Use GetLastError() to determine the cause.
3100 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3102 UINT cp;
3103 INT countW;
3104 LPWSTR srcW;
3105 BOOL ret = FALSE;
3107 if(count == -1) count = strlen(src) + 1;
3109 if (!(cp = get_lcid_codepage( locale )))
3111 FIXME("For locale %04x using current ANSI code page\n", locale);
3112 cp = GetACP();
3115 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
3116 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
3118 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
3120 * NOTE: the target buffer has 1 word for each CHARACTER in the source
3121 * string, with multibyte characters there maybe be more bytes in count
3122 * than character space in the buffer!
3124 ret = GetStringTypeW(type, srcW, countW, chartype);
3125 HeapFree(GetProcessHeap(), 0, srcW);
3127 return ret;
3130 /******************************************************************************
3131 * GetStringTypeExA (KERNEL32.@)
3133 * Get characteristics of the characters making up a string.
3135 * PARAMS
3136 * locale [I] Locale Id for the string
3137 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
3138 * src [I] String to analyse
3139 * count [I] Length of src in chars, or -1 if src is NUL terminated
3140 * chartype [O] Destination for the calculated characteristics
3142 * RETURNS
3143 * Success: TRUE. chartype is filled with the requested characteristics of each char
3144 * in src.
3145 * Failure: FALSE. Use GetLastError() to determine the cause.
3147 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
3149 return GetStringTypeA(locale, type, src, count, chartype);
3152 /* compose a full-width katakana. return consumed source characters. */
3153 static INT compose_katakana( LPCWSTR src, INT srclen, LPWSTR dst )
3155 const static BYTE katakana_map[] = {
3156 /* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
3157 0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
3158 0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
3159 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
3160 0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
3161 0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
3162 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
3163 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
3165 WCHAR before, dummy;
3167 if (!dst)
3168 dst = &dummy;
3170 switch (*src)
3172 case 0x309b: case 0x309c:
3173 *dst = *src - 2;
3174 return 1;
3175 case 0x30f0: case 0x30f1: case 0x30fd:
3176 *dst = *src;
3177 break;
3178 default:
3180 int shift = *src - 0xff61;
3181 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]) )
3182 return 0;
3183 else
3184 *dst = katakana_map[shift] | 0x3000;
3188 if (srclen <= 1)
3189 return 1;
3191 before = *dst;
3193 /* datakuten (voiced sound) */
3194 if (*(src + 1) == 0xff9e)
3196 if ((*src >= 0xff76 && *src <= 0xff84) ||
3197 (*src >= 0xff8a && *src <= 0xff8e) ||
3198 *src == 0x30fd)
3199 *dst += 1;
3200 else if (*src == 0xff73)
3201 *dst = 0x30f4; /* KATAKANA LETTER VU */
3202 else if (*src == 0xff9c)
3203 *dst = 0x30f7; /* KATAKANA LETTER VA */
3204 else if (*src == 0x30f0)
3205 *dst = 0x30f8; /* KATAKANA LETTER VI */
3206 else if (*src == 0x30f1)
3207 *dst = 0x30f9; /* KATAKANA LETTER VE */
3208 else if (*src == 0xff66)
3209 *dst = 0x30fa; /* KATAKANA LETTER VO */
3212 /* handakuten (semi-voiced sound) */
3213 if (*(src + 1) == 0xff9f)
3214 if (*src >= 0xff8a && *src <= 0xff8e)
3215 *dst += 2;
3217 return (*dst != before) ? 2 : 1;
3220 /* map one or two half-width characters to one full-width character */
3221 static INT map_to_fullwidth( LPCWSTR src, INT srclen, LPWSTR dst )
3223 INT n;
3225 if (*src <= '~' && *src > ' ' && *src != '\\')
3226 *dst = *src - 0x20 + 0xff00;
3227 else if (*src == ' ')
3228 *dst = 0x3000;
3229 else if (*src <= 0x00af && *src >= 0x00a2)
3231 const static BYTE misc_symbols_table[] = {
3232 0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
3233 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
3235 if (misc_symbols_table[*src - 0x00a2])
3236 *dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
3237 else
3238 *dst = *src;
3240 else if (*src == 0x20a9) /* WON SIGN */
3241 *dst = 0xffe6;
3242 else if ((n = compose_katakana(src, srclen, dst)) > 0)
3243 return n;
3244 else if (*src >= 0xffa0 && *src <= 0xffdc)
3246 const static BYTE hangul_mapping_table[] = {
3247 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
3248 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
3249 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
3250 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
3251 0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
3252 0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
3253 0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
3254 0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
3257 if (hangul_mapping_table[*src - 0xffa0])
3258 *dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
3259 else
3260 *dst = *src;
3262 else
3263 *dst = *src;
3265 return 1;
3268 /* decompose a full-width katakana character into one or two half-width characters. */
3269 static INT decompose_katakana( WCHAR c, LPWSTR dst, INT dstlen )
3271 const static BYTE katakana_map[] = {
3272 /* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
3273 0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
3274 0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
3275 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
3276 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
3277 0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
3278 0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
3279 0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
3280 0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
3281 0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
3282 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
3283 0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
3284 0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
3286 INT len = 0, shift = c - 0x3099;
3287 BYTE k;
3289 if (shift < 0 || shift >= sizeof(katakana_map)/sizeof(katakana_map[0]))
3290 return 0;
3292 k = katakana_map[shift];
3294 if (!k)
3296 if (dstlen > 0)
3297 *dst = c;
3298 len++;
3300 else if (k > 0x60)
3302 if (dstlen > 0)
3303 *dst = k | 0xff00;
3304 len++;
3306 else
3308 if (dstlen >= 2)
3310 dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
3311 dst[1] = (k == 2) ? 0xff9f : 0xff9e;
3313 len += 2;
3315 return len;
3318 /* map single full-width character to single or double half-width characters. */
3319 static INT map_to_halfwidth(WCHAR c, LPWSTR dst, INT dstlen)
3321 INT n = decompose_katakana(c, dst, dstlen);
3322 if (n > 0)
3323 return n;
3325 if (c == 0x3000)
3326 *dst = ' ';
3327 else if (c == 0x3001)
3328 *dst = 0xff64;
3329 else if (c == 0x3002)
3330 *dst = 0xff61;
3331 else if (c == 0x300c || c == 0x300d)
3332 *dst = (c - 0x300c) + 0xff62;
3333 else if (c >= 0x3131 && c <= 0x3163)
3335 *dst = c - 0x3131 + 0xffa1;
3336 if (*dst >= 0xffbf) *dst += 3;
3337 if (*dst >= 0xffc8) *dst += 2;
3338 if (*dst >= 0xffd0) *dst += 2;
3339 if (*dst >= 0xffd8) *dst += 2;
3341 else if (c == 0x3164)
3342 *dst = 0xffa0;
3343 else if (c == 0x2019)
3344 *dst = '\'';
3345 else if (c == 0x201d)
3346 *dst = '"';
3347 else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
3348 *dst = c - 0xff00 + 0x20;
3349 else if (c >= 0xffe0 && c <= 0xffe6)
3351 const static WCHAR misc_symbol_map[] = {
3352 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9
3354 *dst = misc_symbol_map[c - 0xffe0];
3356 else
3357 *dst = c;
3359 return 1;
3362 /*************************************************************************
3363 * LCMapStringEx (KERNEL32.@)
3365 * Map characters in a locale sensitive string.
3367 * PARAMS
3368 * name [I] Locale name for the conversion.
3369 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
3370 * src [I] String to map
3371 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3372 * dst [O] Destination for mapped string
3373 * dstlen [I] Length of dst in characters
3374 * version [I] reserved, must be NULL
3375 * reserved [I] reserved, must be NULL
3376 * lparam [I] reserved, must be 0
3378 * RETURNS
3379 * Success: The length of the mapped string in dst, including the NUL terminator.
3380 * Failure: 0. Use GetLastError() to determine the cause.
3382 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
3383 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
3385 LPWSTR dst_ptr;
3386 INT len;
3388 if (version) FIXME("unsupported version structure %p\n", version);
3389 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
3390 if (lparam)
3392 static int once;
3393 if (!once++) FIXME("unsupported lparam %lx\n", lparam);
3396 if (!src || !srclen || dstlen < 0)
3398 SetLastError(ERROR_INVALID_PARAMETER);
3399 return 0;
3402 /* mutually exclusive flags */
3403 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
3404 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
3405 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
3406 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
3407 !flags)
3409 SetLastError(ERROR_INVALID_FLAGS);
3410 return 0;
3413 if (!dstlen) dst = NULL;
3415 if (flags & LCMAP_SORTKEY)
3417 INT ret;
3418 if (src == dst)
3420 SetLastError(ERROR_INVALID_FLAGS);
3421 return 0;
3424 if (srclen < 0) srclen = strlenW(src);
3426 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3427 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3429 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
3430 if (ret == 0)
3431 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3432 else
3433 ret++;
3434 return ret;
3437 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
3438 if (flags & SORT_STRINGSORT)
3440 SetLastError(ERROR_INVALID_FLAGS);
3441 return 0;
3443 if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
3444 (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
3445 ((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
3446 (flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
3448 SetLastError(ERROR_INVALID_FLAGS);
3449 return 0;
3452 if (srclen < 0) srclen = strlenW(src) + 1;
3454 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
3455 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3457 if (!dst) /* return required string length */
3459 if (flags & NORM_IGNORESYMBOLS)
3461 for (len = 0; srclen; src++, srclen--)
3463 WCHAR wch = *src;
3464 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
3465 * and skips white space and punctuation characters for
3466 * NORM_IGNORESYMBOLS.
3468 if (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE))
3469 continue;
3470 len++;
3473 else if (flags & LCMAP_FULLWIDTH)
3475 for (len = 0; srclen; src++, srclen--, len++)
3477 if (compose_katakana(src, srclen, NULL) == 2)
3479 src++;
3480 srclen--;
3484 else if (flags & LCMAP_HALFWIDTH)
3486 for (len = 0; srclen; src++, srclen--, len++)
3487 if (decompose_katakana(*src, NULL, 0) == 2)
3488 len++;
3490 else
3491 len = srclen;
3492 return len;
3495 if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
3497 SetLastError(ERROR_INVALID_FLAGS);
3498 return 0;
3501 if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
3503 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3505 WCHAR wch = *src;
3506 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3507 continue;
3508 *dst_ptr++ = wch;
3509 len--;
3511 goto done;
3514 if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
3516 for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
3518 WCHAR wch;
3519 if (flags & LCMAP_FULLWIDTH)
3521 /* map half-width character to full-width one,
3522 e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
3523 if (map_to_fullwidth(src, srclen, &wch) == 2)
3525 src++;
3526 srclen--;
3529 else
3530 wch = *src;
3532 if (flags & LCMAP_KATAKANA)
3534 /* map hiragana to katakana, e.g. U+3041 -> U+30A1.
3535 we can't use C3_HIRAGANA as some characters can't map to katakana */
3536 if ((wch >= 0x3041 && wch <= 0x3096) ||
3537 wch == 0x309D || wch == 0x309E)
3538 wch += 0x60;
3540 else if (flags & LCMAP_HIRAGANA)
3542 /* map katakana to hiragana, e.g. U+30A1 -> U+3041.
3543 we can't use C3_KATAKANA as some characters can't map to hiragana */
3544 if ((wch >= 0x30A1 && wch <= 0x30F6) ||
3545 wch == 0x30FD || wch == 0x30FE)
3546 wch -= 0x60;
3549 if (flags & LCMAP_HALFWIDTH)
3551 /* map full-width character to half-width one,
3552 e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
3553 if (map_to_halfwidth(wch, dst_ptr, dstlen) == 2)
3555 dstlen--;
3556 dst_ptr++;
3557 if (!dstlen)
3559 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3560 return 0;
3564 else
3565 *dst_ptr = wch;
3567 if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)))
3568 goto done;
3570 srclen = dst_ptr - dst;
3571 src = dst;
3574 if (flags & LCMAP_UPPERCASE)
3576 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3578 *dst_ptr++ = toupperW(*src);
3579 len--;
3582 else if (flags & LCMAP_LOWERCASE)
3584 for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
3586 *dst_ptr++ = tolowerW(*src);
3587 len--;
3590 else
3592 len = min(srclen, dstlen);
3593 memcpy(dst, src, len * sizeof(WCHAR));
3594 dst_ptr = dst + len;
3595 srclen -= len;
3598 done:
3599 if (srclen)
3601 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3602 return 0;
3605 return dst_ptr - dst;
3608 /*************************************************************************
3609 * LCMapStringW (KERNEL32.@)
3611 * See LCMapStringA.
3613 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3614 LPWSTR dst, INT dstlen)
3616 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3617 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3619 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3622 /*************************************************************************
3623 * LCMapStringA (KERNEL32.@)
3625 * Map characters in a locale sensitive string.
3627 * PARAMS
3628 * lcid [I] LCID for the conversion.
3629 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3630 * src [I] String to map
3631 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3632 * dst [O] Destination for mapped string
3633 * dstlen [I] Length of dst in characters
3635 * RETURNS
3636 * Success: The length of the mapped string in dst, including the NUL terminator.
3637 * Failure: 0. Use GetLastError() to determine the cause.
3639 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3640 LPSTR dst, INT dstlen)
3642 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3643 LPWSTR srcW, dstW;
3644 INT ret = 0, srclenW, dstlenW;
3645 UINT locale_cp = CP_ACP;
3647 if (!src || !srclen || dstlen < 0)
3649 SetLastError(ERROR_INVALID_PARAMETER);
3650 return 0;
3653 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3655 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3656 if (srclenW)
3657 srcW = bufW;
3658 else
3660 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3661 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3662 if (!srcW)
3664 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3665 return 0;
3667 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3670 if (flags & LCMAP_SORTKEY)
3672 if (src == dst)
3674 SetLastError(ERROR_INVALID_FLAGS);
3675 goto map_string_exit;
3677 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3678 if (ret == 0)
3679 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3680 else
3681 ret++;
3682 goto map_string_exit;
3685 if (flags & SORT_STRINGSORT)
3687 SetLastError(ERROR_INVALID_FLAGS);
3688 goto map_string_exit;
3691 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3692 if (!dstlenW)
3693 goto map_string_exit;
3695 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3696 if (!dstW)
3698 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3699 goto map_string_exit;
3702 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3703 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3704 HeapFree(GetProcessHeap(), 0, dstW);
3706 map_string_exit:
3707 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3708 return ret;
3711 /*************************************************************************
3712 * FoldStringA (KERNEL32.@)
3714 * Map characters in a string.
3716 * PARAMS
3717 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3718 * src [I] String to map
3719 * srclen [I] Length of src, or -1 if src is NUL terminated
3720 * dst [O] Destination for mapped string
3721 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3723 * RETURNS
3724 * Success: The length of the string written to dst, including the terminating NUL. If
3725 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3726 * and dst may be NULL.
3727 * Failure: 0. Use GetLastError() to determine the cause.
3729 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3730 LPSTR dst, INT dstlen)
3732 INT ret = 0, srclenW = 0;
3733 WCHAR *srcW = NULL, *dstW = NULL;
3735 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3737 SetLastError(ERROR_INVALID_PARAMETER);
3738 return 0;
3741 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3742 src, srclen, NULL, 0);
3743 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3745 if (!srcW)
3747 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3748 goto FoldStringA_exit;
3751 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3752 src, srclen, srcW, srclenW);
3754 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3756 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3757 if (ret && dstlen)
3759 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3761 if (!dstW)
3763 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3764 goto FoldStringA_exit;
3767 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3768 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3770 ret = 0;
3771 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3775 HeapFree(GetProcessHeap(), 0, dstW);
3777 FoldStringA_exit:
3778 HeapFree(GetProcessHeap(), 0, srcW);
3779 return ret;
3782 /*************************************************************************
3783 * FoldStringW (KERNEL32.@)
3785 * See FoldStringA.
3787 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3788 LPWSTR dst, INT dstlen)
3790 int ret;
3792 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3794 case 0:
3795 if (dwFlags)
3796 break;
3797 /* Fall through for dwFlags == 0 */
3798 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3799 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3800 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3801 SetLastError(ERROR_INVALID_FLAGS);
3802 return 0;
3805 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3807 SetLastError(ERROR_INVALID_PARAMETER);
3808 return 0;
3811 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3812 if (!ret)
3813 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3814 return ret;
3817 /******************************************************************************
3818 * CompareStringW (KERNEL32.@)
3820 * See CompareStringA.
3822 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3823 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3825 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3828 /******************************************************************************
3829 * CompareStringEx (KERNEL32.@)
3831 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3832 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3834 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3835 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3836 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3837 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3838 INT ret;
3839 static int once;
3841 if (version) FIXME("unexpected version parameter\n");
3842 if (reserved) FIXME("unexpected reserved value\n");
3843 if (lParam) FIXME("unexpected lParam\n");
3845 if (!str1 || !str2)
3847 SetLastError(ERROR_INVALID_PARAMETER);
3848 return 0;
3851 if (flags & ~(supported_flags|semistub_flags))
3853 SetLastError(ERROR_INVALID_FLAGS);
3854 return 0;
3857 if (flags & semistub_flags)
3859 if (!once++)
3860 FIXME("semi-stub behavior for flag(s) 0x%x\n", flags & semistub_flags);
3863 if (len1 < 0) len1 = strlenW(str1);
3864 if (len2 < 0) len2 = strlenW(str2);
3866 ret = wine_compare_string(flags, str1, len1, str2, len2);
3868 if (ret) /* need to translate result */
3869 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3870 return CSTR_EQUAL;
3873 /******************************************************************************
3874 * CompareStringA (KERNEL32.@)
3876 * Compare two locale sensitive strings.
3878 * PARAMS
3879 * lcid [I] LCID for the comparison
3880 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3881 * str1 [I] First string to compare
3882 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3883 * str2 [I] Second string to compare
3884 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3886 * RETURNS
3887 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3888 * str1 is less than, equal to or greater than str2 respectively.
3889 * Failure: FALSE. Use GetLastError() to determine the cause.
3891 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3892 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3894 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3895 WCHAR *buf2W = buf1W + 130;
3896 LPWSTR str1W, str2W;
3897 INT len1W = 0, len2W = 0, ret;
3898 UINT locale_cp = CP_ACP;
3900 if (!str1 || !str2)
3902 SetLastError(ERROR_INVALID_PARAMETER);
3903 return 0;
3905 if (len1 < 0) len1 = strlen(str1);
3906 if (len2 < 0) len2 = strlen(str2);
3908 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3910 if (len1)
3912 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3913 if (len1W)
3914 str1W = buf1W;
3915 else
3917 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3918 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3919 if (!str1W)
3921 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3922 return 0;
3924 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3927 else
3929 len1W = 0;
3930 str1W = buf1W;
3933 if (len2)
3935 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3936 if (len2W)
3937 str2W = buf2W;
3938 else
3940 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3941 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3942 if (!str2W)
3944 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3945 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3946 return 0;
3948 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3951 else
3953 len2W = 0;
3954 str2W = buf2W;
3957 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3959 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3960 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3961 return ret;
3964 /******************************************************************************
3965 * CompareStringOrdinal (KERNEL32.@)
3967 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3969 int ret;
3971 if (!str1 || !str2)
3973 SetLastError(ERROR_INVALID_PARAMETER);
3974 return 0;
3976 if (len1 < 0) len1 = strlenW(str1);
3977 if (len2 < 0) len2 = strlenW(str2);
3979 ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
3980 if (ret < 0) return CSTR_LESS_THAN;
3981 if (ret > 0) return CSTR_GREATER_THAN;
3982 return CSTR_EQUAL;
3985 /*************************************************************************
3986 * lstrcmp (KERNEL32.@)
3987 * lstrcmpA (KERNEL32.@)
3989 * Compare two strings using the current thread locale.
3991 * PARAMS
3992 * str1 [I] First string to compare
3993 * str2 [I] Second string to compare
3995 * RETURNS
3996 * Success: A number less than, equal to or greater than 0 depending on whether
3997 * str1 is less than, equal to or greater than str2 respectively.
3998 * Failure: FALSE. Use GetLastError() to determine the cause.
4000 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
4002 int ret;
4004 if ((str1 == NULL) && (str2 == NULL)) return 0;
4005 if (str1 == NULL) return -1;
4006 if (str2 == NULL) return 1;
4008 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4009 if (ret) ret -= 2;
4011 return ret;
4014 /*************************************************************************
4015 * lstrcmpi (KERNEL32.@)
4016 * lstrcmpiA (KERNEL32.@)
4018 * Compare two strings using the current thread locale, ignoring case.
4020 * PARAMS
4021 * str1 [I] First string to compare
4022 * str2 [I] Second string to compare
4024 * RETURNS
4025 * Success: A number less than, equal to or greater than 0 depending on whether
4026 * str2 is less than, equal to or greater than str1 respectively.
4027 * Failure: FALSE. Use GetLastError() to determine the cause.
4029 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR 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 = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
4038 if (ret) ret -= 2;
4040 return ret;
4043 /*************************************************************************
4044 * lstrcmpW (KERNEL32.@)
4046 * See lstrcmpA.
4048 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
4050 int ret;
4052 if ((str1 == NULL) && (str2 == NULL)) return 0;
4053 if (str1 == NULL) return -1;
4054 if (str2 == NULL) return 1;
4056 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
4057 if (ret) ret -= 2;
4059 return ret;
4062 /*************************************************************************
4063 * lstrcmpiW (KERNEL32.@)
4065 * See lstrcmpiA.
4067 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
4069 int ret;
4071 if ((str1 == NULL) && (str2 == NULL)) return 0;
4072 if (str1 == NULL) return -1;
4073 if (str2 == NULL) return 1;
4075 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
4076 if (ret) ret -= 2;
4078 return ret;
4081 /******************************************************************************
4082 * LOCALE_Init
4084 void LOCALE_Init(void)
4086 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
4087 const union cptable *unix_cp );
4089 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
4091 setlocale( LC_ALL, "" );
4093 #ifdef __APPLE__
4094 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
4095 if (!has_env("LANG"))
4097 const char* mac_locale = get_mac_locale();
4099 setenv( "LANG", mac_locale, 1 );
4100 if (setlocale( LC_ALL, "" ))
4101 TRACE( "setting LANG to '%s'\n", mac_locale );
4102 else
4104 /* no C library locale matching Mac locale; don't pass garbage to children */
4105 unsetenv("LANG");
4106 TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
4109 #endif /* __APPLE__ */
4111 unix_cp = setup_unix_locales();
4112 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
4114 #ifdef __APPLE__
4115 if (!unix_cp)
4116 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
4117 #endif
4119 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
4120 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
4121 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
4123 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
4124 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
4125 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
4126 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
4127 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
4128 if (!unix_cp)
4129 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
4130 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
4132 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
4133 ansi_cptable = wine_cp_get_table( 1252 );
4134 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
4135 oem_cptable = wine_cp_get_table( 437 );
4136 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
4137 mac_cptable = wine_cp_get_table( 10000 );
4138 if (unix_cp != CP_UTF8)
4140 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
4141 unix_cptable = wine_cp_get_table( 28591 );
4144 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
4146 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
4147 ansi_cptable->info.codepage, oem_cptable->info.codepage,
4148 mac_cptable->info.codepage, unix_cp );
4150 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
4153 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
4155 UNICODE_STRING keyName;
4156 OBJECT_ATTRIBUTES attr;
4157 HANDLE hkey;
4159 RtlInitUnicodeString( &keyName, szKeyName );
4160 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
4162 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
4163 hkey = 0;
4165 return hkey;
4168 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
4169 LPWSTR szValueName, ULONG valueNameSize,
4170 LPWSTR szValueData, ULONG valueDataSize)
4172 BYTE buffer[80];
4173 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
4174 DWORD dwLen;
4176 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
4177 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
4178 info->NameLength > valueNameSize ||
4179 info->DataLength > valueDataSize)
4181 return FALSE;
4184 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
4186 memcpy( szValueName, info->Name, info->NameLength);
4187 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
4188 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
4189 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
4191 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
4192 return TRUE;
4195 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
4197 BYTE buffer[128];
4198 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
4199 DWORD dwSize = sizeof(buffer);
4200 UNICODE_STRING valueName;
4202 RtlInitUnicodeString( &valueName, szValueName );
4204 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
4205 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
4206 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
4207 info->DataLength == sizeof(DWORD))
4209 memcpy(lpVal, info->Data, sizeof(DWORD));
4210 return TRUE;
4213 return FALSE;
4216 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
4218 LANGID langId;
4219 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
4220 HRSRC hResource;
4221 BOOL bRet = FALSE;
4223 /* FIXME: Is it correct to use the system default langid? */
4224 langId = GetSystemDefaultLangID();
4226 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
4227 langId = MAKELANGID(PRIMARYLANGID(langId), get_default_sublang( langId ));
4229 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
4231 if (hResource)
4233 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
4235 if (hResDir)
4237 ULONG iResourceIndex = lgrpid & 0xf;
4238 LPCWSTR lpResEntry = LockResource( hResDir );
4239 ULONG i;
4241 for (i = 0; i < iResourceIndex; i++)
4242 lpResEntry += *lpResEntry + 1;
4244 if (*lpResEntry < nameSize)
4246 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
4247 szName[*lpResEntry] = '\0';
4248 bRet = TRUE;
4252 FreeResource( hResource );
4254 return bRet;
4257 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
4258 typedef struct
4260 LANGUAGEGROUP_ENUMPROCA procA;
4261 LANGUAGEGROUP_ENUMPROCW procW;
4262 DWORD dwFlags;
4263 LONG_PTR lParam;
4264 } ENUMLANGUAGEGROUP_CALLBACKS;
4266 /* Internal implementation of EnumSystemLanguageGroupsA/W */
4267 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
4269 WCHAR szNumber[10], szValue[4];
4270 HANDLE hKey;
4271 BOOL bContinue = TRUE;
4272 ULONG ulIndex = 0;
4274 if (!lpProcs)
4276 SetLastError(ERROR_INVALID_PARAMETER);
4277 return FALSE;
4280 switch (lpProcs->dwFlags)
4282 case 0:
4283 /* Default to LGRPID_INSTALLED */
4284 lpProcs->dwFlags = LGRPID_INSTALLED;
4285 /* Fall through... */
4286 case LGRPID_INSTALLED:
4287 case LGRPID_SUPPORTED:
4288 break;
4289 default:
4290 SetLastError(ERROR_INVALID_FLAGS);
4291 return FALSE;
4294 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4296 if (!hKey)
4297 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4299 while (bContinue)
4301 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4302 szValue, sizeof(szValue) ))
4304 BOOL bInstalled = szValue[0] == '1';
4305 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
4307 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
4308 bInstalled ? "" : "not ");
4310 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
4312 WCHAR szGrpName[48];
4314 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
4315 szGrpName[0] = '\0';
4317 if (lpProcs->procW)
4318 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
4319 lpProcs->lParam );
4320 else
4322 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4323 char szGrpNameA[48];
4325 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
4326 * or whether the language names are ever localised. Assume CP_ACP.
4329 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4330 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
4332 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
4333 lpProcs->lParam );
4337 ulIndex++;
4339 else
4340 bContinue = FALSE;
4342 if (!bContinue)
4343 break;
4346 if (hKey)
4347 NtClose( hKey );
4349 return TRUE;
4352 /******************************************************************************
4353 * EnumSystemLanguageGroupsA (KERNEL32.@)
4355 * Call a users function for each language group available on the system.
4357 * PARAMS
4358 * pLangGrpEnumProc [I] Callback function to call for each language group
4359 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
4360 * lParam [I] User parameter to pass to pLangGrpEnumProc
4362 * RETURNS
4363 * Success: TRUE.
4364 * Failure: FALSE. Use GetLastError() to determine the cause.
4366 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
4367 DWORD dwFlags, LONG_PTR lParam)
4369 ENUMLANGUAGEGROUP_CALLBACKS procs;
4371 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4373 procs.procA = pLangGrpEnumProc;
4374 procs.procW = NULL;
4375 procs.dwFlags = dwFlags;
4376 procs.lParam = lParam;
4378 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4381 /******************************************************************************
4382 * EnumSystemLanguageGroupsW (KERNEL32.@)
4384 * See EnumSystemLanguageGroupsA.
4386 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
4387 DWORD dwFlags, LONG_PTR lParam)
4389 ENUMLANGUAGEGROUP_CALLBACKS procs;
4391 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
4393 procs.procA = NULL;
4394 procs.procW = pLangGrpEnumProc;
4395 procs.dwFlags = dwFlags;
4396 procs.lParam = lParam;
4398 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
4401 /******************************************************************************
4402 * IsValidLanguageGroup (KERNEL32.@)
4404 * Determine if a language group is supported and/or installed.
4406 * PARAMS
4407 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
4408 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
4410 * RETURNS
4411 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
4412 * FALSE otherwise.
4414 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
4416 static const WCHAR szFormat[] = { '%','x','\0' };
4417 WCHAR szValueName[16], szValue[2];
4418 BOOL bSupported = FALSE, bInstalled = FALSE;
4419 HANDLE hKey;
4422 switch (dwFlags)
4424 case LGRPID_INSTALLED:
4425 case LGRPID_SUPPORTED:
4427 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
4429 sprintfW( szValueName, szFormat, lgrpid );
4431 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
4433 bSupported = TRUE;
4435 if (szValue[0] == '1')
4436 bInstalled = TRUE;
4439 if (hKey)
4440 NtClose( hKey );
4442 break;
4445 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
4446 (dwFlags == LGRPID_INSTALLED && bInstalled))
4447 return TRUE;
4449 return FALSE;
4452 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
4453 typedef struct
4455 LANGGROUPLOCALE_ENUMPROCA procA;
4456 LANGGROUPLOCALE_ENUMPROCW procW;
4457 DWORD dwFlags;
4458 LGRPID lgrpid;
4459 LONG_PTR lParam;
4460 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
4462 /* Internal implementation of EnumLanguageGrouplocalesA/W */
4463 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
4465 static const WCHAR szAlternateSortsKeyName[] = {
4466 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
4468 WCHAR szNumber[10], szValue[4];
4469 HANDLE hKey;
4470 BOOL bContinue = TRUE, bAlternate = FALSE;
4471 LGRPID lgrpid;
4472 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
4474 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
4476 SetLastError(ERROR_INVALID_PARAMETER);
4477 return FALSE;
4480 if (lpProcs->dwFlags)
4482 SetLastError(ERROR_INVALID_FLAGS);
4483 return FALSE;
4486 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
4488 if (!hKey)
4489 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
4491 while (bContinue)
4493 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
4494 szValue, sizeof(szValue) ))
4496 lgrpid = strtoulW( szValue, NULL, 16 );
4498 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
4499 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
4501 if (lgrpid == lpProcs->lgrpid)
4503 LCID lcid;
4505 lcid = strtoulW( szNumber, NULL, 16 );
4507 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
4508 * '00000437 ;Georgian'
4509 * At present we only pass the LCID string.
4512 if (lpProcs->procW)
4513 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
4514 else
4516 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
4518 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
4520 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
4524 ulIndex++;
4526 else
4528 /* Finished enumerating this key */
4529 if (!bAlternate)
4531 /* Enumerate alternate sorts also */
4532 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4533 bAlternate = TRUE;
4534 ulIndex = 0;
4536 else
4537 bContinue = FALSE; /* Finished both keys */
4540 if (!bContinue)
4541 break;
4544 if (hKey)
4545 NtClose( hKey );
4547 return TRUE;
4550 /******************************************************************************
4551 * EnumLanguageGroupLocalesA (KERNEL32.@)
4553 * Call a users function for every locale in a language group available on the system.
4555 * PARAMS
4556 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4557 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4558 * dwFlags [I] Reserved, set to 0
4559 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4561 * RETURNS
4562 * Success: TRUE.
4563 * Failure: FALSE. Use GetLastError() to determine the cause.
4565 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4566 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4568 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4570 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4572 callbacks.procA = pLangGrpLcEnumProc;
4573 callbacks.procW = NULL;
4574 callbacks.dwFlags = dwFlags;
4575 callbacks.lgrpid = lgrpid;
4576 callbacks.lParam = lParam;
4578 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4581 /******************************************************************************
4582 * EnumLanguageGroupLocalesW (KERNEL32.@)
4584 * See EnumLanguageGroupLocalesA.
4586 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4587 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4589 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4591 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4593 callbacks.procA = NULL;
4594 callbacks.procW = pLangGrpLcEnumProc;
4595 callbacks.dwFlags = dwFlags;
4596 callbacks.lgrpid = lgrpid;
4597 callbacks.lParam = lParam;
4599 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4602 /******************************************************************************
4603 * InvalidateNLSCache (KERNEL32.@)
4605 * Invalidate the cache of NLS values.
4607 * PARAMS
4608 * None.
4610 * RETURNS
4611 * Success: TRUE.
4612 * Failure: FALSE.
4614 BOOL WINAPI InvalidateNLSCache(void)
4616 FIXME("() stub\n");
4617 return FALSE;
4620 /******************************************************************************
4621 * GetUserGeoID (KERNEL32.@)
4623 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4625 GEOID ret = GEOID_NOT_AVAILABLE;
4626 static const WCHAR geoW[] = {'G','e','o',0};
4627 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4628 WCHAR bufferW[40], *end;
4629 DWORD count;
4630 HANDLE hkey, hSubkey = 0;
4631 UNICODE_STRING keyW;
4632 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4633 RtlInitUnicodeString( &keyW, nationW );
4634 count = sizeof(bufferW);
4636 if(!(hkey = create_registry_key())) return ret;
4638 switch( GeoClass ){
4639 case GEOCLASS_NATION:
4640 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4642 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4643 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4644 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4646 break;
4647 case GEOCLASS_REGION:
4648 FIXME("GEOCLASS_REGION not handled yet\n");
4649 break;
4652 NtClose(hkey);
4653 if (hSubkey) NtClose(hSubkey);
4654 return ret;
4657 /******************************************************************************
4658 * SetUserGeoID (KERNEL32.@)
4660 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4662 static const WCHAR geoW[] = {'G','e','o',0};
4663 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4664 static const WCHAR formatW[] = {'%','i',0};
4665 UNICODE_STRING nameW,keyW;
4666 WCHAR bufferW[10];
4667 OBJECT_ATTRIBUTES attr;
4668 HANDLE hkey;
4670 if(!(hkey = create_registry_key())) return FALSE;
4672 attr.Length = sizeof(attr);
4673 attr.RootDirectory = hkey;
4674 attr.ObjectName = &nameW;
4675 attr.Attributes = 0;
4676 attr.SecurityDescriptor = NULL;
4677 attr.SecurityQualityOfService = NULL;
4678 RtlInitUnicodeString( &nameW, geoW );
4679 RtlInitUnicodeString( &keyW, nationW );
4681 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4684 NtClose(attr.RootDirectory);
4685 return FALSE;
4688 sprintfW(bufferW, formatW, GeoID);
4689 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4690 NtClose(attr.RootDirectory);
4691 NtClose(hkey);
4692 return TRUE;
4695 typedef struct
4697 union
4699 UILANGUAGE_ENUMPROCA procA;
4700 UILANGUAGE_ENUMPROCW procW;
4701 } u;
4702 DWORD flags;
4703 LONG_PTR param;
4704 } ENUM_UILANG_CALLBACK;
4706 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4707 LPCSTR name, WORD LangID, LONG_PTR lParam )
4709 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4710 char buf[20];
4712 sprintf(buf, "%08x", (UINT)LangID);
4713 return enum_uilang->u.procA( buf, enum_uilang->param );
4716 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4717 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4719 static const WCHAR formatW[] = {'%','0','8','x',0};
4720 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4721 WCHAR buf[20];
4723 sprintfW( buf, formatW, (UINT)LangID );
4724 return enum_uilang->u.procW( buf, enum_uilang->param );
4727 /******************************************************************************
4728 * EnumUILanguagesA (KERNEL32.@)
4730 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4732 ENUM_UILANG_CALLBACK enum_uilang;
4734 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4736 if(!pUILangEnumProc) {
4737 SetLastError(ERROR_INVALID_PARAMETER);
4738 return FALSE;
4740 if(dwFlags) {
4741 SetLastError(ERROR_INVALID_FLAGS);
4742 return FALSE;
4745 enum_uilang.u.procA = pUILangEnumProc;
4746 enum_uilang.flags = dwFlags;
4747 enum_uilang.param = lParam;
4749 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4750 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4751 (LONG_PTR)&enum_uilang);
4752 return TRUE;
4755 /******************************************************************************
4756 * EnumUILanguagesW (KERNEL32.@)
4758 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4760 ENUM_UILANG_CALLBACK enum_uilang;
4762 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4765 if(!pUILangEnumProc) {
4766 SetLastError(ERROR_INVALID_PARAMETER);
4767 return FALSE;
4769 if(dwFlags) {
4770 SetLastError(ERROR_INVALID_FLAGS);
4771 return FALSE;
4774 enum_uilang.u.procW = pUILangEnumProc;
4775 enum_uilang.flags = dwFlags;
4776 enum_uilang.param = lParam;
4778 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4779 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4780 (LONG_PTR)&enum_uilang);
4781 return TRUE;
4784 enum locationkind {
4785 LOCATION_NATION = 0,
4786 LOCATION_REGION,
4787 LOCATION_BOTH
4790 struct geoinfo_t {
4791 GEOID id;
4792 WCHAR iso2W[3];
4793 WCHAR iso3W[4];
4794 GEOID parent;
4795 INT uncode;
4796 enum locationkind kind;
4799 static const struct geoinfo_t geoinfodata[] = {
4800 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4801 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4802 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4803 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4804 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4805 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4806 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4807 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4808 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4809 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4810 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4811 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4812 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4813 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4814 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4815 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4816 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4817 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4818 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4819 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4820 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4821 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4822 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4823 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4824 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4825 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4826 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4827 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4828 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4829 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4830 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4831 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4832 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4833 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4834 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4835 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4836 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4837 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4838 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4839 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4840 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4841 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4842 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4843 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4844 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4845 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4846 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4847 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4848 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4849 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4850 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4851 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4852 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4853 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4854 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4855 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4856 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4857 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4858 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4859 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4860 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4861 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4862 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4863 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4864 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4865 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4866 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4867 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4868 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4869 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4870 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4871 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4872 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4873 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4874 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4875 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4876 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4877 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4878 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4879 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4880 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4881 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4882 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4883 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4884 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4885 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4886 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4887 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4888 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4889 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4890 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4891 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4892 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4893 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4894 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4895 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4896 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4897 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4898 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4899 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4900 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4901 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4902 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4903 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4904 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4905 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4906 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4907 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4908 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4909 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4910 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4911 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4912 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4913 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4914 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4915 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4916 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4917 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4918 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4919 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4920 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4921 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4922 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4923 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4924 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4925 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4926 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4927 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4928 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4929 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4930 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4931 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4932 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4933 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4934 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4935 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4936 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4937 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4938 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4939 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4940 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4941 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4942 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4943 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4944 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4945 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4946 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4947 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4948 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4949 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4950 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4951 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4952 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4953 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4954 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4955 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4956 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4957 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4958 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4959 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4960 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4961 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4962 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4963 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4964 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4965 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4966 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4967 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4968 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4969 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4970 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4971 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4972 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4973 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4974 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4975 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4976 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4977 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4978 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4979 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4980 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4981 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4982 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4983 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4984 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4985 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4986 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4987 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4988 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4989 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4990 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4991 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4992 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4993 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4994 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4995 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4996 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4997 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4998 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4999 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
5000 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
5001 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
5002 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
5003 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
5004 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
5005 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
5006 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
5007 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
5008 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
5009 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
5010 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
5011 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
5012 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
5013 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
5014 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
5015 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
5016 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
5017 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
5018 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
5019 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
5020 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
5021 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
5022 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
5023 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
5024 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
5025 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
5026 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
5027 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
5028 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
5029 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
5030 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
5031 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
5032 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
5033 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
5034 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
5035 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
5036 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
5037 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
5038 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
5039 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
5040 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
5041 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
5042 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
5043 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
5044 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
5045 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
5046 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
5047 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
5048 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
5049 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
5050 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
5051 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
5052 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
5053 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
5054 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
5055 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
5056 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
5057 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
5058 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
5059 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
5060 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
5061 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
5062 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
5063 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
5064 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
5065 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
5066 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
5067 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
5068 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
5069 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
5070 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
5071 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
5072 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
5073 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
5074 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
5075 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
5076 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
5077 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
5078 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
5079 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
5080 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
5081 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
5082 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
5083 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
5084 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
5085 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
5086 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
5087 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
5088 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
5089 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
5090 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
5091 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
5092 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
5093 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
5094 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
5095 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
5096 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
5097 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
5098 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
5101 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
5103 int min, max;
5105 min = 0;
5106 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
5108 while (min <= max) {
5109 const struct geoinfo_t *ptr;
5110 int n = (min+max)/2;
5112 ptr = &geoinfodata[n];
5113 if (geoid == ptr->id)
5114 /* we don't need empty entries */
5115 return *ptr->iso2W ? ptr : NULL;
5117 if (ptr->id > geoid)
5118 max = n-1;
5119 else
5120 min = n+1;
5123 return NULL;
5126 /******************************************************************************
5127 * GetGeoInfoW (KERNEL32.@)
5129 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
5131 const struct geoinfo_t *ptr;
5132 const WCHAR *str = NULL;
5133 WCHAR buffW[12];
5134 LONG val = 0;
5135 INT len;
5137 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5139 if (!(ptr = get_geoinfo_dataptr(geoid))) {
5140 SetLastError(ERROR_INVALID_PARAMETER);
5141 return 0;
5144 switch (geotype) {
5145 case GEO_NATION:
5146 val = geoid;
5147 break;
5148 case GEO_ISO_UN_NUMBER:
5149 val = ptr->uncode;
5150 break;
5151 case GEO_PARENT:
5152 val = ptr->parent;
5153 break;
5154 case GEO_ISO2:
5155 case GEO_ISO3:
5157 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
5158 break;
5160 case GEO_RFC1766:
5161 case GEO_LCID:
5162 case GEO_FRIENDLYNAME:
5163 case GEO_OFFICIALNAME:
5164 case GEO_TIMEZONES:
5165 case GEO_OFFICIALLANGUAGES:
5166 case GEO_LATITUDE:
5167 case GEO_LONGITUDE:
5168 FIXME("type %d is not supported\n", geotype);
5169 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5170 return 0;
5171 default:
5172 WARN("unrecognized type %d\n", geotype);
5173 SetLastError(ERROR_INVALID_FLAGS);
5174 return 0;
5177 if (val) {
5178 static const WCHAR fmtW[] = {'%','d',0};
5179 sprintfW(buffW, fmtW, val);
5180 str = buffW;
5183 len = strlenW(str) + 1;
5184 if (!data || !data_len)
5185 return len;
5187 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
5188 if (data_len < len)
5189 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5190 return data_len < len ? 0 : len;
5193 /******************************************************************************
5194 * GetGeoInfoA (KERNEL32.@)
5196 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
5198 WCHAR *buffW;
5199 INT len;
5201 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
5203 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
5204 if (!len)
5205 return 0;
5207 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
5208 if (!buffW)
5209 return 0;
5211 GetGeoInfoW(geoid, geotype, buffW, len, lang);
5212 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
5213 if (!data || !data_len) {
5214 HeapFree(GetProcessHeap(), 0, buffW);
5215 return len;
5218 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
5219 HeapFree(GetProcessHeap(), 0, buffW);
5221 if (data_len < len)
5222 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5223 return data_len < len ? 0 : len;
5226 /******************************************************************************
5227 * EnumSystemGeoID (KERNEL32.@)
5229 * Call a users function for every location available on the system.
5231 * PARAMS
5232 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
5233 * parent [I] GEOID for the parent
5234 * enumproc [I] Callback function to call for each location
5236 * RETURNS
5237 * Success: TRUE.
5238 * Failure: FALSE. Use GetLastError() to determine the cause.
5240 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
5242 INT i;
5244 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
5246 if (!enumproc) {
5247 SetLastError(ERROR_INVALID_PARAMETER);
5248 return FALSE;
5251 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
5252 SetLastError(ERROR_INVALID_FLAGS);
5253 return FALSE;
5256 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
5257 const struct geoinfo_t *ptr = &geoinfodata[i];
5259 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
5260 continue;
5262 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
5263 continue;
5265 if (parent && ptr->parent != parent)
5266 continue;
5268 if (!enumproc(ptr->id))
5269 return TRUE;
5272 return TRUE;
5275 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
5277 LCID userlcid;
5279 TRACE("%p, %d\n", localename, buffersize);
5281 userlcid = GetUserDefaultLCID();
5282 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
5285 /******************************************************************************
5286 * NormalizeString (KERNEL32.@)
5288 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
5289 LPWSTR lpDstString, INT cwDstLength)
5291 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
5292 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5293 return 0;
5296 /******************************************************************************
5297 * IsNormalizedString (KERNEL32.@)
5299 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
5301 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
5302 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5303 return FALSE;
5306 enum {
5307 BASE = 36,
5308 TMIN = 1,
5309 TMAX = 26,
5310 SKEW = 38,
5311 DAMP = 700,
5312 INIT_BIAS = 72,
5313 INIT_N = 128
5316 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
5318 INT k;
5320 delta /= (firsttime ? DAMP : 2);
5321 delta += delta/numpoints;
5323 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
5324 delta /= BASE-TMIN;
5325 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
5328 /******************************************************************************
5329 * IdnToAscii (KERNEL32.@)
5330 * Implementation of Punycode based on RFC 3492.
5332 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5333 LPWSTR lpASCIICharStr, INT cchASCIIChar)
5335 static const WCHAR prefixW[] = {'x','n','-','-'};
5337 WCHAR *norm_str;
5338 INT i, label_start, label_end, norm_len, out_label, out = 0;
5340 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5341 lpASCIICharStr, cchASCIIChar);
5343 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
5344 if(!norm_len)
5345 return 0;
5346 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
5347 if(!norm_str) {
5348 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5349 return 0;
5351 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
5352 cchUnicodeChar, norm_str, norm_len);
5353 if(!norm_len) {
5354 HeapFree(GetProcessHeap(), 0, norm_str);
5355 return 0;
5358 for(label_start=0; label_start<norm_len;) {
5359 INT n = INIT_N, bias = INIT_BIAS;
5360 INT delta = 0, b = 0, h;
5362 out_label = out;
5363 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
5364 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
5365 if(norm_str[i] < 0x80)
5366 b++;
5367 label_end = i;
5369 if(b == label_end-label_start) {
5370 if(label_end < norm_len)
5371 b++;
5372 if(!lpASCIICharStr) {
5373 out += b;
5374 }else if(out+b <= cchASCIIChar) {
5375 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
5376 out += b;
5377 }else {
5378 HeapFree(GetProcessHeap(), 0, norm_str);
5379 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5380 return 0;
5382 label_start = label_end+1;
5383 continue;
5386 if(!lpASCIICharStr) {
5387 out += 5+b; /* strlen(xn--...-) */
5388 }else if(out+5+b <= cchASCIIChar) {
5389 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
5390 out += 4;
5391 for(i=label_start; i<label_end; i++)
5392 if(norm_str[i] < 0x80)
5393 lpASCIICharStr[out++] = norm_str[i];
5394 lpASCIICharStr[out++] = '-';
5395 }else {
5396 HeapFree(GetProcessHeap(), 0, norm_str);
5397 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5398 return 0;
5400 if(!b)
5401 out--;
5403 for(h=b; h<label_end-label_start;) {
5404 INT m = 0xffff, q, k;
5406 for(i=label_start; i<label_end; i++) {
5407 if(norm_str[i]>=n && m>norm_str[i])
5408 m = norm_str[i];
5410 delta += (m-n)*(h+1);
5411 n = m;
5413 for(i=label_start; i<label_end; i++) {
5414 if(norm_str[i] < n) {
5415 delta++;
5416 }else if(norm_str[i] == n) {
5417 for(q=delta, k=BASE; ; k+=BASE) {
5418 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5419 INT disp = q<t ? q : t+(q-t)%(BASE-t);
5420 if(!lpASCIICharStr) {
5421 out++;
5422 }else if(out+1 <= cchASCIIChar) {
5423 lpASCIICharStr[out++] = disp<='z'-'a' ?
5424 'a'+disp : '0'+disp-'z'+'a'-1;
5425 }else {
5426 HeapFree(GetProcessHeap(), 0, norm_str);
5427 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5428 return 0;
5430 if(q < t)
5431 break;
5432 q = (q-t)/(BASE-t);
5434 bias = adapt(delta, h+1, h==b);
5435 delta = 0;
5436 h++;
5439 delta++;
5440 n++;
5443 if(out-out_label > 63) {
5444 HeapFree(GetProcessHeap(), 0, norm_str);
5445 SetLastError(ERROR_INVALID_NAME);
5446 return 0;
5449 if(label_end < norm_len) {
5450 if(!lpASCIICharStr) {
5451 out++;
5452 }else if(out+1 <= cchASCIIChar) {
5453 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
5454 }else {
5455 HeapFree(GetProcessHeap(), 0, norm_str);
5456 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5457 return 0;
5460 label_start = label_end+1;
5463 HeapFree(GetProcessHeap(), 0, norm_str);
5464 return out;
5467 /******************************************************************************
5468 * IdnToNameprepUnicode (KERNEL32.@)
5470 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
5471 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
5473 enum {
5474 UNASSIGNED = 0x1,
5475 PROHIBITED = 0x2,
5476 BIDI_RAL = 0x4,
5477 BIDI_L = 0x8
5480 extern const unsigned short nameprep_char_type[] DECLSPEC_HIDDEN;
5481 extern const WCHAR nameprep_mapping[] DECLSPEC_HIDDEN;
5482 const WCHAR *ptr;
5483 WORD flags;
5484 WCHAR buf[64], *map_str, norm_str[64], ch;
5485 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
5486 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
5488 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
5489 lpNameprepCharStr, cchNameprepChar);
5491 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
5492 SetLastError(ERROR_INVALID_FLAGS);
5493 return 0;
5496 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
5497 SetLastError(ERROR_INVALID_PARAMETER);
5498 return 0;
5501 if(cchUnicodeChar == -1)
5502 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
5503 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
5504 SetLastError(ERROR_INVALID_NAME);
5505 return 0;
5508 for(label_start=0; label_start<cchUnicodeChar;) {
5509 ascii_only = TRUE;
5510 for(i=label_start; i<cchUnicodeChar; i++) {
5511 ch = lpUnicodeCharStr[i];
5513 if(i!=cchUnicodeChar-1 && !ch) {
5514 SetLastError(ERROR_INVALID_NAME);
5515 return 0;
5517 /* check if ch is one of label separators defined in RFC3490 */
5518 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
5519 break;
5521 if(ch > 0x7f) {
5522 ascii_only = FALSE;
5523 continue;
5526 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5527 continue;
5528 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5529 || (ch>='0' && ch<='9') || ch=='-')
5530 continue;
5532 SetLastError(ERROR_INVALID_NAME);
5533 return 0;
5535 label_end = i;
5536 /* last label may be empty */
5537 if(label_start==label_end && ch) {
5538 SetLastError(ERROR_INVALID_NAME);
5539 return 0;
5542 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5543 lpUnicodeCharStr[label_end-1]=='-')) {
5544 SetLastError(ERROR_INVALID_NAME);
5545 return 0;
5548 if(ascii_only) {
5549 /* maximal label length is 63 characters */
5550 if(label_end-label_start > 63) {
5551 SetLastError(ERROR_INVALID_NAME);
5552 return 0;
5554 if(label_end < cchUnicodeChar)
5555 label_end++;
5557 if(!lpNameprepCharStr) {
5558 out += label_end-label_start;
5559 }else if(out+label_end-label_start <= cchNameprepChar) {
5560 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5561 (label_end-label_start)*sizeof(WCHAR));
5562 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5563 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5564 out += label_end-label_start;
5565 }else {
5566 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5567 return 0;
5570 label_start = label_end;
5571 continue;
5574 map_len = 0;
5575 for(i=label_start; i<label_end; i++) {
5576 ch = lpUnicodeCharStr[i];
5577 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5578 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5580 if(!ptr[0]) map_len++;
5581 else if(!ptr[1]) map_len++;
5582 else if(!ptr[2]) map_len += 2;
5583 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5585 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5586 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5587 if(!map_str) {
5588 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5589 return 0;
5591 }else {
5592 map_str = buf;
5594 map_len = 0;
5595 for(i=label_start; i<label_end; i++) {
5596 ch = lpUnicodeCharStr[i];
5597 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5598 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5600 if(!ptr[0]) {
5601 map_str[map_len++] = ch;
5602 }else if(!ptr[1]) {
5603 map_str[map_len++] = ptr[0];
5604 }else if(!ptr[2]) {
5605 map_str[map_len++] = ptr[0];
5606 map_str[map_len++] = ptr[1];
5607 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5608 map_str[map_len++] = ptr[0];
5609 map_str[map_len++] = ptr[1];
5610 map_str[map_len++] = ptr[2];
5614 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5615 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5616 if(map_str != buf)
5617 HeapFree(GetProcessHeap(), 0, map_str);
5618 if(!norm_len) {
5619 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5620 SetLastError(ERROR_INVALID_NAME);
5621 return 0;
5624 if(label_end < cchUnicodeChar) {
5625 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5626 label_end++;
5629 if(!lpNameprepCharStr) {
5630 out += norm_len;
5631 }else if(out+norm_len <= cchNameprepChar) {
5632 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5633 out += norm_len;
5634 }else {
5635 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5636 return 0;
5639 have_bidi_ral = prohibit_bidi_ral = FALSE;
5640 mask = PROHIBITED;
5641 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5642 mask |= UNASSIGNED;
5643 for(i=0; i<norm_len; i++) {
5644 ch = norm_str[i];
5645 flags = get_table_entry( nameprep_char_type, ch );
5647 if(flags & mask) {
5648 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5649 : ERROR_NO_UNICODE_TRANSLATION);
5650 return 0;
5653 if(flags & BIDI_RAL)
5654 have_bidi_ral = TRUE;
5655 if(flags & BIDI_L)
5656 prohibit_bidi_ral = TRUE;
5659 if(have_bidi_ral) {
5660 ch = norm_str[0];
5661 flags = get_table_entry( nameprep_char_type, ch );
5662 if((flags & BIDI_RAL) == 0)
5663 prohibit_bidi_ral = TRUE;
5665 ch = norm_str[norm_len-1];
5666 flags = get_table_entry( nameprep_char_type, ch );
5667 if((flags & BIDI_RAL) == 0)
5668 prohibit_bidi_ral = TRUE;
5671 if(have_bidi_ral && prohibit_bidi_ral) {
5672 SetLastError(ERROR_INVALID_NAME);
5673 return 0;
5676 label_start = label_end;
5679 return out;
5682 /******************************************************************************
5683 * IdnToUnicode (KERNEL32.@)
5685 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5686 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5688 extern const unsigned short nameprep_char_type[];
5690 INT i, label_start, label_end, out_label, out = 0;
5691 WCHAR ch;
5693 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5694 lpUnicodeCharStr, cchUnicodeChar);
5696 for(label_start=0; label_start<cchASCIIChar;) {
5697 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5699 out_label = out;
5700 for(i=label_start; i<cchASCIIChar; i++) {
5701 ch = lpASCIICharStr[i];
5703 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5704 SetLastError(ERROR_INVALID_NAME);
5705 return 0;
5708 if(!ch || ch=='.')
5709 break;
5710 if(ch == '-')
5711 delim = i;
5713 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5714 continue;
5715 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5716 || (ch>='0' && ch<='9') || ch=='-')
5717 continue;
5719 SetLastError(ERROR_INVALID_NAME);
5720 return 0;
5722 label_end = i;
5723 /* last label may be empty */
5724 if(label_start==label_end && ch) {
5725 SetLastError(ERROR_INVALID_NAME);
5726 return 0;
5729 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5730 lpASCIICharStr[label_end-1]=='-')) {
5731 SetLastError(ERROR_INVALID_NAME);
5732 return 0;
5734 if(label_end-label_start > 63) {
5735 SetLastError(ERROR_INVALID_NAME);
5736 return 0;
5739 if(label_end-label_start<4 ||
5740 tolowerW(lpASCIICharStr[label_start])!='x' ||
5741 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5742 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5743 if(label_end < cchASCIIChar)
5744 label_end++;
5746 if(!lpUnicodeCharStr) {
5747 out += label_end-label_start;
5748 }else if(out+label_end-label_start <= cchUnicodeChar) {
5749 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5750 (label_end-label_start)*sizeof(WCHAR));
5751 out += label_end-label_start;
5752 }else {
5753 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5754 return 0;
5757 label_start = label_end;
5758 continue;
5761 if(delim == label_start+3)
5762 delim++;
5763 if(!lpUnicodeCharStr) {
5764 out += delim-label_start-4;
5765 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5766 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5767 (delim-label_start-4)*sizeof(WCHAR));
5768 out += delim-label_start-4;
5769 }else {
5770 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5771 return 0;
5773 if(out != out_label)
5774 delim++;
5776 for(i=delim; i<label_end;) {
5777 old_pos = pos;
5778 w = 1;
5779 for(k=BASE; ; k+=BASE) {
5780 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5781 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5782 SetLastError(ERROR_INVALID_NAME);
5783 return 0;
5785 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5786 pos += digit*w;
5787 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5788 if(digit < t)
5789 break;
5790 w *= BASE-t;
5792 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5793 n += pos/(out-out_label+1);
5794 pos %= out-out_label+1;
5796 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5797 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5798 SetLastError(ERROR_INVALID_NAME);
5799 return 0;
5801 if(!lpUnicodeCharStr) {
5802 out++;
5803 }else if(out+1 <= cchASCIIChar) {
5804 memmove(lpUnicodeCharStr+out_label+pos+1,
5805 lpUnicodeCharStr+out_label+pos,
5806 (out-out_label-pos)*sizeof(WCHAR));
5807 lpUnicodeCharStr[out_label+pos] = n;
5808 out++;
5809 }else {
5810 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5811 return 0;
5813 pos++;
5816 if(out-out_label > 63) {
5817 SetLastError(ERROR_INVALID_NAME);
5818 return 0;
5821 if(label_end < cchASCIIChar) {
5822 if(!lpUnicodeCharStr) {
5823 out++;
5824 }else if(out+1 <= cchUnicodeChar) {
5825 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5826 }else {
5827 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5828 return 0;
5831 label_start = label_end+1;
5834 return out;
5838 /******************************************************************************
5839 * GetFileMUIPath (KERNEL32.@)
5842 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5843 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5845 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5846 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5848 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5850 return FALSE;
5853 /******************************************************************************
5854 * GetFileMUIInfo (KERNEL32.@)
5857 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5859 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5861 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5862 return FALSE;