wininet: Only accept proxy and proxy bypass if type is INTERNET_OPEN_TYPE_PROXY.
[wine.git] / dlls / kernel32 / locale.c
blob86720ffaea5f964c4057c8eb57d8e04ca855e5e4
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 /* Copy Ascii string to Unicode without using codepages */
164 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
166 while (n > 1 && *src)
168 *dst++ = (unsigned char)*src++;
169 n--;
171 if (n) *dst = 0;
174 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
176 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
179 /***********************************************************************
180 * get_lcid_codepage
182 * Retrieve the ANSI codepage for a given locale.
184 static inline UINT get_lcid_codepage( LCID lcid )
186 UINT ret;
187 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
188 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
189 return ret;
193 /***********************************************************************
194 * get_codepage_table
196 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
198 static const union cptable *get_codepage_table( unsigned int codepage )
200 const union cptable *ret = NULL;
202 assert( ansi_cptable ); /* init must have been done already */
204 switch(codepage)
206 case CP_ACP:
207 return ansi_cptable;
208 case CP_OEMCP:
209 return oem_cptable;
210 case CP_MACCP:
211 return mac_cptable;
212 case CP_UTF7:
213 case CP_UTF8:
214 break;
215 case CP_THREAD_ACP:
216 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
217 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
218 /* fall through */
219 default:
220 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
221 if (codepage == oem_cptable->info.codepage) return oem_cptable;
222 if (codepage == mac_cptable->info.codepage) return mac_cptable;
223 ret = wine_cp_get_table( codepage );
224 break;
226 return ret;
230 /***********************************************************************
231 * charset_cmp (internal)
233 static int charset_cmp( const void *name, const void *entry )
235 const struct charset_entry *charset = entry;
236 return strcasecmp( name, charset->charset_name );
239 /***********************************************************************
240 * find_charset
242 static UINT find_charset( const WCHAR *name )
244 const struct charset_entry *entry;
245 char charset_name[16];
246 size_t i, j;
248 /* remove punctuation characters from charset name */
249 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
250 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
251 charset_name[j] = 0;
253 entry = bsearch( charset_name, charset_names,
254 sizeof(charset_names)/sizeof(charset_names[0]),
255 sizeof(charset_names[0]), charset_cmp );
256 if (entry) return entry->codepage;
257 return 0;
261 /***********************************************************************
262 * find_locale_id_callback
264 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
265 LPCWSTR name, WORD LangID, LPARAM lParam )
267 struct locale_name *data = (struct locale_name *)lParam;
268 WCHAR buffer[128];
269 int matches = 0;
270 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
272 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
274 /* first check exact name */
275 if (data->win_name[0] &&
276 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
277 buffer, sizeof(buffer)/sizeof(WCHAR) ))
279 if (!strcmpW( data->win_name, buffer ))
281 matches = 4; /* everything matches */
282 goto done;
286 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
287 buffer, sizeof(buffer)/sizeof(WCHAR) ))
288 return TRUE;
289 if (strcmpW( buffer, data->lang )) return TRUE;
290 matches++; /* language name matched */
292 if (data->country)
294 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
295 buffer, sizeof(buffer)/sizeof(WCHAR) ))
297 if (strcmpW( buffer, data->country )) goto done;
298 matches++; /* country name matched */
301 else /* match default language */
303 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
306 if (data->codepage)
308 UINT unix_cp;
309 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
310 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
312 if (unix_cp == data->codepage) matches++;
316 /* FIXME: check sort order */
318 done:
319 if (matches > data->matches)
321 data->lcid = lcid;
322 data->matches = matches;
324 return (data->matches < 4); /* no need to continue for perfect match */
328 /***********************************************************************
329 * parse_locale_name
331 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
332 * Unix format is: lang[_country][.charset][@modifier]
333 * Windows format is: lang[-script][-country][_modifier]
335 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
337 static const WCHAR sepW[] = {'-','_','.','@',0};
338 static const WCHAR winsepW[] = {'-','_',0};
339 static const WCHAR posixW[] = {'P','O','S','I','X',0};
340 static const WCHAR cW[] = {'C',0};
341 static const WCHAR latinW[] = {'l','a','t','i','n',0};
342 static const WCHAR latnW[] = {'-','L','a','t','n',0};
343 WCHAR *p;
345 TRACE("%s\n", debugstr_w(str));
347 name->country = name->charset = name->script = name->modifier = NULL;
348 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
349 name->matches = 0;
350 name->codepage = 0;
351 name->win_name[0] = 0;
352 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
354 if (!(p = strpbrkW( name->lang, sepW )))
356 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
358 name->matches = 4; /* perfect match for default English lcid */
359 return;
361 strcpyW( name->win_name, name->lang );
363 else if (*p == '-') /* Windows format */
365 strcpyW( name->win_name, name->lang );
366 *p++ = 0;
367 name->country = p;
368 if (!(p = strpbrkW( p, winsepW ))) goto done;
369 if (*p == '-')
371 *p++ = 0;
372 name->script = name->country;
373 name->country = p;
374 if (!(p = strpbrkW( p, winsepW ))) goto done;
376 *p++ = 0;
377 name->modifier = p;
379 else /* Unix format */
381 if (*p == '_')
383 *p++ = 0;
384 name->country = p;
385 p = strpbrkW( p, sepW + 2 );
387 if (p && *p == '.')
389 *p++ = 0;
390 name->charset = p;
391 p = strchrW( p, '@' );
393 if (p)
395 *p++ = 0;
396 name->modifier = p;
399 if (name->charset)
400 name->codepage = find_charset( name->charset );
402 /* rebuild a Windows name if possible */
404 if (name->charset) goto done; /* can't specify charset in Windows format */
405 if (name->modifier && strcmpW( name->modifier, latinW ))
406 goto done; /* only Latn script supported for now */
407 strcpyW( name->win_name, name->lang );
408 if (name->modifier) strcatW( name->win_name, latnW );
409 if (name->country)
411 p = name->win_name + strlenW(name->win_name);
412 *p++ = '-';
413 strcpyW( p, name->country );
416 done:
417 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
418 find_locale_id_callback, (LPARAM)name );
422 /***********************************************************************
423 * convert_default_lcid
425 * Get the default LCID to use for a given lctype in GetLocaleInfo.
427 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
429 if (lcid == LOCALE_SYSTEM_DEFAULT ||
430 lcid == LOCALE_USER_DEFAULT ||
431 lcid == LOCALE_NEUTRAL)
433 LCID default_id = 0;
435 switch(lctype & 0xffff)
437 case LOCALE_SSORTNAME:
438 default_id = lcid_LC_COLLATE;
439 break;
441 case LOCALE_FONTSIGNATURE:
442 case LOCALE_IDEFAULTANSICODEPAGE:
443 case LOCALE_IDEFAULTCODEPAGE:
444 case LOCALE_IDEFAULTEBCDICCODEPAGE:
445 case LOCALE_IDEFAULTMACCODEPAGE:
446 case LOCALE_IDEFAULTUNIXCODEPAGE:
447 default_id = lcid_LC_CTYPE;
448 break;
450 case LOCALE_ICURRDIGITS:
451 case LOCALE_ICURRENCY:
452 case LOCALE_IINTLCURRDIGITS:
453 case LOCALE_INEGCURR:
454 case LOCALE_INEGSEPBYSPACE:
455 case LOCALE_INEGSIGNPOSN:
456 case LOCALE_INEGSYMPRECEDES:
457 case LOCALE_IPOSSEPBYSPACE:
458 case LOCALE_IPOSSIGNPOSN:
459 case LOCALE_IPOSSYMPRECEDES:
460 case LOCALE_SCURRENCY:
461 case LOCALE_SINTLSYMBOL:
462 case LOCALE_SMONDECIMALSEP:
463 case LOCALE_SMONGROUPING:
464 case LOCALE_SMONTHOUSANDSEP:
465 case LOCALE_SNATIVECURRNAME:
466 default_id = lcid_LC_MONETARY;
467 break;
469 case LOCALE_IDIGITS:
470 case LOCALE_IDIGITSUBSTITUTION:
471 case LOCALE_ILZERO:
472 case LOCALE_INEGNUMBER:
473 case LOCALE_SDECIMAL:
474 case LOCALE_SGROUPING:
475 case LOCALE_SNAN:
476 case LOCALE_SNATIVEDIGITS:
477 case LOCALE_SNEGATIVESIGN:
478 case LOCALE_SNEGINFINITY:
479 case LOCALE_SPOSINFINITY:
480 case LOCALE_SPOSITIVESIGN:
481 case LOCALE_STHOUSAND:
482 default_id = lcid_LC_NUMERIC;
483 break;
485 case LOCALE_ICALENDARTYPE:
486 case LOCALE_ICENTURY:
487 case LOCALE_IDATE:
488 case LOCALE_IDAYLZERO:
489 case LOCALE_IFIRSTDAYOFWEEK:
490 case LOCALE_IFIRSTWEEKOFYEAR:
491 case LOCALE_ILDATE:
492 case LOCALE_IMONLZERO:
493 case LOCALE_IOPTIONALCALENDAR:
494 case LOCALE_ITIME:
495 case LOCALE_ITIMEMARKPOSN:
496 case LOCALE_ITLZERO:
497 case LOCALE_S1159:
498 case LOCALE_S2359:
499 case LOCALE_SABBREVDAYNAME1:
500 case LOCALE_SABBREVDAYNAME2:
501 case LOCALE_SABBREVDAYNAME3:
502 case LOCALE_SABBREVDAYNAME4:
503 case LOCALE_SABBREVDAYNAME5:
504 case LOCALE_SABBREVDAYNAME6:
505 case LOCALE_SABBREVDAYNAME7:
506 case LOCALE_SABBREVMONTHNAME1:
507 case LOCALE_SABBREVMONTHNAME2:
508 case LOCALE_SABBREVMONTHNAME3:
509 case LOCALE_SABBREVMONTHNAME4:
510 case LOCALE_SABBREVMONTHNAME5:
511 case LOCALE_SABBREVMONTHNAME6:
512 case LOCALE_SABBREVMONTHNAME7:
513 case LOCALE_SABBREVMONTHNAME8:
514 case LOCALE_SABBREVMONTHNAME9:
515 case LOCALE_SABBREVMONTHNAME10:
516 case LOCALE_SABBREVMONTHNAME11:
517 case LOCALE_SABBREVMONTHNAME12:
518 case LOCALE_SABBREVMONTHNAME13:
519 case LOCALE_SDATE:
520 case LOCALE_SDAYNAME1:
521 case LOCALE_SDAYNAME2:
522 case LOCALE_SDAYNAME3:
523 case LOCALE_SDAYNAME4:
524 case LOCALE_SDAYNAME5:
525 case LOCALE_SDAYNAME6:
526 case LOCALE_SDAYNAME7:
527 case LOCALE_SDURATION:
528 case LOCALE_SLONGDATE:
529 case LOCALE_SMONTHNAME1:
530 case LOCALE_SMONTHNAME2:
531 case LOCALE_SMONTHNAME3:
532 case LOCALE_SMONTHNAME4:
533 case LOCALE_SMONTHNAME5:
534 case LOCALE_SMONTHNAME6:
535 case LOCALE_SMONTHNAME7:
536 case LOCALE_SMONTHNAME8:
537 case LOCALE_SMONTHNAME9:
538 case LOCALE_SMONTHNAME10:
539 case LOCALE_SMONTHNAME11:
540 case LOCALE_SMONTHNAME12:
541 case LOCALE_SMONTHNAME13:
542 case LOCALE_SSHORTDATE:
543 case LOCALE_SSHORTESTDAYNAME1:
544 case LOCALE_SSHORTESTDAYNAME2:
545 case LOCALE_SSHORTESTDAYNAME3:
546 case LOCALE_SSHORTESTDAYNAME4:
547 case LOCALE_SSHORTESTDAYNAME5:
548 case LOCALE_SSHORTESTDAYNAME6:
549 case LOCALE_SSHORTESTDAYNAME7:
550 case LOCALE_STIME:
551 case LOCALE_STIMEFORMAT:
552 case LOCALE_SYEARMONTH:
553 default_id = lcid_LC_TIME;
554 break;
556 case LOCALE_IPAPERSIZE:
557 default_id = lcid_LC_PAPER;
558 break;
560 case LOCALE_IMEASURE:
561 default_id = lcid_LC_MEASUREMENT;
562 break;
564 case LOCALE_ICOUNTRY:
565 default_id = lcid_LC_TELEPHONE;
566 break;
568 if (default_id) lcid = default_id;
570 return ConvertDefaultLocale( lcid );
573 /***********************************************************************
574 * is_genitive_name_supported
576 * Determine could LCTYPE basically support genitive name form or not.
578 static BOOL is_genitive_name_supported( LCTYPE lctype )
580 switch(lctype & 0xffff)
582 case LOCALE_SMONTHNAME1:
583 case LOCALE_SMONTHNAME2:
584 case LOCALE_SMONTHNAME3:
585 case LOCALE_SMONTHNAME4:
586 case LOCALE_SMONTHNAME5:
587 case LOCALE_SMONTHNAME6:
588 case LOCALE_SMONTHNAME7:
589 case LOCALE_SMONTHNAME8:
590 case LOCALE_SMONTHNAME9:
591 case LOCALE_SMONTHNAME10:
592 case LOCALE_SMONTHNAME11:
593 case LOCALE_SMONTHNAME12:
594 case LOCALE_SMONTHNAME13:
595 return TRUE;
596 default:
597 return FALSE;
601 /***********************************************************************
602 * create_registry_key
604 * Create the Control Panel\\International registry key.
606 static inline HANDLE create_registry_key(void)
608 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
609 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
610 OBJECT_ATTRIBUTES attr;
611 UNICODE_STRING nameW;
612 HANDLE cpl_key, hkey = 0;
614 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
616 attr.Length = sizeof(attr);
617 attr.RootDirectory = hkey;
618 attr.ObjectName = &nameW;
619 attr.Attributes = 0;
620 attr.SecurityDescriptor = NULL;
621 attr.SecurityQualityOfService = NULL;
622 RtlInitUnicodeString( &nameW, cplW );
624 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
626 NtClose( attr.RootDirectory );
627 attr.RootDirectory = cpl_key;
628 RtlInitUnicodeString( &nameW, intlW );
629 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
631 NtClose( attr.RootDirectory );
632 return hkey;
636 /* update the registry settings for a given locale parameter */
637 /* return TRUE if an update was needed */
638 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
639 const LCTYPE *values, UINT nb_values )
641 static const WCHAR formatW[] = { '%','0','8','x',0 };
642 WCHAR bufferW[40];
643 UNICODE_STRING nameW;
644 DWORD count, i;
646 RtlInitUnicodeString( &nameW, name );
647 count = sizeof(bufferW);
648 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
650 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
651 LPCWSTR text = (LPCWSTR)info->Data;
653 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
654 TRACE( "updating registry, locale %s changed %s -> %08x\n",
655 debugstr_w(name), debugstr_w(text), lcid );
657 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
658 sprintfW( bufferW, formatW, lcid );
659 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
661 for (i = 0; i < nb_values; i++)
663 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
664 sizeof(bufferW)/sizeof(WCHAR) );
665 SetLocaleInfoW( lcid, values[i], bufferW );
667 return TRUE;
671 /***********************************************************************
672 * LOCALE_InitRegistry
674 * Update registry contents on startup if the user locale has changed.
675 * This simulates the action of the Windows control panel.
677 void LOCALE_InitRegistry(void)
679 static const WCHAR acpW[] = {'A','C','P',0};
680 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
681 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
682 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
683 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
684 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
685 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
686 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
687 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
688 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
689 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
690 static const struct
692 LPCWSTR name;
693 USHORT value;
694 } update_cp_values[] = {
695 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
696 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
697 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
699 static const LCTYPE lc_messages_values[] = {
700 LOCALE_SABBREVLANGNAME,
701 LOCALE_SCOUNTRY,
702 LOCALE_SLIST };
703 static const LCTYPE lc_monetary_values[] = {
704 LOCALE_SCURRENCY,
705 LOCALE_ICURRENCY,
706 LOCALE_INEGCURR,
707 LOCALE_ICURRDIGITS,
708 LOCALE_ILZERO,
709 LOCALE_SMONDECIMALSEP,
710 LOCALE_SMONGROUPING,
711 LOCALE_SMONTHOUSANDSEP };
712 static const LCTYPE lc_numeric_values[] = {
713 LOCALE_SDECIMAL,
714 LOCALE_STHOUSAND,
715 LOCALE_IDIGITS,
716 LOCALE_IDIGITSUBSTITUTION,
717 LOCALE_SNATIVEDIGITS,
718 LOCALE_INEGNUMBER,
719 LOCALE_SNEGATIVESIGN,
720 LOCALE_SPOSITIVESIGN,
721 LOCALE_SGROUPING };
722 static const LCTYPE lc_time_values[] = {
723 LOCALE_S1159,
724 LOCALE_S2359,
725 LOCALE_STIME,
726 LOCALE_ITIME,
727 LOCALE_ITLZERO,
728 LOCALE_SSHORTDATE,
729 LOCALE_SLONGDATE,
730 LOCALE_SDATE,
731 LOCALE_ITIMEMARKPOSN,
732 LOCALE_ICALENDARTYPE,
733 LOCALE_IFIRSTDAYOFWEEK,
734 LOCALE_IFIRSTWEEKOFYEAR,
735 LOCALE_STIMEFORMAT,
736 LOCALE_SYEARMONTH,
737 LOCALE_IDATE };
738 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
739 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
740 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
742 UNICODE_STRING nameW;
743 WCHAR bufferW[80];
744 DWORD count, i;
745 HANDLE hkey;
746 LCID lcid = GetUserDefaultLCID();
748 if (!(hkey = create_registry_key()))
749 return; /* don't do anything if we can't create the registry key */
751 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
752 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
753 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
754 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
755 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
756 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
757 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
758 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
759 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
760 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
761 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
762 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
763 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
764 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
766 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
768 static const WCHAR codepageW[] =
769 {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
770 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
771 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
773 OBJECT_ATTRIBUTES attr;
774 HANDLE nls_key;
775 DWORD len = 14;
777 RtlInitUnicodeString( &nameW, codepageW );
778 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
779 while (codepageW[len])
781 nameW.Length = len * sizeof(WCHAR);
782 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
783 NtClose( nls_key );
784 len++;
785 while (codepageW[len] && codepageW[len] != '\\') len++;
787 nameW.Length = len * sizeof(WCHAR);
788 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
790 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
792 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
793 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
794 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
795 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
797 NtClose( nls_key );
801 NtClose( hkey );
805 /***********************************************************************
806 * setup_unix_locales
808 static UINT setup_unix_locales(void)
810 struct locale_name locale_name;
811 WCHAR buffer[128], ctype_buff[128];
812 char *locale;
813 UINT unix_cp = 0;
815 if ((locale = setlocale( LC_CTYPE, NULL )))
817 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
818 parse_locale_name( ctype_buff, &locale_name );
819 lcid_LC_CTYPE = locale_name.lcid;
820 unix_cp = locale_name.codepage;
822 if (!lcid_LC_CTYPE) /* this one needs a default value */
823 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
825 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
826 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
828 #define GET_UNIX_LOCALE(cat) do \
829 if ((locale = setlocale( cat, NULL ))) \
831 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
832 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
833 else { \
834 parse_locale_name( buffer, &locale_name ); \
835 lcid_##cat = locale_name.lcid; \
836 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
837 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
839 } while (0)
841 GET_UNIX_LOCALE( LC_COLLATE );
842 GET_UNIX_LOCALE( LC_MESSAGES );
843 GET_UNIX_LOCALE( LC_MONETARY );
844 GET_UNIX_LOCALE( LC_NUMERIC );
845 GET_UNIX_LOCALE( LC_TIME );
846 #ifdef LC_PAPER
847 GET_UNIX_LOCALE( LC_PAPER );
848 #endif
849 #ifdef LC_MEASUREMENT
850 GET_UNIX_LOCALE( LC_MEASUREMENT );
851 #endif
852 #ifdef LC_TELEPHONE
853 GET_UNIX_LOCALE( LC_TELEPHONE );
854 #endif
856 #undef GET_UNIX_LOCALE
858 return unix_cp;
862 /***********************************************************************
863 * GetUserDefaultLangID (KERNEL32.@)
865 * Get the default language Id for the current user.
867 * PARAMS
868 * None.
870 * RETURNS
871 * The current LANGID of the default language for the current user.
873 LANGID WINAPI GetUserDefaultLangID(void)
875 return LANGIDFROMLCID(GetUserDefaultLCID());
879 /***********************************************************************
880 * GetSystemDefaultLangID (KERNEL32.@)
882 * Get the default language Id for the system.
884 * PARAMS
885 * None.
887 * RETURNS
888 * The current LANGID of the default language for the system.
890 LANGID WINAPI GetSystemDefaultLangID(void)
892 return LANGIDFROMLCID(GetSystemDefaultLCID());
896 /***********************************************************************
897 * GetUserDefaultLCID (KERNEL32.@)
899 * Get the default locale Id for the current user.
901 * PARAMS
902 * None.
904 * RETURNS
905 * The current LCID of the default locale for the current user.
907 LCID WINAPI GetUserDefaultLCID(void)
909 LCID lcid;
910 NtQueryDefaultLocale( TRUE, &lcid );
911 return lcid;
915 /***********************************************************************
916 * GetSystemDefaultLCID (KERNEL32.@)
918 * Get the default locale Id for the system.
920 * PARAMS
921 * None.
923 * RETURNS
924 * The current LCID of the default locale for the system.
926 LCID WINAPI GetSystemDefaultLCID(void)
928 LCID lcid;
929 NtQueryDefaultLocale( FALSE, &lcid );
930 return lcid;
933 /***********************************************************************
934 * GetSystemDefaultLocaleName (KERNEL32.@)
936 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
938 LCID lcid = GetSystemDefaultLCID();
939 return LCIDToLocaleName(lcid, localename, len, 0);
942 /***********************************************************************
943 * GetUserDefaultUILanguage (KERNEL32.@)
945 * Get the default user interface language Id for the current user.
947 * PARAMS
948 * None.
950 * RETURNS
951 * The current LANGID of the default UI language for the current user.
953 LANGID WINAPI GetUserDefaultUILanguage(void)
955 LANGID lang;
956 NtQueryDefaultUILanguage( &lang );
957 return lang;
961 /***********************************************************************
962 * GetSystemDefaultUILanguage (KERNEL32.@)
964 * Get the default user interface language Id for the system.
966 * PARAMS
967 * None.
969 * RETURNS
970 * The current LANGID of the default UI language for the system. This is
971 * typically the same language used during the installation process.
973 LANGID WINAPI GetSystemDefaultUILanguage(void)
975 LANGID lang;
976 NtQueryInstallUILanguage( &lang );
977 return lang;
981 /***********************************************************************
982 * LocaleNameToLCID (KERNEL32.@)
984 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
986 struct locale_name locale_name;
988 if (flags) FIXME( "unsupported flags %x\n", flags );
990 if (name == LOCALE_NAME_USER_DEFAULT)
991 return GetUserDefaultLCID();
993 /* string parsing */
994 parse_locale_name( name, &locale_name );
996 TRACE( "found lcid %x for %s, matches %d\n",
997 locale_name.lcid, debugstr_w(name), locale_name.matches );
999 if (!locale_name.matches)
1001 SetLastError(ERROR_INVALID_PARAMETER);
1002 return 0;
1005 if (locale_name.matches == 1)
1006 WARN( "locale %s not recognized, defaulting to %s\n",
1007 debugstr_w(name), debugstr_w(locale_name.lang) );
1009 return locale_name.lcid;
1013 /***********************************************************************
1014 * LCIDToLocaleName (KERNEL32.@)
1016 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1018 if (flags) FIXME( "unsupported flags %x\n", flags );
1020 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1024 /******************************************************************************
1025 * get_locale_value_name
1027 * Gets the registry value name for a given lctype.
1029 static const WCHAR *get_locale_value_name( DWORD lctype )
1031 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1032 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1033 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1034 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1035 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1036 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1037 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1038 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1039 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1040 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1041 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1042 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1043 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1044 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1045 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1046 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1047 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1048 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1049 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1050 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1051 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1052 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1053 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1054 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1055 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1056 static const WCHAR sListW[] = {'s','L','i','s','t',0};
1057 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1058 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1059 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1060 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1061 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1062 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1063 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1064 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1065 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1066 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1067 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1068 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1069 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1071 switch (lctype)
1073 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1074 * the values are stored in the registry, confirmed under Windows.
1076 case LOCALE_ICALENDARTYPE: return iCalendarTypeW;
1077 case LOCALE_ICURRDIGITS: return iCurrDigitsW;
1078 case LOCALE_ICURRENCY: return iCurrencyW;
1079 case LOCALE_IDIGITS: return iDigitsW;
1080 case LOCALE_IFIRSTDAYOFWEEK: return iFirstDayOfWeekW;
1081 case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1082 case LOCALE_ILZERO: return iLZeroW;
1083 case LOCALE_IMEASURE: return iMeasureW;
1084 case LOCALE_INEGCURR: return iNegCurrW;
1085 case LOCALE_INEGNUMBER: return iNegNumberW;
1086 case LOCALE_IPAPERSIZE: return iPaperSizeW;
1087 case LOCALE_ITIME: return iTimeW;
1088 case LOCALE_S1159: return s1159W;
1089 case LOCALE_S2359: return s2359W;
1090 case LOCALE_SCURRENCY: return sCurrencyW;
1091 case LOCALE_SDATE: return sDateW;
1092 case LOCALE_SDECIMAL: return sDecimalW;
1093 case LOCALE_SGROUPING: return sGroupingW;
1094 case LOCALE_SLIST: return sListW;
1095 case LOCALE_SLONGDATE: return sLongDateW;
1096 case LOCALE_SMONDECIMALSEP: return sMonDecimalSepW;
1097 case LOCALE_SMONGROUPING: return sMonGroupingW;
1098 case LOCALE_SMONTHOUSANDSEP: return sMonThousandSepW;
1099 case LOCALE_SNEGATIVESIGN: return sNegativeSignW;
1100 case LOCALE_SPOSITIVESIGN: return sPositiveSignW;
1101 case LOCALE_SSHORTDATE: return sShortDateW;
1102 case LOCALE_STHOUSAND: return sThousandW;
1103 case LOCALE_STIME: return sTimeW;
1104 case LOCALE_STIMEFORMAT: return sTimeFormatW;
1105 case LOCALE_SYEARMONTH: return sYearMonthW;
1107 /* The following are not listed under MSDN as supported,
1108 * but seem to be used and also stored in the registry.
1110 case LOCALE_ICOUNTRY: return iCountryW;
1111 case LOCALE_IDATE: return iDateW;
1112 case LOCALE_ILDATE: return iLDateW;
1113 case LOCALE_ITLZERO: return iTLZeroW;
1114 case LOCALE_SCOUNTRY: return sCountryW;
1115 case LOCALE_SABBREVLANGNAME: return sLanguageW;
1117 /* The following are used in XP and later */
1118 case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1119 case LOCALE_SNATIVEDIGITS: return sNativeDigitsW;
1120 case LOCALE_ITIMEMARKPOSN: return iTimePrefixW;
1122 return NULL;
1126 /******************************************************************************
1127 * get_registry_locale_info
1129 * Retrieve user-modified locale info from the registry.
1130 * Return length, 0 on error, -1 if not found.
1132 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1134 DWORD size;
1135 INT ret;
1136 HANDLE hkey;
1137 NTSTATUS status;
1138 UNICODE_STRING nameW;
1139 KEY_VALUE_PARTIAL_INFORMATION *info;
1140 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1142 if (!(hkey = create_registry_key())) return -1;
1144 RtlInitUnicodeString( &nameW, value );
1145 size = info_size + len * sizeof(WCHAR);
1147 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1149 NtClose( hkey );
1150 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1151 return 0;
1154 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1156 if (!status)
1158 ret = (size - info_size) / sizeof(WCHAR);
1159 /* append terminating null if needed */
1160 if (!ret || ((WCHAR *)info->Data)[ret-1])
1162 if (ret < len || !buffer) ret++;
1163 else
1165 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1166 ret = 0;
1169 if (ret && buffer)
1171 memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1172 buffer[ret-1] = 0;
1175 else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1177 ret = (size - info_size) / sizeof(WCHAR) + 1;
1179 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1181 ret = -1;
1183 else
1185 SetLastError( RtlNtStatusToDosError(status) );
1186 ret = 0;
1188 NtClose( hkey );
1189 HeapFree( GetProcessHeap(), 0, info );
1190 return ret;
1194 /******************************************************************************
1195 * GetLocaleInfoA (KERNEL32.@)
1197 * Get information about an aspect of a locale.
1199 * PARAMS
1200 * lcid [I] LCID of the locale
1201 * lctype [I] LCTYPE_ flags from "winnls.h"
1202 * buffer [O] Destination for the information
1203 * len [I] Length of buffer in characters
1205 * RETURNS
1206 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1207 * with the information.
1208 * Failure: 0. Use GetLastError() to determine the cause.
1210 * NOTES
1211 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1212 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1213 * which is a bit string.
1215 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1217 WCHAR *bufferW;
1218 INT lenW, ret;
1220 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1222 if (len < 0 || (len && !buffer))
1224 SetLastError( ERROR_INVALID_PARAMETER );
1225 return 0;
1227 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1229 SetLastError( ERROR_INVALID_FLAGS );
1230 return 0;
1233 if (!len) buffer = NULL;
1235 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1237 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1239 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1240 return 0;
1242 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1244 if ((lctype & LOCALE_RETURN_NUMBER) ||
1245 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1247 /* it's not an ASCII string, just bytes */
1248 ret *= sizeof(WCHAR);
1249 if (buffer)
1251 if (ret <= len) memcpy( buffer, bufferW, ret );
1252 else
1254 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1255 ret = 0;
1259 else
1261 UINT codepage = CP_ACP;
1262 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1263 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1266 HeapFree( GetProcessHeap(), 0, bufferW );
1267 return ret;
1270 static int get_value_base_by_lctype( LCTYPE lctype )
1272 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1275 /******************************************************************************
1276 * GetLocaleInfoW (KERNEL32.@)
1278 * See GetLocaleInfoA.
1280 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1282 LANGID lang_id;
1283 HRSRC hrsrc;
1284 HGLOBAL hmem;
1285 INT ret;
1286 UINT lcflags;
1287 const WCHAR *p;
1288 unsigned int i;
1290 if (len < 0 || (len && !buffer))
1292 SetLastError( ERROR_INVALID_PARAMETER );
1293 return 0;
1295 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1296 !is_genitive_name_supported( lctype ))
1298 SetLastError( ERROR_INVALID_FLAGS );
1299 return 0;
1302 if (!len) buffer = NULL;
1304 lcid = convert_default_lcid( lcid, lctype );
1306 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1307 lctype &= 0xffff;
1309 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1311 /* first check for overrides in the registry */
1313 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1314 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1316 const WCHAR *value = get_locale_value_name(lctype);
1318 if (value)
1320 if (lcflags & LOCALE_RETURN_NUMBER)
1322 WCHAR tmp[16];
1323 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1324 if (ret > 0)
1326 WCHAR *end;
1327 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1328 if (*end) /* invalid number */
1330 SetLastError( ERROR_INVALID_FLAGS );
1331 return 0;
1333 ret = sizeof(UINT)/sizeof(WCHAR);
1334 if (!buffer) return ret;
1335 if (ret > len)
1337 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1338 return 0;
1340 memcpy( buffer, &number, sizeof(number) );
1343 else ret = get_registry_locale_info( value, buffer, len );
1345 if (ret != -1) return ret;
1349 /* now load it from kernel resources */
1351 lang_id = LANGIDFROMLCID( lcid );
1353 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1354 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1355 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1357 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1358 ULongToPtr((lctype >> 4) + 1), lang_id )))
1360 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1361 return 0;
1363 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1364 return 0;
1366 p = LockResource( hmem );
1367 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1369 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1370 else if (is_genitive_name_supported( lctype ) && *p)
1372 /* genitive form's stored after a null separator from a nominative */
1373 for (i = 1; i <= *p; i++) if (!p[i]) break;
1375 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1377 ret = *p - i + 1;
1378 p += i;
1380 else ret = i;
1382 else
1383 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1385 if (!buffer) return ret;
1387 if (ret > len)
1389 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1390 return 0;
1393 if (lcflags & LOCALE_RETURN_NUMBER)
1395 UINT number;
1396 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1397 if (!tmp) return 0;
1398 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1399 tmp[*p] = 0;
1400 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1401 if (!*end)
1402 memcpy( buffer, &number, sizeof(number) );
1403 else /* invalid number */
1405 SetLastError( ERROR_INVALID_FLAGS );
1406 ret = 0;
1408 HeapFree( GetProcessHeap(), 0, tmp );
1410 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1411 lcid, lctype, buffer, len, number );
1413 else
1415 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1416 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1418 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1419 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1421 return ret;
1424 /******************************************************************************
1425 * GetLocaleInfoEx (KERNEL32.@)
1427 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1429 LCID lcid = LocaleNameToLCID(locale, 0);
1431 TRACE("%s, lcid=0x%x, 0x%x\n", debugstr_w(locale), lcid, info);
1433 if (!lcid) return 0;
1435 /* special handling for neutral locale names */
1436 if (info == LOCALE_SNAME && strlenW(locale) == 2)
1438 if (len && len < 3)
1440 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1441 return 0;
1444 if (len) strcpyW(buffer, locale);
1445 return 3;
1448 return GetLocaleInfoW(lcid, info, buffer, len);
1451 /******************************************************************************
1452 * SetLocaleInfoA [KERNEL32.@]
1454 * Set information about an aspect of a locale.
1456 * PARAMS
1457 * lcid [I] LCID of the locale
1458 * lctype [I] LCTYPE_ flags from "winnls.h"
1459 * data [I] Information to set
1461 * RETURNS
1462 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1463 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1464 * Failure: FALSE. Use GetLastError() to determine the cause.
1466 * NOTES
1467 * - Values are only be set for the current user locale; the system locale
1468 * settings cannot be changed.
1469 * - Any settings changed by this call are lost when the locale is changed by
1470 * the control panel (in Wine, this happens every time you change LANG).
1471 * - The native implementation of this function does not check that lcid matches
1472 * the current user locale, and simply sets the new values. Wine warns you in
1473 * this case, but behaves the same.
1475 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1477 UINT codepage = CP_ACP;
1478 WCHAR *strW;
1479 DWORD len;
1480 BOOL ret;
1482 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1484 if (!data)
1486 SetLastError( ERROR_INVALID_PARAMETER );
1487 return FALSE;
1489 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1490 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1492 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1493 return FALSE;
1495 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1496 ret = SetLocaleInfoW( lcid, lctype, strW );
1497 HeapFree( GetProcessHeap(), 0, strW );
1498 return ret;
1502 /******************************************************************************
1503 * SetLocaleInfoW (KERNEL32.@)
1505 * See SetLocaleInfoA.
1507 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1509 const WCHAR *value;
1510 static const WCHAR intlW[] = {'i','n','t','l',0 };
1511 UNICODE_STRING valueW;
1512 NTSTATUS status;
1513 HANDLE hkey;
1515 lctype &= 0xffff;
1516 value = get_locale_value_name( lctype );
1518 if (!data || !value)
1520 SetLastError( ERROR_INVALID_PARAMETER );
1521 return FALSE;
1524 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1526 SetLastError( ERROR_INVALID_FLAGS );
1527 return FALSE;
1530 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1532 /* FIXME: should check that data to set is sane */
1534 /* FIXME: profile functions should map to registry */
1535 WriteProfileStringW( intlW, value, data );
1537 if (!(hkey = create_registry_key())) return FALSE;
1538 RtlInitUnicodeString( &valueW, value );
1539 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1541 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1543 /* Set I-value from S value */
1544 WCHAR *lpD, *lpM, *lpY;
1545 WCHAR szBuff[2];
1547 lpD = strrchrW(data, 'd');
1548 lpM = strrchrW(data, 'M');
1549 lpY = strrchrW(data, 'y');
1551 if (lpD <= lpM)
1553 szBuff[0] = '1'; /* D-M-Y */
1555 else
1557 if (lpY <= lpM)
1558 szBuff[0] = '2'; /* Y-M-D */
1559 else
1560 szBuff[0] = '0'; /* M-D-Y */
1563 szBuff[1] = '\0';
1565 if (lctype == LOCALE_SSHORTDATE)
1566 lctype = LOCALE_IDATE;
1567 else
1568 lctype = LOCALE_ILDATE;
1570 value = get_locale_value_name( lctype );
1572 WriteProfileStringW( intlW, value, szBuff );
1574 RtlInitUnicodeString( &valueW, value );
1575 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1578 NtClose( hkey );
1580 if (status) SetLastError( RtlNtStatusToDosError(status) );
1581 return !status;
1585 /******************************************************************************
1586 * GetACP (KERNEL32.@)
1588 * Get the current Ansi code page Id for the system.
1590 * PARAMS
1591 * None.
1593 * RETURNS
1594 * The current Ansi code page identifier for the system.
1596 UINT WINAPI GetACP(void)
1598 assert( ansi_cptable );
1599 return ansi_cptable->info.codepage;
1603 /******************************************************************************
1604 * SetCPGlobal (KERNEL32.@)
1606 * Set the current Ansi code page Id for the system.
1608 * PARAMS
1609 * acp [I] code page ID to be the new ACP.
1611 * RETURNS
1612 * The previous ACP.
1614 UINT WINAPI SetCPGlobal( UINT acp )
1616 UINT ret = GetACP();
1617 const union cptable *new_cptable = wine_cp_get_table( acp );
1619 if (new_cptable) ansi_cptable = new_cptable;
1620 return ret;
1624 /***********************************************************************
1625 * GetOEMCP (KERNEL32.@)
1627 * Get the current OEM code page Id for the system.
1629 * PARAMS
1630 * None.
1632 * RETURNS
1633 * The current OEM code page identifier for the system.
1635 UINT WINAPI GetOEMCP(void)
1637 assert( oem_cptable );
1638 return oem_cptable->info.codepage;
1642 /***********************************************************************
1643 * IsValidCodePage (KERNEL32.@)
1645 * Determine if a given code page identifier is valid.
1647 * PARAMS
1648 * codepage [I] Code page Id to verify.
1650 * RETURNS
1651 * TRUE, If codepage is valid and available on the system,
1652 * FALSE otherwise.
1654 BOOL WINAPI IsValidCodePage( UINT codepage )
1656 switch(codepage) {
1657 case CP_UTF7:
1658 case CP_UTF8:
1659 return TRUE;
1660 default:
1661 return wine_cp_get_table( codepage ) != NULL;
1666 /***********************************************************************
1667 * IsDBCSLeadByteEx (KERNEL32.@)
1669 * Determine if a character is a lead byte in a given code page.
1671 * PARAMS
1672 * codepage [I] Code page for the test.
1673 * testchar [I] Character to test
1675 * RETURNS
1676 * TRUE, if testchar is a lead byte in codepage,
1677 * FALSE otherwise.
1679 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1681 const union cptable *table = get_codepage_table( codepage );
1682 return table && wine_is_dbcs_leadbyte( table, testchar );
1686 /***********************************************************************
1687 * IsDBCSLeadByte (KERNEL32.@)
1688 * IsDBCSLeadByte (KERNEL.207)
1690 * Determine if a character is a lead byte.
1692 * PARAMS
1693 * testchar [I] Character to test
1695 * RETURNS
1696 * TRUE, if testchar is a lead byte in the ANSI code page,
1697 * FALSE otherwise.
1699 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1701 if (!ansi_cptable) return FALSE;
1702 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1706 /***********************************************************************
1707 * GetCPInfo (KERNEL32.@)
1709 * Get information about a code page.
1711 * PARAMS
1712 * codepage [I] Code page number
1713 * cpinfo [O] Destination for code page information
1715 * RETURNS
1716 * Success: TRUE. cpinfo is updated with the information about codepage.
1717 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1719 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1721 const union cptable *table;
1723 if (!cpinfo)
1725 SetLastError( ERROR_INVALID_PARAMETER );
1726 return FALSE;
1729 if (!(table = get_codepage_table( codepage )))
1731 switch(codepage)
1733 case CP_UTF7:
1734 case CP_UTF8:
1735 cpinfo->DefaultChar[0] = 0x3f;
1736 cpinfo->DefaultChar[1] = 0;
1737 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1738 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1739 return TRUE;
1742 SetLastError( ERROR_INVALID_PARAMETER );
1743 return FALSE;
1745 if (table->info.def_char & 0xff00)
1747 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1748 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1750 else
1752 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1753 cpinfo->DefaultChar[1] = 0;
1755 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1756 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1757 else
1758 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1760 return TRUE;
1763 /***********************************************************************
1764 * GetCPInfoExA (KERNEL32.@)
1766 * Get extended information about a code page.
1768 * PARAMS
1769 * codepage [I] Code page number
1770 * dwFlags [I] Reserved, must to 0.
1771 * cpinfo [O] Destination for code page information
1773 * RETURNS
1774 * Success: TRUE. cpinfo is updated with the information about codepage.
1775 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1777 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1779 CPINFOEXW cpinfoW;
1781 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1782 return FALSE;
1784 /* the layout is the same except for CodePageName */
1785 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1786 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1787 return TRUE;
1790 /***********************************************************************
1791 * GetCPInfoExW (KERNEL32.@)
1793 * Unicode version of GetCPInfoExA.
1795 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1797 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1798 return FALSE;
1800 switch(codepage)
1802 case CP_UTF7:
1804 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1806 cpinfo->CodePage = CP_UTF7;
1807 cpinfo->UnicodeDefaultChar = 0x3f;
1808 strcpyW(cpinfo->CodePageName, utf7);
1809 break;
1812 case CP_UTF8:
1814 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1816 cpinfo->CodePage = CP_UTF8;
1817 cpinfo->UnicodeDefaultChar = 0x3f;
1818 strcpyW(cpinfo->CodePageName, utf8);
1819 break;
1822 default:
1824 const union cptable *table = get_codepage_table( codepage );
1826 cpinfo->CodePage = table->info.codepage;
1827 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1828 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1829 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1830 break;
1833 return TRUE;
1836 /***********************************************************************
1837 * EnumSystemCodePagesA (KERNEL32.@)
1839 * Call a user defined function for every code page installed on the system.
1841 * PARAMS
1842 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1843 * flags [I] Reserved, set to 0.
1845 * RETURNS
1846 * TRUE, If all code pages have been enumerated, or
1847 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1849 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1851 const union cptable *table;
1852 char buffer[10];
1853 int index = 0;
1855 for (;;)
1857 if (!(table = wine_cp_enum_table( index++ ))) break;
1858 sprintf( buffer, "%d", table->info.codepage );
1859 if (!lpfnCodePageEnum( buffer )) break;
1861 return TRUE;
1865 /***********************************************************************
1866 * EnumSystemCodePagesW (KERNEL32.@)
1868 * See EnumSystemCodePagesA.
1870 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1872 const union cptable *table;
1873 WCHAR buffer[10], *p;
1874 int page, index = 0;
1876 for (;;)
1878 if (!(table = wine_cp_enum_table( index++ ))) break;
1879 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1880 *--p = 0;
1881 page = table->info.codepage;
1884 *--p = '0' + (page % 10);
1885 page /= 10;
1886 } while( page );
1887 if (!lpfnCodePageEnum( p )) break;
1889 return TRUE;
1893 /***********************************************************************
1894 * MultiByteToWideChar (KERNEL32.@)
1896 * Convert a multibyte character string into a Unicode string.
1898 * PARAMS
1899 * page [I] Codepage character set to convert from
1900 * flags [I] Character mapping flags
1901 * src [I] Source string buffer
1902 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1903 * dst [O] Destination buffer
1904 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1906 * RETURNS
1907 * Success: If dstlen > 0, the number of characters written to dst.
1908 * If dstlen == 0, the number of characters needed to perform the
1909 * conversion. In both cases the count includes the terminating NUL.
1910 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1911 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1912 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1913 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1914 * possible for src.
1916 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1917 LPWSTR dst, INT dstlen )
1919 const union cptable *table;
1920 int ret;
1922 if (!src || !srclen || (!dst && dstlen))
1924 SetLastError( ERROR_INVALID_PARAMETER );
1925 return 0;
1928 if (srclen < 0) srclen = strlen(src) + 1;
1930 switch(page)
1932 case CP_SYMBOL:
1933 if (flags)
1935 SetLastError( ERROR_INVALID_FLAGS );
1936 return 0;
1938 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1939 break;
1940 case CP_UTF7:
1941 if (flags)
1943 SetLastError( ERROR_INVALID_FLAGS );
1944 return 0;
1946 FIXME("UTF-7 not supported\n");
1947 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1948 return 0;
1949 case CP_UNIXCP:
1950 if (unix_cptable)
1952 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1953 break;
1955 #ifdef __APPLE__
1956 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1957 #endif
1958 /* fall through */
1959 case CP_UTF8:
1960 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1961 break;
1962 default:
1963 if (!(table = get_codepage_table( page )))
1965 SetLastError( ERROR_INVALID_PARAMETER );
1966 return 0;
1968 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1969 break;
1972 if (ret < 0)
1974 switch(ret)
1976 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1977 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1979 ret = 0;
1981 TRACE("cp %d %s -> %s, ret = %d\n",
1982 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1983 return ret;
1987 /***********************************************************************
1988 * WideCharToMultiByte (KERNEL32.@)
1990 * Convert a Unicode character string into a multibyte string.
1992 * PARAMS
1993 * page [I] Code page character set to convert to
1994 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
1995 * src [I] Source string buffer
1996 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1997 * dst [O] Destination buffer
1998 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
1999 * defchar [I] Default character to use for conversion if no exact
2000 * conversion can be made
2001 * used [O] Set if default character was used in the conversion
2003 * RETURNS
2004 * Success: If dstlen > 0, the number of characters written to dst.
2005 * If dstlen == 0, number of characters needed to perform the
2006 * conversion. In both cases the count includes the terminating NUL.
2007 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
2008 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
2009 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
2010 * parameter was given.
2012 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
2013 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
2015 const union cptable *table;
2016 int ret, used_tmp;
2018 if (!src || !srclen || (!dst && dstlen))
2020 SetLastError( ERROR_INVALID_PARAMETER );
2021 return 0;
2024 if (srclen < 0) srclen = strlenW(src) + 1;
2026 switch(page)
2028 case CP_SYMBOL:
2029 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2030 if (flags)
2032 SetLastError( ERROR_INVALID_FLAGS );
2033 return 0;
2035 if (defchar || used)
2037 SetLastError( ERROR_INVALID_PARAMETER );
2038 return 0;
2040 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2041 break;
2042 case CP_UTF7:
2043 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2044 if (defchar || used)
2046 SetLastError( ERROR_INVALID_PARAMETER );
2047 return 0;
2049 if (flags)
2051 SetLastError( ERROR_INVALID_FLAGS );
2052 return 0;
2054 FIXME("UTF-7 not supported\n");
2055 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2056 return 0;
2057 case CP_UNIXCP:
2058 if (unix_cptable)
2060 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2061 defchar, used ? &used_tmp : NULL );
2062 break;
2064 /* fall through */
2065 case CP_UTF8:
2066 if (defchar || used)
2068 SetLastError( ERROR_INVALID_PARAMETER );
2069 return 0;
2071 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2072 break;
2073 default:
2074 if (!(table = get_codepage_table( page )))
2076 SetLastError( ERROR_INVALID_PARAMETER );
2077 return 0;
2079 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2080 defchar, used ? &used_tmp : NULL );
2081 if (used) *used = used_tmp;
2082 break;
2085 if (ret < 0)
2087 switch(ret)
2089 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2090 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2092 ret = 0;
2094 TRACE("cp %d %s -> %s, ret = %d\n",
2095 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2096 return ret;
2100 /***********************************************************************
2101 * GetThreadLocale (KERNEL32.@)
2103 * Get the current threads locale.
2105 * PARAMS
2106 * None.
2108 * RETURNS
2109 * The LCID currently associated with the calling thread.
2111 LCID WINAPI GetThreadLocale(void)
2113 LCID ret = NtCurrentTeb()->CurrentLocale;
2114 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2115 return ret;
2118 /**********************************************************************
2119 * SetThreadLocale (KERNEL32.@)
2121 * Set the current threads locale.
2123 * PARAMS
2124 * lcid [I] LCID of the locale to set
2126 * RETURNS
2127 * Success: TRUE. The threads locale is set to lcid.
2128 * Failure: FALSE. Use GetLastError() to determine the cause.
2130 BOOL WINAPI SetThreadLocale( LCID lcid )
2132 TRACE("(0x%04X)\n", lcid);
2134 lcid = ConvertDefaultLocale(lcid);
2136 if (lcid != GetThreadLocale())
2138 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2140 SetLastError(ERROR_INVALID_PARAMETER);
2141 return FALSE;
2144 NtCurrentTeb()->CurrentLocale = lcid;
2146 return TRUE;
2149 /**********************************************************************
2150 * SetThreadUILanguage (KERNEL32.@)
2152 * Set the current threads UI language.
2154 * PARAMS
2155 * langid [I] LANGID of the language to set, or 0 to use
2156 * the available language which is best supported
2157 * for console applications
2159 * RETURNS
2160 * Success: The return value is the same as the input value.
2161 * Failure: The return value differs from the input value.
2162 * Use GetLastError() to determine the cause.
2164 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2166 TRACE("(0x%04x) stub - returning success\n", langid);
2167 return langid;
2170 /******************************************************************************
2171 * ConvertDefaultLocale (KERNEL32.@)
2173 * Convert a default locale identifier into a real identifier.
2175 * PARAMS
2176 * lcid [I] LCID identifier of the locale to convert
2178 * RETURNS
2179 * lcid unchanged, if not a default locale or its sublanguage is
2180 * not SUBLANG_NEUTRAL.
2181 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2182 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2183 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2185 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2187 LANGID langid;
2189 switch (lcid)
2191 case LOCALE_SYSTEM_DEFAULT:
2192 lcid = GetSystemDefaultLCID();
2193 break;
2194 case LOCALE_USER_DEFAULT:
2195 case LOCALE_NEUTRAL:
2196 lcid = GetUserDefaultLCID();
2197 break;
2198 default:
2199 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2200 langid = LANGIDFROMLCID(lcid);
2201 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2203 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2204 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2207 return lcid;
2211 /******************************************************************************
2212 * IsValidLocale (KERNEL32.@)
2214 * Determine if a locale is valid.
2216 * PARAMS
2217 * lcid [I] LCID of the locale to check
2218 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2220 * RETURNS
2221 * TRUE, if lcid is valid,
2222 * FALSE, otherwise.
2224 * NOTES
2225 * Wine does not currently make the distinction between supported and installed. All
2226 * languages supported are installed by default.
2228 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2230 /* check if language is registered in the kernel32 resources */
2231 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2232 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2235 /******************************************************************************
2236 * IsValidLocaleName (KERNEL32.@)
2238 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2240 struct locale_name locale_name;
2242 /* string parsing */
2243 parse_locale_name( locale, &locale_name );
2245 TRACE( "found lcid %x for %s, matches %d\n",
2246 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2248 return locale_name.matches > 0;
2251 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2252 LPCSTR name, WORD LangID, LONG_PTR lParam )
2254 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2255 char buf[20];
2257 sprintf(buf, "%08x", (UINT)LangID);
2258 return lpfnLocaleEnum( buf );
2261 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2262 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2264 static const WCHAR formatW[] = {'%','0','8','x',0};
2265 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2266 WCHAR buf[20];
2267 sprintfW( buf, formatW, (UINT)LangID );
2268 return lpfnLocaleEnum( buf );
2271 /******************************************************************************
2272 * EnumSystemLocalesA (KERNEL32.@)
2274 * Call a users function for each locale available on the system.
2276 * PARAMS
2277 * lpfnLocaleEnum [I] Callback function to call for each locale
2278 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2280 * RETURNS
2281 * Success: TRUE.
2282 * Failure: FALSE. Use GetLastError() to determine the cause.
2284 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2286 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2287 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2288 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2289 (LONG_PTR)lpfnLocaleEnum);
2290 return TRUE;
2294 /******************************************************************************
2295 * EnumSystemLocalesW (KERNEL32.@)
2297 * See EnumSystemLocalesA.
2299 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2301 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2302 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2303 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2304 (LONG_PTR)lpfnLocaleEnum);
2305 return TRUE;
2309 struct enum_locale_ex_data
2311 LOCALE_ENUMPROCEX proc;
2312 DWORD flags;
2313 LPARAM lparam;
2316 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2317 LPCWSTR name, WORD lang, LONG_PTR lparam )
2319 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2320 WCHAR buffer[256];
2321 DWORD neutral;
2322 unsigned int flags;
2324 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2325 buffer, sizeof(buffer) / sizeof(WCHAR) );
2326 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2327 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2328 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2329 neutral = 0;
2330 flags = LOCALE_WINDOWS;
2331 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2332 if (data->flags && ~(data->flags & flags)) return TRUE;
2333 return data->proc( buffer, flags, data->lparam );
2336 /******************************************************************************
2337 * EnumSystemLocalesEx (KERNEL32.@)
2339 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2341 struct enum_locale_ex_data data;
2343 if (reserved)
2345 SetLastError( ERROR_INVALID_PARAMETER );
2346 return FALSE;
2348 data.proc = proc;
2349 data.flags = flags;
2350 data.lparam = lparam;
2351 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2352 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2353 enum_locale_ex_proc, (LONG_PTR)&data );
2354 return TRUE;
2358 /***********************************************************************
2359 * VerLanguageNameA (KERNEL32.@)
2361 * Get the name of a language.
2363 * PARAMS
2364 * wLang [I] LANGID of the language
2365 * szLang [O] Destination for the language name
2367 * RETURNS
2368 * Success: The size of the language name. If szLang is non-NULL, it is filled
2369 * with the name.
2370 * Failure: 0. Use GetLastError() to determine the cause.
2373 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2375 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2379 /***********************************************************************
2380 * VerLanguageNameW (KERNEL32.@)
2382 * See VerLanguageNameA.
2384 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2386 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2390 /******************************************************************************
2391 * GetStringTypeW (KERNEL32.@)
2393 * See GetStringTypeA.
2395 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2397 static const unsigned char type2_map[16] =
2399 C2_NOTAPPLICABLE, /* unassigned */
2400 C2_LEFTTORIGHT, /* L */
2401 C2_RIGHTTOLEFT, /* R */
2402 C2_EUROPENUMBER, /* EN */
2403 C2_EUROPESEPARATOR, /* ES */
2404 C2_EUROPETERMINATOR, /* ET */
2405 C2_ARABICNUMBER, /* AN */
2406 C2_COMMONSEPARATOR, /* CS */
2407 C2_BLOCKSEPARATOR, /* B */
2408 C2_SEGMENTSEPARATOR, /* S */
2409 C2_WHITESPACE, /* WS */
2410 C2_OTHERNEUTRAL, /* ON */
2411 C2_RIGHTTOLEFT, /* AL */
2412 C2_NOTAPPLICABLE, /* NSM */
2413 C2_NOTAPPLICABLE, /* BN */
2414 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2417 if (count == -1) count = strlenW(src) + 1;
2418 switch(type)
2420 case CT_CTYPE1:
2421 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2422 break;
2423 case CT_CTYPE2:
2424 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2425 break;
2426 case CT_CTYPE3:
2428 WARN("CT_CTYPE3: semi-stub.\n");
2429 while (count--)
2431 int c = *src;
2432 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2434 type1 = get_char_typeW( *src++ ) & 0xfff;
2435 /* try to construct type3 from type1 */
2436 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2437 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2438 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2439 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2440 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2441 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2442 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2444 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2445 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2446 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2447 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2448 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2449 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2450 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2451 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2453 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2454 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2455 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2456 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2457 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2458 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2459 *chartype++ = type3;
2461 break;
2463 default:
2464 SetLastError( ERROR_INVALID_PARAMETER );
2465 return FALSE;
2467 return TRUE;
2471 /******************************************************************************
2472 * GetStringTypeExW (KERNEL32.@)
2474 * See GetStringTypeExA.
2476 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2478 /* locale is ignored for Unicode */
2479 return GetStringTypeW( type, src, count, chartype );
2483 /******************************************************************************
2484 * GetStringTypeA (KERNEL32.@)
2486 * Get characteristics of the characters making up a string.
2488 * PARAMS
2489 * locale [I] Locale Id for the string
2490 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2491 * src [I] String to analyse
2492 * count [I] Length of src in chars, or -1 if src is NUL terminated
2493 * chartype [O] Destination for the calculated characteristics
2495 * RETURNS
2496 * Success: TRUE. chartype is filled with the requested characteristics of each char
2497 * in src.
2498 * Failure: FALSE. Use GetLastError() to determine the cause.
2500 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2502 UINT cp;
2503 INT countW;
2504 LPWSTR srcW;
2505 BOOL ret = FALSE;
2507 if(count == -1) count = strlen(src) + 1;
2509 if (!(cp = get_lcid_codepage( locale )))
2511 FIXME("For locale %04x using current ANSI code page\n", locale);
2512 cp = GetACP();
2515 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2516 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2518 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2520 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2521 * string, with multibyte characters there maybe be more bytes in count
2522 * than character space in the buffer!
2524 ret = GetStringTypeW(type, srcW, countW, chartype);
2525 HeapFree(GetProcessHeap(), 0, srcW);
2527 return ret;
2530 /******************************************************************************
2531 * GetStringTypeExA (KERNEL32.@)
2533 * Get characteristics of the characters making up a string.
2535 * PARAMS
2536 * locale [I] Locale Id for the string
2537 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2538 * src [I] String to analyse
2539 * count [I] Length of src in chars, or -1 if src is NUL terminated
2540 * chartype [O] Destination for the calculated characteristics
2542 * RETURNS
2543 * Success: TRUE. chartype is filled with the requested characteristics of each char
2544 * in src.
2545 * Failure: FALSE. Use GetLastError() to determine the cause.
2547 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2549 return GetStringTypeA(locale, type, src, count, chartype);
2552 /*************************************************************************
2553 * LCMapStringEx (KERNEL32.@)
2555 * Map characters in a locale sensitive string.
2557 * PARAMS
2558 * name [I] Locale name for the conversion.
2559 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2560 * src [I] String to map
2561 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2562 * dst [O] Destination for mapped string
2563 * dstlen [I] Length of dst in characters
2564 * version [I] reserved, must be NULL
2565 * reserved [I] reserved, must be NULL
2566 * lparam [I] reserved, must be 0
2568 * RETURNS
2569 * Success: The length of the mapped string in dst, including the NUL terminator.
2570 * Failure: 0. Use GetLastError() to determine the cause.
2572 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2573 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2575 LPWSTR dst_ptr;
2577 if (version) FIXME("unsupported version structure %p\n", version);
2578 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2579 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2581 if (!src || !srclen || dstlen < 0)
2583 SetLastError(ERROR_INVALID_PARAMETER);
2584 return 0;
2587 /* mutually exclusive flags */
2588 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2589 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2590 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2591 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2593 SetLastError(ERROR_INVALID_FLAGS);
2594 return 0;
2597 if (!dstlen) dst = NULL;
2599 if (flags & LCMAP_SORTKEY)
2601 INT ret;
2602 if (src == dst)
2604 SetLastError(ERROR_INVALID_FLAGS);
2605 return 0;
2608 if (srclen < 0) srclen = strlenW(src);
2610 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2611 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2613 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2614 if (ret == 0)
2615 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2616 else
2617 ret++;
2618 return ret;
2621 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2622 if (flags & SORT_STRINGSORT)
2624 SetLastError(ERROR_INVALID_FLAGS);
2625 return 0;
2628 if (srclen < 0) srclen = strlenW(src) + 1;
2630 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2631 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2633 if (!dst) /* return required string length */
2635 INT len;
2637 for (len = 0; srclen; src++, srclen--)
2639 WCHAR wch = *src;
2640 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2641 * and skips white space and punctuation characters for
2642 * NORM_IGNORESYMBOLS.
2644 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2645 continue;
2646 len++;
2648 return len;
2651 if (flags & LCMAP_UPPERCASE)
2653 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2655 WCHAR wch = *src;
2656 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2657 continue;
2658 *dst_ptr++ = toupperW(wch);
2659 dstlen--;
2662 else if (flags & LCMAP_LOWERCASE)
2664 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2666 WCHAR wch = *src;
2667 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2668 continue;
2669 *dst_ptr++ = tolowerW(wch);
2670 dstlen--;
2673 else
2675 if (src == dst)
2677 SetLastError(ERROR_INVALID_FLAGS);
2678 return 0;
2680 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2682 WCHAR wch = *src;
2683 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2684 continue;
2685 *dst_ptr++ = wch;
2686 dstlen--;
2690 if (srclen)
2692 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2693 return 0;
2696 return dst_ptr - dst;
2699 /*************************************************************************
2700 * LCMapStringW (KERNEL32.@)
2702 * See LCMapStringA.
2704 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2705 LPWSTR dst, INT dstlen)
2707 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2708 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2710 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2713 /*************************************************************************
2714 * LCMapStringA (KERNEL32.@)
2716 * Map characters in a locale sensitive string.
2718 * PARAMS
2719 * lcid [I] LCID for the conversion.
2720 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2721 * src [I] String to map
2722 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2723 * dst [O] Destination for mapped string
2724 * dstlen [I] Length of dst in characters
2726 * RETURNS
2727 * Success: The length of the mapped string in dst, including the NUL terminator.
2728 * Failure: 0. Use GetLastError() to determine the cause.
2730 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2731 LPSTR dst, INT dstlen)
2733 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2734 LPWSTR srcW, dstW;
2735 INT ret = 0, srclenW, dstlenW;
2736 UINT locale_cp = CP_ACP;
2738 if (!src || !srclen || dstlen < 0)
2740 SetLastError(ERROR_INVALID_PARAMETER);
2741 return 0;
2744 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2746 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2747 if (srclenW)
2748 srcW = bufW;
2749 else
2751 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2752 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2753 if (!srcW)
2755 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2756 return 0;
2758 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2761 if (flags & LCMAP_SORTKEY)
2763 if (src == dst)
2765 SetLastError(ERROR_INVALID_FLAGS);
2766 goto map_string_exit;
2768 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2769 if (ret == 0)
2770 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2771 else
2772 ret++;
2773 goto map_string_exit;
2776 if (flags & SORT_STRINGSORT)
2778 SetLastError(ERROR_INVALID_FLAGS);
2779 goto map_string_exit;
2782 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2783 if (!dstlenW)
2784 goto map_string_exit;
2786 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2787 if (!dstW)
2789 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2790 goto map_string_exit;
2793 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2794 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2795 HeapFree(GetProcessHeap(), 0, dstW);
2797 map_string_exit:
2798 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2799 return ret;
2802 /*************************************************************************
2803 * FoldStringA (KERNEL32.@)
2805 * Map characters in a string.
2807 * PARAMS
2808 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2809 * src [I] String to map
2810 * srclen [I] Length of src, or -1 if src is NUL terminated
2811 * dst [O] Destination for mapped string
2812 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2814 * RETURNS
2815 * Success: The length of the string written to dst, including the terminating NUL. If
2816 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2817 * and dst may be NULL.
2818 * Failure: 0. Use GetLastError() to determine the cause.
2820 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2821 LPSTR dst, INT dstlen)
2823 INT ret = 0, srclenW = 0;
2824 WCHAR *srcW = NULL, *dstW = NULL;
2826 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2828 SetLastError(ERROR_INVALID_PARAMETER);
2829 return 0;
2832 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2833 src, srclen, NULL, 0);
2834 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2836 if (!srcW)
2838 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2839 goto FoldStringA_exit;
2842 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2843 src, srclen, srcW, srclenW);
2845 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2847 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2848 if (ret && dstlen)
2850 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2852 if (!dstW)
2854 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2855 goto FoldStringA_exit;
2858 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2859 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2861 ret = 0;
2862 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2866 HeapFree(GetProcessHeap(), 0, dstW);
2868 FoldStringA_exit:
2869 HeapFree(GetProcessHeap(), 0, srcW);
2870 return ret;
2873 /*************************************************************************
2874 * FoldStringW (KERNEL32.@)
2876 * See FoldStringA.
2878 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2879 LPWSTR dst, INT dstlen)
2881 int ret;
2883 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2885 case 0:
2886 if (dwFlags)
2887 break;
2888 /* Fall through for dwFlags == 0 */
2889 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2890 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2891 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2892 SetLastError(ERROR_INVALID_FLAGS);
2893 return 0;
2896 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2898 SetLastError(ERROR_INVALID_PARAMETER);
2899 return 0;
2902 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2903 if (!ret)
2904 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2905 return ret;
2908 /******************************************************************************
2909 * CompareStringW (KERNEL32.@)
2911 * See CompareStringA.
2913 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
2914 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2916 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
2919 /******************************************************************************
2920 * CompareStringEx (KERNEL32.@)
2922 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
2923 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
2925 INT ret;
2927 if (version) FIXME("unexpected version parameter\n");
2928 if (reserved) FIXME("unexpected reserved value\n");
2929 if (lParam) FIXME("unexpected lParam\n");
2931 if (!str1 || !str2)
2933 SetLastError(ERROR_INVALID_PARAMETER);
2934 return 0;
2937 if( flags & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2938 SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2940 SetLastError(ERROR_INVALID_FLAGS);
2941 return 0;
2944 /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2945 if (flags & 0x10000000)
2946 WARN("Ignoring unknown flags 0x10000000\n");
2948 if (len1 < 0) len1 = strlenW(str1);
2949 if (len2 < 0) len2 = strlenW(str2);
2951 ret = wine_compare_string(flags, str1, len1, str2, len2);
2953 if (ret) /* need to translate result */
2954 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2955 return CSTR_EQUAL;
2958 /******************************************************************************
2959 * CompareStringA (KERNEL32.@)
2961 * Compare two locale sensitive strings.
2963 * PARAMS
2964 * lcid [I] LCID for the comparison
2965 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
2966 * str1 [I] First string to compare
2967 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
2968 * str2 [I] Second string to compare
2969 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
2971 * RETURNS
2972 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2973 * str1 is less than, equal to or greater than str2 respectively.
2974 * Failure: FALSE. Use GetLastError() to determine the cause.
2976 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
2977 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2979 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2980 WCHAR *buf2W = buf1W + 130;
2981 LPWSTR str1W, str2W;
2982 INT len1W, len2W, ret;
2983 UINT locale_cp = CP_ACP;
2985 if (!str1 || !str2)
2987 SetLastError(ERROR_INVALID_PARAMETER);
2988 return 0;
2990 if (len1 < 0) len1 = strlen(str1);
2991 if (len2 < 0) len2 = strlen(str2);
2993 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2995 if (len1)
2997 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2998 if (len1W)
2999 str1W = buf1W;
3000 else
3002 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
3003 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
3004 if (!str1W)
3006 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3007 return 0;
3009 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3012 else
3014 len1W = 0;
3015 str1W = buf1W;
3018 if (len2)
3020 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3021 if (len2W)
3022 str2W = buf2W;
3023 else
3025 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3026 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3027 if (!str2W)
3029 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3030 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3031 return 0;
3033 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3036 else
3038 len2W = 0;
3039 str2W = buf2W;
3042 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3044 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3045 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3046 return ret;
3049 /******************************************************************************
3050 * CompareStringOrdinal (KERNEL32.@)
3052 INT WINAPI CompareStringOrdinal(const WCHAR *str1, INT len1, const WCHAR *str2, INT len2, BOOL ignore_case)
3054 int ret, len;
3056 if (!str1 || !str2)
3058 SetLastError(ERROR_INVALID_PARAMETER);
3059 return 0;
3061 if (len1 < 0) len1 = strlenW(str1);
3062 if (len2 < 0) len2 = strlenW(str2);
3064 len = min(len1, len2);
3065 if (ignore_case)
3067 ret = memicmpW(str1, str2, len);
3069 else
3071 ret = 0;
3072 for (; len > 0; len--)
3073 if ((ret = (*str1++ - *str2++))) break;
3075 if (!ret) ret = len1 - len2;
3077 if (ret < 0) return CSTR_LESS_THAN;
3078 if (ret > 0) return CSTR_GREATER_THAN;
3079 return CSTR_EQUAL;
3082 /*************************************************************************
3083 * lstrcmp (KERNEL32.@)
3084 * lstrcmpA (KERNEL32.@)
3086 * Compare two strings using the current thread locale.
3088 * PARAMS
3089 * str1 [I] First string to compare
3090 * str2 [I] Second string to compare
3092 * RETURNS
3093 * Success: A number less than, equal to or greater than 0 depending on whether
3094 * str1 is less than, equal to or greater than str2 respectively.
3095 * Failure: FALSE. Use GetLastError() to determine the cause.
3097 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3099 int ret;
3101 if ((str1 == NULL) && (str2 == NULL)) return 0;
3102 if (str1 == NULL) return -1;
3103 if (str2 == NULL) return 1;
3105 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3106 if (ret) ret -= 2;
3108 return ret;
3111 /*************************************************************************
3112 * lstrcmpi (KERNEL32.@)
3113 * lstrcmpiA (KERNEL32.@)
3115 * Compare two strings using the current thread locale, ignoring case.
3117 * PARAMS
3118 * str1 [I] First string to compare
3119 * str2 [I] Second string to compare
3121 * RETURNS
3122 * Success: A number less than, equal to or greater than 0 depending on whether
3123 * str2 is less than, equal to or greater than str1 respectively.
3124 * Failure: FALSE. Use GetLastError() to determine the cause.
3126 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3128 int ret;
3130 if ((str1 == NULL) && (str2 == NULL)) return 0;
3131 if (str1 == NULL) return -1;
3132 if (str2 == NULL) return 1;
3134 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3135 if (ret) ret -= 2;
3137 return ret;
3140 /*************************************************************************
3141 * lstrcmpW (KERNEL32.@)
3143 * See lstrcmpA.
3145 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3147 int ret;
3149 if ((str1 == NULL) && (str2 == NULL)) return 0;
3150 if (str1 == NULL) return -1;
3151 if (str2 == NULL) return 1;
3153 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3154 if (ret) ret -= 2;
3156 return ret;
3159 /*************************************************************************
3160 * lstrcmpiW (KERNEL32.@)
3162 * See lstrcmpiA.
3164 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3166 int ret;
3168 if ((str1 == NULL) && (str2 == NULL)) return 0;
3169 if (str1 == NULL) return -1;
3170 if (str2 == NULL) return 1;
3172 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3173 if (ret) ret -= 2;
3175 return ret;
3178 /******************************************************************************
3179 * LOCALE_Init
3181 void LOCALE_Init(void)
3183 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3184 const union cptable *unix_cp );
3186 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3188 #ifdef __APPLE__
3189 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3190 char user_locale[50];
3192 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3193 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3194 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3195 CFStringRef user_locale_string_ref;
3197 if (user_locale_country_ref)
3199 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
3200 user_locale_lang_ref, user_locale_country_ref);
3202 else
3204 user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
3207 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3208 strcat(user_locale, ".UTF-8");
3210 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3211 setenv( "LANG", user_locale, 0 );
3212 TRACE( "setting locale to '%s'\n", user_locale );
3213 #endif /* __APPLE__ */
3215 setlocale( LC_ALL, "" );
3217 unix_cp = setup_unix_locales();
3218 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3220 #ifdef __APPLE__
3221 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3222 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3224 /* Retrieve the preferred language as chosen in System Preferences. */
3225 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3226 leave things be. */
3227 CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
3228 CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
3229 CFStringRef user_language_string_ref;
3230 if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
3231 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
3232 !CFEqual(user_language_string_ref, user_locale_lang_ref) &&
3233 !CFEqual(user_language_string_ref, canonical_lang_string_ref))
3235 struct locale_name locale_name;
3236 WCHAR buffer[128];
3237 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3238 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3239 parse_locale_name( buffer, &locale_name );
3240 lcid_LC_MESSAGES = locale_name.lcid;
3241 TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
3243 if (preferred_langs)
3244 CFRelease( preferred_langs );
3245 if (canonical_lang_string_ref)
3246 CFRelease( canonical_lang_string_ref );
3249 CFRelease( user_locale_ref );
3250 CFRelease( user_locale_string_ref );
3251 #endif
3253 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3254 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3255 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3257 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3258 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3259 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3260 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3261 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3262 if (!unix_cp)
3263 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3264 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3266 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3267 ansi_cptable = wine_cp_get_table( 1252 );
3268 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3269 oem_cptable = wine_cp_get_table( 437 );
3270 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3271 mac_cptable = wine_cp_get_table( 10000 );
3272 if (unix_cp != CP_UTF8)
3274 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3275 unix_cptable = wine_cp_get_table( 28591 );
3278 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3280 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3281 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3282 mac_cptable->info.codepage, unix_cp );
3284 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3287 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3289 UNICODE_STRING keyName;
3290 OBJECT_ATTRIBUTES attr;
3291 HANDLE hkey;
3293 RtlInitUnicodeString( &keyName, szKeyName );
3294 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3296 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3297 hkey = 0;
3299 return hkey;
3302 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3303 ULONG keyNameSize)
3305 BYTE buffer[80];
3306 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3307 DWORD dwLen;
3309 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3310 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3311 info->NameLength > keyNameSize)
3313 return FALSE;
3316 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3318 memcpy( szKeyName, info->Name, info->NameLength);
3319 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3321 TRACE("returning %s\n", debugstr_w(szKeyName));
3322 return TRUE;
3325 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3326 LPWSTR szValueName, ULONG valueNameSize,
3327 LPWSTR szValueData, ULONG valueDataSize)
3329 BYTE buffer[80];
3330 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3331 DWORD dwLen;
3333 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3334 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3335 info->NameLength > valueNameSize ||
3336 info->DataLength > valueDataSize)
3338 return FALSE;
3341 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3343 memcpy( szValueName, info->Name, info->NameLength);
3344 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3345 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3346 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3348 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3349 return TRUE;
3352 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3354 BYTE buffer[128];
3355 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3356 DWORD dwSize = sizeof(buffer);
3357 UNICODE_STRING valueName;
3359 RtlInitUnicodeString( &valueName, szValueName );
3361 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3362 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3363 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3364 info->DataLength == sizeof(DWORD))
3366 memcpy(lpVal, info->Data, sizeof(DWORD));
3367 return TRUE;
3370 return FALSE;
3373 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3375 LANGID langId;
3376 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3377 HRSRC hResource;
3378 BOOL bRet = FALSE;
3380 /* FIXME: Is it correct to use the system default langid? */
3381 langId = GetSystemDefaultLangID();
3383 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3384 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3386 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3388 if (hResource)
3390 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3392 if (hResDir)
3394 ULONG iResourceIndex = lgrpid & 0xf;
3395 LPCWSTR lpResEntry = LockResource( hResDir );
3396 ULONG i;
3398 for (i = 0; i < iResourceIndex; i++)
3399 lpResEntry += *lpResEntry + 1;
3401 if (*lpResEntry < nameSize)
3403 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3404 szName[*lpResEntry] = '\0';
3405 bRet = TRUE;
3409 FreeResource( hResource );
3411 return bRet;
3414 /* Registry keys for NLS related information */
3416 static const WCHAR szCountryListName[] = {
3417 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3418 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3419 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3420 'T','e','l','e','p','h','o','n','y','\\',
3421 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3425 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3426 typedef struct
3428 LANGUAGEGROUP_ENUMPROCA procA;
3429 LANGUAGEGROUP_ENUMPROCW procW;
3430 DWORD dwFlags;
3431 LONG_PTR lParam;
3432 } ENUMLANGUAGEGROUP_CALLBACKS;
3434 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3435 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3437 WCHAR szNumber[10], szValue[4];
3438 HANDLE hKey;
3439 BOOL bContinue = TRUE;
3440 ULONG ulIndex = 0;
3442 if (!lpProcs)
3444 SetLastError(ERROR_INVALID_PARAMETER);
3445 return FALSE;
3448 switch (lpProcs->dwFlags)
3450 case 0:
3451 /* Default to LGRPID_INSTALLED */
3452 lpProcs->dwFlags = LGRPID_INSTALLED;
3453 /* Fall through... */
3454 case LGRPID_INSTALLED:
3455 case LGRPID_SUPPORTED:
3456 break;
3457 default:
3458 SetLastError(ERROR_INVALID_FLAGS);
3459 return FALSE;
3462 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3464 if (!hKey)
3465 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3467 while (bContinue)
3469 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3470 szValue, sizeof(szValue) ))
3472 BOOL bInstalled = szValue[0] == '1';
3473 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3475 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3476 bInstalled ? "" : "not ");
3478 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3480 WCHAR szGrpName[48];
3482 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3483 szGrpName[0] = '\0';
3485 if (lpProcs->procW)
3486 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3487 lpProcs->lParam );
3488 else
3490 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3491 char szGrpNameA[48];
3493 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3494 * or whether the language names are ever localised. Assume CP_ACP.
3497 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3498 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3500 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3501 lpProcs->lParam );
3505 ulIndex++;
3507 else
3508 bContinue = FALSE;
3510 if (!bContinue)
3511 break;
3514 if (hKey)
3515 NtClose( hKey );
3517 return TRUE;
3520 /******************************************************************************
3521 * EnumSystemLanguageGroupsA (KERNEL32.@)
3523 * Call a users function for each language group available on the system.
3525 * PARAMS
3526 * pLangGrpEnumProc [I] Callback function to call for each language group
3527 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3528 * lParam [I] User parameter to pass to pLangGrpEnumProc
3530 * RETURNS
3531 * Success: TRUE.
3532 * Failure: FALSE. Use GetLastError() to determine the cause.
3534 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3535 DWORD dwFlags, LONG_PTR lParam)
3537 ENUMLANGUAGEGROUP_CALLBACKS procs;
3539 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3541 procs.procA = pLangGrpEnumProc;
3542 procs.procW = NULL;
3543 procs.dwFlags = dwFlags;
3544 procs.lParam = lParam;
3546 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3549 /******************************************************************************
3550 * EnumSystemLanguageGroupsW (KERNEL32.@)
3552 * See EnumSystemLanguageGroupsA.
3554 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3555 DWORD dwFlags, LONG_PTR lParam)
3557 ENUMLANGUAGEGROUP_CALLBACKS procs;
3559 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3561 procs.procA = NULL;
3562 procs.procW = pLangGrpEnumProc;
3563 procs.dwFlags = dwFlags;
3564 procs.lParam = lParam;
3566 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3569 /******************************************************************************
3570 * IsValidLanguageGroup (KERNEL32.@)
3572 * Determine if a language group is supported and/or installed.
3574 * PARAMS
3575 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3576 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3578 * RETURNS
3579 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3580 * FALSE otherwise.
3582 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3584 static const WCHAR szFormat[] = { '%','x','\0' };
3585 WCHAR szValueName[16], szValue[2];
3586 BOOL bSupported = FALSE, bInstalled = FALSE;
3587 HANDLE hKey;
3590 switch (dwFlags)
3592 case LGRPID_INSTALLED:
3593 case LGRPID_SUPPORTED:
3595 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3597 sprintfW( szValueName, szFormat, lgrpid );
3599 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3601 bSupported = TRUE;
3603 if (szValue[0] == '1')
3604 bInstalled = TRUE;
3607 if (hKey)
3608 NtClose( hKey );
3610 break;
3613 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3614 (dwFlags == LGRPID_INSTALLED && bInstalled))
3615 return TRUE;
3617 return FALSE;
3620 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3621 typedef struct
3623 LANGGROUPLOCALE_ENUMPROCA procA;
3624 LANGGROUPLOCALE_ENUMPROCW procW;
3625 DWORD dwFlags;
3626 LGRPID lgrpid;
3627 LONG_PTR lParam;
3628 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3630 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3631 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3633 static const WCHAR szAlternateSortsKeyName[] = {
3634 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3636 WCHAR szNumber[10], szValue[4];
3637 HANDLE hKey;
3638 BOOL bContinue = TRUE, bAlternate = FALSE;
3639 LGRPID lgrpid;
3640 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3642 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3644 SetLastError(ERROR_INVALID_PARAMETER);
3645 return FALSE;
3648 if (lpProcs->dwFlags)
3650 SetLastError(ERROR_INVALID_FLAGS);
3651 return FALSE;
3654 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3656 if (!hKey)
3657 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3659 while (bContinue)
3661 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3662 szValue, sizeof(szValue) ))
3664 lgrpid = strtoulW( szValue, NULL, 16 );
3666 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3667 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3669 if (lgrpid == lpProcs->lgrpid)
3671 LCID lcid;
3673 lcid = strtoulW( szNumber, NULL, 16 );
3675 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3676 * '00000437 ;Georgian'
3677 * At present we only pass the LCID string.
3680 if (lpProcs->procW)
3681 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3682 else
3684 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3686 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3688 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3692 ulIndex++;
3694 else
3696 /* Finished enumerating this key */
3697 if (!bAlternate)
3699 /* Enumerate alternate sorts also */
3700 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3701 bAlternate = TRUE;
3702 ulIndex = 0;
3704 else
3705 bContinue = FALSE; /* Finished both keys */
3708 if (!bContinue)
3709 break;
3712 if (hKey)
3713 NtClose( hKey );
3715 return TRUE;
3718 /******************************************************************************
3719 * EnumLanguageGroupLocalesA (KERNEL32.@)
3721 * Call a users function for every locale in a language group available on the system.
3723 * PARAMS
3724 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3725 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3726 * dwFlags [I] Reserved, set to 0
3727 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3729 * RETURNS
3730 * Success: TRUE.
3731 * Failure: FALSE. Use GetLastError() to determine the cause.
3733 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3734 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3736 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3738 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3740 callbacks.procA = pLangGrpLcEnumProc;
3741 callbacks.procW = NULL;
3742 callbacks.dwFlags = dwFlags;
3743 callbacks.lgrpid = lgrpid;
3744 callbacks.lParam = lParam;
3746 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3749 /******************************************************************************
3750 * EnumLanguageGroupLocalesW (KERNEL32.@)
3752 * See EnumLanguageGroupLocalesA.
3754 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3755 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3757 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3759 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3761 callbacks.procA = NULL;
3762 callbacks.procW = pLangGrpLcEnumProc;
3763 callbacks.dwFlags = dwFlags;
3764 callbacks.lgrpid = lgrpid;
3765 callbacks.lParam = lParam;
3767 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3770 /******************************************************************************
3771 * EnumSystemGeoID (KERNEL32.@)
3773 * Call a users function for every location available on the system.
3775 * PARAMS
3776 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3777 * reserved [I] Reserved, set to 0
3778 * pGeoEnumProc [I] Callback function to call for each location
3780 * RETURNS
3781 * Success: TRUE.
3782 * Failure: FALSE. Use GetLastError() to determine the cause.
3784 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3786 static const WCHAR szCountryCodeValueName[] = {
3787 'C','o','u','n','t','r','y','C','o','d','e','\0'
3789 WCHAR szNumber[10];
3790 HANDLE hKey;
3791 ULONG ulIndex = 0;
3793 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3795 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3797 SetLastError(ERROR_INVALID_PARAMETER);
3798 return FALSE;
3801 hKey = NLS_RegOpenKey( 0, szCountryListName );
3803 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3805 BOOL bContinue = TRUE;
3806 DWORD dwGeoId;
3807 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3809 if (hSubKey)
3811 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3813 TRACE("Got geoid %d\n", dwGeoId);
3815 if (!pGeoEnumProc( dwGeoId ))
3816 bContinue = FALSE;
3819 NtClose( hSubKey );
3822 if (!bContinue)
3823 break;
3825 ulIndex++;
3828 if (hKey)
3829 NtClose( hKey );
3831 return TRUE;
3834 /******************************************************************************
3835 * InvalidateNLSCache (KERNEL32.@)
3837 * Invalidate the cache of NLS values.
3839 * PARAMS
3840 * None.
3842 * RETURNS
3843 * Success: TRUE.
3844 * Failure: FALSE.
3846 BOOL WINAPI InvalidateNLSCache(void)
3848 FIXME("() stub\n");
3849 return FALSE;
3852 /******************************************************************************
3853 * GetUserGeoID (KERNEL32.@)
3855 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3857 GEOID ret = GEOID_NOT_AVAILABLE;
3858 static const WCHAR geoW[] = {'G','e','o',0};
3859 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3860 WCHAR bufferW[40], *end;
3861 DWORD count;
3862 HANDLE hkey, hSubkey = 0;
3863 UNICODE_STRING keyW;
3864 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3865 RtlInitUnicodeString( &keyW, nationW );
3866 count = sizeof(bufferW);
3868 if(!(hkey = create_registry_key())) return ret;
3870 switch( GeoClass ){
3871 case GEOCLASS_NATION:
3872 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3874 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3875 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3876 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3878 break;
3879 case GEOCLASS_REGION:
3880 FIXME("GEOCLASS_REGION not handled yet\n");
3881 break;
3884 NtClose(hkey);
3885 if (hSubkey) NtClose(hSubkey);
3886 return ret;
3889 /******************************************************************************
3890 * SetUserGeoID (KERNEL32.@)
3892 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3894 static const WCHAR geoW[] = {'G','e','o',0};
3895 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3896 static const WCHAR formatW[] = {'%','i',0};
3897 UNICODE_STRING nameW,keyW;
3898 WCHAR bufferW[10];
3899 OBJECT_ATTRIBUTES attr;
3900 HANDLE hkey;
3902 if(!(hkey = create_registry_key())) return FALSE;
3904 attr.Length = sizeof(attr);
3905 attr.RootDirectory = hkey;
3906 attr.ObjectName = &nameW;
3907 attr.Attributes = 0;
3908 attr.SecurityDescriptor = NULL;
3909 attr.SecurityQualityOfService = NULL;
3910 RtlInitUnicodeString( &nameW, geoW );
3911 RtlInitUnicodeString( &keyW, nationW );
3913 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3916 NtClose(attr.RootDirectory);
3917 return FALSE;
3920 sprintfW(bufferW, formatW, GeoID);
3921 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3922 NtClose(attr.RootDirectory);
3923 NtClose(hkey);
3924 return TRUE;
3927 typedef struct
3929 union
3931 UILANGUAGE_ENUMPROCA procA;
3932 UILANGUAGE_ENUMPROCW procW;
3933 } u;
3934 DWORD flags;
3935 LONG_PTR param;
3936 } ENUM_UILANG_CALLBACK;
3938 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3939 LPCSTR name, WORD LangID, LONG_PTR lParam )
3941 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3942 char buf[20];
3944 sprintf(buf, "%08x", (UINT)LangID);
3945 return enum_uilang->u.procA( buf, enum_uilang->param );
3948 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3949 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3951 static const WCHAR formatW[] = {'%','0','8','x',0};
3952 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3953 WCHAR buf[20];
3955 sprintfW( buf, formatW, (UINT)LangID );
3956 return enum_uilang->u.procW( buf, enum_uilang->param );
3959 /******************************************************************************
3960 * EnumUILanguagesA (KERNEL32.@)
3962 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3964 ENUM_UILANG_CALLBACK enum_uilang;
3966 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3968 if(!pUILangEnumProc) {
3969 SetLastError(ERROR_INVALID_PARAMETER);
3970 return FALSE;
3972 if(dwFlags) {
3973 SetLastError(ERROR_INVALID_FLAGS);
3974 return FALSE;
3977 enum_uilang.u.procA = pUILangEnumProc;
3978 enum_uilang.flags = dwFlags;
3979 enum_uilang.param = lParam;
3981 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3982 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3983 (LONG_PTR)&enum_uilang);
3984 return TRUE;
3987 /******************************************************************************
3988 * EnumUILanguagesW (KERNEL32.@)
3990 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3992 ENUM_UILANG_CALLBACK enum_uilang;
3994 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3997 if(!pUILangEnumProc) {
3998 SetLastError(ERROR_INVALID_PARAMETER);
3999 return FALSE;
4001 if(dwFlags) {
4002 SetLastError(ERROR_INVALID_FLAGS);
4003 return FALSE;
4006 enum_uilang.u.procW = pUILangEnumProc;
4007 enum_uilang.flags = dwFlags;
4008 enum_uilang.param = lParam;
4010 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
4011 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
4012 (LONG_PTR)&enum_uilang);
4013 return TRUE;
4016 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
4017 int cchData, LANGID language)
4019 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
4020 return 0;
4023 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
4024 int cchData, LANGID language)
4026 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
4027 return 0;
4030 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
4032 LCID userlcid;
4034 TRACE("%p, %d\n", localename, buffersize);
4036 userlcid = GetUserDefaultLCID();
4037 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
4040 /******************************************************************************
4041 * NormalizeString (KERNEL32.@)
4043 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
4044 LPWSTR lpDstString, INT cwDstLength)
4046 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
4047 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4048 return 0;
4051 /******************************************************************************
4052 * IsNormalizedString (KERNEL32.@)
4054 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4056 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4057 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4058 return FALSE;
4061 enum {
4062 BASE = 36,
4063 TMIN = 1,
4064 TMAX = 26,
4065 SKEW = 38,
4066 DAMP = 700,
4067 INIT_BIAS = 72,
4068 INIT_N = 128
4071 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4073 INT k;
4075 delta /= (firsttime ? DAMP : 2);
4076 delta += delta/numpoints;
4078 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4079 delta /= BASE-TMIN;
4080 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4083 /******************************************************************************
4084 * IdnToAscii (KERNEL32.@)
4085 * Implementation of Punycode based on RFC 3492.
4087 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4088 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4090 static const WCHAR prefixW[] = {'x','n','-','-'};
4092 WCHAR *norm_str;
4093 INT i, label_start, label_end, norm_len, out_label, out = 0;
4095 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4096 lpASCIICharStr, cchASCIIChar);
4098 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4099 if(!norm_len)
4100 return 0;
4101 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4102 if(!norm_str) {
4103 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4104 return 0;
4106 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4107 cchUnicodeChar, norm_str, norm_len);
4108 if(!norm_len) {
4109 HeapFree(GetProcessHeap(), 0, norm_str);
4110 return 0;
4113 for(label_start=0; label_start<norm_len;) {
4114 INT n = INIT_N, bias = INIT_BIAS;
4115 INT delta = 0, b = 0, h;
4117 out_label = out;
4118 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4119 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4120 if(norm_str[i] < 0x80)
4121 b++;
4122 label_end = i;
4124 if(b == label_end-label_start) {
4125 if(label_end < norm_len)
4126 b++;
4127 if(!lpASCIICharStr) {
4128 out += b;
4129 }else if(out+b <= cchASCIIChar) {
4130 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4131 out += b;
4132 }else {
4133 HeapFree(GetProcessHeap(), 0, norm_str);
4134 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4135 return 0;
4137 label_start = label_end+1;
4138 continue;
4141 if(!lpASCIICharStr) {
4142 out += 5+b; /* strlen(xn--...-) */
4143 }else if(out+5+b <= cchASCIIChar) {
4144 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4145 out += 4;
4146 for(i=label_start; i<label_end; i++)
4147 if(norm_str[i] < 0x80)
4148 lpASCIICharStr[out++] = norm_str[i];
4149 lpASCIICharStr[out++] = '-';
4150 }else {
4151 HeapFree(GetProcessHeap(), 0, norm_str);
4152 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4153 return 0;
4155 if(!b)
4156 out--;
4158 for(h=b; h<label_end-label_start;) {
4159 INT m = 0xffff, q, k;
4161 for(i=label_start; i<label_end; i++) {
4162 if(norm_str[i]>=n && m>norm_str[i])
4163 m = norm_str[i];
4165 delta += (m-n)*(h+1);
4166 n = m;
4168 for(i=label_start; i<label_end; i++) {
4169 if(norm_str[i] < n) {
4170 delta++;
4171 }else if(norm_str[i] == n) {
4172 for(q=delta, k=BASE; ; k+=BASE) {
4173 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4174 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4175 if(!lpASCIICharStr) {
4176 out++;
4177 }else if(out+1 <= cchASCIIChar) {
4178 lpASCIICharStr[out++] = disp<='z'-'a' ?
4179 'a'+disp : '0'+disp-'z'+'a'-1;
4180 }else {
4181 HeapFree(GetProcessHeap(), 0, norm_str);
4182 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4183 return 0;
4185 if(q < t)
4186 break;
4187 q = (q-t)/(BASE-t);
4189 bias = adapt(delta, h+1, h==b);
4190 delta = 0;
4191 h++;
4194 delta++;
4195 n++;
4198 if(out-out_label > 63) {
4199 HeapFree(GetProcessHeap(), 0, norm_str);
4200 SetLastError(ERROR_INVALID_NAME);
4201 return 0;
4204 if(label_end < norm_len) {
4205 if(!lpASCIICharStr) {
4206 out++;
4207 }else if(out+1 <= cchASCIIChar) {
4208 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4209 }else {
4210 HeapFree(GetProcessHeap(), 0, norm_str);
4211 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4212 return 0;
4215 label_start = label_end+1;
4218 HeapFree(GetProcessHeap(), 0, norm_str);
4219 return out;
4222 /******************************************************************************
4223 * IdnToNameprepUnicode (KERNEL32.@)
4225 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4226 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4228 enum {
4229 UNASSIGNED = 0x1,
4230 PROHIBITED = 0x2,
4231 BIDI_RAL = 0x4,
4232 BIDI_L = 0x8
4235 extern const unsigned short nameprep_char_type[];
4236 extern const WCHAR nameprep_mapping[];
4237 const WCHAR *ptr;
4238 WORD flags;
4239 WCHAR buf[64], *map_str, norm_str[64], ch;
4240 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4241 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4243 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4244 lpNameprepCharStr, cchNameprepChar);
4246 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4247 SetLastError(ERROR_INVALID_FLAGS);
4248 return 0;
4251 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4252 SetLastError(ERROR_INVALID_PARAMETER);
4253 return 0;
4256 if(cchUnicodeChar == -1)
4257 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4258 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4259 SetLastError(ERROR_INVALID_NAME);
4260 return 0;
4263 for(label_start=0; label_start<cchUnicodeChar;) {
4264 ascii_only = TRUE;
4265 for(i=label_start; i<cchUnicodeChar; i++) {
4266 ch = lpUnicodeCharStr[i];
4268 if(i!=cchUnicodeChar-1 && !ch) {
4269 SetLastError(ERROR_INVALID_NAME);
4270 return 0;
4272 /* check if ch is one of label separators defined in RFC3490 */
4273 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4274 break;
4276 if(ch > 0x7f) {
4277 ascii_only = FALSE;
4278 continue;
4281 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4282 continue;
4283 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4284 || (ch>='0' && ch<='9') || ch=='-')
4285 continue;
4287 SetLastError(ERROR_INVALID_NAME);
4288 return 0;
4290 label_end = i;
4291 /* last label may be empty */
4292 if(label_start==label_end && ch) {
4293 SetLastError(ERROR_INVALID_NAME);
4294 return 0;
4297 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4298 lpUnicodeCharStr[label_end-1]=='-')) {
4299 SetLastError(ERROR_INVALID_NAME);
4300 return 0;
4303 if(ascii_only) {
4304 /* maximal label length is 63 characters */
4305 if(label_end-label_start > 63) {
4306 SetLastError(ERROR_INVALID_NAME);
4307 return 0;
4309 if(label_end < cchUnicodeChar)
4310 label_end++;
4312 if(!lpNameprepCharStr) {
4313 out += label_end-label_start;
4314 }else if(out+label_end-label_start <= cchNameprepChar) {
4315 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4316 (label_end-label_start)*sizeof(WCHAR));
4317 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4318 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4319 out += label_end-label_start;
4320 }else {
4321 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4322 return 0;
4325 label_start = label_end;
4326 continue;
4329 map_len = 0;
4330 for(i=label_start; i<label_end; i++) {
4331 ch = lpUnicodeCharStr[i];
4332 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4333 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4335 if(!ptr[0]) map_len++;
4336 else if(!ptr[1]) map_len++;
4337 else if(!ptr[2]) map_len += 2;
4338 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4340 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4341 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4342 if(!map_str) {
4343 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4344 return 0;
4346 }else {
4347 map_str = buf;
4349 map_len = 0;
4350 for(i=label_start; i<label_end; i++) {
4351 ch = lpUnicodeCharStr[i];
4352 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4353 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4355 if(!ptr[0]) {
4356 map_str[map_len++] = ch;
4357 }else if(!ptr[1]) {
4358 map_str[map_len++] = ptr[0];
4359 }else if(!ptr[2]) {
4360 map_str[map_len++] = ptr[0];
4361 map_str[map_len++] = ptr[1];
4362 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4363 map_str[map_len++] = ptr[0];
4364 map_str[map_len++] = ptr[1];
4365 map_str[map_len++] = ptr[2];
4369 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4370 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4371 if(map_str != buf)
4372 HeapFree(GetProcessHeap(), 0, map_str);
4373 if(!norm_len) {
4374 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4375 SetLastError(ERROR_INVALID_NAME);
4376 return 0;
4379 if(label_end < cchUnicodeChar) {
4380 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4381 label_end++;
4384 if(!lpNameprepCharStr) {
4385 out += norm_len;
4386 }else if(out+norm_len <= cchNameprepChar) {
4387 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4388 out += norm_len;
4389 }else {
4390 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4391 return 0;
4394 have_bidi_ral = prohibit_bidi_ral = FALSE;
4395 mask = PROHIBITED;
4396 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4397 mask |= UNASSIGNED;
4398 for(i=0; i<norm_len; i++) {
4399 ch = norm_str[i];
4400 flags = get_table_entry( nameprep_char_type, ch );
4402 if(flags & mask) {
4403 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4404 : ERROR_NO_UNICODE_TRANSLATION);
4405 return 0;
4408 if(flags & BIDI_RAL)
4409 have_bidi_ral = TRUE;
4410 if(flags & BIDI_L)
4411 prohibit_bidi_ral = TRUE;
4414 if(have_bidi_ral) {
4415 ch = norm_str[0];
4416 flags = get_table_entry( nameprep_char_type, ch );
4417 if((flags & BIDI_RAL) == 0)
4418 prohibit_bidi_ral = TRUE;
4420 ch = norm_str[norm_len-1];
4421 flags = get_table_entry( nameprep_char_type, ch );
4422 if((flags & BIDI_RAL) == 0)
4423 prohibit_bidi_ral = TRUE;
4426 if(have_bidi_ral && prohibit_bidi_ral) {
4427 SetLastError(ERROR_INVALID_NAME);
4428 return 0;
4431 label_start = label_end;
4434 return out;
4437 /******************************************************************************
4438 * IdnToUnicode (KERNEL32.@)
4440 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4441 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4443 extern const unsigned short nameprep_char_type[];
4445 INT i, label_start, label_end, out_label, out = 0;
4446 WCHAR ch;
4448 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4449 lpUnicodeCharStr, cchUnicodeChar);
4451 for(label_start=0; label_start<cchASCIIChar;) {
4452 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4454 out_label = out;
4455 for(i=label_start; i<cchASCIIChar; i++) {
4456 ch = lpASCIICharStr[i];
4458 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4459 SetLastError(ERROR_INVALID_NAME);
4460 return 0;
4463 if(!ch || ch=='.')
4464 break;
4465 if(ch == '-')
4466 delim = i;
4468 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4469 continue;
4470 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4471 || (ch>='0' && ch<='9') || ch=='-')
4472 continue;
4474 SetLastError(ERROR_INVALID_NAME);
4475 return 0;
4477 label_end = i;
4478 /* last label may be empty */
4479 if(label_start==label_end && ch) {
4480 SetLastError(ERROR_INVALID_NAME);
4481 return 0;
4484 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
4485 lpASCIICharStr[label_end-1]=='-')) {
4486 SetLastError(ERROR_INVALID_NAME);
4487 return 0;
4489 if(label_end-label_start > 63) {
4490 SetLastError(ERROR_INVALID_NAME);
4491 return 0;
4494 if(label_end-label_start<4 ||
4495 tolowerW(lpASCIICharStr[label_start])!='x' ||
4496 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4497 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4498 if(label_end < cchASCIIChar)
4499 label_end++;
4501 if(!lpUnicodeCharStr) {
4502 out += label_end-label_start;
4503 }else if(out+label_end-label_start <= cchUnicodeChar) {
4504 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4505 (label_end-label_start)*sizeof(WCHAR));
4506 out += label_end-label_start;
4507 }else {
4508 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4509 return 0;
4512 label_start = label_end;
4513 continue;
4516 if(delim == label_start+3)
4517 delim++;
4518 if(!lpUnicodeCharStr) {
4519 out += delim-label_start-4;
4520 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4521 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4522 (delim-label_start-4)*sizeof(WCHAR));
4523 out += delim-label_start-4;
4524 }else {
4525 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4526 return 0;
4528 if(out != out_label)
4529 delim++;
4531 for(i=delim; i<label_end;) {
4532 old_pos = pos;
4533 w = 1;
4534 for(k=BASE; ; k+=BASE) {
4535 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4536 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4537 SetLastError(ERROR_INVALID_NAME);
4538 return 0;
4540 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4541 pos += digit*w;
4542 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4543 if(digit < t)
4544 break;
4545 w *= BASE-t;
4547 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4548 n += pos/(out-out_label+1);
4549 pos %= out-out_label+1;
4551 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4552 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4553 SetLastError(ERROR_INVALID_NAME);
4554 return 0;
4556 if(!lpUnicodeCharStr) {
4557 out++;
4558 }else if(out+1 <= cchASCIIChar) {
4559 memmove(lpUnicodeCharStr+out_label+pos+1,
4560 lpUnicodeCharStr+out_label+pos,
4561 (out-out_label-pos)*sizeof(WCHAR));
4562 lpUnicodeCharStr[out_label+pos] = n;
4563 out++;
4564 }else {
4565 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4566 return 0;
4568 pos++;
4571 if(out-out_label > 63) {
4572 SetLastError(ERROR_INVALID_NAME);
4573 return 0;
4576 if(label_end < cchASCIIChar) {
4577 if(!lpUnicodeCharStr) {
4578 out++;
4579 }else if(out+1 <= cchUnicodeChar) {
4580 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4581 }else {
4582 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4583 return 0;
4586 label_start = label_end+1;
4589 return out;