kernel32: Cache locale info from the registry.
[wine/multimedia.git] / dlls / kernel32 / locale.c
blobc1d89b74604a299617d4e4cabf50133db3fe3e07
1 /*
2 * Locale support
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2002 Alexandre Julliard for CodeWeavers
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFLocale.h>
37 # include <CoreFoundation/CFString.h>
38 #endif
40 #include "ntstatus.h"
41 #define WIN32_NO_STATUS
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h" /* for RT_STRINGW */
45 #include "winternl.h"
46 #include "wine/unicode.h"
47 #include "winnls.h"
48 #include "winerror.h"
49 #include "winver.h"
50 #include "kernel_private.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(nls);
55 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
56 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
58 /* current code pages */
59 static const union cptable *ansi_cptable;
60 static const union cptable *oem_cptable;
61 static const union cptable *mac_cptable;
62 static const union cptable *unix_cptable; /* NULL if UTF8 */
64 static const WCHAR szLocaleKeyName[] = {
65 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
66 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
67 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
70 static const WCHAR szLangGroupsKeyName[] = {
71 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
72 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
73 'C','o','n','t','r','o','l','\\','N','l','s','\\',
74 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
77 /* Charset to codepage map, sorted by name. */
78 static const struct charset_entry
80 const char *charset_name;
81 UINT codepage;
82 } charset_names[] =
84 { "BIG5", 950 },
85 { "CP1250", 1250 },
86 { "CP1251", 1251 },
87 { "CP1252", 1252 },
88 { "CP1253", 1253 },
89 { "CP1254", 1254 },
90 { "CP1255", 1255 },
91 { "CP1256", 1256 },
92 { "CP1257", 1257 },
93 { "CP1258", 1258 },
94 { "CP932", 932 },
95 { "CP936", 936 },
96 { "CP949", 949 },
97 { "CP950", 950 },
98 { "EUCJP", 20932 },
99 { "GB2312", 936 },
100 { "IBM037", 37 },
101 { "IBM1026", 1026 },
102 { "IBM424", 424 },
103 { "IBM437", 437 },
104 { "IBM500", 500 },
105 { "IBM850", 850 },
106 { "IBM852", 852 },
107 { "IBM855", 855 },
108 { "IBM857", 857 },
109 { "IBM860", 860 },
110 { "IBM861", 861 },
111 { "IBM862", 862 },
112 { "IBM863", 863 },
113 { "IBM864", 864 },
114 { "IBM865", 865 },
115 { "IBM866", 866 },
116 { "IBM869", 869 },
117 { "IBM874", 874 },
118 { "IBM875", 875 },
119 { "ISO88591", 28591 },
120 { "ISO885910", 28600 },
121 { "ISO885913", 28603 },
122 { "ISO885914", 28604 },
123 { "ISO885915", 28605 },
124 { "ISO885916", 28606 },
125 { "ISO88592", 28592 },
126 { "ISO88593", 28593 },
127 { "ISO88594", 28594 },
128 { "ISO88595", 28595 },
129 { "ISO88596", 28596 },
130 { "ISO88597", 28597 },
131 { "ISO88598", 28598 },
132 { "ISO88599", 28599 },
133 { "KOI8R", 20866 },
134 { "KOI8U", 21866 },
135 { "UTF8", CP_UTF8 }
139 struct locale_name
141 WCHAR win_name[128]; /* Windows name ("en-US") */
142 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
143 WCHAR *country; /* country ("US") */
144 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
145 WCHAR *script; /* script ("Latn") for Windows format only */
146 WCHAR *modifier; /* modifier or sort order */
147 LCID lcid; /* corresponding LCID */
148 int matches; /* number of elements matching LCID (0..4) */
149 UINT codepage; /* codepage corresponding to charset */
152 /* locale ids corresponding to the various Unix locale parameters */
153 static LCID lcid_LC_COLLATE;
154 static LCID lcid_LC_CTYPE;
155 static LCID lcid_LC_MESSAGES;
156 static LCID lcid_LC_MONETARY;
157 static LCID lcid_LC_NUMERIC;
158 static LCID lcid_LC_TIME;
159 static LCID lcid_LC_PAPER;
160 static LCID lcid_LC_MEASUREMENT;
161 static LCID lcid_LC_TELEPHONE;
163 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
164 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
165 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
166 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
167 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
168 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
169 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
170 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
171 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
172 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
173 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
174 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
175 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
176 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
177 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
178 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
179 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
180 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
181 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
182 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
183 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
184 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
185 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
186 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
187 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
188 static const WCHAR sListW[] = {'s','L','i','s','t',0};
189 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
190 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
191 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
192 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
193 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
194 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
195 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
196 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
197 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
198 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
199 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
200 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
201 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
203 static struct registry_value
205 DWORD lctype;
206 const WCHAR *name;
207 WCHAR *cached_value;
208 } registry_values[] =
210 { LOCALE_ICALENDARTYPE, iCalendarTypeW },
211 { LOCALE_ICURRDIGITS, iCurrDigitsW },
212 { LOCALE_ICURRENCY, iCurrencyW },
213 { LOCALE_IDIGITS, iDigitsW },
214 { LOCALE_IFIRSTDAYOFWEEK, iFirstDayOfWeekW },
215 { LOCALE_IFIRSTWEEKOFYEAR, iFirstWeekOfYearW },
216 { LOCALE_ILZERO, iLZeroW },
217 { LOCALE_IMEASURE, iMeasureW },
218 { LOCALE_INEGCURR, iNegCurrW },
219 { LOCALE_INEGNUMBER, iNegNumberW },
220 { LOCALE_IPAPERSIZE, iPaperSizeW },
221 { LOCALE_ITIME, iTimeW },
222 { LOCALE_S1159, s1159W },
223 { LOCALE_S2359, s2359W },
224 { LOCALE_SCURRENCY, sCurrencyW },
225 { LOCALE_SDATE, sDateW },
226 { LOCALE_SDECIMAL, sDecimalW },
227 { LOCALE_SGROUPING, sGroupingW },
228 { LOCALE_SLIST, sListW },
229 { LOCALE_SLONGDATE, sLongDateW },
230 { LOCALE_SMONDECIMALSEP, sMonDecimalSepW },
231 { LOCALE_SMONGROUPING, sMonGroupingW },
232 { LOCALE_SMONTHOUSANDSEP, sMonThousandSepW },
233 { LOCALE_SNEGATIVESIGN, sNegativeSignW },
234 { LOCALE_SPOSITIVESIGN, sPositiveSignW },
235 { LOCALE_SSHORTDATE, sShortDateW },
236 { LOCALE_STHOUSAND, sThousandW },
237 { LOCALE_STIME, sTimeW },
238 { LOCALE_STIMEFORMAT, sTimeFormatW },
239 { LOCALE_SYEARMONTH, sYearMonthW },
240 /* The following are not listed under MSDN as supported,
241 * but seem to be used and also stored in the registry.
243 { LOCALE_ICOUNTRY, iCountryW },
244 { LOCALE_IDATE, iDateW },
245 { LOCALE_ILDATE, iLDateW },
246 { LOCALE_ITLZERO, iTLZeroW },
247 { LOCALE_SCOUNTRY, sCountryW },
248 { LOCALE_SABBREVLANGNAME, sLanguageW },
249 /* The following are used in XP and later */
250 { LOCALE_IDIGITSUBSTITUTION, NumShapeW },
251 { LOCALE_SNATIVEDIGITS, sNativeDigitsW },
252 { LOCALE_ITIMEMARKPOSN, iTimePrefixW }
255 static CRITICAL_SECTION cache_section;
256 static CRITICAL_SECTION_DEBUG critsect_debug =
258 0, 0, &cache_section,
259 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
260 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
262 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
264 /* Copy Ascii string to Unicode without using codepages */
265 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
267 while (n > 1 && *src)
269 *dst++ = (unsigned char)*src++;
270 n--;
272 if (n) *dst = 0;
275 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
277 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
280 /***********************************************************************
281 * get_lcid_codepage
283 * Retrieve the ANSI codepage for a given locale.
285 static inline UINT get_lcid_codepage( LCID lcid )
287 UINT ret;
288 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
289 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
290 return ret;
294 /***********************************************************************
295 * get_codepage_table
297 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
299 static const union cptable *get_codepage_table( unsigned int codepage )
301 const union cptable *ret = NULL;
303 assert( ansi_cptable ); /* init must have been done already */
305 switch(codepage)
307 case CP_ACP:
308 return ansi_cptable;
309 case CP_OEMCP:
310 return oem_cptable;
311 case CP_MACCP:
312 return mac_cptable;
313 case CP_UTF7:
314 case CP_UTF8:
315 break;
316 case CP_THREAD_ACP:
317 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
318 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
319 if (!codepage) return ansi_cptable;
320 /* fall through */
321 default:
322 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
323 if (codepage == oem_cptable->info.codepage) return oem_cptable;
324 if (codepage == mac_cptable->info.codepage) return mac_cptable;
325 ret = wine_cp_get_table( codepage );
326 break;
328 return ret;
332 /***********************************************************************
333 * charset_cmp (internal)
335 static int charset_cmp( const void *name, const void *entry )
337 const struct charset_entry *charset = entry;
338 return strcasecmp( name, charset->charset_name );
341 /***********************************************************************
342 * find_charset
344 static UINT find_charset( const WCHAR *name )
346 const struct charset_entry *entry;
347 char charset_name[16];
348 size_t i, j;
350 /* remove punctuation characters from charset name */
351 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
352 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
353 charset_name[j] = 0;
355 entry = bsearch( charset_name, charset_names,
356 sizeof(charset_names)/sizeof(charset_names[0]),
357 sizeof(charset_names[0]), charset_cmp );
358 if (entry) return entry->codepage;
359 return 0;
363 /***********************************************************************
364 * find_locale_id_callback
366 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
367 LPCWSTR name, WORD LangID, LPARAM lParam )
369 struct locale_name *data = (struct locale_name *)lParam;
370 WCHAR buffer[128];
371 int matches = 0;
372 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
374 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
376 /* first check exact name */
377 if (data->win_name[0] &&
378 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
379 buffer, sizeof(buffer)/sizeof(WCHAR) ))
381 if (!strcmpW( data->win_name, buffer ))
383 matches = 4; /* everything matches */
384 goto done;
388 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
389 buffer, sizeof(buffer)/sizeof(WCHAR) ))
390 return TRUE;
391 if (strcmpW( buffer, data->lang )) return TRUE;
392 matches++; /* language name matched */
394 if (data->country)
396 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
397 buffer, sizeof(buffer)/sizeof(WCHAR) ))
399 if (strcmpW( buffer, data->country )) goto done;
400 matches++; /* country name matched */
403 else /* match default language */
405 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
408 if (data->codepage)
410 UINT unix_cp;
411 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
412 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
414 if (unix_cp == data->codepage) matches++;
418 /* FIXME: check sort order */
420 done:
421 if (matches > data->matches)
423 data->lcid = lcid;
424 data->matches = matches;
426 return (data->matches < 4); /* no need to continue for perfect match */
430 /***********************************************************************
431 * parse_locale_name
433 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
434 * Unix format is: lang[_country][.charset][@modifier]
435 * Windows format is: lang[-script][-country][_modifier]
437 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
439 static const WCHAR sepW[] = {'-','_','.','@',0};
440 static const WCHAR winsepW[] = {'-','_',0};
441 static const WCHAR posixW[] = {'P','O','S','I','X',0};
442 static const WCHAR cW[] = {'C',0};
443 static const WCHAR latinW[] = {'l','a','t','i','n',0};
444 static const WCHAR latnW[] = {'-','L','a','t','n',0};
445 WCHAR *p;
447 TRACE("%s\n", debugstr_w(str));
449 name->country = name->charset = name->script = name->modifier = NULL;
450 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
451 name->matches = 0;
452 name->codepage = 0;
453 name->win_name[0] = 0;
454 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
456 if (!(p = strpbrkW( name->lang, sepW )))
458 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
460 name->matches = 4; /* perfect match for default English lcid */
461 return;
463 strcpyW( name->win_name, name->lang );
465 else if (*p == '-') /* Windows format */
467 strcpyW( name->win_name, name->lang );
468 *p++ = 0;
469 name->country = p;
470 if (!(p = strpbrkW( p, winsepW ))) goto done;
471 if (*p == '-')
473 *p++ = 0;
474 name->script = name->country;
475 name->country = p;
476 if (!(p = strpbrkW( p, winsepW ))) goto done;
478 *p++ = 0;
479 name->modifier = p;
481 else /* Unix format */
483 if (*p == '_')
485 *p++ = 0;
486 name->country = p;
487 p = strpbrkW( p, sepW + 2 );
489 if (p && *p == '.')
491 *p++ = 0;
492 name->charset = p;
493 p = strchrW( p, '@' );
495 if (p)
497 *p++ = 0;
498 name->modifier = p;
501 if (name->charset)
502 name->codepage = find_charset( name->charset );
504 /* rebuild a Windows name if possible */
506 if (name->charset) goto done; /* can't specify charset in Windows format */
507 if (name->modifier && strcmpW( name->modifier, latinW ))
508 goto done; /* only Latn script supported for now */
509 strcpyW( name->win_name, name->lang );
510 if (name->modifier) strcatW( name->win_name, latnW );
511 if (name->country)
513 p = name->win_name + strlenW(name->win_name);
514 *p++ = '-';
515 strcpyW( p, name->country );
518 done:
519 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
520 find_locale_id_callback, (LPARAM)name );
524 /***********************************************************************
525 * convert_default_lcid
527 * Get the default LCID to use for a given lctype in GetLocaleInfo.
529 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
531 if (lcid == LOCALE_SYSTEM_DEFAULT ||
532 lcid == LOCALE_USER_DEFAULT ||
533 lcid == LOCALE_NEUTRAL)
535 LCID default_id = 0;
537 switch(lctype & 0xffff)
539 case LOCALE_SSORTNAME:
540 default_id = lcid_LC_COLLATE;
541 break;
543 case LOCALE_FONTSIGNATURE:
544 case LOCALE_IDEFAULTANSICODEPAGE:
545 case LOCALE_IDEFAULTCODEPAGE:
546 case LOCALE_IDEFAULTEBCDICCODEPAGE:
547 case LOCALE_IDEFAULTMACCODEPAGE:
548 case LOCALE_IDEFAULTUNIXCODEPAGE:
549 default_id = lcid_LC_CTYPE;
550 break;
552 case LOCALE_ICURRDIGITS:
553 case LOCALE_ICURRENCY:
554 case LOCALE_IINTLCURRDIGITS:
555 case LOCALE_INEGCURR:
556 case LOCALE_INEGSEPBYSPACE:
557 case LOCALE_INEGSIGNPOSN:
558 case LOCALE_INEGSYMPRECEDES:
559 case LOCALE_IPOSSEPBYSPACE:
560 case LOCALE_IPOSSIGNPOSN:
561 case LOCALE_IPOSSYMPRECEDES:
562 case LOCALE_SCURRENCY:
563 case LOCALE_SINTLSYMBOL:
564 case LOCALE_SMONDECIMALSEP:
565 case LOCALE_SMONGROUPING:
566 case LOCALE_SMONTHOUSANDSEP:
567 case LOCALE_SNATIVECURRNAME:
568 default_id = lcid_LC_MONETARY;
569 break;
571 case LOCALE_IDIGITS:
572 case LOCALE_IDIGITSUBSTITUTION:
573 case LOCALE_ILZERO:
574 case LOCALE_INEGNUMBER:
575 case LOCALE_SDECIMAL:
576 case LOCALE_SGROUPING:
577 case LOCALE_SNAN:
578 case LOCALE_SNATIVEDIGITS:
579 case LOCALE_SNEGATIVESIGN:
580 case LOCALE_SNEGINFINITY:
581 case LOCALE_SPOSINFINITY:
582 case LOCALE_SPOSITIVESIGN:
583 case LOCALE_STHOUSAND:
584 default_id = lcid_LC_NUMERIC;
585 break;
587 case LOCALE_ICALENDARTYPE:
588 case LOCALE_ICENTURY:
589 case LOCALE_IDATE:
590 case LOCALE_IDAYLZERO:
591 case LOCALE_IFIRSTDAYOFWEEK:
592 case LOCALE_IFIRSTWEEKOFYEAR:
593 case LOCALE_ILDATE:
594 case LOCALE_IMONLZERO:
595 case LOCALE_IOPTIONALCALENDAR:
596 case LOCALE_ITIME:
597 case LOCALE_ITIMEMARKPOSN:
598 case LOCALE_ITLZERO:
599 case LOCALE_S1159:
600 case LOCALE_S2359:
601 case LOCALE_SABBREVDAYNAME1:
602 case LOCALE_SABBREVDAYNAME2:
603 case LOCALE_SABBREVDAYNAME3:
604 case LOCALE_SABBREVDAYNAME4:
605 case LOCALE_SABBREVDAYNAME5:
606 case LOCALE_SABBREVDAYNAME6:
607 case LOCALE_SABBREVDAYNAME7:
608 case LOCALE_SABBREVMONTHNAME1:
609 case LOCALE_SABBREVMONTHNAME2:
610 case LOCALE_SABBREVMONTHNAME3:
611 case LOCALE_SABBREVMONTHNAME4:
612 case LOCALE_SABBREVMONTHNAME5:
613 case LOCALE_SABBREVMONTHNAME6:
614 case LOCALE_SABBREVMONTHNAME7:
615 case LOCALE_SABBREVMONTHNAME8:
616 case LOCALE_SABBREVMONTHNAME9:
617 case LOCALE_SABBREVMONTHNAME10:
618 case LOCALE_SABBREVMONTHNAME11:
619 case LOCALE_SABBREVMONTHNAME12:
620 case LOCALE_SABBREVMONTHNAME13:
621 case LOCALE_SDATE:
622 case LOCALE_SDAYNAME1:
623 case LOCALE_SDAYNAME2:
624 case LOCALE_SDAYNAME3:
625 case LOCALE_SDAYNAME4:
626 case LOCALE_SDAYNAME5:
627 case LOCALE_SDAYNAME6:
628 case LOCALE_SDAYNAME7:
629 case LOCALE_SDURATION:
630 case LOCALE_SLONGDATE:
631 case LOCALE_SMONTHNAME1:
632 case LOCALE_SMONTHNAME2:
633 case LOCALE_SMONTHNAME3:
634 case LOCALE_SMONTHNAME4:
635 case LOCALE_SMONTHNAME5:
636 case LOCALE_SMONTHNAME6:
637 case LOCALE_SMONTHNAME7:
638 case LOCALE_SMONTHNAME8:
639 case LOCALE_SMONTHNAME9:
640 case LOCALE_SMONTHNAME10:
641 case LOCALE_SMONTHNAME11:
642 case LOCALE_SMONTHNAME12:
643 case LOCALE_SMONTHNAME13:
644 case LOCALE_SSHORTDATE:
645 case LOCALE_SSHORTESTDAYNAME1:
646 case LOCALE_SSHORTESTDAYNAME2:
647 case LOCALE_SSHORTESTDAYNAME3:
648 case LOCALE_SSHORTESTDAYNAME4:
649 case LOCALE_SSHORTESTDAYNAME5:
650 case LOCALE_SSHORTESTDAYNAME6:
651 case LOCALE_SSHORTESTDAYNAME7:
652 case LOCALE_STIME:
653 case LOCALE_STIMEFORMAT:
654 case LOCALE_SYEARMONTH:
655 default_id = lcid_LC_TIME;
656 break;
658 case LOCALE_IPAPERSIZE:
659 default_id = lcid_LC_PAPER;
660 break;
662 case LOCALE_IMEASURE:
663 default_id = lcid_LC_MEASUREMENT;
664 break;
666 case LOCALE_ICOUNTRY:
667 default_id = lcid_LC_TELEPHONE;
668 break;
670 if (default_id) lcid = default_id;
672 return ConvertDefaultLocale( lcid );
675 /***********************************************************************
676 * is_genitive_name_supported
678 * Determine could LCTYPE basically support genitive name form or not.
680 static BOOL is_genitive_name_supported( LCTYPE lctype )
682 switch(lctype & 0xffff)
684 case LOCALE_SMONTHNAME1:
685 case LOCALE_SMONTHNAME2:
686 case LOCALE_SMONTHNAME3:
687 case LOCALE_SMONTHNAME4:
688 case LOCALE_SMONTHNAME5:
689 case LOCALE_SMONTHNAME6:
690 case LOCALE_SMONTHNAME7:
691 case LOCALE_SMONTHNAME8:
692 case LOCALE_SMONTHNAME9:
693 case LOCALE_SMONTHNAME10:
694 case LOCALE_SMONTHNAME11:
695 case LOCALE_SMONTHNAME12:
696 case LOCALE_SMONTHNAME13:
697 return TRUE;
698 default:
699 return FALSE;
703 /***********************************************************************
704 * create_registry_key
706 * Create the Control Panel\\International registry key.
708 static inline HANDLE create_registry_key(void)
710 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
711 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
712 OBJECT_ATTRIBUTES attr;
713 UNICODE_STRING nameW;
714 HANDLE cpl_key, hkey = 0;
716 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
718 attr.Length = sizeof(attr);
719 attr.RootDirectory = hkey;
720 attr.ObjectName = &nameW;
721 attr.Attributes = 0;
722 attr.SecurityDescriptor = NULL;
723 attr.SecurityQualityOfService = NULL;
724 RtlInitUnicodeString( &nameW, cplW );
726 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
728 NtClose( attr.RootDirectory );
729 attr.RootDirectory = cpl_key;
730 RtlInitUnicodeString( &nameW, intlW );
731 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
733 NtClose( attr.RootDirectory );
734 return hkey;
738 /* update the registry settings for a given locale parameter */
739 /* return TRUE if an update was needed */
740 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
741 const LCTYPE *values, UINT nb_values )
743 static const WCHAR formatW[] = { '%','0','8','x',0 };
744 WCHAR bufferW[40];
745 UNICODE_STRING nameW;
746 DWORD count, i;
748 RtlInitUnicodeString( &nameW, name );
749 count = sizeof(bufferW);
750 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
752 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
753 LPCWSTR text = (LPCWSTR)info->Data;
755 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
756 TRACE( "updating registry, locale %s changed %s -> %08x\n",
757 debugstr_w(name), debugstr_w(text), lcid );
759 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
760 sprintfW( bufferW, formatW, lcid );
761 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
763 for (i = 0; i < nb_values; i++)
765 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
766 sizeof(bufferW)/sizeof(WCHAR) );
767 SetLocaleInfoW( lcid, values[i], bufferW );
769 return TRUE;
773 /***********************************************************************
774 * LOCALE_InitRegistry
776 * Update registry contents on startup if the user locale has changed.
777 * This simulates the action of the Windows control panel.
779 void LOCALE_InitRegistry(void)
781 static const WCHAR acpW[] = {'A','C','P',0};
782 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
783 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
784 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
785 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
786 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
787 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
788 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
789 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
790 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
791 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
792 static const struct
794 LPCWSTR name;
795 USHORT value;
796 } update_cp_values[] = {
797 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
798 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
799 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
801 static const LCTYPE lc_messages_values[] = {
802 LOCALE_SABBREVLANGNAME,
803 LOCALE_SCOUNTRY,
804 LOCALE_SLIST };
805 static const LCTYPE lc_monetary_values[] = {
806 LOCALE_SCURRENCY,
807 LOCALE_ICURRENCY,
808 LOCALE_INEGCURR,
809 LOCALE_ICURRDIGITS,
810 LOCALE_ILZERO,
811 LOCALE_SMONDECIMALSEP,
812 LOCALE_SMONGROUPING,
813 LOCALE_SMONTHOUSANDSEP };
814 static const LCTYPE lc_numeric_values[] = {
815 LOCALE_SDECIMAL,
816 LOCALE_STHOUSAND,
817 LOCALE_IDIGITS,
818 LOCALE_IDIGITSUBSTITUTION,
819 LOCALE_SNATIVEDIGITS,
820 LOCALE_INEGNUMBER,
821 LOCALE_SNEGATIVESIGN,
822 LOCALE_SPOSITIVESIGN,
823 LOCALE_SGROUPING };
824 static const LCTYPE lc_time_values[] = {
825 LOCALE_S1159,
826 LOCALE_S2359,
827 LOCALE_STIME,
828 LOCALE_ITIME,
829 LOCALE_ITLZERO,
830 LOCALE_SSHORTDATE,
831 LOCALE_SLONGDATE,
832 LOCALE_SDATE,
833 LOCALE_ITIMEMARKPOSN,
834 LOCALE_ICALENDARTYPE,
835 LOCALE_IFIRSTDAYOFWEEK,
836 LOCALE_IFIRSTWEEKOFYEAR,
837 LOCALE_STIMEFORMAT,
838 LOCALE_SYEARMONTH,
839 LOCALE_IDATE };
840 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
841 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
842 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
844 UNICODE_STRING nameW;
845 WCHAR bufferW[80];
846 DWORD count, i;
847 HANDLE hkey;
848 LCID lcid = GetUserDefaultLCID();
850 if (!(hkey = create_registry_key()))
851 return; /* don't do anything if we can't create the registry key */
853 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
854 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
855 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
856 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
857 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
858 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
859 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
860 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
861 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
862 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
863 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
864 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
865 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
866 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
868 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
870 static const WCHAR codepageW[] =
871 {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
872 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
873 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
875 OBJECT_ATTRIBUTES attr;
876 HANDLE nls_key;
877 DWORD len = 14;
879 RtlInitUnicodeString( &nameW, codepageW );
880 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
881 while (codepageW[len])
883 nameW.Length = len * sizeof(WCHAR);
884 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
885 NtClose( nls_key );
886 len++;
887 while (codepageW[len] && codepageW[len] != '\\') len++;
889 nameW.Length = len * sizeof(WCHAR);
890 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
892 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
894 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
895 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
896 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
897 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
899 NtClose( nls_key );
903 NtClose( hkey );
907 /***********************************************************************
908 * setup_unix_locales
910 static UINT setup_unix_locales(void)
912 struct locale_name locale_name;
913 WCHAR buffer[128], ctype_buff[128];
914 char *locale;
915 UINT unix_cp = 0;
917 if ((locale = setlocale( LC_CTYPE, NULL )))
919 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
920 parse_locale_name( ctype_buff, &locale_name );
921 lcid_LC_CTYPE = locale_name.lcid;
922 unix_cp = locale_name.codepage;
924 if (!lcid_LC_CTYPE) /* this one needs a default value */
925 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
927 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
928 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
930 #define GET_UNIX_LOCALE(cat) do \
931 if ((locale = setlocale( cat, NULL ))) \
933 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
934 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
935 else { \
936 parse_locale_name( buffer, &locale_name ); \
937 lcid_##cat = locale_name.lcid; \
938 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
939 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
941 } while (0)
943 GET_UNIX_LOCALE( LC_COLLATE );
944 GET_UNIX_LOCALE( LC_MESSAGES );
945 GET_UNIX_LOCALE( LC_MONETARY );
946 GET_UNIX_LOCALE( LC_NUMERIC );
947 GET_UNIX_LOCALE( LC_TIME );
948 #ifdef LC_PAPER
949 GET_UNIX_LOCALE( LC_PAPER );
950 #endif
951 #ifdef LC_MEASUREMENT
952 GET_UNIX_LOCALE( LC_MEASUREMENT );
953 #endif
954 #ifdef LC_TELEPHONE
955 GET_UNIX_LOCALE( LC_TELEPHONE );
956 #endif
958 #undef GET_UNIX_LOCALE
960 return unix_cp;
964 /***********************************************************************
965 * GetUserDefaultLangID (KERNEL32.@)
967 * Get the default language Id for the current user.
969 * PARAMS
970 * None.
972 * RETURNS
973 * The current LANGID of the default language for the current user.
975 LANGID WINAPI GetUserDefaultLangID(void)
977 return LANGIDFROMLCID(GetUserDefaultLCID());
981 /***********************************************************************
982 * GetSystemDefaultLangID (KERNEL32.@)
984 * Get the default language Id for the system.
986 * PARAMS
987 * None.
989 * RETURNS
990 * The current LANGID of the default language for the system.
992 LANGID WINAPI GetSystemDefaultLangID(void)
994 return LANGIDFROMLCID(GetSystemDefaultLCID());
998 /***********************************************************************
999 * GetUserDefaultLCID (KERNEL32.@)
1001 * Get the default locale Id for the current user.
1003 * PARAMS
1004 * None.
1006 * RETURNS
1007 * The current LCID of the default locale for the current user.
1009 LCID WINAPI GetUserDefaultLCID(void)
1011 LCID lcid;
1012 NtQueryDefaultLocale( TRUE, &lcid );
1013 return lcid;
1017 /***********************************************************************
1018 * GetSystemDefaultLCID (KERNEL32.@)
1020 * Get the default locale Id for the system.
1022 * PARAMS
1023 * None.
1025 * RETURNS
1026 * The current LCID of the default locale for the system.
1028 LCID WINAPI GetSystemDefaultLCID(void)
1030 LCID lcid;
1031 NtQueryDefaultLocale( FALSE, &lcid );
1032 return lcid;
1035 /***********************************************************************
1036 * GetSystemDefaultLocaleName (KERNEL32.@)
1038 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
1040 LCID lcid = GetSystemDefaultLCID();
1041 return LCIDToLocaleName(lcid, localename, len, 0);
1044 /***********************************************************************
1045 * GetUserDefaultUILanguage (KERNEL32.@)
1047 * Get the default user interface language Id for the current user.
1049 * PARAMS
1050 * None.
1052 * RETURNS
1053 * The current LANGID of the default UI language for the current user.
1055 LANGID WINAPI GetUserDefaultUILanguage(void)
1057 LANGID lang;
1058 NtQueryDefaultUILanguage( &lang );
1059 return lang;
1063 /***********************************************************************
1064 * GetSystemDefaultUILanguage (KERNEL32.@)
1066 * Get the default user interface language Id for the system.
1068 * PARAMS
1069 * None.
1071 * RETURNS
1072 * The current LANGID of the default UI language for the system. This is
1073 * typically the same language used during the installation process.
1075 LANGID WINAPI GetSystemDefaultUILanguage(void)
1077 LANGID lang;
1078 NtQueryInstallUILanguage( &lang );
1079 return lang;
1083 /***********************************************************************
1084 * LocaleNameToLCID (KERNEL32.@)
1086 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
1088 struct locale_name locale_name;
1090 if (flags) FIXME( "unsupported flags %x\n", flags );
1092 if (name == LOCALE_NAME_USER_DEFAULT)
1093 return GetUserDefaultLCID();
1095 /* string parsing */
1096 parse_locale_name( name, &locale_name );
1098 TRACE( "found lcid %x for %s, matches %d\n",
1099 locale_name.lcid, debugstr_w(name), locale_name.matches );
1101 if (!locale_name.matches)
1103 SetLastError(ERROR_INVALID_PARAMETER);
1104 return 0;
1107 if (locale_name.matches == 1)
1108 WARN( "locale %s not recognized, defaulting to %s\n",
1109 debugstr_w(name), debugstr_w(locale_name.lang) );
1111 return locale_name.lcid;
1115 /***********************************************************************
1116 * LCIDToLocaleName (KERNEL32.@)
1118 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1120 if (flags) FIXME( "unsupported flags %x\n", flags );
1122 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1126 /******************************************************************************
1127 * get_locale_registry_value
1129 * Gets the registry value name and cache for a given lctype.
1131 static struct registry_value *get_locale_registry_value( DWORD lctype )
1133 int i;
1134 for (i=0; i < sizeof(registry_values)/sizeof(registry_values[0]); i++)
1135 if (registry_values[i].lctype == lctype)
1136 return &registry_values[i];
1137 return NULL;
1141 /******************************************************************************
1142 * get_registry_locale_info
1144 * Retrieve user-modified locale info from the registry.
1145 * Return length, 0 on error, -1 if not found.
1147 static INT get_registry_locale_info( struct registry_value *registry_value, LPWSTR buffer, INT len )
1149 DWORD size;
1150 INT ret;
1151 HANDLE hkey;
1152 NTSTATUS status;
1153 UNICODE_STRING nameW;
1154 KEY_VALUE_PARTIAL_INFORMATION *info;
1155 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1157 RtlEnterCriticalSection( &cache_section );
1159 if (!registry_value->cached_value)
1161 if (!(hkey = create_registry_key()))
1163 RtlLeaveCriticalSection( &cache_section );
1164 return -1;
1167 RtlInitUnicodeString( &nameW, registry_value->name );
1168 size = info_size + len * sizeof(WCHAR);
1170 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1172 NtClose( hkey );
1173 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1174 RtlLeaveCriticalSection( &cache_section );
1175 return 0;
1178 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1180 NtClose( hkey );
1182 if (!status)
1184 INT length = (size - info_size) / sizeof(WCHAR);
1185 LPWSTR cached_value;
1187 if (!length || ((WCHAR *)&info->Data)[length-1])
1188 length++;
1190 cached_value = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
1192 if (!cached_value)
1194 HeapFree( GetProcessHeap(), 0, info );
1195 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1196 RtlLeaveCriticalSection( &cache_section );
1197 return 0;
1200 memcpy( cached_value, info->Data, (length-1) * sizeof(WCHAR) );
1201 cached_value[length-1] = 0;
1202 HeapFree( GetProcessHeap(), 0, info );
1203 registry_value->cached_value = cached_value;
1205 else
1207 if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1209 ret = (size - info_size) / sizeof(WCHAR) + 1;
1211 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1213 ret = -1;
1215 else
1217 SetLastError( RtlNtStatusToDosError(status) );
1218 ret = 0;
1220 HeapFree( GetProcessHeap(), 0, info );
1221 RtlLeaveCriticalSection( &cache_section );
1222 return ret;
1226 ret = lstrlenW( registry_value->cached_value ) + 1;
1228 if (buffer)
1230 if (ret > len)
1232 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1233 ret = 0;
1235 else
1237 lstrcpyW( buffer, registry_value->cached_value );
1241 RtlLeaveCriticalSection( &cache_section );
1243 return ret;
1247 /******************************************************************************
1248 * GetLocaleInfoA (KERNEL32.@)
1250 * Get information about an aspect of a locale.
1252 * PARAMS
1253 * lcid [I] LCID of the locale
1254 * lctype [I] LCTYPE_ flags from "winnls.h"
1255 * buffer [O] Destination for the information
1256 * len [I] Length of buffer in characters
1258 * RETURNS
1259 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1260 * with the information.
1261 * Failure: 0. Use GetLastError() to determine the cause.
1263 * NOTES
1264 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1265 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1266 * which is a bit string.
1268 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1270 WCHAR *bufferW;
1271 INT lenW, ret;
1273 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1275 if (len < 0 || (len && !buffer))
1277 SetLastError( ERROR_INVALID_PARAMETER );
1278 return 0;
1280 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1282 SetLastError( ERROR_INVALID_FLAGS );
1283 return 0;
1286 if (!len) buffer = NULL;
1288 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1290 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1292 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1293 return 0;
1295 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1297 if ((lctype & LOCALE_RETURN_NUMBER) ||
1298 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1300 /* it's not an ASCII string, just bytes */
1301 ret *= sizeof(WCHAR);
1302 if (buffer)
1304 if (ret <= len) memcpy( buffer, bufferW, ret );
1305 else
1307 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1308 ret = 0;
1312 else
1314 UINT codepage = CP_ACP;
1315 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1316 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1319 HeapFree( GetProcessHeap(), 0, bufferW );
1320 return ret;
1323 static int get_value_base_by_lctype( LCTYPE lctype )
1325 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1328 /******************************************************************************
1329 * GetLocaleInfoW (KERNEL32.@)
1331 * See GetLocaleInfoA.
1333 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1335 LANGID lang_id;
1336 HRSRC hrsrc;
1337 HGLOBAL hmem;
1338 INT ret;
1339 UINT lcflags;
1340 const WCHAR *p;
1341 unsigned int i;
1343 if (len < 0 || (len && !buffer))
1345 SetLastError( ERROR_INVALID_PARAMETER );
1346 return 0;
1348 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1349 !is_genitive_name_supported( lctype ))
1351 SetLastError( ERROR_INVALID_FLAGS );
1352 return 0;
1355 if (!len) buffer = NULL;
1357 lcid = convert_default_lcid( lcid, lctype );
1359 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1360 lctype &= 0xffff;
1362 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1364 /* first check for overrides in the registry */
1366 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1367 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1369 struct registry_value *value = get_locale_registry_value(lctype);
1371 if (value)
1373 if (lcflags & LOCALE_RETURN_NUMBER)
1375 WCHAR tmp[16];
1376 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1377 if (ret > 0)
1379 WCHAR *end;
1380 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1381 if (*end) /* invalid number */
1383 SetLastError( ERROR_INVALID_FLAGS );
1384 return 0;
1386 ret = sizeof(UINT)/sizeof(WCHAR);
1387 if (!buffer) return ret;
1388 if (ret > len)
1390 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1391 return 0;
1393 memcpy( buffer, &number, sizeof(number) );
1396 else ret = get_registry_locale_info( value, buffer, len );
1398 if (ret != -1) return ret;
1402 /* now load it from kernel resources */
1404 lang_id = LANGIDFROMLCID( lcid );
1406 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1407 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1408 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1410 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1411 ULongToPtr((lctype >> 4) + 1), lang_id )))
1413 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1414 return 0;
1416 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1417 return 0;
1419 p = LockResource( hmem );
1420 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1422 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1423 else if (is_genitive_name_supported( lctype ) && *p)
1425 /* genitive form's stored after a null separator from a nominative */
1426 for (i = 1; i <= *p; i++) if (!p[i]) break;
1428 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1430 ret = *p - i + 1;
1431 p += i;
1433 else ret = i;
1435 else
1436 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1438 if (!buffer) return ret;
1440 if (ret > len)
1442 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1443 return 0;
1446 if (lcflags & LOCALE_RETURN_NUMBER)
1448 UINT number;
1449 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1450 if (!tmp) return 0;
1451 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1452 tmp[*p] = 0;
1453 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1454 if (!*end)
1455 memcpy( buffer, &number, sizeof(number) );
1456 else /* invalid number */
1458 SetLastError( ERROR_INVALID_FLAGS );
1459 ret = 0;
1461 HeapFree( GetProcessHeap(), 0, tmp );
1463 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1464 lcid, lctype, buffer, len, number );
1466 else
1468 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1469 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1471 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1472 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1474 return ret;
1477 /******************************************************************************
1478 * GetLocaleInfoEx (KERNEL32.@)
1480 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1482 LCID lcid = LocaleNameToLCID(locale, 0);
1484 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1486 if (!lcid) return 0;
1488 /* special handling for neutral locale names */
1489 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1491 if (len && len < 3)
1493 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1494 return 0;
1497 if (len) strcpyW(buffer, locale);
1498 return 3;
1501 return GetLocaleInfoW(lcid, info, buffer, len);
1504 /******************************************************************************
1505 * SetLocaleInfoA [KERNEL32.@]
1507 * Set information about an aspect of a locale.
1509 * PARAMS
1510 * lcid [I] LCID of the locale
1511 * lctype [I] LCTYPE_ flags from "winnls.h"
1512 * data [I] Information to set
1514 * RETURNS
1515 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1516 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1517 * Failure: FALSE. Use GetLastError() to determine the cause.
1519 * NOTES
1520 * - Values are only be set for the current user locale; the system locale
1521 * settings cannot be changed.
1522 * - Any settings changed by this call are lost when the locale is changed by
1523 * the control panel (in Wine, this happens every time you change LANG).
1524 * - The native implementation of this function does not check that lcid matches
1525 * the current user locale, and simply sets the new values. Wine warns you in
1526 * this case, but behaves the same.
1528 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1530 UINT codepage = CP_ACP;
1531 WCHAR *strW;
1532 DWORD len;
1533 BOOL ret;
1535 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1537 if (!data)
1539 SetLastError( ERROR_INVALID_PARAMETER );
1540 return FALSE;
1542 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1543 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1545 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1546 return FALSE;
1548 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1549 ret = SetLocaleInfoW( lcid, lctype, strW );
1550 HeapFree( GetProcessHeap(), 0, strW );
1551 return ret;
1555 /******************************************************************************
1556 * SetLocaleInfoW (KERNEL32.@)
1558 * See SetLocaleInfoA.
1560 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1562 struct registry_value *value;
1563 static const WCHAR intlW[] = {'i','n','t','l',0 };
1564 UNICODE_STRING valueW;
1565 NTSTATUS status;
1566 HANDLE hkey;
1568 lctype &= 0xffff;
1569 value = get_locale_registry_value( lctype );
1571 if (!data || !value)
1573 SetLastError( ERROR_INVALID_PARAMETER );
1574 return FALSE;
1577 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1579 SetLastError( ERROR_INVALID_FLAGS );
1580 return FALSE;
1583 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
1585 /* FIXME: should check that data to set is sane */
1587 /* FIXME: profile functions should map to registry */
1588 WriteProfileStringW( intlW, value->name, data );
1590 if (!(hkey = create_registry_key())) return FALSE;
1591 RtlInitUnicodeString( &valueW, value->name );
1592 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1594 RtlEnterCriticalSection( &cache_section );
1595 HeapFree( GetProcessHeap(), 0, value->cached_value );
1596 value->cached_value = NULL;
1597 RtlLeaveCriticalSection( &cache_section );
1599 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1601 /* Set I-value from S value */
1602 WCHAR *lpD, *lpM, *lpY;
1603 WCHAR szBuff[2];
1605 lpD = strrchrW(data, 'd');
1606 lpM = strrchrW(data, 'M');
1607 lpY = strrchrW(data, 'y');
1609 if (lpD <= lpM)
1611 szBuff[0] = '1'; /* D-M-Y */
1613 else
1615 if (lpY <= lpM)
1616 szBuff[0] = '2'; /* Y-M-D */
1617 else
1618 szBuff[0] = '0'; /* M-D-Y */
1621 szBuff[1] = '\0';
1623 if (lctype == LOCALE_SSHORTDATE)
1624 lctype = LOCALE_IDATE;
1625 else
1626 lctype = LOCALE_ILDATE;
1628 value = get_locale_registry_value( lctype );
1630 WriteProfileStringW( intlW, value->name, szBuff );
1632 RtlInitUnicodeString( &valueW, value->name );
1633 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1635 RtlEnterCriticalSection( &cache_section );
1636 HeapFree( GetProcessHeap(), 0, value->cached_value );
1637 value->cached_value = NULL;
1638 RtlLeaveCriticalSection( &cache_section );
1641 NtClose( hkey );
1643 if (status) SetLastError( RtlNtStatusToDosError(status) );
1644 return !status;
1648 /******************************************************************************
1649 * GetACP (KERNEL32.@)
1651 * Get the current Ansi code page Id for the system.
1653 * PARAMS
1654 * None.
1656 * RETURNS
1657 * The current Ansi code page identifier for the system.
1659 UINT WINAPI GetACP(void)
1661 assert( ansi_cptable );
1662 return ansi_cptable->info.codepage;
1666 /******************************************************************************
1667 * SetCPGlobal (KERNEL32.@)
1669 * Set the current Ansi code page Id for the system.
1671 * PARAMS
1672 * acp [I] code page ID to be the new ACP.
1674 * RETURNS
1675 * The previous ACP.
1677 UINT WINAPI SetCPGlobal( UINT acp )
1679 UINT ret = GetACP();
1680 const union cptable *new_cptable = wine_cp_get_table( acp );
1682 if (new_cptable) ansi_cptable = new_cptable;
1683 return ret;
1687 /***********************************************************************
1688 * GetOEMCP (KERNEL32.@)
1690 * Get the current OEM code page Id for the system.
1692 * PARAMS
1693 * None.
1695 * RETURNS
1696 * The current OEM code page identifier for the system.
1698 UINT WINAPI GetOEMCP(void)
1700 assert( oem_cptable );
1701 return oem_cptable->info.codepage;
1705 /***********************************************************************
1706 * IsValidCodePage (KERNEL32.@)
1708 * Determine if a given code page identifier is valid.
1710 * PARAMS
1711 * codepage [I] Code page Id to verify.
1713 * RETURNS
1714 * TRUE, If codepage is valid and available on the system,
1715 * FALSE otherwise.
1717 BOOL WINAPI IsValidCodePage( UINT codepage )
1719 switch(codepage) {
1720 case CP_UTF7:
1721 case CP_UTF8:
1722 return TRUE;
1723 default:
1724 return wine_cp_get_table( codepage ) != NULL;
1729 /***********************************************************************
1730 * IsDBCSLeadByteEx (KERNEL32.@)
1732 * Determine if a character is a lead byte in a given code page.
1734 * PARAMS
1735 * codepage [I] Code page for the test.
1736 * testchar [I] Character to test
1738 * RETURNS
1739 * TRUE, if testchar is a lead byte in codepage,
1740 * FALSE otherwise.
1742 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1744 const union cptable *table = get_codepage_table( codepage );
1745 return table && wine_is_dbcs_leadbyte( table, testchar );
1749 /***********************************************************************
1750 * IsDBCSLeadByte (KERNEL32.@)
1751 * IsDBCSLeadByte (KERNEL.207)
1753 * Determine if a character is a lead byte.
1755 * PARAMS
1756 * testchar [I] Character to test
1758 * RETURNS
1759 * TRUE, if testchar is a lead byte in the ANSI code page,
1760 * FALSE otherwise.
1762 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1764 if (!ansi_cptable) return FALSE;
1765 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1769 /***********************************************************************
1770 * GetCPInfo (KERNEL32.@)
1772 * Get information about a code page.
1774 * PARAMS
1775 * codepage [I] Code page number
1776 * cpinfo [O] Destination for code page information
1778 * RETURNS
1779 * Success: TRUE. cpinfo is updated with the information about codepage.
1780 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1782 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1784 const union cptable *table;
1786 if (!cpinfo)
1788 SetLastError( ERROR_INVALID_PARAMETER );
1789 return FALSE;
1792 if (!(table = get_codepage_table( codepage )))
1794 switch(codepage)
1796 case CP_UTF7:
1797 case CP_UTF8:
1798 cpinfo->DefaultChar[0] = 0x3f;
1799 cpinfo->DefaultChar[1] = 0;
1800 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1801 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1802 return TRUE;
1805 SetLastError( ERROR_INVALID_PARAMETER );
1806 return FALSE;
1808 if (table->info.def_char & 0xff00)
1810 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1811 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1813 else
1815 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1816 cpinfo->DefaultChar[1] = 0;
1818 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1819 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1820 else
1821 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1823 return TRUE;
1826 /***********************************************************************
1827 * GetCPInfoExA (KERNEL32.@)
1829 * Get extended information about a code page.
1831 * PARAMS
1832 * codepage [I] Code page number
1833 * dwFlags [I] Reserved, must to 0.
1834 * cpinfo [O] Destination for code page information
1836 * RETURNS
1837 * Success: TRUE. cpinfo is updated with the information about codepage.
1838 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1840 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1842 CPINFOEXW cpinfoW;
1844 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1845 return FALSE;
1847 /* the layout is the same except for CodePageName */
1848 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1849 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1850 return TRUE;
1853 /***********************************************************************
1854 * GetCPInfoExW (KERNEL32.@)
1856 * Unicode version of GetCPInfoExA.
1858 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1860 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1861 return FALSE;
1863 switch(codepage)
1865 case CP_UTF7:
1867 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1869 cpinfo->CodePage = CP_UTF7;
1870 cpinfo->UnicodeDefaultChar = 0x3f;
1871 strcpyW(cpinfo->CodePageName, utf7);
1872 break;
1875 case CP_UTF8:
1877 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1879 cpinfo->CodePage = CP_UTF8;
1880 cpinfo->UnicodeDefaultChar = 0x3f;
1881 strcpyW(cpinfo->CodePageName, utf8);
1882 break;
1885 default:
1887 const union cptable *table = get_codepage_table( codepage );
1889 cpinfo->CodePage = table->info.codepage;
1890 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1891 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1892 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1893 break;
1896 return TRUE;
1899 /***********************************************************************
1900 * EnumSystemCodePagesA (KERNEL32.@)
1902 * Call a user defined function for every code page installed on the system.
1904 * PARAMS
1905 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1906 * flags [I] Reserved, set to 0.
1908 * RETURNS
1909 * TRUE, If all code pages have been enumerated, or
1910 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1912 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1914 const union cptable *table;
1915 char buffer[10];
1916 int index = 0;
1918 for (;;)
1920 if (!(table = wine_cp_enum_table( index++ ))) break;
1921 sprintf( buffer, "%d", table->info.codepage );
1922 if (!lpfnCodePageEnum( buffer )) break;
1924 return TRUE;
1928 /***********************************************************************
1929 * EnumSystemCodePagesW (KERNEL32.@)
1931 * See EnumSystemCodePagesA.
1933 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1935 const union cptable *table;
1936 WCHAR buffer[10], *p;
1937 int page, index = 0;
1939 for (;;)
1941 if (!(table = wine_cp_enum_table( index++ ))) break;
1942 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1943 *--p = 0;
1944 page = table->info.codepage;
1947 *--p = '0' + (page % 10);
1948 page /= 10;
1949 } while( page );
1950 if (!lpfnCodePageEnum( p )) break;
1952 return TRUE;
1956 /***********************************************************************
1957 * MultiByteToWideChar (KERNEL32.@)
1959 * Convert a multibyte character string into a Unicode string.
1961 * PARAMS
1962 * page [I] Codepage character set to convert from
1963 * flags [I] Character mapping flags
1964 * src [I] Source string buffer
1965 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1966 * dst [O] Destination buffer
1967 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1969 * RETURNS
1970 * Success: If dstlen > 0, the number of characters written to dst.
1971 * If dstlen == 0, the number of characters needed to perform the
1972 * conversion. In both cases the count includes the terminating NUL.
1973 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1974 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1975 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1976 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1977 * possible for src.
1979 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1980 LPWSTR dst, INT dstlen )
1982 const union cptable *table;
1983 int ret;
1985 if (!src || !srclen || (!dst && dstlen))
1987 SetLastError( ERROR_INVALID_PARAMETER );
1988 return 0;
1991 if (srclen < 0) srclen = strlen(src) + 1;
1993 switch(page)
1995 case CP_SYMBOL:
1996 if (flags)
1998 SetLastError( ERROR_INVALID_FLAGS );
1999 return 0;
2001 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
2002 break;
2003 case CP_UTF7:
2004 if (flags)
2006 SetLastError( ERROR_INVALID_FLAGS );
2007 return 0;
2009 FIXME("UTF-7 not supported\n");
2010 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2011 return 0;
2012 case CP_UNIXCP:
2013 if (unix_cptable)
2015 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
2016 break;
2018 #ifdef __APPLE__
2019 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
2020 #endif
2021 /* fall through */
2022 case CP_UTF8:
2023 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
2024 break;
2025 default:
2026 if (!(table = get_codepage_table( page )))
2028 SetLastError( ERROR_INVALID_PARAMETER );
2029 return 0;
2031 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
2032 break;
2035 if (ret < 0)
2037 switch(ret)
2039 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2040 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2042 ret = 0;
2044 TRACE("cp %d %s -> %s, ret = %d\n",
2045 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
2046 return ret;
2050 /***********************************************************************
2051 * WideCharToMultiByte (KERNEL32.@)
2053 * Convert a Unicode character string into a multibyte string.
2055 * PARAMS
2056 * page [I] Code page character set to convert to
2057 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
2058 * src [I] Source string buffer
2059 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
2060 * dst [O] Destination buffer
2061 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
2062 * defchar [I] Default character to use for conversion if no exact
2063 * conversion can be made
2064 * used [O] Set if default character was used in the conversion
2066 * RETURNS
2067 * Success: If dstlen > 0, the number of characters written to dst.
2068 * If dstlen == 0, number of characters needed to perform the
2069 * conversion. In both cases the count includes the terminating NUL.
2070 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2071 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2072 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2073 * parameter was given.
2075 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2076 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2078 const union cptable *table;
2079 int ret, used_tmp;
2081 if (!src || !srclen || (!dst && dstlen))
2083 SetLastError( ERROR_INVALID_PARAMETER );
2084 return 0;
2087 if (srclen < 0) srclen = strlenW(src) + 1;
2089 switch(page)
2091 case CP_SYMBOL:
2092 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2093 if (flags)
2095 SetLastError( ERROR_INVALID_FLAGS );
2096 return 0;
2098 if (defchar || used)
2100 SetLastError( ERROR_INVALID_PARAMETER );
2101 return 0;
2103 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2104 break;
2105 case CP_UTF7:
2106 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2107 if (defchar || used)
2109 SetLastError( ERROR_INVALID_PARAMETER );
2110 return 0;
2112 if (flags)
2114 SetLastError( ERROR_INVALID_FLAGS );
2115 return 0;
2117 FIXME("UTF-7 not supported\n");
2118 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2119 return 0;
2120 case CP_UNIXCP:
2121 if (unix_cptable)
2123 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2124 defchar, used ? &used_tmp : NULL );
2125 break;
2127 /* fall through */
2128 case CP_UTF8:
2129 if (defchar || used)
2131 SetLastError( ERROR_INVALID_PARAMETER );
2132 return 0;
2134 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2135 break;
2136 default:
2137 if (!(table = get_codepage_table( page )))
2139 SetLastError( ERROR_INVALID_PARAMETER );
2140 return 0;
2142 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2143 defchar, used ? &used_tmp : NULL );
2144 if (used) *used = used_tmp;
2145 break;
2148 if (ret < 0)
2150 switch(ret)
2152 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2153 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2155 ret = 0;
2157 TRACE("cp %d %s -> %s, ret = %d\n",
2158 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2159 return ret;
2163 /***********************************************************************
2164 * GetThreadLocale (KERNEL32.@)
2166 * Get the current threads locale.
2168 * PARAMS
2169 * None.
2171 * RETURNS
2172 * The LCID currently associated with the calling thread.
2174 LCID WINAPI GetThreadLocale(void)
2176 LCID ret = NtCurrentTeb()->CurrentLocale;
2177 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2178 return ret;
2181 /**********************************************************************
2182 * SetThreadLocale (KERNEL32.@)
2184 * Set the current threads locale.
2186 * PARAMS
2187 * lcid [I] LCID of the locale to set
2189 * RETURNS
2190 * Success: TRUE. The threads locale is set to lcid.
2191 * Failure: FALSE. Use GetLastError() to determine the cause.
2193 BOOL WINAPI SetThreadLocale( LCID lcid )
2195 TRACE("(0x%04X)\n", lcid);
2197 lcid = ConvertDefaultLocale(lcid);
2199 if (lcid != GetThreadLocale())
2201 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2203 SetLastError(ERROR_INVALID_PARAMETER);
2204 return FALSE;
2207 NtCurrentTeb()->CurrentLocale = lcid;
2209 return TRUE;
2212 /**********************************************************************
2213 * SetThreadUILanguage (KERNEL32.@)
2215 * Set the current threads UI language.
2217 * PARAMS
2218 * langid [I] LANGID of the language to set, or 0 to use
2219 * the available language which is best supported
2220 * for console applications
2222 * RETURNS
2223 * Success: The return value is the same as the input value.
2224 * Failure: The return value differs from the input value.
2225 * Use GetLastError() to determine the cause.
2227 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2229 TRACE("(0x%04x) stub - returning success\n", langid);
2230 return langid;
2233 /******************************************************************************
2234 * ConvertDefaultLocale (KERNEL32.@)
2236 * Convert a default locale identifier into a real identifier.
2238 * PARAMS
2239 * lcid [I] LCID identifier of the locale to convert
2241 * RETURNS
2242 * lcid unchanged, if not a default locale or its sublanguage is
2243 * not SUBLANG_NEUTRAL.
2244 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2245 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2246 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2248 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2250 LANGID langid;
2252 switch (lcid)
2254 case LOCALE_SYSTEM_DEFAULT:
2255 lcid = GetSystemDefaultLCID();
2256 break;
2257 case LOCALE_USER_DEFAULT:
2258 case LOCALE_NEUTRAL:
2259 lcid = GetUserDefaultLCID();
2260 break;
2261 default:
2262 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2263 langid = LANGIDFROMLCID(lcid);
2264 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2266 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2267 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2270 return lcid;
2274 /******************************************************************************
2275 * IsValidLocale (KERNEL32.@)
2277 * Determine if a locale is valid.
2279 * PARAMS
2280 * lcid [I] LCID of the locale to check
2281 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2283 * RETURNS
2284 * TRUE, if lcid is valid,
2285 * FALSE, otherwise.
2287 * NOTES
2288 * Wine does not currently make the distinction between supported and installed. All
2289 * languages supported are installed by default.
2291 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2293 /* check if language is registered in the kernel32 resources */
2294 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2295 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2298 /******************************************************************************
2299 * IsValidLocaleName (KERNEL32.@)
2301 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2303 struct locale_name locale_name;
2305 /* string parsing */
2306 parse_locale_name( locale, &locale_name );
2308 TRACE( "found lcid %x for %s, matches %d\n",
2309 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2311 return locale_name.matches > 0;
2314 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2315 LPCSTR name, WORD LangID, LONG_PTR lParam )
2317 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2318 char buf[20];
2320 sprintf(buf, "%08x", (UINT)LangID);
2321 return lpfnLocaleEnum( buf );
2324 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2325 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2327 static const WCHAR formatW[] = {'%','0','8','x',0};
2328 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2329 WCHAR buf[20];
2330 sprintfW( buf, formatW, (UINT)LangID );
2331 return lpfnLocaleEnum( buf );
2334 /******************************************************************************
2335 * EnumSystemLocalesA (KERNEL32.@)
2337 * Call a users function for each locale available on the system.
2339 * PARAMS
2340 * lpfnLocaleEnum [I] Callback function to call for each locale
2341 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2343 * RETURNS
2344 * Success: TRUE.
2345 * Failure: FALSE. Use GetLastError() to determine the cause.
2347 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2349 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2350 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2351 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2352 (LONG_PTR)lpfnLocaleEnum);
2353 return TRUE;
2357 /******************************************************************************
2358 * EnumSystemLocalesW (KERNEL32.@)
2360 * See EnumSystemLocalesA.
2362 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2364 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2365 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2366 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2367 (LONG_PTR)lpfnLocaleEnum);
2368 return TRUE;
2372 struct enum_locale_ex_data
2374 LOCALE_ENUMPROCEX proc;
2375 DWORD flags;
2376 LPARAM lparam;
2379 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2380 LPCWSTR name, WORD lang, LONG_PTR lparam )
2382 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2383 WCHAR buffer[256];
2384 DWORD neutral;
2385 unsigned int flags;
2387 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2388 buffer, sizeof(buffer) / sizeof(WCHAR) );
2389 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2390 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2391 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2392 neutral = 0;
2393 flags = LOCALE_WINDOWS;
2394 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2395 if (data->flags && ~(data->flags & flags)) return TRUE;
2396 return data->proc( buffer, flags, data->lparam );
2399 /******************************************************************************
2400 * EnumSystemLocalesEx (KERNEL32.@)
2402 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2404 struct enum_locale_ex_data data;
2406 if (reserved)
2408 SetLastError( ERROR_INVALID_PARAMETER );
2409 return FALSE;
2411 data.proc = proc;
2412 data.flags = flags;
2413 data.lparam = lparam;
2414 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2415 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2416 enum_locale_ex_proc, (LONG_PTR)&data );
2417 return TRUE;
2421 /***********************************************************************
2422 * VerLanguageNameA (KERNEL32.@)
2424 * Get the name of a language.
2426 * PARAMS
2427 * wLang [I] LANGID of the language
2428 * szLang [O] Destination for the language name
2430 * RETURNS
2431 * Success: The size of the language name. If szLang is non-NULL, it is filled
2432 * with the name.
2433 * Failure: 0. Use GetLastError() to determine the cause.
2436 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2438 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2442 /***********************************************************************
2443 * VerLanguageNameW (KERNEL32.@)
2445 * See VerLanguageNameA.
2447 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2449 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2453 /******************************************************************************
2454 * GetStringTypeW (KERNEL32.@)
2456 * See GetStringTypeA.
2458 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2460 static const unsigned char type2_map[16] =
2462 C2_NOTAPPLICABLE, /* unassigned */
2463 C2_LEFTTORIGHT, /* L */
2464 C2_RIGHTTOLEFT, /* R */
2465 C2_EUROPENUMBER, /* EN */
2466 C2_EUROPESEPARATOR, /* ES */
2467 C2_EUROPETERMINATOR, /* ET */
2468 C2_ARABICNUMBER, /* AN */
2469 C2_COMMONSEPARATOR, /* CS */
2470 C2_BLOCKSEPARATOR, /* B */
2471 C2_SEGMENTSEPARATOR, /* S */
2472 C2_WHITESPACE, /* WS */
2473 C2_OTHERNEUTRAL, /* ON */
2474 C2_RIGHTTOLEFT, /* AL */
2475 C2_NOTAPPLICABLE, /* NSM */
2476 C2_NOTAPPLICABLE, /* BN */
2477 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2480 if (count == -1) count = strlenW(src) + 1;
2481 switch(type)
2483 case CT_CTYPE1:
2484 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2485 break;
2486 case CT_CTYPE2:
2487 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2488 break;
2489 case CT_CTYPE3:
2491 WARN("CT_CTYPE3: semi-stub.\n");
2492 while (count--)
2494 int c = *src;
2495 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2497 type1 = get_char_typeW( *src++ ) & 0xfff;
2498 /* try to construct type3 from type1 */
2499 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2500 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2501 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2502 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2503 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2504 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2505 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2507 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2508 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2509 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2510 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2511 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2512 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2513 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2514 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2516 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2517 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2518 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2519 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2520 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2521 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2522 *chartype++ = type3;
2524 break;
2526 default:
2527 SetLastError( ERROR_INVALID_PARAMETER );
2528 return FALSE;
2530 return TRUE;
2534 /******************************************************************************
2535 * GetStringTypeExW (KERNEL32.@)
2537 * See GetStringTypeExA.
2539 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2541 /* locale is ignored for Unicode */
2542 return GetStringTypeW( type, src, count, chartype );
2546 /******************************************************************************
2547 * GetStringTypeA (KERNEL32.@)
2549 * Get characteristics of the characters making up a string.
2551 * PARAMS
2552 * locale [I] Locale Id for the string
2553 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2554 * src [I] String to analyse
2555 * count [I] Length of src in chars, or -1 if src is NUL terminated
2556 * chartype [O] Destination for the calculated characteristics
2558 * RETURNS
2559 * Success: TRUE. chartype is filled with the requested characteristics of each char
2560 * in src.
2561 * Failure: FALSE. Use GetLastError() to determine the cause.
2563 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2565 UINT cp;
2566 INT countW;
2567 LPWSTR srcW;
2568 BOOL ret = FALSE;
2570 if(count == -1) count = strlen(src) + 1;
2572 if (!(cp = get_lcid_codepage( locale )))
2574 FIXME("For locale %04x using current ANSI code page\n", locale);
2575 cp = GetACP();
2578 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2579 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2581 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2583 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2584 * string, with multibyte characters there maybe be more bytes in count
2585 * than character space in the buffer!
2587 ret = GetStringTypeW(type, srcW, countW, chartype);
2588 HeapFree(GetProcessHeap(), 0, srcW);
2590 return ret;
2593 /******************************************************************************
2594 * GetStringTypeExA (KERNEL32.@)
2596 * Get characteristics of the characters making up a string.
2598 * PARAMS
2599 * locale [I] Locale Id for the string
2600 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2601 * src [I] String to analyse
2602 * count [I] Length of src in chars, or -1 if src is NUL terminated
2603 * chartype [O] Destination for the calculated characteristics
2605 * RETURNS
2606 * Success: TRUE. chartype is filled with the requested characteristics of each char
2607 * in src.
2608 * Failure: FALSE. Use GetLastError() to determine the cause.
2610 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2612 return GetStringTypeA(locale, type, src, count, chartype);
2615 /*************************************************************************
2616 * LCMapStringEx (KERNEL32.@)
2618 * Map characters in a locale sensitive string.
2620 * PARAMS
2621 * name [I] Locale name for the conversion.
2622 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2623 * src [I] String to map
2624 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2625 * dst [O] Destination for mapped string
2626 * dstlen [I] Length of dst in characters
2627 * version [I] reserved, must be NULL
2628 * reserved [I] reserved, must be NULL
2629 * lparam [I] reserved, must be 0
2631 * RETURNS
2632 * Success: The length of the mapped string in dst, including the NUL terminator.
2633 * Failure: 0. Use GetLastError() to determine the cause.
2635 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2636 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2638 LPWSTR dst_ptr;
2640 if (version) FIXME("unsupported version structure %p\n", version);
2641 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2642 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2644 if (!src || !srclen || dstlen < 0)
2646 SetLastError(ERROR_INVALID_PARAMETER);
2647 return 0;
2650 /* mutually exclusive flags */
2651 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2652 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2653 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2654 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2656 SetLastError(ERROR_INVALID_FLAGS);
2657 return 0;
2660 if (!dstlen) dst = NULL;
2662 if (flags & LCMAP_SORTKEY)
2664 INT ret;
2665 if (src == dst)
2667 SetLastError(ERROR_INVALID_FLAGS);
2668 return 0;
2671 if (srclen < 0) srclen = strlenW(src);
2673 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2674 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2676 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2677 if (ret == 0)
2678 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2679 else
2680 ret++;
2681 return ret;
2684 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2685 if (flags & SORT_STRINGSORT)
2687 SetLastError(ERROR_INVALID_FLAGS);
2688 return 0;
2691 if (srclen < 0) srclen = strlenW(src) + 1;
2693 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2694 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2696 if (!dst) /* return required string length */
2698 INT len;
2700 for (len = 0; srclen; src++, srclen--)
2702 WCHAR wch = *src;
2703 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2704 * and skips white space and punctuation characters for
2705 * NORM_IGNORESYMBOLS.
2707 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2708 continue;
2709 len++;
2711 return len;
2714 if (flags & LCMAP_UPPERCASE)
2716 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2718 WCHAR wch = *src;
2719 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2720 continue;
2721 *dst_ptr++ = toupperW(wch);
2722 dstlen--;
2725 else if (flags & LCMAP_LOWERCASE)
2727 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2729 WCHAR wch = *src;
2730 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2731 continue;
2732 *dst_ptr++ = tolowerW(wch);
2733 dstlen--;
2736 else
2738 if (src == dst)
2740 SetLastError(ERROR_INVALID_FLAGS);
2741 return 0;
2743 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2745 WCHAR wch = *src;
2746 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2747 continue;
2748 *dst_ptr++ = wch;
2749 dstlen--;
2753 if (srclen)
2755 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2756 return 0;
2759 return dst_ptr - dst;
2762 /*************************************************************************
2763 * LCMapStringW (KERNEL32.@)
2765 * See LCMapStringA.
2767 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2768 LPWSTR dst, INT dstlen)
2770 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2771 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2773 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2776 /*************************************************************************
2777 * LCMapStringA (KERNEL32.@)
2779 * Map characters in a locale sensitive string.
2781 * PARAMS
2782 * lcid [I] LCID for the conversion.
2783 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2784 * src [I] String to map
2785 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2786 * dst [O] Destination for mapped string
2787 * dstlen [I] Length of dst in characters
2789 * RETURNS
2790 * Success: The length of the mapped string in dst, including the NUL terminator.
2791 * Failure: 0. Use GetLastError() to determine the cause.
2793 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2794 LPSTR dst, INT dstlen)
2796 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2797 LPWSTR srcW, dstW;
2798 INT ret = 0, srclenW, dstlenW;
2799 UINT locale_cp = CP_ACP;
2801 if (!src || !srclen || dstlen < 0)
2803 SetLastError(ERROR_INVALID_PARAMETER);
2804 return 0;
2807 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2809 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2810 if (srclenW)
2811 srcW = bufW;
2812 else
2814 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2815 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2816 if (!srcW)
2818 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2819 return 0;
2821 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2824 if (flags & LCMAP_SORTKEY)
2826 if (src == dst)
2828 SetLastError(ERROR_INVALID_FLAGS);
2829 goto map_string_exit;
2831 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2832 if (ret == 0)
2833 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2834 else
2835 ret++;
2836 goto map_string_exit;
2839 if (flags & SORT_STRINGSORT)
2841 SetLastError(ERROR_INVALID_FLAGS);
2842 goto map_string_exit;
2845 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2846 if (!dstlenW)
2847 goto map_string_exit;
2849 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2850 if (!dstW)
2852 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2853 goto map_string_exit;
2856 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2857 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2858 HeapFree(GetProcessHeap(), 0, dstW);
2860 map_string_exit:
2861 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2862 return ret;
2865 /*************************************************************************
2866 * FoldStringA (KERNEL32.@)
2868 * Map characters in a string.
2870 * PARAMS
2871 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2872 * src [I] String to map
2873 * srclen [I] Length of src, or -1 if src is NUL terminated
2874 * dst [O] Destination for mapped string
2875 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2877 * RETURNS
2878 * Success: The length of the string written to dst, including the terminating NUL. If
2879 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2880 * and dst may be NULL.
2881 * Failure: 0. Use GetLastError() to determine the cause.
2883 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2884 LPSTR dst, INT dstlen)
2886 INT ret = 0, srclenW = 0;
2887 WCHAR *srcW = NULL, *dstW = NULL;
2889 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2891 SetLastError(ERROR_INVALID_PARAMETER);
2892 return 0;
2895 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2896 src, srclen, NULL, 0);
2897 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2899 if (!srcW)
2901 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2902 goto FoldStringA_exit;
2905 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2906 src, srclen, srcW, srclenW);
2908 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2910 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2911 if (ret && dstlen)
2913 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2915 if (!dstW)
2917 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2918 goto FoldStringA_exit;
2921 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2922 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2924 ret = 0;
2925 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2929 HeapFree(GetProcessHeap(), 0, dstW);
2931 FoldStringA_exit:
2932 HeapFree(GetProcessHeap(), 0, srcW);
2933 return ret;
2936 /*************************************************************************
2937 * FoldStringW (KERNEL32.@)
2939 * See FoldStringA.
2941 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2942 LPWSTR dst, INT dstlen)
2944 int ret;
2946 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2948 case 0:
2949 if (dwFlags)
2950 break;
2951 /* Fall through for dwFlags == 0 */
2952 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2953 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2954 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2955 SetLastError(ERROR_INVALID_FLAGS);
2956 return 0;
2959 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2961 SetLastError(ERROR_INVALID_PARAMETER);
2962 return 0;
2965 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2966 if (!ret)
2967 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2968 return ret;
2971 /******************************************************************************
2972 * CompareStringW (KERNEL32.@)
2974 * See CompareStringA.
2976 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
2977 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2979 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
2982 /******************************************************************************
2983 * CompareStringEx (KERNEL32.@)
2985 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
2986 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
2988 INT ret;
2990 if (version) FIXME("unexpected version parameter\n");
2991 if (reserved) FIXME("unexpected reserved value\n");
2992 if (lParam) FIXME("unexpected lParam\n");
2994 if (!str1 || !str2)
2996 SetLastError(ERROR_INVALID_PARAMETER);
2997 return 0;
3000 if( flags & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
3001 SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
3003 SetLastError(ERROR_INVALID_FLAGS);
3004 return 0;
3007 /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
3008 if (flags & 0x10000000)
3009 WARN("Ignoring unknown flags 0x10000000\n");
3011 if (len1 < 0) len1 = strlenW(str1);
3012 if (len2 < 0) len2 = strlenW(str2);
3014 ret = wine_compare_string(flags, str1, len1, str2, len2);
3016 if (ret) /* need to translate result */
3017 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3018 return CSTR_EQUAL;
3021 /******************************************************************************
3022 * CompareStringA (KERNEL32.@)
3024 * Compare two locale sensitive strings.
3026 * PARAMS
3027 * lcid [I] LCID for the comparison
3028 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3029 * str1 [I] First string to compare
3030 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3031 * str2 [I] Second string to compare
3032 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3034 * RETURNS
3035 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3036 * str1 is less than, equal to or greater than str2 respectively.
3037 * Failure: FALSE. Use GetLastError() to determine the cause.
3039 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3040 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3042 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3043 WCHAR *buf2W = buf1W + 130;
3044 LPWSTR str1W, str2W;
3045 INT len1W = 0, len2W = 0, ret;
3046 UINT locale_cp = CP_ACP;
3048 if (!str1 || !str2)
3050 SetLastError(ERROR_INVALID_PARAMETER);
3051 return 0;
3053 if (len1 < 0) len1 = strlen(str1);
3054 if (len2 < 0) len2 = strlen(str2);
3056 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3058 if (len1)
3060 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3061 if (len1W)
3062 str1W = buf1W;
3063 else
3065 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3066 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3067 if (!str1W)
3069 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3070 return 0;
3072 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3075 else
3077 len1W = 0;
3078 str1W = buf1W;
3081 if (len2)
3083 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3084 if (len2W)
3085 str2W = buf2W;
3086 else
3088 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3089 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3090 if (!str2W)
3092 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3093 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3094 return 0;
3096 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3099 else
3101 len2W = 0;
3102 str2W = buf2W;
3105 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3107 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3108 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3109 return ret;
3112 /******************************************************************************
3113 * CompareStringOrdinal (KERNEL32.@)
3115 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3117 int ret, len;
3119 if (!str1 || !str2)
3121 SetLastError(ERROR_INVALID_PARAMETER);
3122 return 0;
3124 if (len1 < 0) len1 = strlenW(str1);
3125 if (len2 < 0) len2 = strlenW(str2);
3127 len = min(len1, len2);
3128 if (ignore_case)
3130 ret = memicmpW(str1, str2, len);
3132 else
3134 ret = 0;
3135 for (; len > 0; len--)
3136 if ((ret = (*str1++ - *str2++))) break;
3138 if (!ret) ret = len1 - len2;
3140 if (ret < 0) return CSTR_LESS_THAN;
3141 if (ret > 0) return CSTR_GREATER_THAN;
3142 return CSTR_EQUAL;
3145 /*************************************************************************
3146 * lstrcmp (KERNEL32.@)
3147 * lstrcmpA (KERNEL32.@)
3149 * Compare two strings using the current thread locale.
3151 * PARAMS
3152 * str1 [I] First string to compare
3153 * str2 [I] Second string to compare
3155 * RETURNS
3156 * Success: A number less than, equal to or greater than 0 depending on whether
3157 * str1 is less than, equal to or greater than str2 respectively.
3158 * Failure: FALSE. Use GetLastError() to determine the cause.
3160 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3162 int ret;
3164 if ((str1 == NULL) && (str2 == NULL)) return 0;
3165 if (str1 == NULL) return -1;
3166 if (str2 == NULL) return 1;
3168 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3169 if (ret) ret -= 2;
3171 return ret;
3174 /*************************************************************************
3175 * lstrcmpi (KERNEL32.@)
3176 * lstrcmpiA (KERNEL32.@)
3178 * Compare two strings using the current thread locale, ignoring case.
3180 * PARAMS
3181 * str1 [I] First string to compare
3182 * str2 [I] Second string to compare
3184 * RETURNS
3185 * Success: A number less than, equal to or greater than 0 depending on whether
3186 * str2 is less than, equal to or greater than str1 respectively.
3187 * Failure: FALSE. Use GetLastError() to determine the cause.
3189 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3191 int ret;
3193 if ((str1 == NULL) && (str2 == NULL)) return 0;
3194 if (str1 == NULL) return -1;
3195 if (str2 == NULL) return 1;
3197 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3198 if (ret) ret -= 2;
3200 return ret;
3203 /*************************************************************************
3204 * lstrcmpW (KERNEL32.@)
3206 * See lstrcmpA.
3208 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3210 int ret;
3212 if ((str1 == NULL) && (str2 == NULL)) return 0;
3213 if (str1 == NULL) return -1;
3214 if (str2 == NULL) return 1;
3216 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3217 if (ret) ret -= 2;
3219 return ret;
3222 /*************************************************************************
3223 * lstrcmpiW (KERNEL32.@)
3225 * See lstrcmpiA.
3227 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3229 int ret;
3231 if ((str1 == NULL) && (str2 == NULL)) return 0;
3232 if (str1 == NULL) return -1;
3233 if (str2 == NULL) return 1;
3235 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3236 if (ret) ret -= 2;
3238 return ret;
3241 /******************************************************************************
3242 * LOCALE_Init
3244 void LOCALE_Init(void)
3246 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3247 const union cptable *unix_cp );
3249 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3251 #ifdef __APPLE__
3252 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3253 char user_locale[50];
3255 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3256 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3257 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3258 CFStringRef user_locale_string_ref;
3260 if (user_locale_country_ref)
3262 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3263 user_locale_lang_ref, user_locale_country_ref);
3265 else
3267 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3270 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3271 strcat(user_locale, ".UTF-8");
3273 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3274 setenv( "LANG", user_locale, 0 );
3275 TRACE( "setting locale to '%s'\n", user_locale );
3276 #endif /* __APPLE__ */
3278 setlocale( LC_ALL, "" );
3280 unix_cp = setup_unix_locales();
3281 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3283 #ifdef __APPLE__
3284 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3285 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3287 /* Retrieve the preferred language as chosen in System Preferences. */
3288 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3289 leave things be. */
3290 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3291 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3292 CFStringRef user_language_string_ref;
3293 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3294 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3295 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3296 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3298 struct locale_name locale_name;
3299 WCHAR buffer[128];
3300 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3301 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3302 parse_locale_name( buffer, &locale_name );
3303 lcid_LC_MESSAGES = locale_name.lcid;
3304 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3306 if (preferred_langs)
3307 CFRelease( preferred_langs );
3308 if (canonical_lang_string_ref)
3309 CFRelease( canonical_lang_string_ref );
3312 CFRelease( user_locale_ref );
3313 CFRelease( user_locale_string_ref );
3314 #endif
3316 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3317 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3318 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3320 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3321 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3322 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3323 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3324 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3325 if (!unix_cp)
3326 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3327 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3329 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3330 ansi_cptable = wine_cp_get_table( 1252 );
3331 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3332 oem_cptable = wine_cp_get_table( 437 );
3333 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3334 mac_cptable = wine_cp_get_table( 10000 );
3335 if (unix_cp != CP_UTF8)
3337 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3338 unix_cptable = wine_cp_get_table( 28591 );
3341 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3343 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3344 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3345 mac_cptable->info.codepage, unix_cp );
3347 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3350 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3352 UNICODE_STRING keyName;
3353 OBJECT_ATTRIBUTES attr;
3354 HANDLE hkey;
3356 RtlInitUnicodeString( &keyName, szKeyName );
3357 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3359 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3360 hkey = 0;
3362 return hkey;
3365 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3366 ULONG keyNameSize)
3368 BYTE buffer[80];
3369 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3370 DWORD dwLen;
3372 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3373 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3374 info->NameLength > keyNameSize)
3376 return FALSE;
3379 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3381 memcpy( szKeyName, info->Name, info->NameLength);
3382 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3384 TRACE("returning %s\n", debugstr_w(szKeyName));
3385 return TRUE;
3388 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3389 LPWSTR szValueName, ULONG valueNameSize,
3390 LPWSTR szValueData, ULONG valueDataSize)
3392 BYTE buffer[80];
3393 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3394 DWORD dwLen;
3396 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3397 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3398 info->NameLength > valueNameSize ||
3399 info->DataLength > valueDataSize)
3401 return FALSE;
3404 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3406 memcpy( szValueName, info->Name, info->NameLength);
3407 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3408 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3409 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3411 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3412 return TRUE;
3415 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3417 BYTE buffer[128];
3418 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3419 DWORD dwSize = sizeof(buffer);
3420 UNICODE_STRING valueName;
3422 RtlInitUnicodeString( &valueName, szValueName );
3424 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3425 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3426 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3427 info->DataLength == sizeof(DWORD))
3429 memcpy(lpVal, info->Data, sizeof(DWORD));
3430 return TRUE;
3433 return FALSE;
3436 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3438 LANGID langId;
3439 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3440 HRSRC hResource;
3441 BOOL bRet = FALSE;
3443 /* FIXME: Is it correct to use the system default langid? */
3444 langId = GetSystemDefaultLangID();
3446 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3447 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3449 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3451 if (hResource)
3453 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3455 if (hResDir)
3457 ULONG iResourceIndex = lgrpid & 0xf;
3458 LPCWSTR lpResEntry = LockResource( hResDir );
3459 ULONG i;
3461 for (i = 0; i < iResourceIndex; i++)
3462 lpResEntry += *lpResEntry + 1;
3464 if (*lpResEntry < nameSize)
3466 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3467 szName[*lpResEntry] = '\0';
3468 bRet = TRUE;
3472 FreeResource( hResource );
3474 return bRet;
3477 /* Registry keys for NLS related information */
3479 static const WCHAR szCountryListName[] = {
3480 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3481 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3482 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3483 'T','e','l','e','p','h','o','n','y','\\',
3484 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3488 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3489 typedef struct
3491 LANGUAGEGROUP_ENUMPROCA procA;
3492 LANGUAGEGROUP_ENUMPROCW procW;
3493 DWORD dwFlags;
3494 LONG_PTR lParam;
3495 } ENUMLANGUAGEGROUP_CALLBACKS;
3497 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3498 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3500 WCHAR szNumber[10], szValue[4];
3501 HANDLE hKey;
3502 BOOL bContinue = TRUE;
3503 ULONG ulIndex = 0;
3505 if (!lpProcs)
3507 SetLastError(ERROR_INVALID_PARAMETER);
3508 return FALSE;
3511 switch (lpProcs->dwFlags)
3513 case 0:
3514 /* Default to LGRPID_INSTALLED */
3515 lpProcs->dwFlags = LGRPID_INSTALLED;
3516 /* Fall through... */
3517 case LGRPID_INSTALLED:
3518 case LGRPID_SUPPORTED:
3519 break;
3520 default:
3521 SetLastError(ERROR_INVALID_FLAGS);
3522 return FALSE;
3525 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3527 if (!hKey)
3528 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3530 while (bContinue)
3532 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3533 szValue, sizeof(szValue) ))
3535 BOOL bInstalled = szValue[0] == '1';
3536 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3538 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3539 bInstalled ? "" : "not ");
3541 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3543 WCHAR szGrpName[48];
3545 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3546 szGrpName[0] = '\0';
3548 if (lpProcs->procW)
3549 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3550 lpProcs->lParam );
3551 else
3553 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3554 char szGrpNameA[48];
3556 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3557 * or whether the language names are ever localised. Assume CP_ACP.
3560 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3561 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3563 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3564 lpProcs->lParam );
3568 ulIndex++;
3570 else
3571 bContinue = FALSE;
3573 if (!bContinue)
3574 break;
3577 if (hKey)
3578 NtClose( hKey );
3580 return TRUE;
3583 /******************************************************************************
3584 * EnumSystemLanguageGroupsA (KERNEL32.@)
3586 * Call a users function for each language group available on the system.
3588 * PARAMS
3589 * pLangGrpEnumProc [I] Callback function to call for each language group
3590 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3591 * lParam [I] User parameter to pass to pLangGrpEnumProc
3593 * RETURNS
3594 * Success: TRUE.
3595 * Failure: FALSE. Use GetLastError() to determine the cause.
3597 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3598 DWORD dwFlags, LONG_PTR lParam)
3600 ENUMLANGUAGEGROUP_CALLBACKS procs;
3602 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3604 procs.procA = pLangGrpEnumProc;
3605 procs.procW = NULL;
3606 procs.dwFlags = dwFlags;
3607 procs.lParam = lParam;
3609 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3612 /******************************************************************************
3613 * EnumSystemLanguageGroupsW (KERNEL32.@)
3615 * See EnumSystemLanguageGroupsA.
3617 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3618 DWORD dwFlags, LONG_PTR lParam)
3620 ENUMLANGUAGEGROUP_CALLBACKS procs;
3622 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3624 procs.procA = NULL;
3625 procs.procW = pLangGrpEnumProc;
3626 procs.dwFlags = dwFlags;
3627 procs.lParam = lParam;
3629 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3632 /******************************************************************************
3633 * IsValidLanguageGroup (KERNEL32.@)
3635 * Determine if a language group is supported and/or installed.
3637 * PARAMS
3638 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3639 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3641 * RETURNS
3642 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3643 * FALSE otherwise.
3645 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3647 static const WCHAR szFormat[] = { '%','x','\0' };
3648 WCHAR szValueName[16], szValue[2];
3649 BOOL bSupported = FALSE, bInstalled = FALSE;
3650 HANDLE hKey;
3653 switch (dwFlags)
3655 case LGRPID_INSTALLED:
3656 case LGRPID_SUPPORTED:
3658 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3660 sprintfW( szValueName, szFormat, lgrpid );
3662 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3664 bSupported = TRUE;
3666 if (szValue[0] == '1')
3667 bInstalled = TRUE;
3670 if (hKey)
3671 NtClose( hKey );
3673 break;
3676 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3677 (dwFlags == LGRPID_INSTALLED && bInstalled))
3678 return TRUE;
3680 return FALSE;
3683 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3684 typedef struct
3686 LANGGROUPLOCALE_ENUMPROCA procA;
3687 LANGGROUPLOCALE_ENUMPROCW procW;
3688 DWORD dwFlags;
3689 LGRPID lgrpid;
3690 LONG_PTR lParam;
3691 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3693 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3694 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3696 static const WCHAR szAlternateSortsKeyName[] = {
3697 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3699 WCHAR szNumber[10], szValue[4];
3700 HANDLE hKey;
3701 BOOL bContinue = TRUE, bAlternate = FALSE;
3702 LGRPID lgrpid;
3703 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3705 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3707 SetLastError(ERROR_INVALID_PARAMETER);
3708 return FALSE;
3711 if (lpProcs->dwFlags)
3713 SetLastError(ERROR_INVALID_FLAGS);
3714 return FALSE;
3717 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3719 if (!hKey)
3720 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3722 while (bContinue)
3724 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3725 szValue, sizeof(szValue) ))
3727 lgrpid = strtoulW( szValue, NULL, 16 );
3729 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3730 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3732 if (lgrpid == lpProcs->lgrpid)
3734 LCID lcid;
3736 lcid = strtoulW( szNumber, NULL, 16 );
3738 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3739 * '00000437 ;Georgian'
3740 * At present we only pass the LCID string.
3743 if (lpProcs->procW)
3744 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3745 else
3747 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3749 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3751 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3755 ulIndex++;
3757 else
3759 /* Finished enumerating this key */
3760 if (!bAlternate)
3762 /* Enumerate alternate sorts also */
3763 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3764 bAlternate = TRUE;
3765 ulIndex = 0;
3767 else
3768 bContinue = FALSE; /* Finished both keys */
3771 if (!bContinue)
3772 break;
3775 if (hKey)
3776 NtClose( hKey );
3778 return TRUE;
3781 /******************************************************************************
3782 * EnumLanguageGroupLocalesA (KERNEL32.@)
3784 * Call a users function for every locale in a language group available on the system.
3786 * PARAMS
3787 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3788 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3789 * dwFlags [I] Reserved, set to 0
3790 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3792 * RETURNS
3793 * Success: TRUE.
3794 * Failure: FALSE. Use GetLastError() to determine the cause.
3796 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3797 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3799 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3801 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3803 callbacks.procA = pLangGrpLcEnumProc;
3804 callbacks.procW = NULL;
3805 callbacks.dwFlags = dwFlags;
3806 callbacks.lgrpid = lgrpid;
3807 callbacks.lParam = lParam;
3809 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3812 /******************************************************************************
3813 * EnumLanguageGroupLocalesW (KERNEL32.@)
3815 * See EnumLanguageGroupLocalesA.
3817 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3818 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3820 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3822 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3824 callbacks.procA = NULL;
3825 callbacks.procW = pLangGrpLcEnumProc;
3826 callbacks.dwFlags = dwFlags;
3827 callbacks.lgrpid = lgrpid;
3828 callbacks.lParam = lParam;
3830 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3833 /******************************************************************************
3834 * EnumSystemGeoID (KERNEL32.@)
3836 * Call a users function for every location available on the system.
3838 * PARAMS
3839 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3840 * reserved [I] Reserved, set to 0
3841 * pGeoEnumProc [I] Callback function to call for each location
3843 * RETURNS
3844 * Success: TRUE.
3845 * Failure: FALSE. Use GetLastError() to determine the cause.
3847 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3849 static const WCHAR szCountryCodeValueName[] = {
3850 'C','o','u','n','t','r','y','C','o','d','e','\0'
3852 WCHAR szNumber[10];
3853 HANDLE hKey;
3854 ULONG ulIndex = 0;
3856 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3858 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3860 SetLastError(ERROR_INVALID_PARAMETER);
3861 return FALSE;
3864 hKey = NLS_RegOpenKey( 0, szCountryListName );
3866 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3868 BOOL bContinue = TRUE;
3869 DWORD dwGeoId;
3870 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3872 if (hSubKey)
3874 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3876 TRACE("Got geoid %d\n", dwGeoId);
3878 if (!pGeoEnumProc( dwGeoId ))
3879 bContinue = FALSE;
3882 NtClose( hSubKey );
3885 if (!bContinue)
3886 break;
3888 ulIndex++;
3891 if (hKey)
3892 NtClose( hKey );
3894 return TRUE;
3897 /******************************************************************************
3898 * InvalidateNLSCache (KERNEL32.@)
3900 * Invalidate the cache of NLS values.
3902 * PARAMS
3903 * None.
3905 * RETURNS
3906 * Success: TRUE.
3907 * Failure: FALSE.
3909 BOOL WINAPI InvalidateNLSCache(void)
3911 FIXME("() stub\n");
3912 return FALSE;
3915 /******************************************************************************
3916 * GetUserGeoID (KERNEL32.@)
3918 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3920 GEOID ret = GEOID_NOT_AVAILABLE;
3921 static const WCHAR geoW[] = {'G','e','o',0};
3922 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3923 WCHAR bufferW[40], *end;
3924 DWORD count;
3925 HANDLE hkey, hSubkey = 0;
3926 UNICODE_STRING keyW;
3927 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3928 RtlInitUnicodeString( &keyW, nationW );
3929 count = sizeof(bufferW);
3931 if(!(hkey = create_registry_key())) return ret;
3933 switch( GeoClass ){
3934 case GEOCLASS_NATION:
3935 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3937 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3938 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3939 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3941 break;
3942 case GEOCLASS_REGION:
3943 FIXME("GEOCLASS_REGION not handled yet\n");
3944 break;
3947 NtClose(hkey);
3948 if (hSubkey) NtClose(hSubkey);
3949 return ret;
3952 /******************************************************************************
3953 * SetUserGeoID (KERNEL32.@)
3955 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3957 static const WCHAR geoW[] = {'G','e','o',0};
3958 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3959 static const WCHAR formatW[] = {'%','i',0};
3960 UNICODE_STRING nameW,keyW;
3961 WCHAR bufferW[10];
3962 OBJECT_ATTRIBUTES attr;
3963 HANDLE hkey;
3965 if(!(hkey = create_registry_key())) return FALSE;
3967 attr.Length = sizeof(attr);
3968 attr.RootDirectory = hkey;
3969 attr.ObjectName = &nameW;
3970 attr.Attributes = 0;
3971 attr.SecurityDescriptor = NULL;
3972 attr.SecurityQualityOfService = NULL;
3973 RtlInitUnicodeString( &nameW, geoW );
3974 RtlInitUnicodeString( &keyW, nationW );
3976 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3979 NtClose(attr.RootDirectory);
3980 return FALSE;
3983 sprintfW(bufferW, formatW, GeoID);
3984 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3985 NtClose(attr.RootDirectory);
3986 NtClose(hkey);
3987 return TRUE;
3990 typedef struct
3992 union
3994 UILANGUAGE_ENUMPROCA procA;
3995 UILANGUAGE_ENUMPROCW procW;
3996 } u;
3997 DWORD flags;
3998 LONG_PTR param;
3999 } ENUM_UILANG_CALLBACK;
4001 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4002 LPCSTR name, WORD LangID, LONG_PTR lParam )
4004 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4005 char buf[20];
4007 sprintf(buf, "%08x", (UINT)LangID);
4008 return enum_uilang->u.procA( buf, enum_uilang->param );
4011 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4012 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4014 static const WCHAR formatW[] = {'%','0','8','x',0};
4015 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4016 WCHAR buf[20];
4018 sprintfW( buf, formatW, (UINT)LangID );
4019 return enum_uilang->u.procW( buf, enum_uilang->param );
4022 /******************************************************************************
4023 * EnumUILanguagesA (KERNEL32.@)
4025 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4027 ENUM_UILANG_CALLBACK enum_uilang;
4029 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4031 if(!pUILangEnumProc) {
4032 SetLastError(ERROR_INVALID_PARAMETER);
4033 return FALSE;
4035 if(dwFlags) {
4036 SetLastError(ERROR_INVALID_FLAGS);
4037 return FALSE;
4040 enum_uilang.u.procA = pUILangEnumProc;
4041 enum_uilang.flags = dwFlags;
4042 enum_uilang.param = lParam;
4044 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4045 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4046 (LONG_PTR)&enum_uilang);
4047 return TRUE;
4050 /******************************************************************************
4051 * EnumUILanguagesW (KERNEL32.@)
4053 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4055 ENUM_UILANG_CALLBACK enum_uilang;
4057 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4060 if(!pUILangEnumProc) {
4061 SetLastError(ERROR_INVALID_PARAMETER);
4062 return FALSE;
4064 if(dwFlags) {
4065 SetLastError(ERROR_INVALID_FLAGS);
4066 return FALSE;
4069 enum_uilang.u.procW = pUILangEnumProc;
4070 enum_uilang.flags = dwFlags;
4071 enum_uilang.param = lParam;
4073 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4074 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4075 (LONG_PTR)&enum_uilang);
4076 return TRUE;
4079 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
4080 int cchData, LANGID language)
4082 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
4083 return 0;
4086 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
4087 int cchData, LANGID language)
4089 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
4090 return 0;
4093 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4095 LCID userlcid;
4097 TRACE("%p, %d\n", localename, buffersize);
4099 userlcid = GetUserDefaultLCID();
4100 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4103 /******************************************************************************
4104 * NormalizeString (KERNEL32.@)
4106 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4107 LPWSTR lpDstString, INT cwDstLength)
4109 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4110 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4111 return 0;
4114 /******************************************************************************
4115 * IsNormalizedString (KERNEL32.@)
4117 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4119 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4120 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4121 return FALSE;
4124 enum {
4125 BASE = 36,
4126 TMIN = 1,
4127 TMAX = 26,
4128 SKEW = 38,
4129 DAMP = 700,
4130 INIT_BIAS = 72,
4131 INIT_N = 128
4134 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4136 INT k;
4138 delta /= (firsttime ? DAMP : 2);
4139 delta += delta/numpoints;
4141 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4142 delta /= BASE-TMIN;
4143 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4146 /******************************************************************************
4147 * IdnToAscii (KERNEL32.@)
4148 * Implementation of Punycode based on RFC 3492.
4150 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4151 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4153 static const WCHAR prefixW[] = {'x','n','-','-'};
4155 WCHAR *norm_str;
4156 INT i, label_start, label_end, norm_len, out_label, out = 0;
4158 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4159 lpASCIICharStr, cchASCIIChar);
4161 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4162 if(!norm_len)
4163 return 0;
4164 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4165 if(!norm_str) {
4166 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4167 return 0;
4169 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4170 cchUnicodeChar, norm_str, norm_len);
4171 if(!norm_len) {
4172 HeapFree(GetProcessHeap(), 0, norm_str);
4173 return 0;
4176 for(label_start=0; label_start<norm_len;) {
4177 INT n = INIT_N, bias = INIT_BIAS;
4178 INT delta = 0, b = 0, h;
4180 out_label = out;
4181 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4182 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4183 if(norm_str[i] < 0x80)
4184 b++;
4185 label_end = i;
4187 if(b == label_end-label_start) {
4188 if(label_end < norm_len)
4189 b++;
4190 if(!lpASCIICharStr) {
4191 out += b;
4192 }else if(out+b <= cchASCIIChar) {
4193 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4194 out += b;
4195 }else {
4196 HeapFree(GetProcessHeap(), 0, norm_str);
4197 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4198 return 0;
4200 label_start = label_end+1;
4201 continue;
4204 if(!lpASCIICharStr) {
4205 out += 5+b; /* strlen(xn--...-) */
4206 }else if(out+5+b <= cchASCIIChar) {
4207 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4208 out += 4;
4209 for(i=label_start; i<label_end; i++)
4210 if(norm_str[i] < 0x80)
4211 lpASCIICharStr[out++] = norm_str[i];
4212 lpASCIICharStr[out++] = '-';
4213 }else {
4214 HeapFree(GetProcessHeap(), 0, norm_str);
4215 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4216 return 0;
4218 if(!b)
4219 out--;
4221 for(h=b; h<label_end-label_start;) {
4222 INT m = 0xffff, q, k;
4224 for(i=label_start; i<label_end; i++) {
4225 if(norm_str[i]>=n && m>norm_str[i])
4226 m = norm_str[i];
4228 delta += (m-n)*(h+1);
4229 n = m;
4231 for(i=label_start; i<label_end; i++) {
4232 if(norm_str[i] < n) {
4233 delta++;
4234 }else if(norm_str[i] == n) {
4235 for(q=delta, k=BASE; ; k+=BASE) {
4236 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4237 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4238 if(!lpASCIICharStr) {
4239 out++;
4240 }else if(out+1 <= cchASCIIChar) {
4241 lpASCIICharStr[out++] = disp<='z'-'a' ?
4242 'a'+disp : '0'+disp-'z'+'a'-1;
4243 }else {
4244 HeapFree(GetProcessHeap(), 0, norm_str);
4245 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4246 return 0;
4248 if(q < t)
4249 break;
4250 q = (q-t)/(BASE-t);
4252 bias = adapt(delta, h+1, h==b);
4253 delta = 0;
4254 h++;
4257 delta++;
4258 n++;
4261 if(out-out_label > 63) {
4262 HeapFree(GetProcessHeap(), 0, norm_str);
4263 SetLastError(ERROR_INVALID_NAME);
4264 return 0;
4267 if(label_end < norm_len) {
4268 if(!lpASCIICharStr) {
4269 out++;
4270 }else if(out+1 <= cchASCIIChar) {
4271 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4272 }else {
4273 HeapFree(GetProcessHeap(), 0, norm_str);
4274 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4275 return 0;
4278 label_start = label_end+1;
4281 HeapFree(GetProcessHeap(), 0, norm_str);
4282 return out;
4285 /******************************************************************************
4286 * IdnToNameprepUnicode (KERNEL32.@)
4288 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4289 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4291 enum {
4292 UNASSIGNED = 0x1,
4293 PROHIBITED = 0x2,
4294 BIDI_RAL = 0x4,
4295 BIDI_L = 0x8
4298 extern const unsigned short nameprep_char_type[];
4299 extern const WCHAR nameprep_mapping[];
4300 const WCHAR *ptr;
4301 WORD flags;
4302 WCHAR buf[64], *map_str, norm_str[64], ch;
4303 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4304 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4306 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4307 lpNameprepCharStr, cchNameprepChar);
4309 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4310 SetLastError(ERROR_INVALID_FLAGS);
4311 return 0;
4314 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4315 SetLastError(ERROR_INVALID_PARAMETER);
4316 return 0;
4319 if(cchUnicodeChar == -1)
4320 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4321 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4322 SetLastError(ERROR_INVALID_NAME);
4323 return 0;
4326 for(label_start=0; label_start<cchUnicodeChar;) {
4327 ascii_only = TRUE;
4328 for(i=label_start; i<cchUnicodeChar; i++) {
4329 ch = lpUnicodeCharStr[i];
4331 if(i!=cchUnicodeChar-1 && !ch) {
4332 SetLastError(ERROR_INVALID_NAME);
4333 return 0;
4335 /* check if ch is one of label separators defined in RFC3490 */
4336 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4337 break;
4339 if(ch > 0x7f) {
4340 ascii_only = FALSE;
4341 continue;
4344 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4345 continue;
4346 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4347 || (ch>='0' && ch<='9') || ch=='-')
4348 continue;
4350 SetLastError(ERROR_INVALID_NAME);
4351 return 0;
4353 label_end = i;
4354 /* last label may be empty */
4355 if(label_start==label_end && ch) {
4356 SetLastError(ERROR_INVALID_NAME);
4357 return 0;
4360 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4361 lpUnicodeCharStr[label_end-1]=='-')) {
4362 SetLastError(ERROR_INVALID_NAME);
4363 return 0;
4366 if(ascii_only) {
4367 /* maximal label length is 63 characters */
4368 if(label_end-label_start > 63) {
4369 SetLastError(ERROR_INVALID_NAME);
4370 return 0;
4372 if(label_end < cchUnicodeChar)
4373 label_end++;
4375 if(!lpNameprepCharStr) {
4376 out += label_end-label_start;
4377 }else if(out+label_end-label_start <= cchNameprepChar) {
4378 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4379 (label_end-label_start)*sizeof(WCHAR));
4380 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4381 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4382 out += label_end-label_start;
4383 }else {
4384 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4385 return 0;
4388 label_start = label_end;
4389 continue;
4392 map_len = 0;
4393 for(i=label_start; i<label_end; i++) {
4394 ch = lpUnicodeCharStr[i];
4395 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4396 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4398 if(!ptr[0]) map_len++;
4399 else if(!ptr[1]) map_len++;
4400 else if(!ptr[2]) map_len += 2;
4401 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4403 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4404 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4405 if(!map_str) {
4406 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4407 return 0;
4409 }else {
4410 map_str = buf;
4412 map_len = 0;
4413 for(i=label_start; i<label_end; i++) {
4414 ch = lpUnicodeCharStr[i];
4415 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4416 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4418 if(!ptr[0]) {
4419 map_str[map_len++] = ch;
4420 }else if(!ptr[1]) {
4421 map_str[map_len++] = ptr[0];
4422 }else if(!ptr[2]) {
4423 map_str[map_len++] = ptr[0];
4424 map_str[map_len++] = ptr[1];
4425 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4426 map_str[map_len++] = ptr[0];
4427 map_str[map_len++] = ptr[1];
4428 map_str[map_len++] = ptr[2];
4432 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4433 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4434 if(map_str != buf)
4435 HeapFree(GetProcessHeap(), 0, map_str);
4436 if(!norm_len) {
4437 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4438 SetLastError(ERROR_INVALID_NAME);
4439 return 0;
4442 if(label_end < cchUnicodeChar) {
4443 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4444 label_end++;
4447 if(!lpNameprepCharStr) {
4448 out += norm_len;
4449 }else if(out+norm_len <= cchNameprepChar) {
4450 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4451 out += norm_len;
4452 }else {
4453 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4454 return 0;
4457 have_bidi_ral = prohibit_bidi_ral = FALSE;
4458 mask = PROHIBITED;
4459 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4460 mask |= UNASSIGNED;
4461 for(i=0; i<norm_len; i++) {
4462 ch = norm_str[i];
4463 flags = get_table_entry( nameprep_char_type, ch );
4465 if(flags & mask) {
4466 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4467 : ERROR_NO_UNICODE_TRANSLATION);
4468 return 0;
4471 if(flags & BIDI_RAL)
4472 have_bidi_ral = TRUE;
4473 if(flags & BIDI_L)
4474 prohibit_bidi_ral = TRUE;
4477 if(have_bidi_ral) {
4478 ch = norm_str[0];
4479 flags = get_table_entry( nameprep_char_type, ch );
4480 if((flags & BIDI_RAL) == 0)
4481 prohibit_bidi_ral = TRUE;
4483 ch = norm_str[norm_len-1];
4484 flags = get_table_entry( nameprep_char_type, ch );
4485 if((flags & BIDI_RAL) == 0)
4486 prohibit_bidi_ral = TRUE;
4489 if(have_bidi_ral && prohibit_bidi_ral) {
4490 SetLastError(ERROR_INVALID_NAME);
4491 return 0;
4494 label_start = label_end;
4497 return out;
4500 /******************************************************************************
4501 * IdnToUnicode (KERNEL32.@)
4503 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4504 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4506 extern const unsigned short nameprep_char_type[];
4508 INT i, label_start, label_end, out_label, out = 0;
4509 WCHAR ch;
4511 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4512 lpUnicodeCharStr, cchUnicodeChar);
4514 for(label_start=0; label_start<cchASCIIChar;) {
4515 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4517 out_label = out;
4518 for(i=label_start; i<cchASCIIChar; i++) {
4519 ch = lpASCIICharStr[i];
4521 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4522 SetLastError(ERROR_INVALID_NAME);
4523 return 0;
4526 if(!ch || ch=='.')
4527 break;
4528 if(ch == '-')
4529 delim = i;
4531 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4532 continue;
4533 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4534 || (ch>='0' && ch<='9') || ch=='-')
4535 continue;
4537 SetLastError(ERROR_INVALID_NAME);
4538 return 0;
4540 label_end = i;
4541 /* last label may be empty */
4542 if(label_start==label_end && ch) {
4543 SetLastError(ERROR_INVALID_NAME);
4544 return 0;
4547 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
4548 lpASCIICharStr[label_end-1]=='-')) {
4549 SetLastError(ERROR_INVALID_NAME);
4550 return 0;
4552 if(label_end-label_start > 63) {
4553 SetLastError(ERROR_INVALID_NAME);
4554 return 0;
4557 if(label_end-label_start<4 ||
4558 tolowerW(lpASCIICharStr[label_start])!='x' ||
4559 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4560 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4561 if(label_end < cchASCIIChar)
4562 label_end++;
4564 if(!lpUnicodeCharStr) {
4565 out += label_end-label_start;
4566 }else if(out+label_end-label_start <= cchUnicodeChar) {
4567 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4568 (label_end-label_start)*sizeof(WCHAR));
4569 out += label_end-label_start;
4570 }else {
4571 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4572 return 0;
4575 label_start = label_end;
4576 continue;
4579 if(delim == label_start+3)
4580 delim++;
4581 if(!lpUnicodeCharStr) {
4582 out += delim-label_start-4;
4583 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4584 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4585 (delim-label_start-4)*sizeof(WCHAR));
4586 out += delim-label_start-4;
4587 }else {
4588 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4589 return 0;
4591 if(out != out_label)
4592 delim++;
4594 for(i=delim; i<label_end;) {
4595 old_pos = pos;
4596 w = 1;
4597 for(k=BASE; ; k+=BASE) {
4598 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4599 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4600 SetLastError(ERROR_INVALID_NAME);
4601 return 0;
4603 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4604 pos += digit*w;
4605 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4606 if(digit < t)
4607 break;
4608 w *= BASE-t;
4610 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4611 n += pos/(out-out_label+1);
4612 pos %= out-out_label+1;
4614 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4615 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4616 SetLastError(ERROR_INVALID_NAME);
4617 return 0;
4619 if(!lpUnicodeCharStr) {
4620 out++;
4621 }else if(out+1 <= cchASCIIChar) {
4622 memmove(lpUnicodeCharStr+out_label+pos+1,
4623 lpUnicodeCharStr+out_label+pos,
4624 (out-out_label-pos)*sizeof(WCHAR));
4625 lpUnicodeCharStr[out_label+pos] = n;
4626 out++;
4627 }else {
4628 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4629 return 0;
4631 pos++;
4634 if(out-out_label > 63) {
4635 SetLastError(ERROR_INVALID_NAME);
4636 return 0;
4639 if(label_end < cchASCIIChar) {
4640 if(!lpUnicodeCharStr) {
4641 out++;
4642 }else if(out+1 <= cchUnicodeChar) {
4643 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4644 }else {
4645 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4646 return 0;
4649 label_start = label_end+1;
4652 return out;