wined3d: Avoid the deprecated syntax for SM3 varyings on core profile.
[wine/multimedia.git] / dlls / kernel32 / locale.c
blobc0a66efd56317e0d6a42223e2e4b6ca06f43d259
1 /*
2 * Locale support
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2002 Alexandre Julliard for CodeWeavers
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFLocale.h>
37 # include <CoreFoundation/CFString.h>
38 #endif
40 #include "ntstatus.h"
41 #define WIN32_NO_STATUS
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h" /* for RT_STRINGW */
45 #include "winternl.h"
46 #include "wine/unicode.h"
47 #include "winnls.h"
48 #include "winerror.h"
49 #include "winver.h"
50 #include "kernel_private.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(nls);
55 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
56 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
58 /* current code pages */
59 static const union cptable *ansi_cptable;
60 static const union cptable *oem_cptable;
61 static const union cptable *mac_cptable;
62 static const union cptable *unix_cptable; /* NULL if UTF8 */
64 static const WCHAR szLocaleKeyName[] = {
65 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
66 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
67 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
70 static const WCHAR szLangGroupsKeyName[] = {
71 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
72 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
73 'C','o','n','t','r','o','l','\\','N','l','s','\\',
74 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
77 /* Charset to codepage map, sorted by name. */
78 static const struct charset_entry
80 const char *charset_name;
81 UINT codepage;
82 } charset_names[] =
84 { "BIG5", 950 },
85 { "CP1250", 1250 },
86 { "CP1251", 1251 },
87 { "CP1252", 1252 },
88 { "CP1253", 1253 },
89 { "CP1254", 1254 },
90 { "CP1255", 1255 },
91 { "CP1256", 1256 },
92 { "CP1257", 1257 },
93 { "CP1258", 1258 },
94 { "CP932", 932 },
95 { "CP936", 936 },
96 { "CP949", 949 },
97 { "CP950", 950 },
98 { "EUCJP", 20932 },
99 { "GB2312", 936 },
100 { "IBM037", 37 },
101 { "IBM1026", 1026 },
102 { "IBM424", 424 },
103 { "IBM437", 437 },
104 { "IBM500", 500 },
105 { "IBM850", 850 },
106 { "IBM852", 852 },
107 { "IBM855", 855 },
108 { "IBM857", 857 },
109 { "IBM860", 860 },
110 { "IBM861", 861 },
111 { "IBM862", 862 },
112 { "IBM863", 863 },
113 { "IBM864", 864 },
114 { "IBM865", 865 },
115 { "IBM866", 866 },
116 { "IBM869", 869 },
117 { "IBM874", 874 },
118 { "IBM875", 875 },
119 { "ISO88591", 28591 },
120 { "ISO885910", 28600 },
121 { "ISO885913", 28603 },
122 { "ISO885914", 28604 },
123 { "ISO885915", 28605 },
124 { "ISO885916", 28606 },
125 { "ISO88592", 28592 },
126 { "ISO88593", 28593 },
127 { "ISO88594", 28594 },
128 { "ISO88595", 28595 },
129 { "ISO88596", 28596 },
130 { "ISO88597", 28597 },
131 { "ISO88598", 28598 },
132 { "ISO88599", 28599 },
133 { "KOI8R", 20866 },
134 { "KOI8U", 21866 },
135 { "UTF8", CP_UTF8 }
139 struct locale_name
141 WCHAR win_name[128]; /* Windows name ("en-US") */
142 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
143 WCHAR *country; /* country ("US") */
144 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
145 WCHAR *script; /* script ("Latn") for Windows format only */
146 WCHAR *modifier; /* modifier or sort order */
147 LCID lcid; /* corresponding LCID */
148 int matches; /* number of elements matching LCID (0..4) */
149 UINT codepage; /* codepage corresponding to charset */
152 /* locale ids corresponding to the various Unix locale parameters */
153 static LCID lcid_LC_COLLATE;
154 static LCID lcid_LC_CTYPE;
155 static LCID lcid_LC_MESSAGES;
156 static LCID lcid_LC_MONETARY;
157 static LCID lcid_LC_NUMERIC;
158 static LCID lcid_LC_TIME;
159 static LCID lcid_LC_PAPER;
160 static LCID lcid_LC_MEASUREMENT;
161 static LCID lcid_LC_TELEPHONE;
163 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
164 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
165 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
166 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
167 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
168 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
169 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
170 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
171 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
172 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
173 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
174 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
175 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
176 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
177 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
178 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
179 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
180 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
181 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
182 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
183 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
184 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
185 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
186 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
187 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
188 static const WCHAR sListW[] = {'s','L','i','s','t',0};
189 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
190 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
191 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
192 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
193 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
194 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
195 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
196 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
197 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
198 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
199 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
200 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
201 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
203 static struct registry_value
205 DWORD lctype;
206 const WCHAR *name;
207 WCHAR *cached_value;
208 } registry_values[] =
210 { LOCALE_ICALENDARTYPE, iCalendarTypeW },
211 { LOCALE_ICURRDIGITS, iCurrDigitsW },
212 { LOCALE_ICURRENCY, iCurrencyW },
213 { LOCALE_IDIGITS, iDigitsW },
214 { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
215 { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
216 { LOCALE_ILZERO, iLZeroW },
217 { LOCALE_IMEASURE, iMeasureW },
218 { LOCALE_INEGCURR, iNegCurrW },
219 { LOCALE_INEGNUMBER, iNegNumberW },
220 { LOCALE_IPAPERSIZE, iPaperSizeW },
221 { LOCALE_ITIME, iTimeW },
222 { LOCALE_S1159, s1159W },
223 { LOCALE_S2359, s2359W },
224 { LOCALE_SCURRENCY, sCurrencyW },
225 { LOCALE_SDATE, sDateW },
226 { LOCALE_SDECIMAL, sDecimalW },
227 { LOCALE_SGROUPING, sGroupingW },
228 { LOCALE_SLIST, sListW },
229 { LOCALE_SLONGDATE, sLongDateW },
230 { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
231 { LOCALE_SMONGROUPING, sMonGroupingW },
232 { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
233 { LOCALE_SNEGATIVESIGN, sNegativeSignW },
234 { LOCALE_SPOSITIVESIGN, sPositiveSignW },
235 { LOCALE_SSHORTDATE, sShortDateW },
236 { LOCALE_STHOUSAND, sThousandW },
237 { LOCALE_STIME, sTimeW },
238 { LOCALE_STIMEFORMAT, sTimeFormatW },
239 { LOCALE_SYEARMONTH, sYearMonthW },
240 /* The following are not listed under MSDN as supported,
241 * but seem to be used and also stored in the registry.
243 { LOCALE_ICOUNTRY, iCountryW },
244 { LOCALE_IDATE, iDateW },
245 { LOCALE_ILDATE, iLDateW },
246 { LOCALE_ITLZERO, iTLZeroW },
247 { LOCALE_SCOUNTRY, sCountryW },
248 { LOCALE_SABBREVLANGNAME, sLanguageW },
249 /* The following are used in XP and later */
250 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
251 { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
252 { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
255 static CRITICAL_SECTION cache_section;
256 static CRITICAL_SECTION_DEBUG critsect_debug =
258 0, 0, &cache_section,
259 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
260 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
262 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
264 /* Copy Ascii string to Unicode without using codepages */
265 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
267 while (n > 1 && *src)
269 *dst++ = (unsigned char)*src++;
270 n--;
272 if (n) *dst = 0;
275 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
277 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
280 /***********************************************************************
281 * get_lcid_codepage
283 * Retrieve the ANSI codepage for a given locale.
285 static inline UINT get_lcid_codepage( LCID lcid )
287 UINT ret;
288 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
289 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
290 return ret;
294 /***********************************************************************
295 * get_codepage_table
297 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
299 static const union cptable *get_codepage_table( unsigned int codepage )
301 const union cptable *ret = NULL;
303 assert( ansi_cptable ); /* init must have been done already */
305 switch(codepage)
307 case CP_ACP:
308 return ansi_cptable;
309 case CP_OEMCP:
310 return oem_cptable;
311 case CP_MACCP:
312 return mac_cptable;
313 case CP_UTF7:
314 case CP_UTF8:
315 break;
316 case CP_THREAD_ACP:
317 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
318 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
319 if (!codepage) return ansi_cptable;
320 /* fall through */
321 default:
322 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
323 if (codepage == oem_cptable->info.codepage) return oem_cptable;
324 if (codepage == mac_cptable->info.codepage) return mac_cptable;
325 ret = wine_cp_get_table( codepage );
326 break;
328 return ret;
332 /***********************************************************************
333 * charset_cmp (internal)
335 static int charset_cmp( const void *name, const void *entry )
337 const struct charset_entry *charset = entry;
338 return strcasecmp( name, charset->charset_name );
341 /***********************************************************************
342 * find_charset
344 static UINT find_charset( const WCHAR *name )
346 const struct charset_entry *entry;
347 char charset_name[16];
348 size_t i, j;
350 /* remove punctuation characters from charset name */
351 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
352 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
353 charset_name[j] = 0;
355 entry = bsearch( charset_name, charset_names,
356 sizeof(charset_names)/sizeof(charset_names[0]),
357 sizeof(charset_names[0]), charset_cmp );
358 if (entry) return entry->codepage;
359 return 0;
363 /***********************************************************************
364 * find_locale_id_callback
366 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
367 LPCWSTR name, WORD LangID, LPARAM lParam )
369 struct locale_name *data = (struct locale_name *)lParam;
370 WCHAR buffer[128];
371 int matches = 0;
372 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
374 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
376 /* first check exact name */
377 if (data->win_name[0] &&
378 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
379 buffer, sizeof(buffer)/sizeof(WCHAR) ))
381 if (!strcmpW( data->win_name, buffer ))
383 matches = 4; /* everything matches */
384 goto done;
388 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
389 buffer, sizeof(buffer)/sizeof(WCHAR) ))
390 return TRUE;
391 if (strcmpW( buffer, data->lang )) return TRUE;
392 matches++; /* language name matched */
394 if (data->country)
396 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
397 buffer, sizeof(buffer)/sizeof(WCHAR) ))
399 if (strcmpW( buffer, data->country )) goto done;
400 matches++; /* country name matched */
403 else /* match default language */
405 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
408 if (data->codepage)
410 UINT unix_cp;
411 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
412 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
414 if (unix_cp == data->codepage) matches++;
418 /* FIXME: check sort order */
420 done:
421 if (matches > data->matches)
423 data->lcid = lcid;
424 data->matches = matches;
426 return (data->matches < 4); /* no need to continue for perfect match */
430 /***********************************************************************
431 * parse_locale_name
433 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
434 * Unix format is: lang[_country][.charset][@modifier]
435 * Windows format is: lang[-script][-country][_modifier]
437 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
439 static const WCHAR sepW[] = {'-','_','.','@',0};
440 static const WCHAR winsepW[] = {'-','_',0};
441 static const WCHAR posixW[] = {'P','O','S','I','X',0};
442 static const WCHAR cW[] = {'C',0};
443 static const WCHAR latinW[] = {'l','a','t','i','n',0};
444 static const WCHAR latnW[] = {'-','L','a','t','n',0};
445 WCHAR *p;
447 TRACE("%s\n", debugstr_w(str));
449 name->country = name->charset = name->script = name->modifier = NULL;
450 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
451 name->matches = 0;
452 name->codepage = 0;
453 name->win_name[0] = 0;
454 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
456 if (!*name->lang)
458 name->lcid = LOCALE_INVARIANT;
459 name->matches = 4;
460 return;
463 if (!(p = strpbrkW( name->lang, sepW )))
465 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
467 name->matches = 4; /* perfect match for default English lcid */
468 return;
470 strcpyW( name->win_name, name->lang );
472 else if (*p == '-') /* Windows format */
474 strcpyW( name->win_name, name->lang );
475 *p++ = 0;
476 name->country = p;
477 if (!(p = strpbrkW( p, winsepW ))) goto done;
478 if (*p == '-')
480 *p++ = 0;
481 name->script = name->country;
482 name->country = p;
483 if (!(p = strpbrkW( p, winsepW ))) goto done;
485 *p++ = 0;
486 name->modifier = p;
488 else /* Unix format */
490 if (*p == '_')
492 *p++ = 0;
493 name->country = p;
494 p = strpbrkW( p, sepW + 2 );
496 if (p && *p == '.')
498 *p++ = 0;
499 name->charset = p;
500 p = strchrW( p, '@' );
502 if (p)
504 *p++ = 0;
505 name->modifier = p;
508 if (name->charset)
509 name->codepage = find_charset( name->charset );
511 /* rebuild a Windows name if possible */
513 if (name->charset) goto done; /* can't specify charset in Windows format */
514 if (name->modifier && strcmpW( name->modifier, latinW ))
515 goto done; /* only Latn script supported for now */
516 strcpyW( name->win_name, name->lang );
517 if (name->modifier) strcatW( name->win_name, latnW );
518 if (name->country)
520 p = name->win_name + strlenW(name->win_name);
521 *p++ = '-';
522 strcpyW( p, name->country );
525 done:
526 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
527 find_locale_id_callback, (LPARAM)name );
531 /***********************************************************************
532 * convert_default_lcid
534 * Get the default LCID to use for a given lctype in GetLocaleInfo.
536 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
538 if (lcid == LOCALE_SYSTEM_DEFAULT ||
539 lcid == LOCALE_USER_DEFAULT ||
540 lcid == LOCALE_NEUTRAL)
542 LCID default_id = 0;
544 switch(lctype & 0xffff)
546 case LOCALE_SSORTNAME:
547 default_id = lcid_LC_COLLATE;
548 break;
550 case LOCALE_FONTSIGNATURE:
551 case LOCALE_IDEFAULTANSICODEPAGE:
552 case LOCALE_IDEFAULTCODEPAGE:
553 case LOCALE_IDEFAULTEBCDICCODEPAGE:
554 case LOCALE_IDEFAULTMACCODEPAGE:
555 case LOCALE_IDEFAULTUNIXCODEPAGE:
556 default_id = lcid_LC_CTYPE;
557 break;
559 case LOCALE_ICURRDIGITS:
560 case LOCALE_ICURRENCY:
561 case LOCALE_IINTLCURRDIGITS:
562 case LOCALE_INEGCURR:
563 case LOCALE_INEGSEPBYSPACE:
564 case LOCALE_INEGSIGNPOSN:
565 case LOCALE_INEGSYMPRECEDES:
566 case LOCALE_IPOSSEPBYSPACE:
567 case LOCALE_IPOSSIGNPOSN:
568 case LOCALE_IPOSSYMPRECEDES:
569 case LOCALE_SCURRENCY:
570 case LOCALE_SINTLSYMBOL:
571 case LOCALE_SMONDECIMALSEP:
572 case LOCALE_SMONGROUPING:
573 case LOCALE_SMONTHOUSANDSEP:
574 case LOCALE_SNATIVECURRNAME:
575 default_id = lcid_LC_MONETARY;
576 break;
578 case LOCALE_IDIGITS:
579 case LOCALE_IDIGITSUBSTITUTION:
580 case LOCALE_ILZERO:
581 case LOCALE_INEGNUMBER:
582 case LOCALE_SDECIMAL:
583 case LOCALE_SGROUPING:
584 case LOCALE_SNAN:
585 case LOCALE_SNATIVEDIGITS:
586 case LOCALE_SNEGATIVESIGN:
587 case LOCALE_SNEGINFINITY:
588 case LOCALE_SPOSINFINITY:
589 case LOCALE_SPOSITIVESIGN:
590 case LOCALE_STHOUSAND:
591 default_id = lcid_LC_NUMERIC;
592 break;
594 case LOCALE_ICALENDARTYPE:
595 case LOCALE_ICENTURY:
596 case LOCALE_IDATE:
597 case LOCALE_IDAYLZERO:
598 case LOCALE_IFIRSTDAYOFWEEK:
599 case LOCALE_IFIRSTWEEKOFYEAR:
600 case LOCALE_ILDATE:
601 case LOCALE_IMONLZERO:
602 case LOCALE_IOPTIONALCALENDAR:
603 case LOCALE_ITIME:
604 case LOCALE_ITIMEMARKPOSN:
605 case LOCALE_ITLZERO:
606 case LOCALE_S1159:
607 case LOCALE_S2359:
608 case LOCALE_SABBREVDAYNAME1:
609 case LOCALE_SABBREVDAYNAME2:
610 case LOCALE_SABBREVDAYNAME3:
611 case LOCALE_SABBREVDAYNAME4:
612 case LOCALE_SABBREVDAYNAME5:
613 case LOCALE_SABBREVDAYNAME6:
614 case LOCALE_SABBREVDAYNAME7:
615 case LOCALE_SABBREVMONTHNAME1:
616 case LOCALE_SABBREVMONTHNAME2:
617 case LOCALE_SABBREVMONTHNAME3:
618 case LOCALE_SABBREVMONTHNAME4:
619 case LOCALE_SABBREVMONTHNAME5:
620 case LOCALE_SABBREVMONTHNAME6:
621 case LOCALE_SABBREVMONTHNAME7:
622 case LOCALE_SABBREVMONTHNAME8:
623 case LOCALE_SABBREVMONTHNAME9:
624 case LOCALE_SABBREVMONTHNAME10:
625 case LOCALE_SABBREVMONTHNAME11:
626 case LOCALE_SABBREVMONTHNAME12:
627 case LOCALE_SABBREVMONTHNAME13:
628 case LOCALE_SDATE:
629 case LOCALE_SDAYNAME1:
630 case LOCALE_SDAYNAME2:
631 case LOCALE_SDAYNAME3:
632 case LOCALE_SDAYNAME4:
633 case LOCALE_SDAYNAME5:
634 case LOCALE_SDAYNAME6:
635 case LOCALE_SDAYNAME7:
636 case LOCALE_SDURATION:
637 case LOCALE_SLONGDATE:
638 case LOCALE_SMONTHNAME1:
639 case LOCALE_SMONTHNAME2:
640 case LOCALE_SMONTHNAME3:
641 case LOCALE_SMONTHNAME4:
642 case LOCALE_SMONTHNAME5:
643 case LOCALE_SMONTHNAME6:
644 case LOCALE_SMONTHNAME7:
645 case LOCALE_SMONTHNAME8:
646 case LOCALE_SMONTHNAME9:
647 case LOCALE_SMONTHNAME10:
648 case LOCALE_SMONTHNAME11:
649 case LOCALE_SMONTHNAME12:
650 case LOCALE_SMONTHNAME13:
651 case LOCALE_SSHORTDATE:
652 case LOCALE_SSHORTESTDAYNAME1:
653 case LOCALE_SSHORTESTDAYNAME2:
654 case LOCALE_SSHORTESTDAYNAME3:
655 case LOCALE_SSHORTESTDAYNAME4:
656 case LOCALE_SSHORTESTDAYNAME5:
657 case LOCALE_SSHORTESTDAYNAME6:
658 case LOCALE_SSHORTESTDAYNAME7:
659 case LOCALE_STIME:
660 case LOCALE_STIMEFORMAT:
661 case LOCALE_SYEARMONTH:
662 default_id = lcid_LC_TIME;
663 break;
665 case LOCALE_IPAPERSIZE:
666 default_id = lcid_LC_PAPER;
667 break;
669 case LOCALE_IMEASURE:
670 default_id = lcid_LC_MEASUREMENT;
671 break;
673 case LOCALE_ICOUNTRY:
674 default_id = lcid_LC_TELEPHONE;
675 break;
677 if (default_id) lcid = default_id;
679 return ConvertDefaultLocale( lcid );
682 /***********************************************************************
683 * is_genitive_name_supported
685 * Determine could LCTYPE basically support genitive name form or not.
687 static BOOL is_genitive_name_supported( LCTYPE lctype )
689 switch(lctype & 0xffff)
691 case LOCALE_SMONTHNAME1:
692 case LOCALE_SMONTHNAME2:
693 case LOCALE_SMONTHNAME3:
694 case LOCALE_SMONTHNAME4:
695 case LOCALE_SMONTHNAME5:
696 case LOCALE_SMONTHNAME6:
697 case LOCALE_SMONTHNAME7:
698 case LOCALE_SMONTHNAME8:
699 case LOCALE_SMONTHNAME9:
700 case LOCALE_SMONTHNAME10:
701 case LOCALE_SMONTHNAME11:
702 case LOCALE_SMONTHNAME12:
703 case LOCALE_SMONTHNAME13:
704 return TRUE;
705 default:
706 return FALSE;
710 /***********************************************************************
711 * create_registry_key
713 * Create the Control Panel\\International registry key.
715 static inline HANDLE create_registry_key(void)
717 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
718 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
719 OBJECT_ATTRIBUTES attr;
720 UNICODE_STRING nameW;
721 HANDLE cpl_key, hkey = 0;
723 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
725 attr.Length = sizeof(attr);
726 attr.RootDirectory = hkey;
727 attr.ObjectName = &nameW;
728 attr.Attributes = 0;
729 attr.SecurityDescriptor = NULL;
730 attr.SecurityQualityOfService = NULL;
731 RtlInitUnicodeString( &nameW, cplW );
733 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
735 NtClose( attr.RootDirectory );
736 attr.RootDirectory = cpl_key;
737 RtlInitUnicodeString( &nameW, intlW );
738 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
740 NtClose( attr.RootDirectory );
741 return hkey;
745 /* update the registry settings for a given locale parameter */
746 /* return TRUE if an update was needed */
747 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
748 const LCTYPE *values, UINT nb_values )
750 static const WCHAR formatW[] = { '%','0','8','x',0 };
751 WCHAR bufferW[40];
752 UNICODE_STRING nameW;
753 DWORD count, i;
755 RtlInitUnicodeString( &nameW, name );
756 count = sizeof(bufferW);
757 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
759 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
760 LPCWSTR text = (LPCWSTR)info->Data;
762 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
763 TRACE( "updating registry, locale %s changed %s -> %08x\n",
764 debugstr_w(name), debugstr_w(text), lcid );
766 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
767 sprintfW( bufferW, formatW, lcid );
768 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
770 for (i = 0; i < nb_values; i++)
772 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
773 sizeof(bufferW)/sizeof(WCHAR) );
774 SetLocaleInfoW( lcid, values[i], bufferW );
776 return TRUE;
780 /***********************************************************************
781 * LOCALE_InitRegistry
783 * Update registry contents on startup if the user locale has changed.
784 * This simulates the action of the Windows control panel.
786 void LOCALE_InitRegistry(void)
788 static const WCHAR acpW[] = {'A','C','P',0};
789 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
790 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
791 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
792 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
793 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
794 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
795 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
796 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
797 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
798 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
799 static const struct
801 LPCWSTR name;
802 USHORT value;
803 } update_cp_values[] = {
804 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
805 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
806 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
808 static const LCTYPE lc_messages_values[] = {
809 LOCALE_SABBREVLANGNAME,
810 LOCALE_SCOUNTRY,
811 LOCALE_SLIST };
812 static const LCTYPE lc_monetary_values[] = {
813 LOCALE_SCURRENCY,
814 LOCALE_ICURRENCY,
815 LOCALE_INEGCURR,
816 LOCALE_ICURRDIGITS,
817 LOCALE_ILZERO,
818 LOCALE_SMONDECIMALSEP,
819 LOCALE_SMONGROUPING,
820 LOCALE_SMONTHOUSANDSEP };
821 static const LCTYPE lc_numeric_values[] = {
822 LOCALE_SDECIMAL,
823 LOCALE_STHOUSAND,
824 LOCALE_IDIGITS,
825 LOCALE_IDIGITSUBSTITUTION,
826 LOCALE_SNATIVEDIGITS,
827 LOCALE_INEGNUMBER,
828 LOCALE_SNEGATIVESIGN,
829 LOCALE_SPOSITIVESIGN,
830 LOCALE_SGROUPING };
831 static const LCTYPE lc_time_values[] = {
832 LOCALE_S1159,
833 LOCALE_S2359,
834 LOCALE_STIME,
835 LOCALE_ITIME,
836 LOCALE_ITLZERO,
837 LOCALE_SSHORTDATE,
838 LOCALE_SLONGDATE,
839 LOCALE_SDATE,
840 LOCALE_ITIMEMARKPOSN,
841 LOCALE_ICALENDARTYPE,
842 LOCALE_IFIRSTDAYOFWEEK,
843 LOCALE_IFIRSTWEEKOFYEAR,
844 LOCALE_STIMEFORMAT,
845 LOCALE_SYEARMONTH,
846 LOCALE_IDATE };
847 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
848 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
849 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
851 UNICODE_STRING nameW;
852 WCHAR bufferW[80];
853 DWORD count, i;
854 HANDLE hkey;
855 LCID lcid = GetUserDefaultLCID();
857 if (!(hkey = create_registry_key()))
858 return; /* don't do anything if we can't create the registry key */
860 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
861 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
862 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
863 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
864 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
865 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
866 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
867 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
868 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
869 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
870 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
871 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
872 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
873 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
875 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
877 static const WCHAR codepageW[] =
878 {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
879 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
880 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
882 OBJECT_ATTRIBUTES attr;
883 HANDLE nls_key;
884 DWORD len = 14;
886 RtlInitUnicodeString( &nameW, codepageW );
887 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
888 while (codepageW[len])
890 nameW.Length = len * sizeof(WCHAR);
891 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
892 NtClose( nls_key );
893 len++;
894 while (codepageW[len] && codepageW[len] != '\\') len++;
896 nameW.Length = len * sizeof(WCHAR);
897 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
899 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
901 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
902 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
903 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
904 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
906 NtClose( nls_key );
910 NtClose( hkey );
914 /***********************************************************************
915 * setup_unix_locales
917 static UINT setup_unix_locales(void)
919 struct locale_name locale_name;
920 WCHAR buffer[128], ctype_buff[128];
921 char *locale;
922 UINT unix_cp = 0;
924 if ((locale = setlocale( LC_CTYPE, NULL )))
926 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
927 parse_locale_name( ctype_buff, &locale_name );
928 lcid_LC_CTYPE = locale_name.lcid;
929 unix_cp = locale_name.codepage;
931 if (!lcid_LC_CTYPE) /* this one needs a default value */
932 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
934 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
935 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
937 #define GET_UNIX_LOCALE(cat) do \
938 if ((locale = setlocale( cat, NULL ))) \
940 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
941 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
942 else { \
943 parse_locale_name( buffer, &locale_name ); \
944 lcid_##cat = locale_name.lcid; \
945 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
946 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
948 } while (0)
950 GET_UNIX_LOCALE( LC_COLLATE );
951 GET_UNIX_LOCALE( LC_MESSAGES );
952 GET_UNIX_LOCALE( LC_MONETARY );
953 GET_UNIX_LOCALE( LC_NUMERIC );
954 GET_UNIX_LOCALE( LC_TIME );
955 #ifdef LC_PAPER
956 GET_UNIX_LOCALE( LC_PAPER );
957 #endif
958 #ifdef LC_MEASUREMENT
959 GET_UNIX_LOCALE( LC_MEASUREMENT );
960 #endif
961 #ifdef LC_TELEPHONE
962 GET_UNIX_LOCALE( LC_TELEPHONE );
963 #endif
965 #undef GET_UNIX_LOCALE
967 return unix_cp;
971 /***********************************************************************
972 * GetUserDefaultLangID (KERNEL32.@)
974 * Get the default language Id for the current user.
976 * PARAMS
977 * None.
979 * RETURNS
980 * The current LANGID of the default language for the current user.
982 LANGID WINAPI GetUserDefaultLangID(void)
984 return LANGIDFROMLCID(GetUserDefaultLCID());
988 /***********************************************************************
989 * GetSystemDefaultLangID (KERNEL32.@)
991 * Get the default language Id for the system.
993 * PARAMS
994 * None.
996 * RETURNS
997 * The current LANGID of the default language for the system.
999 LANGID WINAPI GetSystemDefaultLangID(void)
1001 return LANGIDFROMLCID(GetSystemDefaultLCID());
1005 /***********************************************************************
1006 * GetUserDefaultLCID (KERNEL32.@)
1008 * Get the default locale Id for the current user.
1010 * PARAMS
1011 * None.
1013 * RETURNS
1014 * The current LCID of the default locale for the current user.
1016 LCID WINAPI GetUserDefaultLCID(void)
1018 LCID lcid;
1019 NtQueryDefaultLocale( TRUE, &lcid );
1020 return lcid;
1024 /***********************************************************************
1025 * GetSystemDefaultLCID (KERNEL32.@)
1027 * Get the default locale Id for the system.
1029 * PARAMS
1030 * None.
1032 * RETURNS
1033 * The current LCID of the default locale for the system.
1035 LCID WINAPI GetSystemDefaultLCID(void)
1037 LCID lcid;
1038 NtQueryDefaultLocale( FALSE, &lcid );
1039 return lcid;
1042 /***********************************************************************
1043 * GetSystemDefaultLocaleName (KERNEL32.@)
1045 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1047 LCID lcid = GetSystemDefaultLCID();
1048 return LCIDToLocaleName(lcid, localename, len, 0);
1051 /***********************************************************************
1052 * GetUserDefaultUILanguage (KERNEL32.@)
1054 * Get the default user interface language Id for the current user.
1056 * PARAMS
1057 * None.
1059 * RETURNS
1060 * The current LANGID of the default UI language for the current user.
1062 LANGID WINAPI GetUserDefaultUILanguage(void)
1064 LANGID lang;
1065 NtQueryDefaultUILanguage( &lang );
1066 return lang;
1070 /***********************************************************************
1071 * GetSystemDefaultUILanguage (KERNEL32.@)
1073 * Get the default user interface language Id for the system.
1075 * PARAMS
1076 * None.
1078 * RETURNS
1079 * The current LANGID of the default UI language for the system. This is
1080 * typically the same language used during the installation process.
1082 LANGID WINAPI GetSystemDefaultUILanguage(void)
1084 LANGID lang;
1085 NtQueryInstallUILanguage( &lang );
1086 return lang;
1090 /***********************************************************************
1091 * LocaleNameToLCID (KERNEL32.@)
1093 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1095 struct locale_name locale_name;
1097 if (flags) FIXME( "unsupported flags %x\n", flags );
1099 if (name == LOCALE_NAME_USER_DEFAULT)
1100 return GetUserDefaultLCID();
1102 /* string parsing */
1103 parse_locale_name( name, &locale_name );
1105 TRACE( "found lcid %x for %s, matches %d\n",
1106 locale_name.lcid, debugstr_w(name), locale_name.matches );
1108 if (!locale_name.matches)
1110 SetLastError(ERROR_INVALID_PARAMETER);
1111 return 0;
1114 if (locale_name.matches == 1)
1115 WARN( "locale %s not recognized, defaulting to %s\n",
1116 debugstr_w(name), debugstr_w(locale_name.lang) );
1118 return locale_name.lcid;
1122 /***********************************************************************
1123 * LCIDToLocaleName (KERNEL32.@)
1125 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1127 if (flags) FIXME( "unsupported flags %x\n", flags );
1129 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1133 /******************************************************************************
1134 * get_locale_registry_value
1136 * Gets the registry value name and cache for a given lctype.
1138 static struct registry_value *get_locale_registry_value( DWORD lctype )
1140 int i;
1141 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1142 if (registry_values[i].lctype == lctype)
1143 return &registry_values[i];
1144 return NULL;
1148 /******************************************************************************
1149 * get_registry_locale_info
1151 * Retrieve user-modified locale info from the registry.
1152 * Return length, 0 on error, -1 if not found.
1154 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1156 DWORD size;
1157 INT ret;
1158 HANDLE hkey;
1159 NTSTATUS status;
1160 UNICODE_STRING nameW;
1161 KEY_VALUE_PARTIAL_INFORMATION *info;
1162 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1164 RtlEnterCriticalSection( &cache_section );
1166 if (!registry_value->cached_value)
1168 if (!(hkey = create_registry_key()))
1170 RtlLeaveCriticalSection( &cache_section );
1171 return -1;
1174 RtlInitUnicodeString( &nameW, registry_value->name );
1175 size = info_size + len * sizeof(WCHAR);
1177 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1179 NtClose( hkey );
1180 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1181 RtlLeaveCriticalSection( &cache_section );
1182 return 0;
1185 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1187 /* try again with a bigger buffer when we have to return the correct size */
1188 if (status == STATUS_BUFFER_OVERFLOW && !buffer && size > info_size)
1190 KEY_VALUE_PARTIAL_INFORMATION *new_info;
1191 if ((new_info = HeapReAlloc( GetProcessHeap(), 0, info, size )))
1193 info = new_info;
1194 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1198 NtClose( hkey );
1200 if (!status)
1202 INT length = (size - info_size) / sizeof(WCHAR);
1203 LPWSTR cached_value;
1205 if (!length || ((WCHAR *)&info->Data)[length-1])
1206 length++;
1208 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1210 if (!cached_value)
1212 HeapFree( GetProcessHeap(), 0, info );
1213 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1214 RtlLeaveCriticalSection( &cache_section );
1215 return 0;
1218 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1219 cached_value[length-1] = 0;
1220 HeapFree( GetProcessHeap(), 0, info );
1221 registry_value->cached_value = cached_value;
1223 else
1225 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1227 ret = (size - info_size) / sizeof(WCHAR);
1229 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1231 ret = -1;
1233 else
1235 SetLastError( RtlNtStatusToDosError(status) );
1236 ret = 0;
1238 HeapFree( GetProcessHeap(), 0, info );
1239 RtlLeaveCriticalSection( &cache_section );
1240 return ret;
1244 ret = lstrlenW( registry_value->cached_value ) + 1;
1246 if (buffer)
1248 if (ret > len)
1250 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1251 ret = 0;
1253 else
1255 lstrcpyW( buffer, registry_value->cached_value );
1259 RtlLeaveCriticalSection( &cache_section );
1261 return ret;
1265 /******************************************************************************
1266 * GetLocaleInfoA (KERNEL32.@)
1268 * Get information about an aspect of a locale.
1270 * PARAMS
1271 * lcid [I] LCID of the locale
1272 * lctype [I] LCTYPE_ flags from "winnls.h"
1273 * buffer [O] Destination for the information
1274 * len [I] Length of buffer in characters
1276 * RETURNS
1277 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1278 * with the information.
1279 * Failure: 0. Use GetLastError() to determine the cause.
1281 * NOTES
1282 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1283 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1284 * which is a bit string.
1286 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1288 WCHAR *bufferW;
1289 INT lenW, ret;
1291 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1293 if (len < 0 || (len && !buffer))
1295 SetLastError( ERROR_INVALID_PARAMETER );
1296 return 0;
1298 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1300 SetLastError( ERROR_INVALID_FLAGS );
1301 return 0;
1304 if (!len) buffer = NULL;
1306 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1308 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1310 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1311 return 0;
1313 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1315 if ((lctype & LOCALE_RETURN_NUMBER) ||
1316 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1318 /* it's not an ASCII string, just bytes */
1319 ret *= sizeof(WCHAR);
1320 if (buffer)
1322 if (ret <= len) memcpy( buffer, bufferW, ret );
1323 else
1325 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1326 ret = 0;
1330 else
1332 UINT codepage = CP_ACP;
1333 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1334 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1337 HeapFree( GetProcessHeap(), 0, bufferW );
1338 return ret;
1341 static int get_value_base_by_lctype( LCTYPE lctype )
1343 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1346 /******************************************************************************
1347 * GetLocaleInfoW (KERNEL32.@)
1349 * See GetLocaleInfoA.
1351 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1353 LANGID lang_id;
1354 HRSRC hrsrc;
1355 HGLOBAL hmem;
1356 INT ret;
1357 UINT lcflags;
1358 const WCHAR *p;
1359 unsigned int i;
1361 if (len < 0 || (len && !buffer))
1363 SetLastError( ERROR_INVALID_PARAMETER );
1364 return 0;
1366 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1367 !is_genitive_name_supported( lctype ))
1369 SetLastError( ERROR_INVALID_FLAGS );
1370 return 0;
1373 if (!len) buffer = NULL;
1375 lcid = convert_default_lcid( lcid, lctype );
1377 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1378 lctype &= 0xffff;
1380 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1382 /* first check for overrides in the registry */
1384 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1385 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1387 struct registry_value *value = get_locale_registry_value(lctype);
1389 if (value)
1391 if (lcflags & LOCALE_RETURN_NUMBER)
1393 WCHAR tmp[16];
1394 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1395 if (ret > 0)
1397 WCHAR *end;
1398 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1399 if (*end) /* invalid number */
1401 SetLastError( ERROR_INVALID_FLAGS );
1402 return 0;
1404 ret = sizeof(UINT)/sizeof(WCHAR);
1405 if (!buffer) return ret;
1406 if (ret > len)
1408 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1409 return 0;
1411 memcpy( buffer, &number, sizeof(number) );
1414 else ret = get_registry_locale_info( value, buffer, len );
1416 if (ret != -1) return ret;
1420 /* now load it from kernel resources */
1422 lang_id = LANGIDFROMLCID( lcid );
1424 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1425 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1426 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1428 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1429 ULongToPtr((lctype >> 4) + 1), lang_id )))
1431 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1432 return 0;
1434 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1435 return 0;
1437 p = LockResource( hmem );
1438 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1440 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1441 else if (is_genitive_name_supported( lctype ) && *p)
1443 /* genitive form's stored after a null separator from a nominative */
1444 for (i = 1; i <= *p; i++) if (!p[i]) break;
1446 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1448 ret = *p - i + 1;
1449 p += i;
1451 else ret = i;
1453 else
1454 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1456 if (!buffer) return ret;
1458 if (ret > len)
1460 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1461 return 0;
1464 if (lcflags & LOCALE_RETURN_NUMBER)
1466 UINT number;
1467 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1468 if (!tmp) return 0;
1469 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1470 tmp[*p] = 0;
1471 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1472 if (!*end)
1473 memcpy( buffer, &number, sizeof(number) );
1474 else /* invalid number */
1476 SetLastError( ERROR_INVALID_FLAGS );
1477 ret = 0;
1479 HeapFree( GetProcessHeap(), 0, tmp );
1481 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1482 lcid, lctype, buffer, len, number );
1484 else
1486 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1487 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1489 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1490 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1492 return ret;
1495 /******************************************************************************
1496 * GetLocaleInfoEx (KERNEL32.@)
1498 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1500 LCID lcid = LocaleNameToLCID(locale, 0);
1502 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1504 if (!lcid) return 0;
1506 /* special handling for neutral locale names */
1507 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1509 if (len && len < 3)
1511 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1512 return 0;
1515 if (len) strcpyW(buffer, locale);
1516 return 3;
1519 return GetLocaleInfoW(lcid, info, buffer, len);
1522 /******************************************************************************
1523 * SetLocaleInfoA [KERNEL32.@]
1525 * Set information about an aspect of a locale.
1527 * PARAMS
1528 * lcid [I] LCID of the locale
1529 * lctype [I] LCTYPE_ flags from "winnls.h"
1530 * data [I] Information to set
1532 * RETURNS
1533 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1534 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1535 * Failure: FALSE. Use GetLastError() to determine the cause.
1537 * NOTES
1538 * - Values are only be set for the current user locale; the system locale
1539 * settings cannot be changed.
1540 * - Any settings changed by this call are lost when the locale is changed by
1541 * the control panel (in Wine, this happens every time you change LANG).
1542 * - The native implementation of this function does not check that lcid matches
1543 * the current user locale, and simply sets the new values. Wine warns you in
1544 * this case, but behaves the same.
1546 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1548 UINT codepage = CP_ACP;
1549 WCHAR *strW;
1550 DWORD len;
1551 BOOL ret;
1553 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1555 if (!data)
1557 SetLastError( ERROR_INVALID_PARAMETER );
1558 return FALSE;
1560 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1561 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1563 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1564 return FALSE;
1566 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1567 ret = SetLocaleInfoW( lcid, lctype, strW );
1568 HeapFree( GetProcessHeap(), 0, strW );
1569 return ret;
1573 /******************************************************************************
1574 * SetLocaleInfoW (KERNEL32.@)
1576 * See SetLocaleInfoA.
1578 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1580 struct registry_value *value;
1581 static const WCHAR intlW[] = {'i','n','t','l',0 };
1582 UNICODE_STRING valueW;
1583 NTSTATUS status;
1584 HANDLE hkey;
1586 lctype &= 0xffff;
1587 value = get_locale_registry_value( lctype );
1589 if (!data || !value)
1591 SetLastError( ERROR_INVALID_PARAMETER );
1592 return FALSE;
1595 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1597 SetLastError( ERROR_INVALID_FLAGS );
1598 return FALSE;
1601 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1603 /* FIXME: should check that data to set is sane */
1605 /* FIXME: profile functions should map to registry */
1606 WriteProfileStringW( intlW, value->name, data );
1608 if (!(hkey = create_registry_key())) return FALSE;
1609 RtlInitUnicodeString( &valueW, value->name );
1610 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1612 RtlEnterCriticalSection( &cache_section );
1613 HeapFree( GetProcessHeap(), 0, value->cached_value );
1614 value->cached_value = NULL;
1615 RtlLeaveCriticalSection( &cache_section );
1617 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1619 /* Set I-value from S value */
1620 WCHAR *lpD, *lpM, *lpY;
1621 WCHAR szBuff[2];
1623 lpD = strrchrW(data, 'd');
1624 lpM = strrchrW(data, 'M');
1625 lpY = strrchrW(data, 'y');
1627 if (lpD <= lpM)
1629 szBuff[0] = '1'; /* D-M-Y */
1631 else
1633 if (lpY <= lpM)
1634 szBuff[0] = '2'; /* Y-M-D */
1635 else
1636 szBuff[0] = '0'; /* M-D-Y */
1639 szBuff[1] = '\0';
1641 if (lctype == LOCALE_SSHORTDATE)
1642 lctype = LOCALE_IDATE;
1643 else
1644 lctype = LOCALE_ILDATE;
1646 value = get_locale_registry_value( lctype );
1648 WriteProfileStringW( intlW, value->name, szBuff );
1650 RtlInitUnicodeString( &valueW, value->name );
1651 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1653 RtlEnterCriticalSection( &cache_section );
1654 HeapFree( GetProcessHeap(), 0, value->cached_value );
1655 value->cached_value = NULL;
1656 RtlLeaveCriticalSection( &cache_section );
1659 NtClose( hkey );
1661 if (status) SetLastError( RtlNtStatusToDosError(status) );
1662 return !status;
1666 /******************************************************************************
1667 * GetACP (KERNEL32.@)
1669 * Get the current Ansi code page Id for the system.
1671 * PARAMS
1672 * None.
1674 * RETURNS
1675 * The current Ansi code page identifier for the system.
1677 UINT WINAPI GetACP(void)
1679 assert( ansi_cptable );
1680 return ansi_cptable->info.codepage;
1684 /******************************************************************************
1685 * SetCPGlobal (KERNEL32.@)
1687 * Set the current Ansi code page Id for the system.
1689 * PARAMS
1690 * acp [I] code page ID to be the new ACP.
1692 * RETURNS
1693 * The previous ACP.
1695 UINT WINAPI SetCPGlobal( UINT acp )
1697 UINT ret = GetACP();
1698 const union cptable *new_cptable = wine_cp_get_table( acp );
1700 if (new_cptable) ansi_cptable = new_cptable;
1701 return ret;
1705 /***********************************************************************
1706 * GetOEMCP (KERNEL32.@)
1708 * Get the current OEM code page Id for the system.
1710 * PARAMS
1711 * None.
1713 * RETURNS
1714 * The current OEM code page identifier for the system.
1716 UINT WINAPI GetOEMCP(void)
1718 assert( oem_cptable );
1719 return oem_cptable->info.codepage;
1723 /***********************************************************************
1724 * IsValidCodePage (KERNEL32.@)
1726 * Determine if a given code page identifier is valid.
1728 * PARAMS
1729 * codepage [I] Code page Id to verify.
1731 * RETURNS
1732 * TRUE, If codepage is valid and available on the system,
1733 * FALSE otherwise.
1735 BOOL WINAPI IsValidCodePage( UINT codepage )
1737 switch(codepage) {
1738 case CP_UTF7:
1739 case CP_UTF8:
1740 return TRUE;
1741 default:
1742 return wine_cp_get_table( codepage ) != NULL;
1747 /***********************************************************************
1748 * IsDBCSLeadByteEx (KERNEL32.@)
1750 * Determine if a character is a lead byte in a given code page.
1752 * PARAMS
1753 * codepage [I] Code page for the test.
1754 * testchar [I] Character to test
1756 * RETURNS
1757 * TRUE, if testchar is a lead byte in codepage,
1758 * FALSE otherwise.
1760 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1762 const union cptable *table = get_codepage_table( codepage );
1763 return table && wine_is_dbcs_leadbyte( table, testchar );
1767 /***********************************************************************
1768 * IsDBCSLeadByte (KERNEL32.@)
1769 * IsDBCSLeadByte (KERNEL.207)
1771 * Determine if a character is a lead byte.
1773 * PARAMS
1774 * testchar [I] Character to test
1776 * RETURNS
1777 * TRUE, if testchar is a lead byte in the ANSI code page,
1778 * FALSE otherwise.
1780 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1782 if (!ansi_cptable) return FALSE;
1783 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1787 /***********************************************************************
1788 * GetCPInfo (KERNEL32.@)
1790 * Get information about a code page.
1792 * PARAMS
1793 * codepage [I] Code page number
1794 * cpinfo [O] Destination for code page information
1796 * RETURNS
1797 * Success: TRUE. cpinfo is updated with the information about codepage.
1798 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1800 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1802 const union cptable *table;
1804 if (!cpinfo)
1806 SetLastError( ERROR_INVALID_PARAMETER );
1807 return FALSE;
1810 if (!(table = get_codepage_table( codepage )))
1812 switch(codepage)
1814 case CP_UTF7:
1815 case CP_UTF8:
1816 cpinfo->DefaultChar[0] = 0x3f;
1817 cpinfo->DefaultChar[1] = 0;
1818 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1819 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1820 return TRUE;
1823 SetLastError( ERROR_INVALID_PARAMETER );
1824 return FALSE;
1826 if (table->info.def_char & 0xff00)
1828 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1829 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1831 else
1833 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1834 cpinfo->DefaultChar[1] = 0;
1836 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1837 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1838 else
1839 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1841 return TRUE;
1844 /***********************************************************************
1845 * GetCPInfoExA (KERNEL32.@)
1847 * Get extended information about a code page.
1849 * PARAMS
1850 * codepage [I] Code page number
1851 * dwFlags [I] Reserved, must to 0.
1852 * cpinfo [O] Destination for code page information
1854 * RETURNS
1855 * Success: TRUE. cpinfo is updated with the information about codepage.
1856 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1858 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1860 CPINFOEXW cpinfoW;
1862 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1863 return FALSE;
1865 /* the layout is the same except for CodePageName */
1866 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1867 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1868 return TRUE;
1871 /***********************************************************************
1872 * GetCPInfoExW (KERNEL32.@)
1874 * Unicode version of GetCPInfoExA.
1876 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1878 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1879 return FALSE;
1881 switch(codepage)
1883 case CP_UTF7:
1885 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1887 cpinfo->CodePage = CP_UTF7;
1888 cpinfo->UnicodeDefaultChar = 0x3f;
1889 strcpyW(cpinfo->CodePageName, utf7);
1890 break;
1893 case CP_UTF8:
1895 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1897 cpinfo->CodePage = CP_UTF8;
1898 cpinfo->UnicodeDefaultChar = 0x3f;
1899 strcpyW(cpinfo->CodePageName, utf8);
1900 break;
1903 default:
1905 const union cptable *table = get_codepage_table( codepage );
1907 cpinfo->CodePage = table->info.codepage;
1908 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1909 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1910 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1911 break;
1914 return TRUE;
1917 /***********************************************************************
1918 * EnumSystemCodePagesA (KERNEL32.@)
1920 * Call a user defined function for every code page installed on the system.
1922 * PARAMS
1923 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1924 * flags [I] Reserved, set to 0.
1926 * RETURNS
1927 * TRUE, If all code pages have been enumerated, or
1928 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1930 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1932 const union cptable *table;
1933 char buffer[10];
1934 int index = 0;
1936 for (;;)
1938 if (!(table = wine_cp_enum_table( index++ ))) break;
1939 sprintf( buffer, "%d", table->info.codepage );
1940 if (!lpfnCodePageEnum( buffer )) break;
1942 return TRUE;
1946 /***********************************************************************
1947 * EnumSystemCodePagesW (KERNEL32.@)
1949 * See EnumSystemCodePagesA.
1951 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1953 const union cptable *table;
1954 WCHAR buffer[10], *p;
1955 int page, index = 0;
1957 for (;;)
1959 if (!(table = wine_cp_enum_table( index++ ))) break;
1960 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1961 *--p = 0;
1962 page = table->info.codepage;
1965 *--p = '0' + (page % 10);
1966 page /= 10;
1967 } while( page );
1968 if (!lpfnCodePageEnum( p )) break;
1970 return TRUE;
1974 /***********************************************************************
1975 * utf7_write_w
1977 * Helper for utf7_mbstowcs
1979 * RETURNS
1980 * TRUE on success, FALSE on error
1982 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
1984 if (dstlen > 0)
1986 if (*index >= dstlen)
1987 return FALSE;
1989 dst[*index] = character;
1992 (*index)++;
1994 return TRUE;
1997 /***********************************************************************
1998 * utf7_mbstowcs
2000 * UTF-7 to UTF-16 string conversion, helper for MultiByteToWideChar
2002 * RETURNS
2003 * On success, the number of characters written
2004 * On dst buffer overflow, -1
2006 static int utf7_mbstowcs(const char *src, int srclen, WCHAR *dst, int dstlen)
2008 static const signed char base64_decoding_table[] =
2010 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
2011 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
2012 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
2013 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
2014 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
2015 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
2016 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
2017 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
2020 const char *source_end = src + srclen;
2021 int dest_index = 0;
2023 DWORD byte_pair = 0;
2024 short offset = 0;
2026 while (src < source_end)
2028 if (*src == '+')
2030 src++;
2031 if (src >= source_end)
2032 break;
2034 if (*src == '-')
2036 /* just a plus sign escaped as +- */
2037 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
2038 return -1;
2039 src++;
2040 continue;
2045 signed char sextet = *src;
2046 if (sextet == '-')
2048 /* skip over the dash and end base64 decoding
2049 * the current, unfinished byte pair is discarded */
2050 src++;
2051 offset = 0;
2052 break;
2054 if (sextet < 0)
2056 /* the next character of src is < 0 and therefore not part of a base64 sequence
2057 * the current, unfinished byte pair is NOT discarded in this case
2058 * this is probably a bug in Windows */
2059 break;
2062 sextet = base64_decoding_table[sextet];
2063 if (sextet == -1)
2065 /* -1 means that the next character of src is not part of a base64 sequence
2066 * in other words, all sextets in this base64 sequence have been processed
2067 * the current, unfinished byte pair is discarded */
2068 offset = 0;
2069 break;
2072 byte_pair = (byte_pair << 6) | sextet;
2073 offset += 6;
2075 if (offset >= 16)
2077 /* this byte pair is done */
2078 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
2079 return -1;
2080 offset -= 16;
2083 src++;
2085 while (src < source_end);
2087 else
2089 /* we have to convert to unsigned char in case *src < 0 */
2090 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
2091 return -1;
2092 src++;
2096 return dest_index;
2099 /***********************************************************************
2100 * MultiByteToWideChar (KERNEL32.@)
2102 * Convert a multibyte character string into a Unicode string.
2104 * PARAMS
2105 * page [I] Codepage character set to convert from
2106 * flags [I] Character mapping flags
2107 * src [I] Source string buffer
2108 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
2109 * dst [O] Destination buffer
2110 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
2112 * RETURNS
2113 * Success: If dstlen > 0, the number of characters written to dst.
2114 * If dstlen == 0, the number of characters needed to perform the
2115 * conversion. In both cases the count includes the terminating NUL.
2116 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2117 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2118 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
2119 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
2120 * possible for src.
2122 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
2123 LPWSTR dst, INT dstlen )
2125 const union cptable *table;
2126 int ret;
2128 if (!src || !srclen || (!dst && dstlen))
2130 SetLastError( ERROR_INVALID_PARAMETER );
2131 return 0;
2134 if (srclen < 0) srclen = strlen(src) + 1;
2136 switch(page)
2138 case CP_SYMBOL:
2139 if (flags)
2141 SetLastError( ERROR_INVALID_FLAGS );
2142 return 0;
2144 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2145 break;
2146 case CP_UTF7:
2147 if (flags)
2149 SetLastError( ERROR_INVALID_FLAGS );
2150 return 0;
2152 ret = utf7_mbstowcs( src, srclen, dst, dstlen );
2153 break;
2154 case CP_UNIXCP:
2155 if (unix_cptable)
2157 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2158 break;
2160 #ifdef __APPLE__
2161 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2162 #endif
2163 /* fall through */
2164 case CP_UTF8:
2165 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2166 break;
2167 default:
2168 if (!(table = get_codepage_table( page )))
2170 SetLastError( ERROR_INVALID_PARAMETER );
2171 return 0;
2173 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2174 break;
2177 if (ret < 0)
2179 switch(ret)
2181 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2182 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2184 ret = 0;
2186 TRACE("cp %d %s -> %s, ret = %d\n",
2187 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2188 return ret;
2192 /***********************************************************************
2193 * utf7_can_directly_encode
2195 * Helper for utf7_wcstombs
2197 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
2199 static const BOOL directly_encodable_table[] =
2201 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
2202 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
2203 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
2204 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
2205 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
2206 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
2207 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
2208 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
2211 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
2214 /***********************************************************************
2215 * utf7_write_c
2217 * Helper for utf7_wcstombs
2219 * RETURNS
2220 * TRUE on success, FALSE on error
2222 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
2224 if (dstlen > 0)
2226 if (*index >= dstlen)
2227 return FALSE;
2229 dst[*index] = character;
2232 (*index)++;
2234 return TRUE;
2237 /***********************************************************************
2238 * utf7_wcstombs
2240 * UTF-16 to UTF-7 string conversion, helper for WideCharToMultiByte
2242 * RETURNS
2243 * On success, the number of characters written
2244 * On dst buffer overflow, -1
2246 static int utf7_wcstombs(const WCHAR *src, int srclen, char *dst, int dstlen)
2248 static const char base64_encoding_table[] =
2249 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2251 const WCHAR *source_end = src + srclen;
2252 int dest_index = 0;
2254 while (src < source_end)
2256 if (*src == '+')
2258 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2259 return -1;
2260 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2261 return -1;
2262 src++;
2264 else if (utf7_can_directly_encode(*src))
2266 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
2267 return -1;
2268 src++;
2270 else
2272 unsigned int offset = 0;
2273 DWORD byte_pair = 0;
2275 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
2276 return -1;
2278 while (src < source_end && !utf7_can_directly_encode(*src))
2280 byte_pair = (byte_pair << 16) | *src;
2281 offset += 16;
2282 while (offset >= 6)
2284 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
2285 return -1;
2286 offset -= 6;
2288 src++;
2291 if (offset)
2293 /* Windows won't create a padded base64 character if there's no room for the - sign
2294 * as well ; this is probably a bug in Windows */
2295 if (dstlen > 0 && dest_index + 1 >= dstlen)
2296 return -1;
2298 byte_pair <<= (6 - offset);
2299 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
2300 return -1;
2303 /* Windows always explicitly terminates the base64 sequence
2304 even though RFC 2152 (page 3, rule 2) does not require this */
2305 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
2306 return -1;
2310 return dest_index;
2313 /***********************************************************************
2314 * WideCharToMultiByte (KERNEL32.@)
2316 * Convert a Unicode character string into a multibyte string.
2318 * PARAMS
2319 * page [I] Code page character set to convert to
2320 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2321 * src [I] Source string buffer
2322 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2323 * dst [O] Destination buffer
2324 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2325 * defchar [I] Default character to use for conversion if no exact
2326 * conversion can be made
2327 * used [O] Set if default character was used in the conversion
2329 * RETURNS
2330 * Success: If dstlen > 0, the number of characters written to dst.
2331 * If dstlen == 0, number of characters needed to perform the
2332 * conversion. In both cases the count includes the terminating NUL.
2333 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2334 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2335 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2336 * parameter was given.
2338 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2339 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2341 const union cptable *table;
2342 int ret, used_tmp;
2344 if (!src || !srclen || (!dst && dstlen))
2346 SetLastError( ERROR_INVALID_PARAMETER );
2347 return 0;
2350 if (srclen < 0) srclen = strlenW(src) + 1;
2352 switch(page)
2354 case CP_SYMBOL:
2355 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2356 if (flags)
2358 SetLastError( ERROR_INVALID_FLAGS );
2359 return 0;
2361 if (defchar || used)
2363 SetLastError( ERROR_INVALID_PARAMETER );
2364 return 0;
2366 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2367 break;
2368 case CP_UTF7:
2369 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2370 if (defchar || used)
2372 SetLastError( ERROR_INVALID_PARAMETER );
2373 return 0;
2375 if (flags)
2377 SetLastError( ERROR_INVALID_FLAGS );
2378 return 0;
2380 ret = utf7_wcstombs( src, srclen, dst, dstlen );
2381 break;
2382 case CP_UNIXCP:
2383 if (unix_cptable)
2385 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2386 defchar, used ? &used_tmp : NULL );
2387 break;
2389 /* fall through */
2390 case CP_UTF8:
2391 if (defchar || used)
2393 SetLastError( ERROR_INVALID_PARAMETER );
2394 return 0;
2396 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2397 break;
2398 default:
2399 if (!(table = get_codepage_table( page )))
2401 SetLastError( ERROR_INVALID_PARAMETER );
2402 return 0;
2404 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2405 defchar, used ? &used_tmp : NULL );
2406 if (used) *used = used_tmp;
2407 break;
2410 if (ret < 0)
2412 switch(ret)
2414 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2415 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2417 ret = 0;
2419 TRACE("cp %d %s -> %s, ret = %d\n",
2420 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2421 return ret;
2425 /***********************************************************************
2426 * GetThreadLocale (KERNEL32.@)
2428 * Get the current threads locale.
2430 * PARAMS
2431 * None.
2433 * RETURNS
2434 * The LCID currently associated with the calling thread.
2436 LCID WINAPI GetThreadLocale(void)
2438 LCID ret = NtCurrentTeb()->CurrentLocale;
2439 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2440 return ret;
2443 /**********************************************************************
2444 * SetThreadLocale (KERNEL32.@)
2446 * Set the current threads locale.
2448 * PARAMS
2449 * lcid [I] LCID of the locale to set
2451 * RETURNS
2452 * Success: TRUE. The threads locale is set to lcid.
2453 * Failure: FALSE. Use GetLastError() to determine the cause.
2455 BOOL WINAPI SetThreadLocale( LCID lcid )
2457 TRACE("(0x%04X)\n", lcid);
2459 lcid = ConvertDefaultLocale(lcid);
2461 if (lcid != GetThreadLocale())
2463 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2465 SetLastError(ERROR_INVALID_PARAMETER);
2466 return FALSE;
2469 NtCurrentTeb()->CurrentLocale = lcid;
2471 return TRUE;
2474 /**********************************************************************
2475 * SetThreadUILanguage (KERNEL32.@)
2477 * Set the current threads UI language.
2479 * PARAMS
2480 * langid [I] LANGID of the language to set, or 0 to use
2481 * the available language which is best supported
2482 * for console applications
2484 * RETURNS
2485 * Success: The return value is the same as the input value.
2486 * Failure: The return value differs from the input value.
2487 * Use GetLastError() to determine the cause.
2489 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2491 TRACE("(0x%04x) stub - returning success\n", langid);
2492 return langid;
2495 /******************************************************************************
2496 * ConvertDefaultLocale (KERNEL32.@)
2498 * Convert a default locale identifier into a real identifier.
2500 * PARAMS
2501 * lcid [I] LCID identifier of the locale to convert
2503 * RETURNS
2504 * lcid unchanged, if not a default locale or its sublanguage is
2505 * not SUBLANG_NEUTRAL.
2506 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2507 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2508 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2510 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2512 LANGID langid;
2514 switch (lcid)
2516 case LOCALE_INVARIANT:
2517 /* keep as-is */
2518 break;
2519 case LOCALE_SYSTEM_DEFAULT:
2520 lcid = GetSystemDefaultLCID();
2521 break;
2522 case LOCALE_USER_DEFAULT:
2523 case LOCALE_NEUTRAL:
2524 lcid = GetUserDefaultLCID();
2525 break;
2526 default:
2527 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2528 langid = LANGIDFROMLCID(lcid);
2529 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2531 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2532 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2535 return lcid;
2539 /******************************************************************************
2540 * IsValidLocale (KERNEL32.@)
2542 * Determine if a locale is valid.
2544 * PARAMS
2545 * lcid [I] LCID of the locale to check
2546 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2548 * RETURNS
2549 * TRUE, if lcid is valid,
2550 * FALSE, otherwise.
2552 * NOTES
2553 * Wine does not currently make the distinction between supported and installed. All
2554 * languages supported are installed by default.
2556 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2558 /* check if language is registered in the kernel32 resources */
2559 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2560 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2563 /******************************************************************************
2564 * IsValidLocaleName (KERNEL32.@)
2566 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2568 struct locale_name locale_name;
2570 /* string parsing */
2571 parse_locale_name( locale, &locale_name );
2573 TRACE( "found lcid %x for %s, matches %d\n",
2574 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2576 return locale_name.matches > 0;
2579 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2580 LPCSTR name, WORD LangID, LONG_PTR lParam )
2582 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2583 char buf[20];
2585 sprintf(buf, "%08x", (UINT)LangID);
2586 return lpfnLocaleEnum( buf );
2589 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2590 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2592 static const WCHAR formatW[] = {'%','0','8','x',0};
2593 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2594 WCHAR buf[20];
2595 sprintfW( buf, formatW, (UINT)LangID );
2596 return lpfnLocaleEnum( buf );
2599 /******************************************************************************
2600 * EnumSystemLocalesA (KERNEL32.@)
2602 * Call a users function for each locale available on the system.
2604 * PARAMS
2605 * lpfnLocaleEnum [I] Callback function to call for each locale
2606 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2608 * RETURNS
2609 * Success: TRUE.
2610 * Failure: FALSE. Use GetLastError() to determine the cause.
2612 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2614 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2615 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2616 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2617 (LONG_PTR)lpfnLocaleEnum);
2618 return TRUE;
2622 /******************************************************************************
2623 * EnumSystemLocalesW (KERNEL32.@)
2625 * See EnumSystemLocalesA.
2627 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2629 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2630 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2631 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2632 (LONG_PTR)lpfnLocaleEnum);
2633 return TRUE;
2637 struct enum_locale_ex_data
2639 LOCALE_ENUMPROCEX proc;
2640 DWORD flags;
2641 LPARAM lparam;
2644 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2645 LPCWSTR name, WORD lang, LONG_PTR lparam )
2647 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2648 WCHAR buffer[256];
2649 DWORD neutral;
2650 unsigned int flags;
2652 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2653 buffer, sizeof(buffer) / sizeof(WCHAR) );
2654 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2655 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2656 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2657 neutral = 0;
2658 flags = LOCALE_WINDOWS;
2659 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2660 if (data->flags && !(data->flags & flags)) return TRUE;
2661 return data->proc( buffer, flags, data->lparam );
2664 /******************************************************************************
2665 * EnumSystemLocalesEx (KERNEL32.@)
2667 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2669 struct enum_locale_ex_data data;
2671 if (reserved)
2673 SetLastError( ERROR_INVALID_PARAMETER );
2674 return FALSE;
2676 data.proc = proc;
2677 data.flags = flags;
2678 data.lparam = lparam;
2679 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2680 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2681 enum_locale_ex_proc, (LONG_PTR)&data );
2682 return TRUE;
2686 /***********************************************************************
2687 * VerLanguageNameA (KERNEL32.@)
2689 * Get the name of a language.
2691 * PARAMS
2692 * wLang [I] LANGID of the language
2693 * szLang [O] Destination for the language name
2695 * RETURNS
2696 * Success: The size of the language name. If szLang is non-NULL, it is filled
2697 * with the name.
2698 * Failure: 0. Use GetLastError() to determine the cause.
2701 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2703 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2707 /***********************************************************************
2708 * VerLanguageNameW (KERNEL32.@)
2710 * See VerLanguageNameA.
2712 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2714 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2718 /******************************************************************************
2719 * GetStringTypeW (KERNEL32.@)
2721 * See GetStringTypeA.
2723 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2725 static const unsigned char type2_map[16] =
2727 C2_NOTAPPLICABLE, /* unassigned */
2728 C2_LEFTTORIGHT, /* L */
2729 C2_RIGHTTOLEFT, /* R */
2730 C2_EUROPENUMBER, /* EN */
2731 C2_EUROPESEPARATOR, /* ES */
2732 C2_EUROPETERMINATOR, /* ET */
2733 C2_ARABICNUMBER, /* AN */
2734 C2_COMMONSEPARATOR, /* CS */
2735 C2_BLOCKSEPARATOR, /* B */
2736 C2_SEGMENTSEPARATOR, /* S */
2737 C2_WHITESPACE, /* WS */
2738 C2_OTHERNEUTRAL, /* ON */
2739 C2_RIGHTTOLEFT, /* AL */
2740 C2_NOTAPPLICABLE, /* NSM */
2741 C2_NOTAPPLICABLE, /* BN */
2742 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2745 if (!src)
2747 SetLastError( ERROR_INVALID_PARAMETER );
2748 return FALSE;
2751 if (count == -1) count = strlenW(src) + 1;
2752 switch(type)
2754 case CT_CTYPE1:
2755 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2756 break;
2757 case CT_CTYPE2:
2758 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2759 break;
2760 case CT_CTYPE3:
2762 WARN("CT_CTYPE3: semi-stub.\n");
2763 while (count--)
2765 int c = *src;
2766 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2768 type1 = get_char_typeW( *src++ ) & 0xfff;
2769 /* try to construct type3 from type1 */
2770 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2771 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2772 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2773 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2774 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2775 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2776 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2778 if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
2779 if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
2781 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2782 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2783 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2784 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2785 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2786 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2787 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2788 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2790 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2791 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2792 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2793 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2794 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2795 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2796 *chartype++ = type3;
2798 break;
2800 default:
2801 SetLastError( ERROR_INVALID_PARAMETER );
2802 return FALSE;
2804 return TRUE;
2808 /******************************************************************************
2809 * GetStringTypeExW (KERNEL32.@)
2811 * See GetStringTypeExA.
2813 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2815 /* locale is ignored for Unicode */
2816 return GetStringTypeW( type, src, count, chartype );
2820 /******************************************************************************
2821 * GetStringTypeA (KERNEL32.@)
2823 * Get characteristics of the characters making up a string.
2825 * PARAMS
2826 * locale [I] Locale Id for the string
2827 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2828 * src [I] String to analyse
2829 * count [I] Length of src in chars, or -1 if src is NUL terminated
2830 * chartype [O] Destination for the calculated characteristics
2832 * RETURNS
2833 * Success: TRUE. chartype is filled with the requested characteristics of each char
2834 * in src.
2835 * Failure: FALSE. Use GetLastError() to determine the cause.
2837 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2839 UINT cp;
2840 INT countW;
2841 LPWSTR srcW;
2842 BOOL ret = FALSE;
2844 if(count == -1) count = strlen(src) + 1;
2846 if (!(cp = get_lcid_codepage( locale )))
2848 FIXME("For locale %04x using current ANSI code page\n", locale);
2849 cp = GetACP();
2852 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2853 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2855 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2857 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2858 * string, with multibyte characters there maybe be more bytes in count
2859 * than character space in the buffer!
2861 ret = GetStringTypeW(type, srcW, countW, chartype);
2862 HeapFree(GetProcessHeap(), 0, srcW);
2864 return ret;
2867 /******************************************************************************
2868 * GetStringTypeExA (KERNEL32.@)
2870 * Get characteristics of the characters making up a string.
2872 * PARAMS
2873 * locale [I] Locale Id for the string
2874 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2875 * src [I] String to analyse
2876 * count [I] Length of src in chars, or -1 if src is NUL terminated
2877 * chartype [O] Destination for the calculated characteristics
2879 * RETURNS
2880 * Success: TRUE. chartype is filled with the requested characteristics of each char
2881 * in src.
2882 * Failure: FALSE. Use GetLastError() to determine the cause.
2884 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2886 return GetStringTypeA(locale, type, src, count, chartype);
2889 /*************************************************************************
2890 * LCMapStringEx (KERNEL32.@)
2892 * Map characters in a locale sensitive string.
2894 * PARAMS
2895 * name [I] Locale name for the conversion.
2896 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2897 * src [I] String to map
2898 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2899 * dst [O] Destination for mapped string
2900 * dstlen [I] Length of dst in characters
2901 * version [I] reserved, must be NULL
2902 * reserved [I] reserved, must be NULL
2903 * lparam [I] reserved, must be 0
2905 * RETURNS
2906 * Success: The length of the mapped string in dst, including the NUL terminator.
2907 * Failure: 0. Use GetLastError() to determine the cause.
2909 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2910 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2912 LPWSTR dst_ptr;
2914 if (version) FIXME("unsupported version structure %p\n", version);
2915 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2916 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2918 if (!src || !srclen || dstlen < 0)
2920 SetLastError(ERROR_INVALID_PARAMETER);
2921 return 0;
2924 /* mutually exclusive flags */
2925 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2926 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2927 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2928 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2930 SetLastError(ERROR_INVALID_FLAGS);
2931 return 0;
2934 if (!dstlen) dst = NULL;
2936 if (flags & LCMAP_SORTKEY)
2938 INT ret;
2939 if (src == dst)
2941 SetLastError(ERROR_INVALID_FLAGS);
2942 return 0;
2945 if (srclen < 0) srclen = strlenW(src);
2947 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2948 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2950 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2951 if (ret == 0)
2952 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2953 else
2954 ret++;
2955 return ret;
2958 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2959 if (flags & SORT_STRINGSORT)
2961 SetLastError(ERROR_INVALID_FLAGS);
2962 return 0;
2965 if (srclen < 0) srclen = strlenW(src) + 1;
2967 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2968 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2970 if (!dst) /* return required string length */
2972 INT len;
2974 for (len = 0; srclen; src++, srclen--)
2976 WCHAR wch = *src;
2977 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2978 * and skips white space and punctuation characters for
2979 * NORM_IGNORESYMBOLS.
2981 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2982 continue;
2983 len++;
2985 return len;
2988 if (flags & LCMAP_UPPERCASE)
2990 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2992 WCHAR wch = *src;
2993 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2994 continue;
2995 *dst_ptr++ = toupperW(wch);
2996 dstlen--;
2999 else if (flags & LCMAP_LOWERCASE)
3001 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3003 WCHAR wch = *src;
3004 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3005 continue;
3006 *dst_ptr++ = tolowerW(wch);
3007 dstlen--;
3010 else
3012 if (src == dst)
3014 SetLastError(ERROR_INVALID_FLAGS);
3015 return 0;
3017 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
3019 WCHAR wch = *src;
3020 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
3021 continue;
3022 *dst_ptr++ = wch;
3023 dstlen--;
3027 if (srclen)
3029 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3030 return 0;
3033 return dst_ptr - dst;
3036 /*************************************************************************
3037 * LCMapStringW (KERNEL32.@)
3039 * See LCMapStringA.
3041 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
3042 LPWSTR dst, INT dstlen)
3044 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
3045 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
3047 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
3050 /*************************************************************************
3051 * LCMapStringA (KERNEL32.@)
3053 * Map characters in a locale sensitive string.
3055 * PARAMS
3056 * lcid [I] LCID for the conversion.
3057 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
3058 * src [I] String to map
3059 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
3060 * dst [O] Destination for mapped string
3061 * dstlen [I] Length of dst in characters
3063 * RETURNS
3064 * Success: The length of the mapped string in dst, including the NUL terminator.
3065 * Failure: 0. Use GetLastError() to determine the cause.
3067 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
3068 LPSTR dst, INT dstlen)
3070 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
3071 LPWSTR srcW, dstW;
3072 INT ret = 0, srclenW, dstlenW;
3073 UINT locale_cp = CP_ACP;
3075 if (!src || !srclen || dstlen < 0)
3077 SetLastError(ERROR_INVALID_PARAMETER);
3078 return 0;
3081 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3083 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
3084 if (srclenW)
3085 srcW = bufW;
3086 else
3088 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
3089 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3090 if (!srcW)
3092 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3093 return 0;
3095 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
3098 if (flags & LCMAP_SORTKEY)
3100 if (src == dst)
3102 SetLastError(ERROR_INVALID_FLAGS);
3103 goto map_string_exit;
3105 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
3106 if (ret == 0)
3107 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3108 else
3109 ret++;
3110 goto map_string_exit;
3113 if (flags & SORT_STRINGSORT)
3115 SetLastError(ERROR_INVALID_FLAGS);
3116 goto map_string_exit;
3119 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
3120 if (!dstlenW)
3121 goto map_string_exit;
3123 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
3124 if (!dstW)
3126 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3127 goto map_string_exit;
3130 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
3131 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
3132 HeapFree(GetProcessHeap(), 0, dstW);
3134 map_string_exit:
3135 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
3136 return ret;
3139 /*************************************************************************
3140 * FoldStringA (KERNEL32.@)
3142 * Map characters in a string.
3144 * PARAMS
3145 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
3146 * src [I] String to map
3147 * srclen [I] Length of src, or -1 if src is NUL terminated
3148 * dst [O] Destination for mapped string
3149 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
3151 * RETURNS
3152 * Success: The length of the string written to dst, including the terminating NUL. If
3153 * dstlen is 0, the value returned is the same, but nothing is written to dst,
3154 * and dst may be NULL.
3155 * Failure: 0. Use GetLastError() to determine the cause.
3157 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
3158 LPSTR dst, INT dstlen)
3160 INT ret = 0, srclenW = 0;
3161 WCHAR *srcW = NULL, *dstW = NULL;
3163 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3165 SetLastError(ERROR_INVALID_PARAMETER);
3166 return 0;
3169 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3170 src, srclen, NULL, 0);
3171 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
3173 if (!srcW)
3175 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3176 goto FoldStringA_exit;
3179 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
3180 src, srclen, srcW, srclenW);
3182 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
3184 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
3185 if (ret && dstlen)
3187 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
3189 if (!dstW)
3191 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3192 goto FoldStringA_exit;
3195 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
3196 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
3198 ret = 0;
3199 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3203 HeapFree(GetProcessHeap(), 0, dstW);
3205 FoldStringA_exit:
3206 HeapFree(GetProcessHeap(), 0, srcW);
3207 return ret;
3210 /*************************************************************************
3211 * FoldStringW (KERNEL32.@)
3213 * See FoldStringA.
3215 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
3216 LPWSTR dst, INT dstlen)
3218 int ret;
3220 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
3222 case 0:
3223 if (dwFlags)
3224 break;
3225 /* Fall through for dwFlags == 0 */
3226 case MAP_PRECOMPOSED|MAP_COMPOSITE:
3227 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
3228 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
3229 SetLastError(ERROR_INVALID_FLAGS);
3230 return 0;
3233 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
3235 SetLastError(ERROR_INVALID_PARAMETER);
3236 return 0;
3239 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
3240 if (!ret)
3241 SetLastError(ERROR_INSUFFICIENT_BUFFER);
3242 return ret;
3245 /******************************************************************************
3246 * CompareStringW (KERNEL32.@)
3248 * See CompareStringA.
3250 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
3251 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
3253 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
3256 /******************************************************************************
3257 * CompareStringEx (KERNEL32.@)
3259 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
3260 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
3262 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
3263 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
3264 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
3265 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
3266 INT ret;
3268 if (version) FIXME("unexpected version parameter\n");
3269 if (reserved) FIXME("unexpected reserved value\n");
3270 if (lParam) FIXME("unexpected lParam\n");
3272 if (!str1 || !str2)
3274 SetLastError(ERROR_INVALID_PARAMETER);
3275 return 0;
3278 if (flags & ~(supported_flags|semistub_flags))
3280 SetLastError(ERROR_INVALID_FLAGS);
3281 return 0;
3284 if (flags & semistub_flags)
3285 FIXME("semi-stub behavor for flag(s) 0x%x\n", flags & semistub_flags);
3287 if (len1 < 0) len1 = strlenW(str1);
3288 if (len2 < 0) len2 = strlenW(str2);
3290 ret = wine_compare_string(flags, str1, len1, str2, len2);
3292 if (ret) /* need to translate result */
3293 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3294 return CSTR_EQUAL;
3297 /******************************************************************************
3298 * CompareStringA (KERNEL32.@)
3300 * Compare two locale sensitive strings.
3302 * PARAMS
3303 * lcid [I] LCID for the comparison
3304 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3305 * str1 [I] First string to compare
3306 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3307 * str2 [I] Second string to compare
3308 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3310 * RETURNS
3311 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3312 * str1 is less than, equal to or greater than str2 respectively.
3313 * Failure: FALSE. Use GetLastError() to determine the cause.
3315 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3316 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3318 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3319 WCHAR *buf2W = buf1W + 130;
3320 LPWSTR str1W, str2W;
3321 INT len1W = 0, len2W = 0, ret;
3322 UINT locale_cp = CP_ACP;
3324 if (!str1 || !str2)
3326 SetLastError(ERROR_INVALID_PARAMETER);
3327 return 0;
3329 if (len1 < 0) len1 = strlen(str1);
3330 if (len2 < 0) len2 = strlen(str2);
3332 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3334 if (len1)
3336 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3337 if (len1W)
3338 str1W = buf1W;
3339 else
3341 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3342 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3343 if (!str1W)
3345 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3346 return 0;
3348 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3351 else
3353 len1W = 0;
3354 str1W = buf1W;
3357 if (len2)
3359 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3360 if (len2W)
3361 str2W = buf2W;
3362 else
3364 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3365 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3366 if (!str2W)
3368 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3369 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3370 return 0;
3372 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3375 else
3377 len2W = 0;
3378 str2W = buf2W;
3381 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3383 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3384 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3385 return ret;
3388 /******************************************************************************
3389 * CompareStringOrdinal (KERNEL32.@)
3391 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3393 int ret, len;
3395 if (!str1 || !str2)
3397 SetLastError(ERROR_INVALID_PARAMETER);
3398 return 0;
3400 if (len1 < 0) len1 = strlenW(str1);
3401 if (len2 < 0) len2 = strlenW(str2);
3403 len = min(len1, len2);
3404 if (ignore_case)
3406 ret = memicmpW(str1, str2, len);
3408 else
3410 ret = 0;
3411 for (; len > 0; len--)
3412 if ((ret = (*str1++ - *str2++))) break;
3414 if (!ret) ret = len1 - len2;
3416 if (ret < 0) return CSTR_LESS_THAN;
3417 if (ret > 0) return CSTR_GREATER_THAN;
3418 return CSTR_EQUAL;
3421 /*************************************************************************
3422 * lstrcmp (KERNEL32.@)
3423 * lstrcmpA (KERNEL32.@)
3425 * Compare two strings using the current thread locale.
3427 * PARAMS
3428 * str1 [I] First string to compare
3429 * str2 [I] Second string to compare
3431 * RETURNS
3432 * Success: A number less than, equal to or greater than 0 depending on whether
3433 * str1 is less than, equal to or greater than str2 respectively.
3434 * Failure: FALSE. Use GetLastError() to determine the cause.
3436 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3438 int ret;
3440 if ((str1 == NULL) && (str2 == NULL)) return 0;
3441 if (str1 == NULL) return -1;
3442 if (str2 == NULL) return 1;
3444 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3445 if (ret) ret -= 2;
3447 return ret;
3450 /*************************************************************************
3451 * lstrcmpi (KERNEL32.@)
3452 * lstrcmpiA (KERNEL32.@)
3454 * Compare two strings using the current thread locale, ignoring case.
3456 * PARAMS
3457 * str1 [I] First string to compare
3458 * str2 [I] Second string to compare
3460 * RETURNS
3461 * Success: A number less than, equal to or greater than 0 depending on whether
3462 * str2 is less than, equal to or greater than str1 respectively.
3463 * Failure: FALSE. Use GetLastError() to determine the cause.
3465 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3467 int ret;
3469 if ((str1 == NULL) && (str2 == NULL)) return 0;
3470 if (str1 == NULL) return -1;
3471 if (str2 == NULL) return 1;
3473 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3474 if (ret) ret -= 2;
3476 return ret;
3479 /*************************************************************************
3480 * lstrcmpW (KERNEL32.@)
3482 * See lstrcmpA.
3484 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3486 int ret;
3488 if ((str1 == NULL) && (str2 == NULL)) return 0;
3489 if (str1 == NULL) return -1;
3490 if (str2 == NULL) return 1;
3492 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3493 if (ret) ret -= 2;
3495 return ret;
3498 /*************************************************************************
3499 * lstrcmpiW (KERNEL32.@)
3501 * See lstrcmpiA.
3503 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3505 int ret;
3507 if ((str1 == NULL) && (str2 == NULL)) return 0;
3508 if (str1 == NULL) return -1;
3509 if (str2 == NULL) return 1;
3511 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3512 if (ret) ret -= 2;
3514 return ret;
3517 /******************************************************************************
3518 * LOCALE_Init
3520 void LOCALE_Init(void)
3522 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3523 const union cptable *unix_cp );
3525 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3527 #ifdef __APPLE__
3528 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3529 char user_locale[50];
3531 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3532 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3533 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3534 CFStringRef user_locale_string_ref;
3536 if (user_locale_country_ref)
3538 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3539 user_locale_lang_ref, user_locale_country_ref);
3541 else
3543 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3546 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3547 strcat(user_locale, ".UTF-8");
3549 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3550 setenv( "LANG", user_locale, 0 );
3551 TRACE( "setting locale to '%s'\n", user_locale );
3552 #endif /* __APPLE__ */
3554 setlocale( LC_ALL, "" );
3556 unix_cp = setup_unix_locales();
3557 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3559 #ifdef __APPLE__
3560 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3561 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3563 /* Retrieve the preferred language as chosen in System Preferences. */
3564 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3565 leave things be. */
3566 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3567 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3568 CFStringRef user_language_string_ref;
3569 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3570 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3571 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3572 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3574 struct locale_name locale_name;
3575 WCHAR buffer[128];
3576 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3577 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3578 parse_locale_name( buffer, &locale_name );
3579 lcid_LC_MESSAGES = locale_name.lcid;
3580 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3582 if (preferred_langs)
3583 CFRelease( preferred_langs );
3584 if (canonical_lang_string_ref)
3585 CFRelease( canonical_lang_string_ref );
3588 CFRelease( user_locale_ref );
3589 CFRelease( user_locale_string_ref );
3590 #endif
3592 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3593 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3594 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3596 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3597 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3598 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3599 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3600 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3601 if (!unix_cp)
3602 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3603 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3605 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3606 ansi_cptable = wine_cp_get_table( 1252 );
3607 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3608 oem_cptable = wine_cp_get_table( 437 );
3609 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3610 mac_cptable = wine_cp_get_table( 10000 );
3611 if (unix_cp != CP_UTF8)
3613 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3614 unix_cptable = wine_cp_get_table( 28591 );
3617 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3619 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3620 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3621 mac_cptable->info.codepage, unix_cp );
3623 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3626 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3628 UNICODE_STRING keyName;
3629 OBJECT_ATTRIBUTES attr;
3630 HANDLE hkey;
3632 RtlInitUnicodeString( &keyName, szKeyName );
3633 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3635 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3636 hkey = 0;
3638 return hkey;
3641 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3642 LPWSTR szValueName, ULONG valueNameSize,
3643 LPWSTR szValueData, ULONG valueDataSize)
3645 BYTE buffer[80];
3646 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3647 DWORD dwLen;
3649 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3650 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3651 info->NameLength > valueNameSize ||
3652 info->DataLength > valueDataSize)
3654 return FALSE;
3657 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3659 memcpy( szValueName, info->Name, info->NameLength);
3660 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3661 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3662 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3664 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3665 return TRUE;
3668 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3670 BYTE buffer[128];
3671 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3672 DWORD dwSize = sizeof(buffer);
3673 UNICODE_STRING valueName;
3675 RtlInitUnicodeString( &valueName, szValueName );
3677 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3678 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3679 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3680 info->DataLength == sizeof(DWORD))
3682 memcpy(lpVal, info->Data, sizeof(DWORD));
3683 return TRUE;
3686 return FALSE;
3689 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3691 LANGID langId;
3692 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3693 HRSRC hResource;
3694 BOOL bRet = FALSE;
3696 /* FIXME: Is it correct to use the system default langid? */
3697 langId = GetSystemDefaultLangID();
3699 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3700 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3702 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3704 if (hResource)
3706 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3708 if (hResDir)
3710 ULONG iResourceIndex = lgrpid & 0xf;
3711 LPCWSTR lpResEntry = LockResource( hResDir );
3712 ULONG i;
3714 for (i = 0; i < iResourceIndex; i++)
3715 lpResEntry += *lpResEntry + 1;
3717 if (*lpResEntry < nameSize)
3719 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3720 szName[*lpResEntry] = '\0';
3721 bRet = TRUE;
3725 FreeResource( hResource );
3727 return bRet;
3730 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3731 typedef struct
3733 LANGUAGEGROUP_ENUMPROCA procA;
3734 LANGUAGEGROUP_ENUMPROCW procW;
3735 DWORD dwFlags;
3736 LONG_PTR lParam;
3737 } ENUMLANGUAGEGROUP_CALLBACKS;
3739 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3740 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3742 WCHAR szNumber[10], szValue[4];
3743 HANDLE hKey;
3744 BOOL bContinue = TRUE;
3745 ULONG ulIndex = 0;
3747 if (!lpProcs)
3749 SetLastError(ERROR_INVALID_PARAMETER);
3750 return FALSE;
3753 switch (lpProcs->dwFlags)
3755 case 0:
3756 /* Default to LGRPID_INSTALLED */
3757 lpProcs->dwFlags = LGRPID_INSTALLED;
3758 /* Fall through... */
3759 case LGRPID_INSTALLED:
3760 case LGRPID_SUPPORTED:
3761 break;
3762 default:
3763 SetLastError(ERROR_INVALID_FLAGS);
3764 return FALSE;
3767 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3769 if (!hKey)
3770 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3772 while (bContinue)
3774 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3775 szValue, sizeof(szValue) ))
3777 BOOL bInstalled = szValue[0] == '1';
3778 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3780 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3781 bInstalled ? "" : "not ");
3783 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3785 WCHAR szGrpName[48];
3787 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3788 szGrpName[0] = '\0';
3790 if (lpProcs->procW)
3791 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3792 lpProcs->lParam );
3793 else
3795 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3796 char szGrpNameA[48];
3798 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3799 * or whether the language names are ever localised. Assume CP_ACP.
3802 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3803 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3805 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3806 lpProcs->lParam );
3810 ulIndex++;
3812 else
3813 bContinue = FALSE;
3815 if (!bContinue)
3816 break;
3819 if (hKey)
3820 NtClose( hKey );
3822 return TRUE;
3825 /******************************************************************************
3826 * EnumSystemLanguageGroupsA (KERNEL32.@)
3828 * Call a users function for each language group available on the system.
3830 * PARAMS
3831 * pLangGrpEnumProc [I] Callback function to call for each language group
3832 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3833 * lParam [I] User parameter to pass to pLangGrpEnumProc
3835 * RETURNS
3836 * Success: TRUE.
3837 * Failure: FALSE. Use GetLastError() to determine the cause.
3839 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3840 DWORD dwFlags, LONG_PTR lParam)
3842 ENUMLANGUAGEGROUP_CALLBACKS procs;
3844 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3846 procs.procA = pLangGrpEnumProc;
3847 procs.procW = NULL;
3848 procs.dwFlags = dwFlags;
3849 procs.lParam = lParam;
3851 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3854 /******************************************************************************
3855 * EnumSystemLanguageGroupsW (KERNEL32.@)
3857 * See EnumSystemLanguageGroupsA.
3859 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3860 DWORD dwFlags, LONG_PTR lParam)
3862 ENUMLANGUAGEGROUP_CALLBACKS procs;
3864 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3866 procs.procA = NULL;
3867 procs.procW = pLangGrpEnumProc;
3868 procs.dwFlags = dwFlags;
3869 procs.lParam = lParam;
3871 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3874 /******************************************************************************
3875 * IsValidLanguageGroup (KERNEL32.@)
3877 * Determine if a language group is supported and/or installed.
3879 * PARAMS
3880 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3881 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3883 * RETURNS
3884 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3885 * FALSE otherwise.
3887 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3889 static const WCHAR szFormat[] = { '%','x','\0' };
3890 WCHAR szValueName[16], szValue[2];
3891 BOOL bSupported = FALSE, bInstalled = FALSE;
3892 HANDLE hKey;
3895 switch (dwFlags)
3897 case LGRPID_INSTALLED:
3898 case LGRPID_SUPPORTED:
3900 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3902 sprintfW( szValueName, szFormat, lgrpid );
3904 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3906 bSupported = TRUE;
3908 if (szValue[0] == '1')
3909 bInstalled = TRUE;
3912 if (hKey)
3913 NtClose( hKey );
3915 break;
3918 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3919 (dwFlags == LGRPID_INSTALLED && bInstalled))
3920 return TRUE;
3922 return FALSE;
3925 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3926 typedef struct
3928 LANGGROUPLOCALE_ENUMPROCA procA;
3929 LANGGROUPLOCALE_ENUMPROCW procW;
3930 DWORD dwFlags;
3931 LGRPID lgrpid;
3932 LONG_PTR lParam;
3933 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3935 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3936 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3938 static const WCHAR szAlternateSortsKeyName[] = {
3939 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3941 WCHAR szNumber[10], szValue[4];
3942 HANDLE hKey;
3943 BOOL bContinue = TRUE, bAlternate = FALSE;
3944 LGRPID lgrpid;
3945 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3947 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3949 SetLastError(ERROR_INVALID_PARAMETER);
3950 return FALSE;
3953 if (lpProcs->dwFlags)
3955 SetLastError(ERROR_INVALID_FLAGS);
3956 return FALSE;
3959 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3961 if (!hKey)
3962 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3964 while (bContinue)
3966 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3967 szValue, sizeof(szValue) ))
3969 lgrpid = strtoulW( szValue, NULL, 16 );
3971 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3972 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3974 if (lgrpid == lpProcs->lgrpid)
3976 LCID lcid;
3978 lcid = strtoulW( szNumber, NULL, 16 );
3980 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3981 * '00000437 ;Georgian'
3982 * At present we only pass the LCID string.
3985 if (lpProcs->procW)
3986 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3987 else
3989 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3991 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3993 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3997 ulIndex++;
3999 else
4001 /* Finished enumerating this key */
4002 if (!bAlternate)
4004 /* Enumerate alternate sorts also */
4005 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
4006 bAlternate = TRUE;
4007 ulIndex = 0;
4009 else
4010 bContinue = FALSE; /* Finished both keys */
4013 if (!bContinue)
4014 break;
4017 if (hKey)
4018 NtClose( hKey );
4020 return TRUE;
4023 /******************************************************************************
4024 * EnumLanguageGroupLocalesA (KERNEL32.@)
4026 * Call a users function for every locale in a language group available on the system.
4028 * PARAMS
4029 * pLangGrpLcEnumProc [I] Callback function to call for each locale
4030 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
4031 * dwFlags [I] Reserved, set to 0
4032 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
4034 * RETURNS
4035 * Success: TRUE.
4036 * Failure: FALSE. Use GetLastError() to determine the cause.
4038 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
4039 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4041 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4043 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4045 callbacks.procA = pLangGrpLcEnumProc;
4046 callbacks.procW = NULL;
4047 callbacks.dwFlags = dwFlags;
4048 callbacks.lgrpid = lgrpid;
4049 callbacks.lParam = lParam;
4051 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4054 /******************************************************************************
4055 * EnumLanguageGroupLocalesW (KERNEL32.@)
4057 * See EnumLanguageGroupLocalesA.
4059 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
4060 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
4062 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
4064 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
4066 callbacks.procA = NULL;
4067 callbacks.procW = pLangGrpLcEnumProc;
4068 callbacks.dwFlags = dwFlags;
4069 callbacks.lgrpid = lgrpid;
4070 callbacks.lParam = lParam;
4072 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
4075 /******************************************************************************
4076 * InvalidateNLSCache (KERNEL32.@)
4078 * Invalidate the cache of NLS values.
4080 * PARAMS
4081 * None.
4083 * RETURNS
4084 * Success: TRUE.
4085 * Failure: FALSE.
4087 BOOL WINAPI InvalidateNLSCache(void)
4089 FIXME("() stub\n");
4090 return FALSE;
4093 /******************************************************************************
4094 * GetUserGeoID (KERNEL32.@)
4096 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
4098 GEOID ret = GEOID_NOT_AVAILABLE;
4099 static const WCHAR geoW[] = {'G','e','o',0};
4100 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4101 WCHAR bufferW[40], *end;
4102 DWORD count;
4103 HANDLE hkey, hSubkey = 0;
4104 UNICODE_STRING keyW;
4105 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
4106 RtlInitUnicodeString( &keyW, nationW );
4107 count = sizeof(bufferW);
4109 if(!(hkey = create_registry_key())) return ret;
4111 switch( GeoClass ){
4112 case GEOCLASS_NATION:
4113 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
4115 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
4116 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
4117 ret = strtolW((LPCWSTR)info->Data, &end, 10);
4119 break;
4120 case GEOCLASS_REGION:
4121 FIXME("GEOCLASS_REGION not handled yet\n");
4122 break;
4125 NtClose(hkey);
4126 if (hSubkey) NtClose(hSubkey);
4127 return ret;
4130 /******************************************************************************
4131 * SetUserGeoID (KERNEL32.@)
4133 BOOL WINAPI SetUserGeoID( GEOID GeoID )
4135 static const WCHAR geoW[] = {'G','e','o',0};
4136 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
4137 static const WCHAR formatW[] = {'%','i',0};
4138 UNICODE_STRING nameW,keyW;
4139 WCHAR bufferW[10];
4140 OBJECT_ATTRIBUTES attr;
4141 HANDLE hkey;
4143 if(!(hkey = create_registry_key())) return FALSE;
4145 attr.Length = sizeof(attr);
4146 attr.RootDirectory = hkey;
4147 attr.ObjectName = &nameW;
4148 attr.Attributes = 0;
4149 attr.SecurityDescriptor = NULL;
4150 attr.SecurityQualityOfService = NULL;
4151 RtlInitUnicodeString( &nameW, geoW );
4152 RtlInitUnicodeString( &keyW, nationW );
4154 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
4157 NtClose(attr.RootDirectory);
4158 return FALSE;
4161 sprintfW(bufferW, formatW, GeoID);
4162 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
4163 NtClose(attr.RootDirectory);
4164 NtClose(hkey);
4165 return TRUE;
4168 typedef struct
4170 union
4172 UILANGUAGE_ENUMPROCA procA;
4173 UILANGUAGE_ENUMPROCW procW;
4174 } u;
4175 DWORD flags;
4176 LONG_PTR param;
4177 } ENUM_UILANG_CALLBACK;
4179 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4180 LPCSTR name, WORD LangID, LONG_PTR lParam )
4182 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4183 char buf[20];
4185 sprintf(buf, "%08x", (UINT)LangID);
4186 return enum_uilang->u.procA( buf, enum_uilang->param );
4189 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4190 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4192 static const WCHAR formatW[] = {'%','0','8','x',0};
4193 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4194 WCHAR buf[20];
4196 sprintfW( buf, formatW, (UINT)LangID );
4197 return enum_uilang->u.procW( buf, enum_uilang->param );
4200 /******************************************************************************
4201 * EnumUILanguagesA (KERNEL32.@)
4203 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4205 ENUM_UILANG_CALLBACK enum_uilang;
4207 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4209 if(!pUILangEnumProc) {
4210 SetLastError(ERROR_INVALID_PARAMETER);
4211 return FALSE;
4213 if(dwFlags) {
4214 SetLastError(ERROR_INVALID_FLAGS);
4215 return FALSE;
4218 enum_uilang.u.procA = pUILangEnumProc;
4219 enum_uilang.flags = dwFlags;
4220 enum_uilang.param = lParam;
4222 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4223 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4224 (LONG_PTR)&enum_uilang);
4225 return TRUE;
4228 /******************************************************************************
4229 * EnumUILanguagesW (KERNEL32.@)
4231 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4233 ENUM_UILANG_CALLBACK enum_uilang;
4235 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4238 if(!pUILangEnumProc) {
4239 SetLastError(ERROR_INVALID_PARAMETER);
4240 return FALSE;
4242 if(dwFlags) {
4243 SetLastError(ERROR_INVALID_FLAGS);
4244 return FALSE;
4247 enum_uilang.u.procW = pUILangEnumProc;
4248 enum_uilang.flags = dwFlags;
4249 enum_uilang.param = lParam;
4251 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4252 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4253 (LONG_PTR)&enum_uilang);
4254 return TRUE;
4257 enum locationkind {
4258 LOCATION_NATION = 0,
4259 LOCATION_REGION,
4260 LOCATION_BOTH
4263 struct geoinfo_t {
4264 GEOID id;
4265 WCHAR iso2W[3];
4266 WCHAR iso3W[4];
4267 GEOID parent;
4268 INT uncode;
4269 enum locationkind kind;
4272 static const struct geoinfo_t geoinfodata[] = {
4273 { 2, {'A','G',0}, {'A','T','G',0}, 10039880, 28 }, /* Antigua and Barbuda */
4274 { 3, {'A','F',0}, {'A','F','G',0}, 47614, 4 }, /* Afghanistan */
4275 { 4, {'D','Z',0}, {'D','Z','A',0}, 42487, 12 }, /* Algeria */
4276 { 5, {'A','Z',0}, {'A','Z','E',0}, 47611, 31 }, /* Azerbaijan */
4277 { 6, {'A','L',0}, {'A','L','B',0}, 47610, 8 }, /* Albania */
4278 { 7, {'A','M',0}, {'A','R','M',0}, 47611, 51 }, /* Armenia */
4279 { 8, {'A','D',0}, {'A','N','D',0}, 47610, 20 }, /* Andorra */
4280 { 9, {'A','O',0}, {'A','G','O',0}, 42484, 24 }, /* Angola */
4281 { 10, {'A','S',0}, {'A','S','M',0}, 26286, 16 }, /* American Samoa */
4282 { 11, {'A','R',0}, {'A','R','G',0}, 31396, 32 }, /* Argentina */
4283 { 12, {'A','U',0}, {'A','U','S',0}, 10210825, 36 }, /* Australia */
4284 { 14, {'A','T',0}, {'A','U','T',0}, 10210824, 40 }, /* Austria */
4285 { 17, {'B','H',0}, {'B','H','R',0}, 47611, 48 }, /* Bahrain */
4286 { 18, {'B','B',0}, {'B','R','B',0}, 10039880, 52 }, /* Barbados */
4287 { 19, {'B','W',0}, {'B','W','A',0}, 10039883, 72 }, /* Botswana */
4288 { 20, {'B','M',0}, {'B','M','U',0}, 23581, 60 }, /* Bermuda */
4289 { 21, {'B','E',0}, {'B','E','L',0}, 10210824, 56 }, /* Belgium */
4290 { 22, {'B','S',0}, {'B','H','S',0}, 10039880, 44 }, /* Bahamas, The */
4291 { 23, {'B','D',0}, {'B','G','D',0}, 47614, 50 }, /* Bangladesh */
4292 { 24, {'B','Z',0}, {'B','L','Z',0}, 27082, 84 }, /* Belize */
4293 { 25, {'B','A',0}, {'B','I','H',0}, 47610, 70 }, /* Bosnia and Herzegovina */
4294 { 26, {'B','O',0}, {'B','O','L',0}, 31396, 68 }, /* Bolivia */
4295 { 27, {'M','M',0}, {'M','M','R',0}, 47599, 104 }, /* Myanmar */
4296 { 28, {'B','J',0}, {'B','E','N',0}, 42483, 204 }, /* Benin */
4297 { 29, {'B','Y',0}, {'B','L','R',0}, 47609, 112 }, /* Belarus */
4298 { 30, {'S','B',0}, {'S','L','B',0}, 20900, 90 }, /* Solomon Islands */
4299 { 32, {'B','R',0}, {'B','R','A',0}, 31396, 76 }, /* Brazil */
4300 { 34, {'B','T',0}, {'B','T','N',0}, 47614, 64 }, /* Bhutan */
4301 { 35, {'B','G',0}, {'B','G','R',0}, 47609, 100 }, /* Bulgaria */
4302 { 37, {'B','N',0}, {'B','R','N',0}, 47599, 96 }, /* Brunei */
4303 { 38, {'B','I',0}, {'B','D','I',0}, 47603, 108 }, /* Burundi */
4304 { 39, {'C','A',0}, {'C','A','N',0}, 23581, 124 }, /* Canada */
4305 { 40, {'K','H',0}, {'K','H','M',0}, 47599, 116 }, /* Cambodia */
4306 { 41, {'T','D',0}, {'T','C','D',0}, 42484, 148 }, /* Chad */
4307 { 42, {'L','K',0}, {'L','K','A',0}, 47614, 144 }, /* Sri Lanka */
4308 { 43, {'C','G',0}, {'C','O','G',0}, 42484, 178 }, /* Congo */
4309 { 44, {'C','D',0}, {'C','O','D',0}, 42484, 180 }, /* Congo (DRC) */
4310 { 45, {'C','N',0}, {'C','H','N',0}, 47600, 156 }, /* China */
4311 { 46, {'C','L',0}, {'C','H','L',0}, 31396, 152 }, /* Chile */
4312 { 49, {'C','M',0}, {'C','M','R',0}, 42484, 120 }, /* Cameroon */
4313 { 50, {'K','M',0}, {'C','O','M',0}, 47603, 174 }, /* Comoros */
4314 { 51, {'C','O',0}, {'C','O','L',0}, 31396, 170 }, /* Colombia */
4315 { 54, {'C','R',0}, {'C','R','I',0}, 27082, 188 }, /* Costa Rica */
4316 { 55, {'C','F',0}, {'C','A','F',0}, 42484, 140 }, /* Central African Republic */
4317 { 56, {'C','U',0}, {'C','U','B',0}, 10039880, 192 }, /* Cuba */
4318 { 57, {'C','V',0}, {'C','P','V',0}, 42483, 132 }, /* Cape Verde */
4319 { 59, {'C','Y',0}, {'C','Y','P',0}, 47611, 196 }, /* Cyprus */
4320 { 61, {'D','K',0}, {'D','N','K',0}, 10039882, 208 }, /* Denmark */
4321 { 62, {'D','J',0}, {'D','J','I',0}, 47603, 262 }, /* Djibouti */
4322 { 63, {'D','M',0}, {'D','M','A',0}, 10039880, 212 }, /* Dominica */
4323 { 65, {'D','O',0}, {'D','O','M',0}, 10039880, 214 }, /* Dominican Republic */
4324 { 66, {'E','C',0}, {'E','C','U',0}, 31396, 218 }, /* Ecuador */
4325 { 67, {'E','G',0}, {'E','G','Y',0}, 42487, 818 }, /* Egypt */
4326 { 68, {'I','E',0}, {'I','R','L',0}, 10039882, 372 }, /* Ireland */
4327 { 69, {'G','Q',0}, {'G','N','Q',0}, 42484, 226 }, /* Equatorial Guinea */
4328 { 70, {'E','E',0}, {'E','S','T',0}, 10039882, 233 }, /* Estonia */
4329 { 71, {'E','R',0}, {'E','R','I',0}, 47603, 232 }, /* Eritrea */
4330 { 72, {'S','V',0}, {'S','L','V',0}, 27082, 222 }, /* El Salvador */
4331 { 73, {'E','T',0}, {'E','T','H',0}, 47603, 231 }, /* Ethiopia */
4332 { 75, {'C','Z',0}, {'C','Z','E',0}, 47609, 203 }, /* Czech Republic */
4333 { 77, {'F','I',0}, {'F','I','N',0}, 10039882, 246 }, /* Finland */
4334 { 78, {'F','J',0}, {'F','J','I',0}, 20900, 242 }, /* Fiji Islands */
4335 { 80, {'F','M',0}, {'F','S','M',0}, 21206, 583 }, /* Micronesia */
4336 { 81, {'F','O',0}, {'F','R','O',0}, 10039882, 234 }, /* Faroe Islands */
4337 { 84, {'F','R',0}, {'F','R','A',0}, 10210824, 250 }, /* France */
4338 { 86, {'G','M',0}, {'G','M','B',0}, 42483, 270 }, /* Gambia, The */
4339 { 87, {'G','A',0}, {'G','A','B',0}, 42484, 266 }, /* Gabon */
4340 { 88, {'G','E',0}, {'G','E','O',0}, 47611, 268 }, /* Georgia */
4341 { 89, {'G','H',0}, {'G','H','A',0}, 42483, 288 }, /* Ghana */
4342 { 90, {'G','I',0}, {'G','I','B',0}, 47610, 292 }, /* Gibraltar */
4343 { 91, {'G','D',0}, {'G','R','D',0}, 10039880, 308 }, /* Grenada */
4344 { 93, {'G','L',0}, {'G','R','L',0}, 23581, 304 }, /* Greenland */
4345 { 94, {'D','E',0}, {'D','E','U',0}, 10210824, 276 }, /* Germany */
4346 { 98, {'G','R',0}, {'G','R','C',0}, 47610, 300 }, /* Greece */
4347 { 99, {'G','T',0}, {'G','T','M',0}, 27082, 320 }, /* Guatemala */
4348 { 100, {'G','N',0}, {'G','I','N',0}, 42483, 324 }, /* Guinea */
4349 { 101, {'G','Y',0}, {'G','U','Y',0}, 31396, 328 }, /* Guyana */
4350 { 103, {'H','T',0}, {'H','T','I',0}, 10039880, 332 }, /* Haiti */
4351 { 104, {'H','K',0}, {'H','K','G',0}, 47600, 344 }, /* Hong Kong S.A.R. */
4352 { 106, {'H','N',0}, {'H','N','D',0}, 27082, 340 }, /* Honduras */
4353 { 108, {'H','R',0}, {'H','R','V',0}, 47610, 191 }, /* Croatia */
4354 { 109, {'H','U',0}, {'H','U','N',0}, 47609, 348 }, /* Hungary */
4355 { 110, {'I','S',0}, {'I','S','L',0}, 10039882, 352 }, /* Iceland */
4356 { 111, {'I','D',0}, {'I','D','N',0}, 47599, 360 }, /* Indonesia */
4357 { 113, {'I','N',0}, {'I','N','D',0}, 47614, 356 }, /* India */
4358 { 114, {'I','O',0}, {'I','O','T',0}, 39070, 86 }, /* British Indian Ocean Territory */
4359 { 116, {'I','R',0}, {'I','R','N',0}, 47614, 364 }, /* Iran */
4360 { 117, {'I','L',0}, {'I','S','R',0}, 47611, 376 }, /* Israel */
4361 { 118, {'I','T',0}, {'I','T','A',0}, 47610, 380 }, /* Italy */
4362 { 119, {'C','I',0}, {'C','I','V',0}, 42483, 384 }, /* Côte d'Ivoire */
4363 { 121, {'I','Q',0}, {'I','R','Q',0}, 47611, 368 }, /* Iraq */
4364 { 122, {'J','P',0}, {'J','P','N',0}, 47600, 392 }, /* Japan */
4365 { 124, {'J','M',0}, {'J','A','M',0}, 10039880, 388 }, /* Jamaica */
4366 { 125, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Jan Mayen */
4367 { 126, {'J','O',0}, {'J','O','R',0}, 47611, 400 }, /* Jordan */
4368 { 127, {'X','X',0}, {'X','X',0}, 161832256 }, /* Johnston Atoll */
4369 { 129, {'K','E',0}, {'K','E','N',0}, 47603, 404 }, /* Kenya */
4370 { 130, {'K','G',0}, {'K','G','Z',0}, 47590, 417 }, /* Kyrgyzstan */
4371 { 131, {'K','P',0}, {'P','R','K',0}, 47600, 408 }, /* North Korea */
4372 { 133, {'K','I',0}, {'K','I','R',0}, 21206, 296 }, /* Kiribati */
4373 { 134, {'K','R',0}, {'K','O','R',0}, 47600, 410 }, /* Korea */
4374 { 136, {'K','W',0}, {'K','W','T',0}, 47611, 414 }, /* Kuwait */
4375 { 137, {'K','Z',0}, {'K','A','Z',0}, 47590, 398 }, /* Kazakhstan */
4376 { 138, {'L','A',0}, {'L','A','O',0}, 47599, 418 }, /* Laos */
4377 { 139, {'L','B',0}, {'L','B','N',0}, 47611, 422 }, /* Lebanon */
4378 { 140, {'L','V',0}, {'L','V','A',0}, 10039882, 428 }, /* Latvia */
4379 { 141, {'L','T',0}, {'L','T','U',0}, 10039882, 440 }, /* Lithuania */
4380 { 142, {'L','R',0}, {'L','B','R',0}, 42483, 430 }, /* Liberia */
4381 { 143, {'S','K',0}, {'S','V','K',0}, 47609, 703 }, /* Slovakia */
4382 { 145, {'L','I',0}, {'L','I','E',0}, 10210824, 438 }, /* Liechtenstein */
4383 { 146, {'L','S',0}, {'L','S','O',0}, 10039883, 426 }, /* Lesotho */
4384 { 147, {'L','U',0}, {'L','U','X',0}, 10210824, 442 }, /* Luxembourg */
4385 { 148, {'L','Y',0}, {'L','B','Y',0}, 42487, 434 }, /* Libya */
4386 { 149, {'M','G',0}, {'M','D','G',0}, 47603, 450 }, /* Madagascar */
4387 { 151, {'M','O',0}, {'M','A','C',0}, 47600, 446 }, /* Macao S.A.R. */
4388 { 152, {'M','D',0}, {'M','D','A',0}, 47609, 498 }, /* Moldova */
4389 { 154, {'M','N',0}, {'M','N','G',0}, 47600, 496 }, /* Mongolia */
4390 { 156, {'M','W',0}, {'M','W','I',0}, 47603, 454 }, /* Malawi */
4391 { 157, {'M','L',0}, {'M','L','I',0}, 42483, 466 }, /* Mali */
4392 { 158, {'M','C',0}, {'M','C','O',0}, 10210824, 492 }, /* Monaco */
4393 { 159, {'M','A',0}, {'M','A','R',0}, 42487, 504 }, /* Morocco */
4394 { 160, {'M','U',0}, {'M','U','S',0}, 47603, 480 }, /* Mauritius */
4395 { 162, {'M','R',0}, {'M','R','T',0}, 42483, 478 }, /* Mauritania */
4396 { 163, {'M','T',0}, {'M','L','T',0}, 47610, 470 }, /* Malta */
4397 { 164, {'O','M',0}, {'O','M','N',0}, 47611, 512 }, /* Oman */
4398 { 165, {'M','V',0}, {'M','D','V',0}, 47614, 462 }, /* Maldives */
4399 { 166, {'M','X',0}, {'M','E','X',0}, 27082, 484 }, /* Mexico */
4400 { 167, {'M','Y',0}, {'M','Y','S',0}, 47599, 458 }, /* Malaysia */
4401 { 168, {'M','Z',0}, {'M','O','Z',0}, 47603, 508 }, /* Mozambique */
4402 { 173, {'N','E',0}, {'N','E','R',0}, 42483, 562 }, /* Niger */
4403 { 174, {'V','U',0}, {'V','U','T',0}, 20900, 548 }, /* Vanuatu */
4404 { 175, {'N','G',0}, {'N','G','A',0}, 42483, 566 }, /* Nigeria */
4405 { 176, {'N','L',0}, {'N','L','D',0}, 10210824, 528 }, /* Netherlands */
4406 { 177, {'N','O',0}, {'N','O','R',0}, 10039882, 578 }, /* Norway */
4407 { 178, {'N','P',0}, {'N','P','L',0}, 47614, 524 }, /* Nepal */
4408 { 180, {'N','R',0}, {'N','R','U',0}, 21206, 520 }, /* Nauru */
4409 { 181, {'S','R',0}, {'S','U','R',0}, 31396, 740 }, /* Suriname */
4410 { 182, {'N','I',0}, {'N','I','C',0}, 27082, 558 }, /* Nicaragua */
4411 { 183, {'N','Z',0}, {'N','Z','L',0}, 10210825, 554 }, /* New Zealand */
4412 { 184, {'P','S',0}, {'P','S','E',0}, 47611, 275 }, /* Palestinian Authority */
4413 { 185, {'P','Y',0}, {'P','R','Y',0}, 31396, 600 }, /* Paraguay */
4414 { 187, {'P','E',0}, {'P','E','R',0}, 31396, 604 }, /* Peru */
4415 { 190, {'P','K',0}, {'P','A','K',0}, 47614, 586 }, /* Pakistan */
4416 { 191, {'P','L',0}, {'P','O','L',0}, 47609, 616 }, /* Poland */
4417 { 192, {'P','A',0}, {'P','A','N',0}, 27082, 591 }, /* Panama */
4418 { 193, {'P','T',0}, {'P','R','T',0}, 47610, 620 }, /* Portugal */
4419 { 194, {'P','G',0}, {'P','N','G',0}, 20900, 598 }, /* Papua New Guinea */
4420 { 195, {'P','W',0}, {'P','L','W',0}, 21206, 585 }, /* Palau */
4421 { 196, {'G','W',0}, {'G','N','B',0}, 42483, 624 }, /* Guinea-Bissau */
4422 { 197, {'Q','A',0}, {'Q','A','T',0}, 47611, 634 }, /* Qatar */
4423 { 198, {'R','E',0}, {'R','E','U',0}, 47603, 638 }, /* Reunion */
4424 { 199, {'M','H',0}, {'M','H','L',0}, 21206, 584 }, /* Marshall Islands */
4425 { 200, {'R','O',0}, {'R','O','U',0}, 47609, 642 }, /* Romania */
4426 { 201, {'P','H',0}, {'P','H','L',0}, 47599, 608 }, /* Philippines */
4427 { 202, {'P','R',0}, {'P','R','I',0}, 10039880, 630 }, /* Puerto Rico */
4428 { 203, {'R','U',0}, {'R','U','S',0}, 47609, 643 }, /* Russia */
4429 { 204, {'R','W',0}, {'R','W','A',0}, 47603, 646 }, /* Rwanda */
4430 { 205, {'S','A',0}, {'S','A','U',0}, 47611, 682 }, /* Saudi Arabia */
4431 { 206, {'P','M',0}, {'S','P','M',0}, 23581, 666 }, /* St. Pierre and Miquelon */
4432 { 207, {'K','N',0}, {'K','N','A',0}, 10039880, 659 }, /* St. Kitts and Nevis */
4433 { 208, {'S','C',0}, {'S','Y','C',0}, 47603, 690 }, /* Seychelles */
4434 { 209, {'Z','A',0}, {'Z','A','F',0}, 10039883, 710 }, /* South Africa */
4435 { 210, {'S','N',0}, {'S','E','N',0}, 42483, 686 }, /* Senegal */
4436 { 212, {'S','I',0}, {'S','V','N',0}, 47610, 705 }, /* Slovenia */
4437 { 213, {'S','L',0}, {'S','L','E',0}, 42483, 694 }, /* Sierra Leone */
4438 { 214, {'S','M',0}, {'S','M','R',0}, 47610, 674 }, /* San Marino */
4439 { 215, {'S','G',0}, {'S','G','P',0}, 47599, 702 }, /* Singapore */
4440 { 216, {'S','O',0}, {'S','O','M',0}, 47603, 706 }, /* Somalia */
4441 { 217, {'E','S',0}, {'E','S','P',0}, 47610, 724 }, /* Spain */
4442 { 218, {'L','C',0}, {'L','C','A',0}, 10039880, 662 }, /* St. Lucia */
4443 { 219, {'S','D',0}, {'S','D','N',0}, 42487, 736 }, /* Sudan */
4444 { 220, {'S','J',0}, {'S','J','M',0}, 10039882, 744 }, /* Svalbard */
4445 { 221, {'S','E',0}, {'S','W','E',0}, 10039882, 752 }, /* Sweden */
4446 { 222, {'S','Y',0}, {'S','Y','R',0}, 47611, 760 }, /* Syria */
4447 { 223, {'C','H',0}, {'C','H','E',0}, 10210824, 756 }, /* Switzerland */
4448 { 224, {'A','E',0}, {'A','R','E',0}, 47611, 784 }, /* United Arab Emirates */
4449 { 225, {'T','T',0}, {'T','T','O',0}, 10039880, 780 }, /* Trinidad and Tobago */
4450 { 227, {'T','H',0}, {'T','H','A',0}, 47599, 764 }, /* Thailand */
4451 { 228, {'T','J',0}, {'T','J','K',0}, 47590, 762 }, /* Tajikistan */
4452 { 231, {'T','O',0}, {'T','O','N',0}, 26286, 776 }, /* Tonga */
4453 { 232, {'T','G',0}, {'T','G','O',0}, 42483, 768 }, /* Togo */
4454 { 233, {'S','T',0}, {'S','T','P',0}, 42484, 678 }, /* São Tomé and Príncipe */
4455 { 234, {'T','N',0}, {'T','U','N',0}, 42487, 788 }, /* Tunisia */
4456 { 235, {'T','R',0}, {'T','U','R',0}, 47611, 792 }, /* Turkey */
4457 { 236, {'T','V',0}, {'T','U','V',0}, 26286, 798 }, /* Tuvalu */
4458 { 237, {'T','W',0}, {'T','W','N',0}, 47600, 158 }, /* Taiwan */
4459 { 238, {'T','M',0}, {'T','K','M',0}, 47590, 795 }, /* Turkmenistan */
4460 { 239, {'T','Z',0}, {'T','Z','A',0}, 47603, 834 }, /* Tanzania */
4461 { 240, {'U','G',0}, {'U','G','A',0}, 47603, 800 }, /* Uganda */
4462 { 241, {'U','A',0}, {'U','K','R',0}, 47609, 804 }, /* Ukraine */
4463 { 242, {'G','B',0}, {'G','B','R',0}, 10039882, 826 }, /* United Kingdom */
4464 { 244, {'U','S',0}, {'U','S','A',0}, 23581, 840 }, /* United States */
4465 { 245, {'B','F',0}, {'B','F','A',0}, 42483, 854 }, /* Burkina Faso */
4466 { 246, {'U','Y',0}, {'U','R','Y',0}, 31396, 858 }, /* Uruguay */
4467 { 247, {'U','Z',0}, {'U','Z','B',0}, 47590, 860 }, /* Uzbekistan */
4468 { 248, {'V','C',0}, {'V','C','T',0}, 10039880, 670 }, /* St. Vincent and the Grenadines */
4469 { 249, {'V','E',0}, {'V','E','N',0}, 31396, 862 }, /* Bolivarian Republic of Venezuela */
4470 { 251, {'V','N',0}, {'V','N','M',0}, 47599, 704 }, /* Vietnam */
4471 { 252, {'V','I',0}, {'V','I','R',0}, 10039880, 850 }, /* Virgin Islands */
4472 { 253, {'V','A',0}, {'V','A','T',0}, 47610, 336 }, /* Vatican City */
4473 { 254, {'N','A',0}, {'N','A','M',0}, 10039883, 516 }, /* Namibia */
4474 { 257, {'E','H',0}, {'E','S','H',0}, 42487, 732 }, /* Western Sahara (disputed) */
4475 { 258, {'X','X',0}, {'X','X',0}, 161832256 }, /* Wake Island */
4476 { 259, {'W','S',0}, {'W','S','M',0}, 26286, 882 }, /* Samoa */
4477 { 260, {'S','Z',0}, {'S','W','Z',0}, 10039883, 748 }, /* Swaziland */
4478 { 261, {'Y','E',0}, {'Y','E','M',0}, 47611, 887 }, /* Yemen */
4479 { 263, {'Z','M',0}, {'Z','M','B',0}, 47603, 894 }, /* Zambia */
4480 { 264, {'Z','W',0}, {'Z','W','E',0}, 47603, 716 }, /* Zimbabwe */
4481 { 269, {'C','S',0}, {'S','C','G',0}, 47610, 891 }, /* Serbia and Montenegro (Former) */
4482 { 270, {'M','E',0}, {'M','N','E',0}, 47610, 499 }, /* Montenegro */
4483 { 271, {'R','S',0}, {'S','R','B',0}, 47610, 688 }, /* Serbia */
4484 { 273, {'C','W',0}, {'C','U','W',0}, 10039880, 531 }, /* Curaçao */
4485 { 276, {'S','S',0}, {'S','S','D',0}, 42487, 728 }, /* South Sudan */
4486 { 300, {'A','I',0}, {'A','I','A',0}, 10039880, 660 }, /* Anguilla */
4487 { 301, {'A','Q',0}, {'A','T','A',0}, 39070, 10 }, /* Antarctica */
4488 { 302, {'A','W',0}, {'A','B','W',0}, 10039880, 533 }, /* Aruba */
4489 { 303, {'X','X',0}, {'X','X',0}, 39070 }, /* Ascension Island */
4490 { 304, {'X','X',0}, {'X','X',0}, 10210825 }, /* Ashmore and Cartier Islands */
4491 { 305, {'X','X',0}, {'X','X',0}, 161832256 }, /* Baker Island */
4492 { 306, {'B','V',0}, {'B','V','T',0}, 39070, 74 }, /* Bouvet Island */
4493 { 307, {'K','Y',0}, {'C','Y','M',0}, 10039880, 136 }, /* Cayman Islands */
4494 { 308, {'X','X',0}, {'X','X',0}, 10210824, 0, LOCATION_BOTH }, /* Channel Islands */
4495 { 309, {'C','X',0}, {'C','X','R',0}, 12, 162 }, /* Christmas Island */
4496 { 310, {'X','X',0}, {'X','X',0}, 27114 }, /* Clipperton Island */
4497 { 311, {'C','C',0}, {'C','C','K',0}, 10210825, 166 }, /* Cocos (Keeling) Islands */
4498 { 312, {'C','K',0}, {'C','O','K',0}, 26286, 184 }, /* Cook Islands */
4499 { 313, {'X','X',0}, {'X','X',0}, 10210825 }, /* Coral Sea Islands */
4500 { 314, {'X','X',0}, {'X','X',0}, 114 }, /* Diego Garcia */
4501 { 315, {'F','K',0}, {'F','L','K',0}, 31396, 238 }, /* Falkland Islands (Islas Malvinas) */
4502 { 317, {'G','F',0}, {'G','U','F',0}, 31396, 254 }, /* French Guiana */
4503 { 318, {'P','F',0}, {'P','Y','F',0}, 26286, 258 }, /* French Polynesia */
4504 { 319, {'T','F',0}, {'A','T','F',0}, 39070, 260 }, /* French Southern and Antarctic Lands */
4505 { 321, {'G','P',0}, {'G','L','P',0}, 10039880, 312 }, /* Guadeloupe */
4506 { 322, {'G','U',0}, {'G','U','M',0}, 21206, 316 }, /* Guam */
4507 { 323, {'X','X',0}, {'X','X',0}, 39070 }, /* Guantanamo Bay */
4508 { 324, {'G','G',0}, {'G','G','Y',0}, 308, 831 }, /* Guernsey */
4509 { 325, {'H','M',0}, {'H','M','D',0}, 39070, 334 }, /* Heard Island and McDonald Islands */
4510 { 326, {'X','X',0}, {'X','X',0}, 161832256 }, /* Howland Island */
4511 { 327, {'X','X',0}, {'X','X',0}, 161832256 }, /* Jarvis Island */
4512 { 328, {'J','E',0}, {'J','E','Y',0}, 308, 832 }, /* Jersey */
4513 { 329, {'X','X',0}, {'X','X',0}, 161832256 }, /* Kingman Reef */
4514 { 330, {'M','Q',0}, {'M','T','Q',0}, 10039880, 474 }, /* Martinique */
4515 { 331, {'Y','T',0}, {'M','Y','T',0}, 47603, 175 }, /* Mayotte */
4516 { 332, {'M','S',0}, {'M','S','R',0}, 10039880, 500 }, /* Montserrat */
4517 { 333, {'A','N',0}, {'A','N','T',0}, 10039880, 530, LOCATION_BOTH }, /* Netherlands Antilles (Former) */
4518 { 334, {'N','C',0}, {'N','C','L',0}, 20900, 540 }, /* New Caledonia */
4519 { 335, {'N','U',0}, {'N','I','U',0}, 26286, 570 }, /* Niue */
4520 { 336, {'N','F',0}, {'N','F','K',0}, 10210825, 574 }, /* Norfolk Island */
4521 { 337, {'M','P',0}, {'M','N','P',0}, 21206, 580 }, /* Northern Mariana Islands */
4522 { 338, {'X','X',0}, {'X','X',0}, 161832256 }, /* Palmyra Atoll */
4523 { 339, {'P','N',0}, {'P','C','N',0}, 26286, 612 }, /* Pitcairn Islands */
4524 { 340, {'X','X',0}, {'X','X',0}, 337 }, /* Rota Island */
4525 { 341, {'X','X',0}, {'X','X',0}, 337 }, /* Saipan */
4526 { 342, {'G','S',0}, {'S','G','S',0}, 39070, 239 }, /* South Georgia and the South Sandwich Islands */
4527 { 343, {'S','H',0}, {'S','H','N',0}, 42483, 654 }, /* St. Helena */
4528 { 346, {'X','X',0}, {'X','X',0}, 337 }, /* Tinian Island */
4529 { 347, {'T','K',0}, {'T','K','L',0}, 26286, 772 }, /* Tokelau */
4530 { 348, {'X','X',0}, {'X','X',0}, 39070 }, /* Tristan da Cunha */
4531 { 349, {'T','C',0}, {'T','C','A',0}, 10039880, 796 }, /* Turks and Caicos Islands */
4532 { 351, {'V','G',0}, {'V','G','B',0}, 10039880, 92 }, /* Virgin Islands, British */
4533 { 352, {'W','F',0}, {'W','L','F',0}, 26286, 876 }, /* Wallis and Futuna */
4534 { 742, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Africa */
4535 { 2129, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Asia */
4536 { 10541, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Europe */
4537 { 15126, {'I','M',0}, {'I','M','N',0}, 10039882, 833 }, /* Man, Isle of */
4538 { 19618, {'M','K',0}, {'M','K','D',0}, 47610, 807 }, /* Macedonia, Former Yugoslav Republic of */
4539 { 20900, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Melanesia */
4540 { 21206, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Micronesia */
4541 { 21242, {'X','X',0}, {'X','X',0}, 161832256 }, /* Midway Islands */
4542 { 23581, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Northern America */
4543 { 26286, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Polynesia */
4544 { 27082, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Central America */
4545 { 27114, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Oceania */
4546 { 30967, {'S','X',0}, {'S','X','M',0}, 10039880, 534 }, /* Sint Maarten (Dutch part) */
4547 { 31396, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* South America */
4548 { 31706, {'M','F',0}, {'M','A','F',0}, 10039880, 663 }, /* Saint Martin (French part) */
4549 { 39070, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* World */
4550 { 42483, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Western Africa */
4551 { 42484, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Middle Africa */
4552 { 42487, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Northern Africa */
4553 { 47590, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Central Asia */
4554 { 47599, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* South-Eastern Asia */
4555 { 47600, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Eastern Asia */
4556 { 47603, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Eastern Africa */
4557 { 47609, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Eastern Europe */
4558 { 47610, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Southern Europe */
4559 { 47611, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Middle East */
4560 { 47614, {'X','X',0}, {'X','X',0}, 2129, 0, LOCATION_REGION }, /* Southern Asia */
4561 { 7299303, {'T','L',0}, {'T','L','S',0}, 47599, 626 }, /* Democratic Republic of Timor-Leste */
4562 { 10026358, {'X','X',0}, {'X','X',0}, 39070, 0, LOCATION_REGION }, /* Americas */
4563 { 10028789, {'A','X',0}, {'A','L','A',0}, 10039882, 248 }, /* Åland Islands */
4564 { 10039880, {'X','X',0}, {'X','X',0}, 161832257, 0, LOCATION_REGION }, /* Caribbean */
4565 { 10039882, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Northern Europe */
4566 { 10039883, {'X','X',0}, {'X','X',0}, 742, 0, LOCATION_REGION }, /* Southern Africa */
4567 { 10210824, {'X','X',0}, {'X','X',0}, 10541, 0, LOCATION_REGION }, /* Western Europe */
4568 { 10210825, {'X','X',0}, {'X','X',0}, 27114, 0, LOCATION_REGION }, /* Australia and New Zealand */
4569 { 161832015, {'B','L',0}, {'B','L','M',0}, 10039880, 652 }, /* Saint Barthélemy */
4570 { 161832256, {'U','M',0}, {'U','M','I',0}, 27114, 581 }, /* U.S. Minor Outlying Islands */
4571 { 161832257, {'X','X',0}, {'X','X',0}, 10026358, 0, LOCATION_REGION }, /* Latin America and the Caribbean */
4574 static const struct geoinfo_t *get_geoinfo_dataptr(GEOID geoid)
4576 int min, max;
4578 min = 0;
4579 max = sizeof(geoinfodata)/sizeof(struct geoinfo_t)-1;
4581 while (min <= max) {
4582 const struct geoinfo_t *ptr;
4583 int n = (min+max)/2;
4585 ptr = &geoinfodata[n];
4586 if (geoid == ptr->id)
4587 /* we don't need empty entries */
4588 return *ptr->iso2W ? ptr : NULL;
4590 if (ptr->id > geoid)
4591 max = n-1;
4592 else
4593 min = n+1;
4596 return NULL;
4599 /******************************************************************************
4600 * GetGeoInfoW (KERNEL32.@)
4602 INT WINAPI GetGeoInfoW(GEOID geoid, GEOTYPE geotype, LPWSTR data, int data_len, LANGID lang)
4604 const struct geoinfo_t *ptr;
4605 const WCHAR *str = NULL;
4606 WCHAR buffW[12];
4607 LONG val = 0;
4608 INT len;
4610 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4612 if (!(ptr = get_geoinfo_dataptr(geoid))) {
4613 SetLastError(ERROR_INVALID_PARAMETER);
4614 return 0;
4617 switch (geotype) {
4618 case GEO_NATION:
4619 val = geoid;
4620 break;
4621 case GEO_ISO_UN_NUMBER:
4622 val = ptr->uncode;
4623 break;
4624 case GEO_PARENT:
4625 val = ptr->parent;
4626 break;
4627 case GEO_ISO2:
4628 case GEO_ISO3:
4630 str = geotype == GEO_ISO2 ? ptr->iso2W : ptr->iso3W;
4631 break;
4633 case GEO_RFC1766:
4634 case GEO_LCID:
4635 case GEO_FRIENDLYNAME:
4636 case GEO_OFFICIALNAME:
4637 case GEO_TIMEZONES:
4638 case GEO_OFFICIALLANGUAGES:
4639 case GEO_LATITUDE:
4640 case GEO_LONGITUDE:
4641 FIXME("type %d is not supported\n", geotype);
4642 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4643 return 0;
4644 default:
4645 WARN("unrecognized type %d\n", geotype);
4646 SetLastError(ERROR_INVALID_FLAGS);
4647 return 0;
4650 if (val) {
4651 static const WCHAR fmtW[] = {'%','d',0};
4652 sprintfW(buffW, fmtW, val);
4653 str = buffW;
4656 len = strlenW(str) + 1;
4657 if (!data || !data_len)
4658 return len;
4660 memcpy(data, str, min(len, data_len)*sizeof(WCHAR));
4661 if (data_len < len)
4662 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4663 return data_len < len ? 0 : len;
4666 /******************************************************************************
4667 * GetGeoInfoA (KERNEL32.@)
4669 INT WINAPI GetGeoInfoA(GEOID geoid, GEOTYPE geotype, LPSTR data, int data_len, LANGID lang)
4671 WCHAR *buffW;
4672 INT len;
4674 TRACE("%d %d %p %d %d\n", geoid, geotype, data, data_len, lang);
4676 len = GetGeoInfoW(geoid, geotype, NULL, 0, lang);
4677 if (!len)
4678 return 0;
4680 buffW = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
4681 if (!buffW)
4682 return 0;
4684 GetGeoInfoW(geoid, geotype, buffW, len, lang);
4685 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, NULL, 0, NULL, NULL);
4686 if (!data || !data_len) {
4687 HeapFree(GetProcessHeap(), 0, buffW);
4688 return len;
4691 len = WideCharToMultiByte(CP_ACP, 0, buffW, -1, data, data_len, NULL, NULL);
4692 HeapFree(GetProcessHeap(), 0, buffW);
4694 if (data_len < len)
4695 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4696 return data_len < len ? 0 : len;
4699 /******************************************************************************
4700 * EnumSystemGeoID (KERNEL32.@)
4702 * Call a users function for every location available on the system.
4704 * PARAMS
4705 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
4706 * parent [I] GEOID for the parent
4707 * enumproc [I] Callback function to call for each location
4709 * RETURNS
4710 * Success: TRUE.
4711 * Failure: FALSE. Use GetLastError() to determine the cause.
4713 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID parent, GEO_ENUMPROC enumproc)
4715 INT i;
4717 TRACE("(%d, %d, %p)\n", geoclass, parent, enumproc);
4719 if (!enumproc) {
4720 SetLastError(ERROR_INVALID_PARAMETER);
4721 return FALSE;
4724 if (geoclass != GEOCLASS_NATION && geoclass != GEOCLASS_REGION) {
4725 SetLastError(ERROR_INVALID_FLAGS);
4726 return FALSE;
4729 for (i = 0; i < sizeof(geoinfodata)/sizeof(struct geoinfo_t); i++) {
4730 const struct geoinfo_t *ptr = &geoinfodata[i];
4732 if (geoclass == GEOCLASS_NATION && (ptr->kind == LOCATION_REGION))
4733 continue;
4735 if (geoclass == GEOCLASS_REGION && (ptr->kind == LOCATION_NATION))
4736 continue;
4738 if (parent && ptr->parent != parent)
4739 continue;
4741 if (!enumproc(ptr->id))
4742 return TRUE;
4745 return TRUE;
4748 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4750 LCID userlcid;
4752 TRACE("%p, %d\n", localename, buffersize);
4754 userlcid = GetUserDefaultLCID();
4755 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4758 /******************************************************************************
4759 * NormalizeString (KERNEL32.@)
4761 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4762 LPWSTR lpDstString, INT cwDstLength)
4764 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4765 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4766 return 0;
4769 /******************************************************************************
4770 * IsNormalizedString (KERNEL32.@)
4772 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4774 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4775 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4776 return FALSE;
4779 enum {
4780 BASE = 36,
4781 TMIN = 1,
4782 TMAX = 26,
4783 SKEW = 38,
4784 DAMP = 700,
4785 INIT_BIAS = 72,
4786 INIT_N = 128
4789 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4791 INT k;
4793 delta /= (firsttime ? DAMP : 2);
4794 delta += delta/numpoints;
4796 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4797 delta /= BASE-TMIN;
4798 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4801 /******************************************************************************
4802 * IdnToAscii (KERNEL32.@)
4803 * Implementation of Punycode based on RFC 3492.
4805 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4806 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4808 static const WCHAR prefixW[] = {'x','n','-','-'};
4810 WCHAR *norm_str;
4811 INT i, label_start, label_end, norm_len, out_label, out = 0;
4813 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4814 lpASCIICharStr, cchASCIIChar);
4816 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4817 if(!norm_len)
4818 return 0;
4819 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4820 if(!norm_str) {
4821 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4822 return 0;
4824 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4825 cchUnicodeChar, norm_str, norm_len);
4826 if(!norm_len) {
4827 HeapFree(GetProcessHeap(), 0, norm_str);
4828 return 0;
4831 for(label_start=0; label_start<norm_len;) {
4832 INT n = INIT_N, bias = INIT_BIAS;
4833 INT delta = 0, b = 0, h;
4835 out_label = out;
4836 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4837 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4838 if(norm_str[i] < 0x80)
4839 b++;
4840 label_end = i;
4842 if(b == label_end-label_start) {
4843 if(label_end < norm_len)
4844 b++;
4845 if(!lpASCIICharStr) {
4846 out += b;
4847 }else if(out+b <= cchASCIIChar) {
4848 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4849 out += b;
4850 }else {
4851 HeapFree(GetProcessHeap(), 0, norm_str);
4852 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4853 return 0;
4855 label_start = label_end+1;
4856 continue;
4859 if(!lpASCIICharStr) {
4860 out += 5+b; /* strlen(xn--...-) */
4861 }else if(out+5+b <= cchASCIIChar) {
4862 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4863 out += 4;
4864 for(i=label_start; i<label_end; i++)
4865 if(norm_str[i] < 0x80)
4866 lpASCIICharStr[out++] = norm_str[i];
4867 lpASCIICharStr[out++] = '-';
4868 }else {
4869 HeapFree(GetProcessHeap(), 0, norm_str);
4870 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4871 return 0;
4873 if(!b)
4874 out--;
4876 for(h=b; h<label_end-label_start;) {
4877 INT m = 0xffff, q, k;
4879 for(i=label_start; i<label_end; i++) {
4880 if(norm_str[i]>=n && m>norm_str[i])
4881 m = norm_str[i];
4883 delta += (m-n)*(h+1);
4884 n = m;
4886 for(i=label_start; i<label_end; i++) {
4887 if(norm_str[i] < n) {
4888 delta++;
4889 }else if(norm_str[i] == n) {
4890 for(q=delta, k=BASE; ; k+=BASE) {
4891 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4892 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4893 if(!lpASCIICharStr) {
4894 out++;
4895 }else if(out+1 <= cchASCIIChar) {
4896 lpASCIICharStr[out++] = disp<='z'-'a' ?
4897 'a'+disp : '0'+disp-'z'+'a'-1;
4898 }else {
4899 HeapFree(GetProcessHeap(), 0, norm_str);
4900 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4901 return 0;
4903 if(q < t)
4904 break;
4905 q = (q-t)/(BASE-t);
4907 bias = adapt(delta, h+1, h==b);
4908 delta = 0;
4909 h++;
4912 delta++;
4913 n++;
4916 if(out-out_label > 63) {
4917 HeapFree(GetProcessHeap(), 0, norm_str);
4918 SetLastError(ERROR_INVALID_NAME);
4919 return 0;
4922 if(label_end < norm_len) {
4923 if(!lpASCIICharStr) {
4924 out++;
4925 }else if(out+1 <= cchASCIIChar) {
4926 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4927 }else {
4928 HeapFree(GetProcessHeap(), 0, norm_str);
4929 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4930 return 0;
4933 label_start = label_end+1;
4936 HeapFree(GetProcessHeap(), 0, norm_str);
4937 return out;
4940 /******************************************************************************
4941 * IdnToNameprepUnicode (KERNEL32.@)
4943 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4944 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4946 enum {
4947 UNASSIGNED = 0x1,
4948 PROHIBITED = 0x2,
4949 BIDI_RAL = 0x4,
4950 BIDI_L = 0x8
4953 extern const unsigned short nameprep_char_type[];
4954 extern const WCHAR nameprep_mapping[];
4955 const WCHAR *ptr;
4956 WORD flags;
4957 WCHAR buf[64], *map_str, norm_str[64], ch;
4958 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4959 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4961 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4962 lpNameprepCharStr, cchNameprepChar);
4964 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4965 SetLastError(ERROR_INVALID_FLAGS);
4966 return 0;
4969 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4970 SetLastError(ERROR_INVALID_PARAMETER);
4971 return 0;
4974 if(cchUnicodeChar == -1)
4975 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4976 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4977 SetLastError(ERROR_INVALID_NAME);
4978 return 0;
4981 for(label_start=0; label_start<cchUnicodeChar;) {
4982 ascii_only = TRUE;
4983 for(i=label_start; i<cchUnicodeChar; i++) {
4984 ch = lpUnicodeCharStr[i];
4986 if(i!=cchUnicodeChar-1 && !ch) {
4987 SetLastError(ERROR_INVALID_NAME);
4988 return 0;
4990 /* check if ch is one of label separators defined in RFC3490 */
4991 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4992 break;
4994 if(ch > 0x7f) {
4995 ascii_only = FALSE;
4996 continue;
4999 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5000 continue;
5001 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5002 || (ch>='0' && ch<='9') || ch=='-')
5003 continue;
5005 SetLastError(ERROR_INVALID_NAME);
5006 return 0;
5008 label_end = i;
5009 /* last label may be empty */
5010 if(label_start==label_end && ch) {
5011 SetLastError(ERROR_INVALID_NAME);
5012 return 0;
5015 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
5016 lpUnicodeCharStr[label_end-1]=='-')) {
5017 SetLastError(ERROR_INVALID_NAME);
5018 return 0;
5021 if(ascii_only) {
5022 /* maximal label length is 63 characters */
5023 if(label_end-label_start > 63) {
5024 SetLastError(ERROR_INVALID_NAME);
5025 return 0;
5027 if(label_end < cchUnicodeChar)
5028 label_end++;
5030 if(!lpNameprepCharStr) {
5031 out += label_end-label_start;
5032 }else if(out+label_end-label_start <= cchNameprepChar) {
5033 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
5034 (label_end-label_start)*sizeof(WCHAR));
5035 if(lpUnicodeCharStr[label_end-1] > 0x7f)
5036 lpNameprepCharStr[out+label_end-label_start-1] = '.';
5037 out += label_end-label_start;
5038 }else {
5039 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5040 return 0;
5043 label_start = label_end;
5044 continue;
5047 map_len = 0;
5048 for(i=label_start; i<label_end; i++) {
5049 ch = lpUnicodeCharStr[i];
5050 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5051 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5053 if(!ptr[0]) map_len++;
5054 else if(!ptr[1]) map_len++;
5055 else if(!ptr[2]) map_len += 2;
5056 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
5058 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
5059 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
5060 if(!map_str) {
5061 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5062 return 0;
5064 }else {
5065 map_str = buf;
5067 map_len = 0;
5068 for(i=label_start; i<label_end; i++) {
5069 ch = lpUnicodeCharStr[i];
5070 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
5071 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
5073 if(!ptr[0]) {
5074 map_str[map_len++] = ch;
5075 }else if(!ptr[1]) {
5076 map_str[map_len++] = ptr[0];
5077 }else if(!ptr[2]) {
5078 map_str[map_len++] = ptr[0];
5079 map_str[map_len++] = ptr[1];
5080 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
5081 map_str[map_len++] = ptr[0];
5082 map_str[map_len++] = ptr[1];
5083 map_str[map_len++] = ptr[2];
5087 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
5088 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
5089 if(map_str != buf)
5090 HeapFree(GetProcessHeap(), 0, map_str);
5091 if(!norm_len) {
5092 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5093 SetLastError(ERROR_INVALID_NAME);
5094 return 0;
5097 if(label_end < cchUnicodeChar) {
5098 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
5099 label_end++;
5102 if(!lpNameprepCharStr) {
5103 out += norm_len;
5104 }else if(out+norm_len <= cchNameprepChar) {
5105 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
5106 out += norm_len;
5107 }else {
5108 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5109 return 0;
5112 have_bidi_ral = prohibit_bidi_ral = FALSE;
5113 mask = PROHIBITED;
5114 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
5115 mask |= UNASSIGNED;
5116 for(i=0; i<norm_len; i++) {
5117 ch = norm_str[i];
5118 flags = get_table_entry( nameprep_char_type, ch );
5120 if(flags & mask) {
5121 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
5122 : ERROR_NO_UNICODE_TRANSLATION);
5123 return 0;
5126 if(flags & BIDI_RAL)
5127 have_bidi_ral = TRUE;
5128 if(flags & BIDI_L)
5129 prohibit_bidi_ral = TRUE;
5132 if(have_bidi_ral) {
5133 ch = norm_str[0];
5134 flags = get_table_entry( nameprep_char_type, ch );
5135 if((flags & BIDI_RAL) == 0)
5136 prohibit_bidi_ral = TRUE;
5138 ch = norm_str[norm_len-1];
5139 flags = get_table_entry( nameprep_char_type, ch );
5140 if((flags & BIDI_RAL) == 0)
5141 prohibit_bidi_ral = TRUE;
5144 if(have_bidi_ral && prohibit_bidi_ral) {
5145 SetLastError(ERROR_INVALID_NAME);
5146 return 0;
5149 label_start = label_end;
5152 return out;
5155 /******************************************************************************
5156 * IdnToUnicode (KERNEL32.@)
5158 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
5159 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
5161 extern const unsigned short nameprep_char_type[];
5163 INT i, label_start, label_end, out_label, out = 0;
5164 WCHAR ch;
5166 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
5167 lpUnicodeCharStr, cchUnicodeChar);
5169 for(label_start=0; label_start<cchASCIIChar;) {
5170 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
5172 out_label = out;
5173 for(i=label_start; i<cchASCIIChar; i++) {
5174 ch = lpASCIICharStr[i];
5176 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
5177 SetLastError(ERROR_INVALID_NAME);
5178 return 0;
5181 if(!ch || ch=='.')
5182 break;
5183 if(ch == '-')
5184 delim = i;
5186 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
5187 continue;
5188 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
5189 || (ch>='0' && ch<='9') || ch=='-')
5190 continue;
5192 SetLastError(ERROR_INVALID_NAME);
5193 return 0;
5195 label_end = i;
5196 /* last label may be empty */
5197 if(label_start==label_end && ch) {
5198 SetLastError(ERROR_INVALID_NAME);
5199 return 0;
5202 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
5203 lpASCIICharStr[label_end-1]=='-')) {
5204 SetLastError(ERROR_INVALID_NAME);
5205 return 0;
5207 if(label_end-label_start > 63) {
5208 SetLastError(ERROR_INVALID_NAME);
5209 return 0;
5212 if(label_end-label_start<4 ||
5213 tolowerW(lpASCIICharStr[label_start])!='x' ||
5214 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
5215 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
5216 if(label_end < cchASCIIChar)
5217 label_end++;
5219 if(!lpUnicodeCharStr) {
5220 out += label_end-label_start;
5221 }else if(out+label_end-label_start <= cchUnicodeChar) {
5222 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
5223 (label_end-label_start)*sizeof(WCHAR));
5224 out += label_end-label_start;
5225 }else {
5226 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5227 return 0;
5230 label_start = label_end;
5231 continue;
5234 if(delim == label_start+3)
5235 delim++;
5236 if(!lpUnicodeCharStr) {
5237 out += delim-label_start-4;
5238 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
5239 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
5240 (delim-label_start-4)*sizeof(WCHAR));
5241 out += delim-label_start-4;
5242 }else {
5243 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5244 return 0;
5246 if(out != out_label)
5247 delim++;
5249 for(i=delim; i<label_end;) {
5250 old_pos = pos;
5251 w = 1;
5252 for(k=BASE; ; k+=BASE) {
5253 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
5254 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
5255 SetLastError(ERROR_INVALID_NAME);
5256 return 0;
5258 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
5259 pos += digit*w;
5260 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
5261 if(digit < t)
5262 break;
5263 w *= BASE-t;
5265 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
5266 n += pos/(out-out_label+1);
5267 pos %= out-out_label+1;
5269 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
5270 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
5271 SetLastError(ERROR_INVALID_NAME);
5272 return 0;
5274 if(!lpUnicodeCharStr) {
5275 out++;
5276 }else if(out+1 <= cchASCIIChar) {
5277 memmove(lpUnicodeCharStr+out_label+pos+1,
5278 lpUnicodeCharStr+out_label+pos,
5279 (out-out_label-pos)*sizeof(WCHAR));
5280 lpUnicodeCharStr[out_label+pos] = n;
5281 out++;
5282 }else {
5283 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5284 return 0;
5286 pos++;
5289 if(out-out_label > 63) {
5290 SetLastError(ERROR_INVALID_NAME);
5291 return 0;
5294 if(label_end < cchASCIIChar) {
5295 if(!lpUnicodeCharStr) {
5296 out++;
5297 }else if(out+1 <= cchUnicodeChar) {
5298 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
5299 }else {
5300 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5301 return 0;
5304 label_start = label_end+1;
5307 return out;
5311 /******************************************************************************
5312 * GetUserPreferredUILanguages (KERNEL32.@)
5314 BOOL WINAPI GetUserPreferredUILanguages(DWORD flags, PULONG numlangs, PZZWSTR langbuffer, PULONG bufferlen)
5316 FIXME( "stub: %u %p %p %p\n", flags, numlangs, langbuffer, bufferlen );
5317 return FALSE;
5320 /******************************************************************************
5321 * GetFileMUIPath (KERNEL32.@)
5324 BOOL WINAPI GetFileMUIPath(DWORD flags, PCWSTR filepath, PWSTR language, PULONG languagelen,
5325 PWSTR muipath, PULONG muipathlen, PULONGLONG enumerator)
5327 FIXME("stub: 0x%x, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath),
5328 debugstr_w(language), languagelen, muipath, muipathlen, enumerator);
5330 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5332 return FALSE;
5335 /******************************************************************************
5336 * GetFileMUIInfo (KERNEL32.@)
5339 BOOL WINAPI GetFileMUIInfo(DWORD flags, PCWSTR path, FILEMUIINFO *info, DWORD *size)
5341 FIXME("stub: %u, %s, %p, %p\n", flags, debugstr_w(path), info, size);
5343 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5344 return FALSE;