kernel32: Fix typo in enum_locale_ex_proc.
[wine.git] / dlls / kernel32 / locale.c
blob26c00f55f6631837fa2837f1a892ff638a392874
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 DWORD supported_flags = NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|SORT_STRINGSORT
2989 |NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP;
2990 DWORD semistub_flags = NORM_LINGUISTIC_CASING|LINGUISTIC_IGNORECASE|0x10000000;
2991 /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */
2992 INT ret;
2994 if (version) FIXME("unexpected version parameter\n");
2995 if (reserved) FIXME("unexpected reserved value\n");
2996 if (lParam) FIXME("unexpected lParam\n");
2998 if (!str1 || !str2)
3000 SetLastError(ERROR_INVALID_PARAMETER);
3001 return 0;
3004 if (flags & ~(supported_flags|semistub_flags))
3006 SetLastError(ERROR_INVALID_FLAGS);
3007 return 0;
3010 if (flags & semistub_flags)
3011 FIXME("semi-stub behavor for flag(s) 0x%x\n", flags & semistub_flags);
3013 if (len1 < 0) len1 = strlenW(str1);
3014 if (len2 < 0) len2 = strlenW(str2);
3016 ret = wine_compare_string(flags, str1, len1, str2, len2);
3018 if (ret) /* need to translate result */
3019 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
3020 return CSTR_EQUAL;
3023 /******************************************************************************
3024 * CompareStringA (KERNEL32.@)
3026 * Compare two locale sensitive strings.
3028 * PARAMS
3029 * lcid [I] LCID for the comparison
3030 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
3031 * str1 [I] First string to compare
3032 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
3033 * str2 [I] Second string to compare
3034 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
3036 * RETURNS
3037 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
3038 * str1 is less than, equal to or greater than str2 respectively.
3039 * Failure: FALSE. Use GetLastError() to determine the cause.
3041 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
3042 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
3044 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
3045 WCHAR *buf2W = buf1W + 130;
3046 LPWSTR str1W, str2W;
3047 INT len1W = 0, len2W = 0, ret;
3048 UINT locale_cp = CP_ACP;
3050 if (!str1 || !str2)
3052 SetLastError(ERROR_INVALID_PARAMETER);
3053 return 0;
3055 if (len1 < 0) len1 = strlen(str1);
3056 if (len2 < 0) len2 = strlen(str2);
3058 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
3060 if (len1)
3062 if (len1 <= 130) len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
3063 if (len1W)
3064 str1W = buf1W;
3065 else
3067 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3068 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3069 if (!str1W)
3071 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3072 return 0;
3074 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3077 else
3079 len1W = 0;
3080 str1W = buf1W;
3083 if (len2)
3085 if (len2 <= 130) len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3086 if (len2W)
3087 str2W = buf2W;
3088 else
3090 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3091 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3092 if (!str2W)
3094 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3095 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3096 return 0;
3098 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3101 else
3103 len2W = 0;
3104 str2W = buf2W;
3107 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3109 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3110 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3111 return ret;
3114 /******************************************************************************
3115 * CompareStringOrdinal (KERNEL32.@)
3117 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3119 int ret, len;
3121 if (!str1 || !str2)
3123 SetLastError(ERROR_INVALID_PARAMETER);
3124 return 0;
3126 if (len1 < 0) len1 = strlenW(str1);
3127 if (len2 < 0) len2 = strlenW(str2);
3129 len = min(len1, len2);
3130 if (ignore_case)
3132 ret = memicmpW(str1, str2, len);
3134 else
3136 ret = 0;
3137 for (; len > 0; len--)
3138 if ((ret = (*str1++ - *str2++))) break;
3140 if (!ret) ret = len1 - len2;
3142 if (ret < 0) return CSTR_LESS_THAN;
3143 if (ret > 0) return CSTR_GREATER_THAN;
3144 return CSTR_EQUAL;
3147 /*************************************************************************
3148 * lstrcmp (KERNEL32.@)
3149 * lstrcmpA (KERNEL32.@)
3151 * Compare two strings using the current thread locale.
3153 * PARAMS
3154 * str1 [I] First string to compare
3155 * str2 [I] Second string to compare
3157 * RETURNS
3158 * Success: A number less than, equal to or greater than 0 depending on whether
3159 * str1 is less than, equal to or greater than str2 respectively.
3160 * Failure: FALSE. Use GetLastError() to determine the cause.
3162 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3164 int ret;
3166 if ((str1 == NULL) && (str2 == NULL)) return 0;
3167 if (str1 == NULL) return -1;
3168 if (str2 == NULL) return 1;
3170 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3171 if (ret) ret -= 2;
3173 return ret;
3176 /*************************************************************************
3177 * lstrcmpi (KERNEL32.@)
3178 * lstrcmpiA (KERNEL32.@)
3180 * Compare two strings using the current thread locale, ignoring case.
3182 * PARAMS
3183 * str1 [I] First string to compare
3184 * str2 [I] Second string to compare
3186 * RETURNS
3187 * Success: A number less than, equal to or greater than 0 depending on whether
3188 * str2 is less than, equal to or greater than str1 respectively.
3189 * Failure: FALSE. Use GetLastError() to determine the cause.
3191 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3193 int ret;
3195 if ((str1 == NULL) && (str2 == NULL)) return 0;
3196 if (str1 == NULL) return -1;
3197 if (str2 == NULL) return 1;
3199 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3200 if (ret) ret -= 2;
3202 return ret;
3205 /*************************************************************************
3206 * lstrcmpW (KERNEL32.@)
3208 * See lstrcmpA.
3210 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3212 int ret;
3214 if ((str1 == NULL) && (str2 == NULL)) return 0;
3215 if (str1 == NULL) return -1;
3216 if (str2 == NULL) return 1;
3218 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3219 if (ret) ret -= 2;
3221 return ret;
3224 /*************************************************************************
3225 * lstrcmpiW (KERNEL32.@)
3227 * See lstrcmpiA.
3229 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3231 int ret;
3233 if ((str1 == NULL) && (str2 == NULL)) return 0;
3234 if (str1 == NULL) return -1;
3235 if (str2 == NULL) return 1;
3237 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3238 if (ret) ret -= 2;
3240 return ret;
3243 /******************************************************************************
3244 * LOCALE_Init
3246 void LOCALE_Init(void)
3248 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3249 const union cptable *unix_cp );
3251 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3253 #ifdef __APPLE__
3254 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3255 char user_locale[50];
3257 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3258 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3259 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3260 CFStringRef user_locale_string_ref;
3262 if (user_locale_country_ref)
3264 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3265 user_locale_lang_ref, user_locale_country_ref);
3267 else
3269 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3272 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3273 strcat(user_locale, ".UTF-8");
3275 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3276 setenv( "LANG", user_locale, 0 );
3277 TRACE( "setting locale to '%s'\n", user_locale );
3278 #endif /* __APPLE__ */
3280 setlocale( LC_ALL, "" );
3282 unix_cp = setup_unix_locales();
3283 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3285 #ifdef __APPLE__
3286 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3287 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3289 /* Retrieve the preferred language as chosen in System Preferences. */
3290 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3291 leave things be. */
3292 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3293 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3294 CFStringRef user_language_string_ref;
3295 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3296 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3297 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3298 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3300 struct locale_name locale_name;
3301 WCHAR buffer[128];
3302 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3303 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3304 parse_locale_name( buffer, &locale_name );
3305 lcid_LC_MESSAGES = locale_name.lcid;
3306 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3308 if (preferred_langs)
3309 CFRelease( preferred_langs );
3310 if (canonical_lang_string_ref)
3311 CFRelease( canonical_lang_string_ref );
3314 CFRelease( user_locale_ref );
3315 CFRelease( user_locale_string_ref );
3316 #endif
3318 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3319 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3320 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3322 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3323 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3324 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3325 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3326 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3327 if (!unix_cp)
3328 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3329 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3331 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3332 ansi_cptable = wine_cp_get_table( 1252 );
3333 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3334 oem_cptable = wine_cp_get_table( 437 );
3335 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3336 mac_cptable = wine_cp_get_table( 10000 );
3337 if (unix_cp != CP_UTF8)
3339 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3340 unix_cptable = wine_cp_get_table( 28591 );
3343 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3345 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3346 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3347 mac_cptable->info.codepage, unix_cp );
3349 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3352 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3354 UNICODE_STRING keyName;
3355 OBJECT_ATTRIBUTES attr;
3356 HANDLE hkey;
3358 RtlInitUnicodeString( &keyName, szKeyName );
3359 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3361 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3362 hkey = 0;
3364 return hkey;
3367 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3368 ULONG keyNameSize)
3370 BYTE buffer[80];
3371 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3372 DWORD dwLen;
3374 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3375 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3376 info->NameLength > keyNameSize)
3378 return FALSE;
3381 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3383 memcpy( szKeyName, info->Name, info->NameLength);
3384 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3386 TRACE("returning %s\n", debugstr_w(szKeyName));
3387 return TRUE;
3390 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3391 LPWSTR szValueName, ULONG valueNameSize,
3392 LPWSTR szValueData, ULONG valueDataSize)
3394 BYTE buffer[80];
3395 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3396 DWORD dwLen;
3398 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3399 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3400 info->NameLength > valueNameSize ||
3401 info->DataLength > valueDataSize)
3403 return FALSE;
3406 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3408 memcpy( szValueName, info->Name, info->NameLength);
3409 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3410 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3411 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3413 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3414 return TRUE;
3417 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3419 BYTE buffer[128];
3420 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3421 DWORD dwSize = sizeof(buffer);
3422 UNICODE_STRING valueName;
3424 RtlInitUnicodeString( &valueName, szValueName );
3426 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3427 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3428 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3429 info->DataLength == sizeof(DWORD))
3431 memcpy(lpVal, info->Data, sizeof(DWORD));
3432 return TRUE;
3435 return FALSE;
3438 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3440 LANGID langId;
3441 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3442 HRSRC hResource;
3443 BOOL bRet = FALSE;
3445 /* FIXME: Is it correct to use the system default langid? */
3446 langId = GetSystemDefaultLangID();
3448 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3449 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3451 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3453 if (hResource)
3455 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3457 if (hResDir)
3459 ULONG iResourceIndex = lgrpid & 0xf;
3460 LPCWSTR lpResEntry = LockResource( hResDir );
3461 ULONG i;
3463 for (i = 0; i < iResourceIndex; i++)
3464 lpResEntry += *lpResEntry + 1;
3466 if (*lpResEntry < nameSize)
3468 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3469 szName[*lpResEntry] = '\0';
3470 bRet = TRUE;
3474 FreeResource( hResource );
3476 return bRet;
3479 /* Registry keys for NLS related information */
3481 static const WCHAR szCountryListName[] = {
3482 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3483 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3484 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3485 'T','e','l','e','p','h','o','n','y','\\',
3486 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3490 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3491 typedef struct
3493 LANGUAGEGROUP_ENUMPROCA procA;
3494 LANGUAGEGROUP_ENUMPROCW procW;
3495 DWORD dwFlags;
3496 LONG_PTR lParam;
3497 } ENUMLANGUAGEGROUP_CALLBACKS;
3499 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3500 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3502 WCHAR szNumber[10], szValue[4];
3503 HANDLE hKey;
3504 BOOL bContinue = TRUE;
3505 ULONG ulIndex = 0;
3507 if (!lpProcs)
3509 SetLastError(ERROR_INVALID_PARAMETER);
3510 return FALSE;
3513 switch (lpProcs->dwFlags)
3515 case 0:
3516 /* Default to LGRPID_INSTALLED */
3517 lpProcs->dwFlags = LGRPID_INSTALLED;
3518 /* Fall through... */
3519 case LGRPID_INSTALLED:
3520 case LGRPID_SUPPORTED:
3521 break;
3522 default:
3523 SetLastError(ERROR_INVALID_FLAGS);
3524 return FALSE;
3527 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3529 if (!hKey)
3530 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3532 while (bContinue)
3534 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3535 szValue, sizeof(szValue) ))
3537 BOOL bInstalled = szValue[0] == '1';
3538 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3540 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3541 bInstalled ? "" : "not ");
3543 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3545 WCHAR szGrpName[48];
3547 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3548 szGrpName[0] = '\0';
3550 if (lpProcs->procW)
3551 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3552 lpProcs->lParam );
3553 else
3555 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3556 char szGrpNameA[48];
3558 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3559 * or whether the language names are ever localised. Assume CP_ACP.
3562 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3563 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3565 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3566 lpProcs->lParam );
3570 ulIndex++;
3572 else
3573 bContinue = FALSE;
3575 if (!bContinue)
3576 break;
3579 if (hKey)
3580 NtClose( hKey );
3582 return TRUE;
3585 /******************************************************************************
3586 * EnumSystemLanguageGroupsA (KERNEL32.@)
3588 * Call a users function for each language group available on the system.
3590 * PARAMS
3591 * pLangGrpEnumProc [I] Callback function to call for each language group
3592 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3593 * lParam [I] User parameter to pass to pLangGrpEnumProc
3595 * RETURNS
3596 * Success: TRUE.
3597 * Failure: FALSE. Use GetLastError() to determine the cause.
3599 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3600 DWORD dwFlags, LONG_PTR lParam)
3602 ENUMLANGUAGEGROUP_CALLBACKS procs;
3604 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3606 procs.procA = pLangGrpEnumProc;
3607 procs.procW = NULL;
3608 procs.dwFlags = dwFlags;
3609 procs.lParam = lParam;
3611 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3614 /******************************************************************************
3615 * EnumSystemLanguageGroupsW (KERNEL32.@)
3617 * See EnumSystemLanguageGroupsA.
3619 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3620 DWORD dwFlags, LONG_PTR lParam)
3622 ENUMLANGUAGEGROUP_CALLBACKS procs;
3624 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3626 procs.procA = NULL;
3627 procs.procW = pLangGrpEnumProc;
3628 procs.dwFlags = dwFlags;
3629 procs.lParam = lParam;
3631 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3634 /******************************************************************************
3635 * IsValidLanguageGroup (KERNEL32.@)
3637 * Determine if a language group is supported and/or installed.
3639 * PARAMS
3640 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3641 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3643 * RETURNS
3644 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3645 * FALSE otherwise.
3647 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3649 static const WCHAR szFormat[] = { '%','x','\0' };
3650 WCHAR szValueName[16], szValue[2];
3651 BOOL bSupported = FALSE, bInstalled = FALSE;
3652 HANDLE hKey;
3655 switch (dwFlags)
3657 case LGRPID_INSTALLED:
3658 case LGRPID_SUPPORTED:
3660 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3662 sprintfW( szValueName, szFormat, lgrpid );
3664 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3666 bSupported = TRUE;
3668 if (szValue[0] == '1')
3669 bInstalled = TRUE;
3672 if (hKey)
3673 NtClose( hKey );
3675 break;
3678 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3679 (dwFlags == LGRPID_INSTALLED && bInstalled))
3680 return TRUE;
3682 return FALSE;
3685 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3686 typedef struct
3688 LANGGROUPLOCALE_ENUMPROCA procA;
3689 LANGGROUPLOCALE_ENUMPROCW procW;
3690 DWORD dwFlags;
3691 LGRPID lgrpid;
3692 LONG_PTR lParam;
3693 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3695 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3696 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3698 static const WCHAR szAlternateSortsKeyName[] = {
3699 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3701 WCHAR szNumber[10], szValue[4];
3702 HANDLE hKey;
3703 BOOL bContinue = TRUE, bAlternate = FALSE;
3704 LGRPID lgrpid;
3705 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3707 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3709 SetLastError(ERROR_INVALID_PARAMETER);
3710 return FALSE;
3713 if (lpProcs->dwFlags)
3715 SetLastError(ERROR_INVALID_FLAGS);
3716 return FALSE;
3719 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3721 if (!hKey)
3722 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3724 while (bContinue)
3726 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3727 szValue, sizeof(szValue) ))
3729 lgrpid = strtoulW( szValue, NULL, 16 );
3731 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3732 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3734 if (lgrpid == lpProcs->lgrpid)
3736 LCID lcid;
3738 lcid = strtoulW( szNumber, NULL, 16 );
3740 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3741 * '00000437 ;Georgian'
3742 * At present we only pass the LCID string.
3745 if (lpProcs->procW)
3746 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3747 else
3749 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3751 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3753 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3757 ulIndex++;
3759 else
3761 /* Finished enumerating this key */
3762 if (!bAlternate)
3764 /* Enumerate alternate sorts also */
3765 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3766 bAlternate = TRUE;
3767 ulIndex = 0;
3769 else
3770 bContinue = FALSE; /* Finished both keys */
3773 if (!bContinue)
3774 break;
3777 if (hKey)
3778 NtClose( hKey );
3780 return TRUE;
3783 /******************************************************************************
3784 * EnumLanguageGroupLocalesA (KERNEL32.@)
3786 * Call a users function for every locale in a language group available on the system.
3788 * PARAMS
3789 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3790 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3791 * dwFlags [I] Reserved, set to 0
3792 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3794 * RETURNS
3795 * Success: TRUE.
3796 * Failure: FALSE. Use GetLastError() to determine the cause.
3798 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3799 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3801 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3803 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3805 callbacks.procA = pLangGrpLcEnumProc;
3806 callbacks.procW = NULL;
3807 callbacks.dwFlags = dwFlags;
3808 callbacks.lgrpid = lgrpid;
3809 callbacks.lParam = lParam;
3811 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3814 /******************************************************************************
3815 * EnumLanguageGroupLocalesW (KERNEL32.@)
3817 * See EnumLanguageGroupLocalesA.
3819 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3820 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3822 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3824 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3826 callbacks.procA = NULL;
3827 callbacks.procW = pLangGrpLcEnumProc;
3828 callbacks.dwFlags = dwFlags;
3829 callbacks.lgrpid = lgrpid;
3830 callbacks.lParam = lParam;
3832 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3835 /******************************************************************************
3836 * EnumSystemGeoID (KERNEL32.@)
3838 * Call a users function for every location available on the system.
3840 * PARAMS
3841 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3842 * reserved [I] Reserved, set to 0
3843 * pGeoEnumProc [I] Callback function to call for each location
3845 * RETURNS
3846 * Success: TRUE.
3847 * Failure: FALSE. Use GetLastError() to determine the cause.
3849 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3851 static const WCHAR szCountryCodeValueName[] = {
3852 'C','o','u','n','t','r','y','C','o','d','e','\0'
3854 WCHAR szNumber[10];
3855 HANDLE hKey;
3856 ULONG ulIndex = 0;
3858 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3860 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3862 SetLastError(ERROR_INVALID_PARAMETER);
3863 return FALSE;
3866 hKey = NLS_RegOpenKey( 0, szCountryListName );
3868 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3870 BOOL bContinue = TRUE;
3871 DWORD dwGeoId;
3872 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3874 if (hSubKey)
3876 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3878 TRACE("Got geoid %d\n", dwGeoId);
3880 if (!pGeoEnumProc( dwGeoId ))
3881 bContinue = FALSE;
3884 NtClose( hSubKey );
3887 if (!bContinue)
3888 break;
3890 ulIndex++;
3893 if (hKey)
3894 NtClose( hKey );
3896 return TRUE;
3899 /******************************************************************************
3900 * InvalidateNLSCache (KERNEL32.@)
3902 * Invalidate the cache of NLS values.
3904 * PARAMS
3905 * None.
3907 * RETURNS
3908 * Success: TRUE.
3909 * Failure: FALSE.
3911 BOOL WINAPI InvalidateNLSCache(void)
3913 FIXME("() stub\n");
3914 return FALSE;
3917 /******************************************************************************
3918 * GetUserGeoID (KERNEL32.@)
3920 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3922 GEOID ret = GEOID_NOT_AVAILABLE;
3923 static const WCHAR geoW[] = {'G','e','o',0};
3924 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3925 WCHAR bufferW[40], *end;
3926 DWORD count;
3927 HANDLE hkey, hSubkey = 0;
3928 UNICODE_STRING keyW;
3929 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3930 RtlInitUnicodeString( &keyW, nationW );
3931 count = sizeof(bufferW);
3933 if(!(hkey = create_registry_key())) return ret;
3935 switch( GeoClass ){
3936 case GEOCLASS_NATION:
3937 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3939 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3940 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3941 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3943 break;
3944 case GEOCLASS_REGION:
3945 FIXME("GEOCLASS_REGION not handled yet\n");
3946 break;
3949 NtClose(hkey);
3950 if (hSubkey) NtClose(hSubkey);
3951 return ret;
3954 /******************************************************************************
3955 * SetUserGeoID (KERNEL32.@)
3957 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3959 static const WCHAR geoW[] = {'G','e','o',0};
3960 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3961 static const WCHAR formatW[] = {'%','i',0};
3962 UNICODE_STRING nameW,keyW;
3963 WCHAR bufferW[10];
3964 OBJECT_ATTRIBUTES attr;
3965 HANDLE hkey;
3967 if(!(hkey = create_registry_key())) return FALSE;
3969 attr.Length = sizeof(attr);
3970 attr.RootDirectory = hkey;
3971 attr.ObjectName = &nameW;
3972 attr.Attributes = 0;
3973 attr.SecurityDescriptor = NULL;
3974 attr.SecurityQualityOfService = NULL;
3975 RtlInitUnicodeString( &nameW, geoW );
3976 RtlInitUnicodeString( &keyW, nationW );
3978 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3981 NtClose(attr.RootDirectory);
3982 return FALSE;
3985 sprintfW(bufferW, formatW, GeoID);
3986 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3987 NtClose(attr.RootDirectory);
3988 NtClose(hkey);
3989 return TRUE;
3992 typedef struct
3994 union
3996 UILANGUAGE_ENUMPROCA procA;
3997 UILANGUAGE_ENUMPROCW procW;
3998 } u;
3999 DWORD flags;
4000 LONG_PTR param;
4001 } ENUM_UILANG_CALLBACK;
4003 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
4004 LPCSTR name, WORD LangID, LONG_PTR lParam )
4006 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4007 char buf[20];
4009 sprintf(buf, "%08x", (UINT)LangID);
4010 return enum_uilang->u.procA( buf, enum_uilang->param );
4013 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
4014 LPCWSTR name, WORD LangID, LONG_PTR lParam )
4016 static const WCHAR formatW[] = {'%','0','8','x',0};
4017 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
4018 WCHAR buf[20];
4020 sprintfW( buf, formatW, (UINT)LangID );
4021 return enum_uilang->u.procW( buf, enum_uilang->param );
4024 /******************************************************************************
4025 * EnumUILanguagesA (KERNEL32.@)
4027 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4029 ENUM_UILANG_CALLBACK enum_uilang;
4031 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4033 if(!pUILangEnumProc) {
4034 SetLastError(ERROR_INVALID_PARAMETER);
4035 return FALSE;
4037 if(dwFlags) {
4038 SetLastError(ERROR_INVALID_FLAGS);
4039 return FALSE;
4042 enum_uilang.u.procA = pUILangEnumProc;
4043 enum_uilang.flags = dwFlags;
4044 enum_uilang.param = lParam;
4046 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
4047 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
4048 (LONG_PTR)&enum_uilang);
4049 return TRUE;
4052 /******************************************************************************
4053 * EnumUILanguagesW (KERNEL32.@)
4055 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
4057 ENUM_UILANG_CALLBACK enum_uilang;
4059 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
4062 if(!pUILangEnumProc) {
4063 SetLastError(ERROR_INVALID_PARAMETER);
4064 return FALSE;
4066 if(dwFlags) {
4067 SetLastError(ERROR_INVALID_FLAGS);
4068 return FALSE;
4071 enum_uilang.u.procW = pUILangEnumProc;
4072 enum_uilang.flags = dwFlags;
4073 enum_uilang.param = lParam;
4075 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4076 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4077 (LONG_PTR)&enum_uilang);
4078 return TRUE;
4081 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
4082 int cchData, LANGID language)
4084 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
4085 return 0;
4088 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
4089 int cchData, LANGID language)
4091 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
4092 return 0;
4095 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4097 LCID userlcid;
4099 TRACE("%p, %d\n", localename, buffersize);
4101 userlcid = GetUserDefaultLCID();
4102 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4105 /******************************************************************************
4106 * NormalizeString (KERNEL32.@)
4108 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4109 LPWSTR lpDstString, INT cwDstLength)
4111 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4112 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4113 return 0;
4116 /******************************************************************************
4117 * IsNormalizedString (KERNEL32.@)
4119 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4121 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4122 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4123 return FALSE;
4126 enum {
4127 BASE = 36,
4128 TMIN = 1,
4129 TMAX = 26,
4130 SKEW = 38,
4131 DAMP = 700,
4132 INIT_BIAS = 72,
4133 INIT_N = 128
4136 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4138 INT k;
4140 delta /= (firsttime ? DAMP : 2);
4141 delta += delta/numpoints;
4143 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4144 delta /= BASE-TMIN;
4145 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4148 /******************************************************************************
4149 * IdnToAscii (KERNEL32.@)
4150 * Implementation of Punycode based on RFC 3492.
4152 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4153 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4155 static const WCHAR prefixW[] = {'x','n','-','-'};
4157 WCHAR *norm_str;
4158 INT i, label_start, label_end, norm_len, out_label, out = 0;
4160 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4161 lpASCIICharStr, cchASCIIChar);
4163 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4164 if(!norm_len)
4165 return 0;
4166 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4167 if(!norm_str) {
4168 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4169 return 0;
4171 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4172 cchUnicodeChar, norm_str, norm_len);
4173 if(!norm_len) {
4174 HeapFree(GetProcessHeap(), 0, norm_str);
4175 return 0;
4178 for(label_start=0; label_start<norm_len;) {
4179 INT n = INIT_N, bias = INIT_BIAS;
4180 INT delta = 0, b = 0, h;
4182 out_label = out;
4183 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4184 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4185 if(norm_str[i] < 0x80)
4186 b++;
4187 label_end = i;
4189 if(b == label_end-label_start) {
4190 if(label_end < norm_len)
4191 b++;
4192 if(!lpASCIICharStr) {
4193 out += b;
4194 }else if(out+b <= cchASCIIChar) {
4195 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4196 out += b;
4197 }else {
4198 HeapFree(GetProcessHeap(), 0, norm_str);
4199 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4200 return 0;
4202 label_start = label_end+1;
4203 continue;
4206 if(!lpASCIICharStr) {
4207 out += 5+b; /* strlen(xn--...-) */
4208 }else if(out+5+b <= cchASCIIChar) {
4209 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4210 out += 4;
4211 for(i=label_start; i<label_end; i++)
4212 if(norm_str[i] < 0x80)
4213 lpASCIICharStr[out++] = norm_str[i];
4214 lpASCIICharStr[out++] = '-';
4215 }else {
4216 HeapFree(GetProcessHeap(), 0, norm_str);
4217 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4218 return 0;
4220 if(!b)
4221 out--;
4223 for(h=b; h<label_end-label_start;) {
4224 INT m = 0xffff, q, k;
4226 for(i=label_start; i<label_end; i++) {
4227 if(norm_str[i]>=n && m>norm_str[i])
4228 m = norm_str[i];
4230 delta += (m-n)*(h+1);
4231 n = m;
4233 for(i=label_start; i<label_end; i++) {
4234 if(norm_str[i] < n) {
4235 delta++;
4236 }else if(norm_str[i] == n) {
4237 for(q=delta, k=BASE; ; k+=BASE) {
4238 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4239 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4240 if(!lpASCIICharStr) {
4241 out++;
4242 }else if(out+1 <= cchASCIIChar) {
4243 lpASCIICharStr[out++] = disp<='z'-'a' ?
4244 'a'+disp : '0'+disp-'z'+'a'-1;
4245 }else {
4246 HeapFree(GetProcessHeap(), 0, norm_str);
4247 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4248 return 0;
4250 if(q < t)
4251 break;
4252 q = (q-t)/(BASE-t);
4254 bias = adapt(delta, h+1, h==b);
4255 delta = 0;
4256 h++;
4259 delta++;
4260 n++;
4263 if(out-out_label > 63) {
4264 HeapFree(GetProcessHeap(), 0, norm_str);
4265 SetLastError(ERROR_INVALID_NAME);
4266 return 0;
4269 if(label_end < norm_len) {
4270 if(!lpASCIICharStr) {
4271 out++;
4272 }else if(out+1 <= cchASCIIChar) {
4273 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4274 }else {
4275 HeapFree(GetProcessHeap(), 0, norm_str);
4276 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4277 return 0;
4280 label_start = label_end+1;
4283 HeapFree(GetProcessHeap(), 0, norm_str);
4284 return out;
4287 /******************************************************************************
4288 * IdnToNameprepUnicode (KERNEL32.@)
4290 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4291 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4293 enum {
4294 UNASSIGNED = 0x1,
4295 PROHIBITED = 0x2,
4296 BIDI_RAL = 0x4,
4297 BIDI_L = 0x8
4300 extern const unsigned short nameprep_char_type[];
4301 extern const WCHAR nameprep_mapping[];
4302 const WCHAR *ptr;
4303 WORD flags;
4304 WCHAR buf[64], *map_str, norm_str[64], ch;
4305 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4306 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4308 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4309 lpNameprepCharStr, cchNameprepChar);
4311 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4312 SetLastError(ERROR_INVALID_FLAGS);
4313 return 0;
4316 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4317 SetLastError(ERROR_INVALID_PARAMETER);
4318 return 0;
4321 if(cchUnicodeChar == -1)
4322 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4323 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4324 SetLastError(ERROR_INVALID_NAME);
4325 return 0;
4328 for(label_start=0; label_start<cchUnicodeChar;) {
4329 ascii_only = TRUE;
4330 for(i=label_start; i<cchUnicodeChar; i++) {
4331 ch = lpUnicodeCharStr[i];
4333 if(i!=cchUnicodeChar-1 && !ch) {
4334 SetLastError(ERROR_INVALID_NAME);
4335 return 0;
4337 /* check if ch is one of label separators defined in RFC3490 */
4338 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4339 break;
4341 if(ch > 0x7f) {
4342 ascii_only = FALSE;
4343 continue;
4346 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4347 continue;
4348 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4349 || (ch>='0' && ch<='9') || ch=='-')
4350 continue;
4352 SetLastError(ERROR_INVALID_NAME);
4353 return 0;
4355 label_end = i;
4356 /* last label may be empty */
4357 if(label_start==label_end && ch) {
4358 SetLastError(ERROR_INVALID_NAME);
4359 return 0;
4362 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4363 lpUnicodeCharStr[label_end-1]=='-')) {
4364 SetLastError(ERROR_INVALID_NAME);
4365 return 0;
4368 if(ascii_only) {
4369 /* maximal label length is 63 characters */
4370 if(label_end-label_start > 63) {
4371 SetLastError(ERROR_INVALID_NAME);
4372 return 0;
4374 if(label_end < cchUnicodeChar)
4375 label_end++;
4377 if(!lpNameprepCharStr) {
4378 out += label_end-label_start;
4379 }else if(out+label_end-label_start <= cchNameprepChar) {
4380 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4381 (label_end-label_start)*sizeof(WCHAR));
4382 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4383 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4384 out += label_end-label_start;
4385 }else {
4386 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4387 return 0;
4390 label_start = label_end;
4391 continue;
4394 map_len = 0;
4395 for(i=label_start; i<label_end; i++) {
4396 ch = lpUnicodeCharStr[i];
4397 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4398 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4400 if(!ptr[0]) map_len++;
4401 else if(!ptr[1]) map_len++;
4402 else if(!ptr[2]) map_len += 2;
4403 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4405 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4406 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4407 if(!map_str) {
4408 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4409 return 0;
4411 }else {
4412 map_str = buf;
4414 map_len = 0;
4415 for(i=label_start; i<label_end; i++) {
4416 ch = lpUnicodeCharStr[i];
4417 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4418 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4420 if(!ptr[0]) {
4421 map_str[map_len++] = ch;
4422 }else if(!ptr[1]) {
4423 map_str[map_len++] = ptr[0];
4424 }else if(!ptr[2]) {
4425 map_str[map_len++] = ptr[0];
4426 map_str[map_len++] = ptr[1];
4427 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4428 map_str[map_len++] = ptr[0];
4429 map_str[map_len++] = ptr[1];
4430 map_str[map_len++] = ptr[2];
4434 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4435 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4436 if(map_str != buf)
4437 HeapFree(GetProcessHeap(), 0, map_str);
4438 if(!norm_len) {
4439 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4440 SetLastError(ERROR_INVALID_NAME);
4441 return 0;
4444 if(label_end < cchUnicodeChar) {
4445 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4446 label_end++;
4449 if(!lpNameprepCharStr) {
4450 out += norm_len;
4451 }else if(out+norm_len <= cchNameprepChar) {
4452 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4453 out += norm_len;
4454 }else {
4455 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4456 return 0;
4459 have_bidi_ral = prohibit_bidi_ral = FALSE;
4460 mask = PROHIBITED;
4461 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4462 mask |= UNASSIGNED;
4463 for(i=0; i<norm_len; i++) {
4464 ch = norm_str[i];
4465 flags = get_table_entry( nameprep_char_type, ch );
4467 if(flags & mask) {
4468 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4469 : ERROR_NO_UNICODE_TRANSLATION);
4470 return 0;
4473 if(flags & BIDI_RAL)
4474 have_bidi_ral = TRUE;
4475 if(flags & BIDI_L)
4476 prohibit_bidi_ral = TRUE;
4479 if(have_bidi_ral) {
4480 ch = norm_str[0];
4481 flags = get_table_entry( nameprep_char_type, ch );
4482 if((flags & BIDI_RAL) == 0)
4483 prohibit_bidi_ral = TRUE;
4485 ch = norm_str[norm_len-1];
4486 flags = get_table_entry( nameprep_char_type, ch );
4487 if((flags & BIDI_RAL) == 0)
4488 prohibit_bidi_ral = TRUE;
4491 if(have_bidi_ral && prohibit_bidi_ral) {
4492 SetLastError(ERROR_INVALID_NAME);
4493 return 0;
4496 label_start = label_end;
4499 return out;
4502 /******************************************************************************
4503 * IdnToUnicode (KERNEL32.@)
4505 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4506 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4508 extern const unsigned short nameprep_char_type[];
4510 INT i, label_start, label_end, out_label, out = 0;
4511 WCHAR ch;
4513 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4514 lpUnicodeCharStr, cchUnicodeChar);
4516 for(label_start=0; label_start<cchASCIIChar;) {
4517 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4519 out_label = out;
4520 for(i=label_start; i<cchASCIIChar; i++) {
4521 ch = lpASCIICharStr[i];
4523 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4524 SetLastError(ERROR_INVALID_NAME);
4525 return 0;
4528 if(!ch || ch=='.')
4529 break;
4530 if(ch == '-')
4531 delim = i;
4533 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4534 continue;
4535 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4536 || (ch>='0' && ch<='9') || ch=='-')
4537 continue;
4539 SetLastError(ERROR_INVALID_NAME);
4540 return 0;
4542 label_end = i;
4543 /* last label may be empty */
4544 if(label_start==label_end && ch) {
4545 SetLastError(ERROR_INVALID_NAME);
4546 return 0;
4549 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
4550 lpASCIICharStr[label_end-1]=='-')) {
4551 SetLastError(ERROR_INVALID_NAME);
4552 return 0;
4554 if(label_end-label_start > 63) {
4555 SetLastError(ERROR_INVALID_NAME);
4556 return 0;
4559 if(label_end-label_start<4 ||
4560 tolowerW(lpASCIICharStr[label_start])!='x' ||
4561 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4562 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4563 if(label_end < cchASCIIChar)
4564 label_end++;
4566 if(!lpUnicodeCharStr) {
4567 out += label_end-label_start;
4568 }else if(out+label_end-label_start <= cchUnicodeChar) {
4569 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4570 (label_end-label_start)*sizeof(WCHAR));
4571 out += label_end-label_start;
4572 }else {
4573 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4574 return 0;
4577 label_start = label_end;
4578 continue;
4581 if(delim == label_start+3)
4582 delim++;
4583 if(!lpUnicodeCharStr) {
4584 out += delim-label_start-4;
4585 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4586 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4587 (delim-label_start-4)*sizeof(WCHAR));
4588 out += delim-label_start-4;
4589 }else {
4590 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4591 return 0;
4593 if(out != out_label)
4594 delim++;
4596 for(i=delim; i<label_end;) {
4597 old_pos = pos;
4598 w = 1;
4599 for(k=BASE; ; k+=BASE) {
4600 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4601 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4602 SetLastError(ERROR_INVALID_NAME);
4603 return 0;
4605 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4606 pos += digit*w;
4607 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4608 if(digit < t)
4609 break;
4610 w *= BASE-t;
4612 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4613 n += pos/(out-out_label+1);
4614 pos %= out-out_label+1;
4616 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4617 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4618 SetLastError(ERROR_INVALID_NAME);
4619 return 0;
4621 if(!lpUnicodeCharStr) {
4622 out++;
4623 }else if(out+1 <= cchASCIIChar) {
4624 memmove(lpUnicodeCharStr+out_label+pos+1,
4625 lpUnicodeCharStr+out_label+pos,
4626 (out-out_label-pos)*sizeof(WCHAR));
4627 lpUnicodeCharStr[out_label+pos] = n;
4628 out++;
4629 }else {
4630 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4631 return 0;
4633 pos++;
4636 if(out-out_label > 63) {
4637 SetLastError(ERROR_INVALID_NAME);
4638 return 0;
4641 if(label_end < cchASCIIChar) {
4642 if(!lpUnicodeCharStr) {
4643 out++;
4644 }else if(out+1 <= cchUnicodeChar) {
4645 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4646 }else {
4647 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4648 return 0;
4651 label_start = label_end+1;
4654 return out;