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
25 #include "wine/port.h"
36 # include <CoreFoundation/CFBundle.h>
37 # include <CoreFoundation/CFLocale.h>
38 # include <CoreFoundation/CFString.h>
42 #define WIN32_NO_STATUS
45 #include "winuser.h" /* for RT_STRINGW */
47 #include "wine/unicode.h"
51 #include "kernel_private.h"
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(nls);
56 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
57 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
59 /* current code pages */
60 static const union cptable *ansi_cptable;
61 static const union cptable *oem_cptable;
62 static const union cptable *mac_cptable;
63 static const union cptable *unix_cptable; /* NULL if UTF8 */
65 static const WCHAR szLocaleKeyName[] = {
66 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
67 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
68 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
71 static const WCHAR szLangGroupsKeyName[] = {
72 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
73 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
74 'C','o','n','t','r','o','l','\\','N','l','s','\\',
75 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
78 /* Charset to codepage map, sorted by name. */
79 static const struct charset_entry
81 const char *charset_name;
120 { "ISO88591", 28591 },
121 { "ISO885910", 28600 },
122 { "ISO885913", 28603 },
123 { "ISO885914", 28604 },
124 { "ISO885915", 28605 },
125 { "ISO885916", 28606 },
126 { "ISO88592", 28592 },
127 { "ISO88593", 28593 },
128 { "ISO88594", 28594 },
129 { "ISO88595", 28595 },
130 { "ISO88596", 28596 },
131 { "ISO88597", 28597 },
132 { "ISO88598", 28598 },
133 { "ISO88599", 28599 },
142 WCHAR win_name[128]; /* Windows name ("en-US") */
143 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
144 WCHAR *country; /* country ("US") */
145 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
146 WCHAR *script; /* script ("Latn") for Windows format only */
147 WCHAR *modifier; /* modifier or sort order */
148 LCID lcid; /* corresponding LCID */
149 int matches; /* number of elements matching LCID (0..4) */
150 UINT codepage; /* codepage corresponding to charset */
153 /* locale ids corresponding to the various Unix locale parameters */
154 static LCID lcid_LC_COLLATE;
155 static LCID lcid_LC_CTYPE;
156 static LCID lcid_LC_MESSAGES;
157 static LCID lcid_LC_MONETARY;
158 static LCID lcid_LC_NUMERIC;
159 static LCID lcid_LC_TIME;
160 static LCID lcid_LC_PAPER;
161 static LCID lcid_LC_MEASUREMENT;
162 static LCID lcid_LC_TELEPHONE;
164 /* Copy Ascii string to Unicode without using codepages */
165 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
167 while (n > 1 && *src)
169 *dst++ = (unsigned char)*src++;
175 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
177 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
180 /***********************************************************************
183 * Retrieve the ANSI codepage for a given locale.
185 static inline UINT get_lcid_codepage( LCID lcid )
188 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
189 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
194 /***********************************************************************
197 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
199 static const union cptable *get_codepage_table( unsigned int codepage )
201 const union cptable *ret = NULL;
203 assert( ansi_cptable ); /* init must have been done already */
217 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
218 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
221 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
222 if (codepage == oem_cptable->info.codepage) return oem_cptable;
223 if (codepage == mac_cptable->info.codepage) return mac_cptable;
224 ret = wine_cp_get_table( codepage );
231 /***********************************************************************
232 * charset_cmp (internal)
234 static int charset_cmp( const void *name, const void *entry )
236 const struct charset_entry *charset = entry;
237 return strcasecmp( name, charset->charset_name );
240 /***********************************************************************
243 static UINT find_charset( const WCHAR *name )
245 const struct charset_entry *entry;
246 char charset_name[16];
249 /* remove punctuation characters from charset name */
250 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
251 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
254 entry = bsearch( charset_name, charset_names,
255 sizeof(charset_names)/sizeof(charset_names[0]),
256 sizeof(charset_names[0]), charset_cmp );
257 if (entry) return entry->codepage;
262 /***********************************************************************
263 * find_locale_id_callback
265 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
266 LPCWSTR name, WORD LangID, LPARAM lParam )
268 struct locale_name *data = (struct locale_name *)lParam;
271 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
273 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
275 /* first check exact name */
276 if (data->win_name[0] &&
277 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
278 buffer, sizeof(buffer)/sizeof(WCHAR) ))
280 if (!strcmpW( data->win_name, buffer ))
282 matches = 4; /* everything matches */
287 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
288 buffer, sizeof(buffer)/sizeof(WCHAR) ))
290 if (strcmpW( buffer, data->lang )) return TRUE;
291 matches++; /* language name matched */
295 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
296 buffer, sizeof(buffer)/sizeof(WCHAR) ))
298 if (strcmpW( buffer, data->country )) goto done;
299 matches++; /* country name matched */
302 else /* match default language */
304 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
310 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
311 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
313 if (unix_cp == data->codepage) matches++;
317 /* FIXME: check sort order */
320 if (matches > data->matches)
323 data->matches = matches;
325 return (data->matches < 4); /* no need to continue for perfect match */
329 /***********************************************************************
332 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
333 * Unix format is: lang[_country][.charset][@modifier]
334 * Windows format is: lang[-script][-country][_modifier]
336 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
338 static const WCHAR sepW[] = {'-','_','.','@',0};
339 static const WCHAR winsepW[] = {'-','_',0};
340 static const WCHAR posixW[] = {'P','O','S','I','X',0};
341 static const WCHAR cW[] = {'C',0};
342 static const WCHAR latinW[] = {'l','a','t','i','n',0};
343 static const WCHAR latnW[] = {'-','L','a','t','n',0};
346 TRACE("%s\n", debugstr_w(str));
348 name->country = name->charset = name->script = name->modifier = NULL;
349 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
352 name->win_name[0] = 0;
353 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
355 if (!(p = strpbrkW( name->lang, sepW )))
357 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
359 name->matches = 4; /* perfect match for default English lcid */
362 strcpyW( name->win_name, name->lang );
364 else if (*p == '-') /* Windows format */
366 strcpyW( name->win_name, name->lang );
369 if (!(p = strpbrkW( p, winsepW ))) goto done;
373 name->script = name->country;
375 if (!(p = strpbrkW( p, winsepW ))) goto done;
380 else /* Unix format */
386 p = strpbrkW( p, sepW + 2 );
392 p = strchrW( p, '@' );
401 name->codepage = find_charset( name->charset );
403 /* rebuild a Windows name if possible */
405 if (name->charset) goto done; /* can't specify charset in Windows format */
406 if (name->modifier && strcmpW( name->modifier, latinW ))
407 goto done; /* only Latn script supported for now */
408 strcpyW( name->win_name, name->lang );
409 if (name->modifier) strcatW( name->win_name, latnW );
412 p = name->win_name + strlenW(name->win_name);
414 strcpyW( p, name->country );
418 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
419 find_locale_id_callback, (LPARAM)name );
423 /***********************************************************************
424 * convert_default_lcid
426 * Get the default LCID to use for a given lctype in GetLocaleInfo.
428 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
430 if (lcid == LOCALE_SYSTEM_DEFAULT ||
431 lcid == LOCALE_USER_DEFAULT ||
432 lcid == LOCALE_NEUTRAL)
436 switch(lctype & 0xffff)
438 case LOCALE_SSORTNAME:
439 default_id = lcid_LC_COLLATE;
442 case LOCALE_FONTSIGNATURE:
443 case LOCALE_IDEFAULTANSICODEPAGE:
444 case LOCALE_IDEFAULTCODEPAGE:
445 case LOCALE_IDEFAULTEBCDICCODEPAGE:
446 case LOCALE_IDEFAULTMACCODEPAGE:
447 case LOCALE_IDEFAULTUNIXCODEPAGE:
448 default_id = lcid_LC_CTYPE;
451 case LOCALE_ICURRDIGITS:
452 case LOCALE_ICURRENCY:
453 case LOCALE_IINTLCURRDIGITS:
454 case LOCALE_INEGCURR:
455 case LOCALE_INEGSEPBYSPACE:
456 case LOCALE_INEGSIGNPOSN:
457 case LOCALE_INEGSYMPRECEDES:
458 case LOCALE_IPOSSEPBYSPACE:
459 case LOCALE_IPOSSIGNPOSN:
460 case LOCALE_IPOSSYMPRECEDES:
461 case LOCALE_SCURRENCY:
462 case LOCALE_SINTLSYMBOL:
463 case LOCALE_SMONDECIMALSEP:
464 case LOCALE_SMONGROUPING:
465 case LOCALE_SMONTHOUSANDSEP:
466 case LOCALE_SNATIVECURRNAME:
467 default_id = lcid_LC_MONETARY;
471 case LOCALE_IDIGITSUBSTITUTION:
473 case LOCALE_INEGNUMBER:
474 case LOCALE_SDECIMAL:
475 case LOCALE_SGROUPING:
477 case LOCALE_SNATIVEDIGITS:
478 case LOCALE_SNEGATIVESIGN:
479 case LOCALE_SNEGINFINITY:
480 case LOCALE_SPOSINFINITY:
481 case LOCALE_SPOSITIVESIGN:
482 case LOCALE_STHOUSAND:
483 default_id = lcid_LC_NUMERIC;
486 case LOCALE_ICALENDARTYPE:
487 case LOCALE_ICENTURY:
489 case LOCALE_IDAYLZERO:
490 case LOCALE_IFIRSTDAYOFWEEK:
491 case LOCALE_IFIRSTWEEKOFYEAR:
493 case LOCALE_IMONLZERO:
494 case LOCALE_IOPTIONALCALENDAR:
496 case LOCALE_ITIMEMARKPOSN:
500 case LOCALE_SABBREVDAYNAME1:
501 case LOCALE_SABBREVDAYNAME2:
502 case LOCALE_SABBREVDAYNAME3:
503 case LOCALE_SABBREVDAYNAME4:
504 case LOCALE_SABBREVDAYNAME5:
505 case LOCALE_SABBREVDAYNAME6:
506 case LOCALE_SABBREVDAYNAME7:
507 case LOCALE_SABBREVMONTHNAME1:
508 case LOCALE_SABBREVMONTHNAME2:
509 case LOCALE_SABBREVMONTHNAME3:
510 case LOCALE_SABBREVMONTHNAME4:
511 case LOCALE_SABBREVMONTHNAME5:
512 case LOCALE_SABBREVMONTHNAME6:
513 case LOCALE_SABBREVMONTHNAME7:
514 case LOCALE_SABBREVMONTHNAME8:
515 case LOCALE_SABBREVMONTHNAME9:
516 case LOCALE_SABBREVMONTHNAME10:
517 case LOCALE_SABBREVMONTHNAME11:
518 case LOCALE_SABBREVMONTHNAME12:
519 case LOCALE_SABBREVMONTHNAME13:
521 case LOCALE_SDAYNAME1:
522 case LOCALE_SDAYNAME2:
523 case LOCALE_SDAYNAME3:
524 case LOCALE_SDAYNAME4:
525 case LOCALE_SDAYNAME5:
526 case LOCALE_SDAYNAME6:
527 case LOCALE_SDAYNAME7:
528 case LOCALE_SDURATION:
529 case LOCALE_SLONGDATE:
530 case LOCALE_SMONTHNAME1:
531 case LOCALE_SMONTHNAME2:
532 case LOCALE_SMONTHNAME3:
533 case LOCALE_SMONTHNAME4:
534 case LOCALE_SMONTHNAME5:
535 case LOCALE_SMONTHNAME6:
536 case LOCALE_SMONTHNAME7:
537 case LOCALE_SMONTHNAME8:
538 case LOCALE_SMONTHNAME9:
539 case LOCALE_SMONTHNAME10:
540 case LOCALE_SMONTHNAME11:
541 case LOCALE_SMONTHNAME12:
542 case LOCALE_SMONTHNAME13:
543 case LOCALE_SSHORTDATE:
544 case LOCALE_SSHORTESTDAYNAME1:
545 case LOCALE_SSHORTESTDAYNAME2:
546 case LOCALE_SSHORTESTDAYNAME3:
547 case LOCALE_SSHORTESTDAYNAME4:
548 case LOCALE_SSHORTESTDAYNAME5:
549 case LOCALE_SSHORTESTDAYNAME6:
550 case LOCALE_SSHORTESTDAYNAME7:
552 case LOCALE_STIMEFORMAT:
553 case LOCALE_SYEARMONTH:
554 default_id = lcid_LC_TIME;
557 case LOCALE_IPAPERSIZE:
558 default_id = lcid_LC_PAPER;
561 case LOCALE_IMEASURE:
562 default_id = lcid_LC_MEASUREMENT;
565 case LOCALE_ICOUNTRY:
566 default_id = lcid_LC_TELEPHONE;
569 if (default_id) lcid = default_id;
571 return ConvertDefaultLocale( lcid );
574 /***********************************************************************
575 * is_genitive_name_supported
577 * Determine could LCTYPE basically support genitive name form or not.
579 static BOOL is_genitive_name_supported( LCTYPE lctype )
581 switch(lctype & 0xffff)
583 case LOCALE_SMONTHNAME1:
584 case LOCALE_SMONTHNAME2:
585 case LOCALE_SMONTHNAME3:
586 case LOCALE_SMONTHNAME4:
587 case LOCALE_SMONTHNAME5:
588 case LOCALE_SMONTHNAME6:
589 case LOCALE_SMONTHNAME7:
590 case LOCALE_SMONTHNAME8:
591 case LOCALE_SMONTHNAME9:
592 case LOCALE_SMONTHNAME10:
593 case LOCALE_SMONTHNAME11:
594 case LOCALE_SMONTHNAME12:
595 case LOCALE_SMONTHNAME13:
602 /***********************************************************************
603 * create_registry_key
605 * Create the Control Panel\\International registry key.
607 static inline HANDLE create_registry_key(void)
609 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
610 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
611 OBJECT_ATTRIBUTES attr;
612 UNICODE_STRING nameW;
613 HANDLE cpl_key, hkey = 0;
615 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
617 attr.Length = sizeof(attr);
618 attr.RootDirectory = hkey;
619 attr.ObjectName = &nameW;
621 attr.SecurityDescriptor = NULL;
622 attr.SecurityQualityOfService = NULL;
623 RtlInitUnicodeString( &nameW, cplW );
625 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
627 NtClose( attr.RootDirectory );
628 attr.RootDirectory = cpl_key;
629 RtlInitUnicodeString( &nameW, intlW );
630 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
632 NtClose( attr.RootDirectory );
637 /* update the registry settings for a given locale parameter */
638 /* return TRUE if an update was needed */
639 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
640 const LCTYPE *values, UINT nb_values )
642 static const WCHAR formatW[] = { '%','0','8','x',0 };
644 UNICODE_STRING nameW;
647 RtlInitUnicodeString( &nameW, name );
648 count = sizeof(bufferW);
649 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
651 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
652 LPCWSTR text = (LPCWSTR)info->Data;
654 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
655 TRACE( "updating registry, locale %s changed %s -> %08x\n",
656 debugstr_w(name), debugstr_w(text), lcid );
658 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
659 sprintfW( bufferW, formatW, lcid );
660 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
662 for (i = 0; i < nb_values; i++)
664 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
665 sizeof(bufferW)/sizeof(WCHAR) );
666 SetLocaleInfoW( lcid, values[i], bufferW );
672 /***********************************************************************
673 * LOCALE_InitRegistry
675 * Update registry contents on startup if the user locale has changed.
676 * This simulates the action of the Windows control panel.
678 void LOCALE_InitRegistry(void)
680 static const WCHAR acpW[] = {'A','C','P',0};
681 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
682 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
683 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
684 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
685 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
686 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
687 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
688 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
689 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
690 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
695 } update_cp_values[] = {
696 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
697 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
698 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
700 static const LCTYPE lc_messages_values[] = {
701 LOCALE_SABBREVLANGNAME,
704 static const LCTYPE lc_monetary_values[] = {
710 LOCALE_SMONDECIMALSEP,
712 LOCALE_SMONTHOUSANDSEP };
713 static const LCTYPE lc_numeric_values[] = {
717 LOCALE_IDIGITSUBSTITUTION,
718 LOCALE_SNATIVEDIGITS,
720 LOCALE_SNEGATIVESIGN,
721 LOCALE_SPOSITIVESIGN,
723 static const LCTYPE lc_time_values[] = {
732 LOCALE_ITIMEMARKPOSN,
733 LOCALE_ICALENDARTYPE,
734 LOCALE_IFIRSTDAYOFWEEK,
735 LOCALE_IFIRSTWEEKOFYEAR,
739 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
740 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
741 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
743 UNICODE_STRING nameW;
747 LCID lcid = GetUserDefaultLCID();
749 if (!(hkey = create_registry_key()))
750 return; /* don't do anything if we can't create the registry key */
752 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
753 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
754 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
755 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
756 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
757 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
758 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
759 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
760 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
761 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
762 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
763 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
764 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
765 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
767 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
769 static const WCHAR codepageW[] =
770 {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
771 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
772 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
774 OBJECT_ATTRIBUTES attr;
778 RtlInitUnicodeString( &nameW, codepageW );
779 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
780 while (codepageW[len])
782 nameW.Length = len * sizeof(WCHAR);
783 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
786 while (codepageW[len] && codepageW[len] != '\\') len++;
788 nameW.Length = len * sizeof(WCHAR);
789 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
791 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
793 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
794 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
795 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
796 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
806 /***********************************************************************
809 static UINT setup_unix_locales(void)
811 struct locale_name locale_name;
812 WCHAR buffer[128], ctype_buff[128];
816 if ((locale = setlocale( LC_CTYPE, NULL )))
818 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
819 parse_locale_name( ctype_buff, &locale_name );
820 lcid_LC_CTYPE = locale_name.lcid;
821 unix_cp = locale_name.codepage;
823 if (!lcid_LC_CTYPE) /* this one needs a default value */
824 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
826 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
827 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
829 #define GET_UNIX_LOCALE(cat) do \
830 if ((locale = setlocale( cat, NULL ))) \
832 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
833 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
835 parse_locale_name( buffer, &locale_name ); \
836 lcid_##cat = locale_name.lcid; \
837 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
838 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
842 GET_UNIX_LOCALE( LC_COLLATE );
843 GET_UNIX_LOCALE( LC_MESSAGES );
844 GET_UNIX_LOCALE( LC_MONETARY );
845 GET_UNIX_LOCALE( LC_NUMERIC );
846 GET_UNIX_LOCALE( LC_TIME );
848 GET_UNIX_LOCALE( LC_PAPER );
850 #ifdef LC_MEASUREMENT
851 GET_UNIX_LOCALE( LC_MEASUREMENT );
854 GET_UNIX_LOCALE( LC_TELEPHONE );
857 #undef GET_UNIX_LOCALE
863 /***********************************************************************
864 * GetUserDefaultLangID (KERNEL32.@)
866 * Get the default language Id for the current user.
872 * The current LANGID of the default language for the current user.
874 LANGID WINAPI GetUserDefaultLangID(void)
876 return LANGIDFROMLCID(GetUserDefaultLCID());
880 /***********************************************************************
881 * GetSystemDefaultLangID (KERNEL32.@)
883 * Get the default language Id for the system.
889 * The current LANGID of the default language for the system.
891 LANGID WINAPI GetSystemDefaultLangID(void)
893 return LANGIDFROMLCID(GetSystemDefaultLCID());
897 /***********************************************************************
898 * GetUserDefaultLCID (KERNEL32.@)
900 * Get the default locale Id for the current user.
906 * The current LCID of the default locale for the current user.
908 LCID WINAPI GetUserDefaultLCID(void)
911 NtQueryDefaultLocale( TRUE, &lcid );
916 /***********************************************************************
917 * GetSystemDefaultLCID (KERNEL32.@)
919 * Get the default locale Id for the system.
925 * The current LCID of the default locale for the system.
927 LCID WINAPI GetSystemDefaultLCID(void)
930 NtQueryDefaultLocale( FALSE, &lcid );
934 /***********************************************************************
935 * GetSystemDefaultLocaleName (KERNEL32.@)
937 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
939 LCID lcid = GetSystemDefaultLCID();
940 return LCIDToLocaleName(lcid, localename, len, 0);
943 /***********************************************************************
944 * GetUserDefaultUILanguage (KERNEL32.@)
946 * Get the default user interface language Id for the current user.
952 * The current LANGID of the default UI language for the current user.
954 LANGID WINAPI GetUserDefaultUILanguage(void)
957 NtQueryDefaultUILanguage( &lang );
962 /***********************************************************************
963 * GetSystemDefaultUILanguage (KERNEL32.@)
965 * Get the default user interface language Id for the system.
971 * The current LANGID of the default UI language for the system. This is
972 * typically the same language used during the installation process.
974 LANGID WINAPI GetSystemDefaultUILanguage(void)
977 NtQueryInstallUILanguage( &lang );
982 /***********************************************************************
983 * LocaleNameToLCID (KERNEL32.@)
985 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
987 struct locale_name locale_name;
989 if (flags) FIXME( "unsupported flags %x\n", flags );
991 if (name == LOCALE_NAME_USER_DEFAULT)
992 return GetUserDefaultLCID();
995 parse_locale_name( name, &locale_name );
997 TRACE( "found lcid %x for %s, matches %d\n",
998 locale_name.lcid, debugstr_w(name), locale_name.matches );
1000 if (!locale_name.matches)
1002 SetLastError(ERROR_INVALID_PARAMETER);
1006 if (locale_name.matches == 1)
1007 WARN( "locale %s not recognized, defaulting to %s\n",
1008 debugstr_w(name), debugstr_w(locale_name.lang) );
1010 return locale_name.lcid;
1014 /***********************************************************************
1015 * LCIDToLocaleName (KERNEL32.@)
1017 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1019 if (flags) FIXME( "unsupported flags %x\n", flags );
1021 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1025 /******************************************************************************
1026 * get_locale_value_name
1028 * Gets the registry value name for a given lctype.
1030 static const WCHAR *get_locale_value_name( DWORD lctype )
1032 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1033 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1034 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1035 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1036 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1037 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1038 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1039 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1040 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1041 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1042 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1043 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1044 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1045 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1046 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1047 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1048 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1049 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1050 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1051 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1052 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1053 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1054 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1055 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1056 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1057 static const WCHAR sListW[] = {'s','L','i','s','t',0};
1058 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1059 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1060 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1061 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1062 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1063 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1064 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1065 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1066 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1067 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1068 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1069 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1070 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1074 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1075 * the values are stored in the registry, confirmed under Windows.
1077 case LOCALE_ICALENDARTYPE: return iCalendarTypeW;
1078 case LOCALE_ICURRDIGITS: return iCurrDigitsW;
1079 case LOCALE_ICURRENCY: return iCurrencyW;
1080 case LOCALE_IDIGITS: return iDigitsW;
1081 case LOCALE_IFIRSTDAYOFWEEK: return iFirstDayOfWeekW;
1082 case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1083 case LOCALE_ILZERO: return iLZeroW;
1084 case LOCALE_IMEASURE: return iMeasureW;
1085 case LOCALE_INEGCURR: return iNegCurrW;
1086 case LOCALE_INEGNUMBER: return iNegNumberW;
1087 case LOCALE_IPAPERSIZE: return iPaperSizeW;
1088 case LOCALE_ITIME: return iTimeW;
1089 case LOCALE_S1159: return s1159W;
1090 case LOCALE_S2359: return s2359W;
1091 case LOCALE_SCURRENCY: return sCurrencyW;
1092 case LOCALE_SDATE: return sDateW;
1093 case LOCALE_SDECIMAL: return sDecimalW;
1094 case LOCALE_SGROUPING: return sGroupingW;
1095 case LOCALE_SLIST: return sListW;
1096 case LOCALE_SLONGDATE: return sLongDateW;
1097 case LOCALE_SMONDECIMALSEP: return sMonDecimalSepW;
1098 case LOCALE_SMONGROUPING: return sMonGroupingW;
1099 case LOCALE_SMONTHOUSANDSEP: return sMonThousandSepW;
1100 case LOCALE_SNEGATIVESIGN: return sNegativeSignW;
1101 case LOCALE_SPOSITIVESIGN: return sPositiveSignW;
1102 case LOCALE_SSHORTDATE: return sShortDateW;
1103 case LOCALE_STHOUSAND: return sThousandW;
1104 case LOCALE_STIME: return sTimeW;
1105 case LOCALE_STIMEFORMAT: return sTimeFormatW;
1106 case LOCALE_SYEARMONTH: return sYearMonthW;
1108 /* The following are not listed under MSDN as supported,
1109 * but seem to be used and also stored in the registry.
1111 case LOCALE_ICOUNTRY: return iCountryW;
1112 case LOCALE_IDATE: return iDateW;
1113 case LOCALE_ILDATE: return iLDateW;
1114 case LOCALE_ITLZERO: return iTLZeroW;
1115 case LOCALE_SCOUNTRY: return sCountryW;
1116 case LOCALE_SABBREVLANGNAME: return sLanguageW;
1118 /* The following are used in XP and later */
1119 case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1120 case LOCALE_SNATIVEDIGITS: return sNativeDigitsW;
1121 case LOCALE_ITIMEMARKPOSN: return iTimePrefixW;
1127 /******************************************************************************
1128 * get_registry_locale_info
1130 * Retrieve user-modified locale info from the registry.
1131 * Return length, 0 on error, -1 if not found.
1133 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1139 UNICODE_STRING nameW;
1140 KEY_VALUE_PARTIAL_INFORMATION *info;
1141 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1143 if (!(hkey = create_registry_key())) return -1;
1145 RtlInitUnicodeString( &nameW, value );
1146 size = info_size + len * sizeof(WCHAR);
1148 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1151 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1155 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1159 ret = (size - info_size) / sizeof(WCHAR);
1160 /* append terminating null if needed */
1161 if (!ret || ((WCHAR *)info->Data)[ret-1])
1163 if (ret < len || !buffer) ret++;
1166 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1172 memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1176 else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1178 ret = (size - info_size) / sizeof(WCHAR) + 1;
1180 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1186 SetLastError( RtlNtStatusToDosError(status) );
1190 HeapFree( GetProcessHeap(), 0, info );
1195 /******************************************************************************
1196 * GetLocaleInfoA (KERNEL32.@)
1198 * Get information about an aspect of a locale.
1201 * lcid [I] LCID of the locale
1202 * lctype [I] LCTYPE_ flags from "winnls.h"
1203 * buffer [O] Destination for the information
1204 * len [I] Length of buffer in characters
1207 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1208 * with the information.
1209 * Failure: 0. Use GetLastError() to determine the cause.
1212 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1213 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1214 * which is a bit string.
1216 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1221 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1223 if (len < 0 || (len && !buffer))
1225 SetLastError( ERROR_INVALID_PARAMETER );
1228 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1230 SetLastError( ERROR_INVALID_FLAGS );
1234 if (!len) buffer = NULL;
1236 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1238 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1240 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1243 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1245 if ((lctype & LOCALE_RETURN_NUMBER) ||
1246 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1248 /* it's not an ASCII string, just bytes */
1249 ret *= sizeof(WCHAR);
1252 if (ret <= len) memcpy( buffer, bufferW, ret );
1255 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1262 UINT codepage = CP_ACP;
1263 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1264 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1267 HeapFree( GetProcessHeap(), 0, bufferW );
1271 static int get_value_base_by_lctype( LCTYPE lctype )
1273 return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
1276 /******************************************************************************
1277 * GetLocaleInfoW (KERNEL32.@)
1279 * See GetLocaleInfoA.
1281 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1291 if (len < 0 || (len && !buffer))
1293 SetLastError( ERROR_INVALID_PARAMETER );
1296 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1297 !is_genitive_name_supported( lctype ))
1299 SetLastError( ERROR_INVALID_FLAGS );
1303 if (!len) buffer = NULL;
1305 lcid = convert_default_lcid( lcid, lctype );
1307 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1310 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1312 /* first check for overrides in the registry */
1314 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1315 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1317 const WCHAR *value = get_locale_value_name(lctype);
1321 if (lcflags & LOCALE_RETURN_NUMBER)
1324 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1328 UINT number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1329 if (*end) /* invalid number */
1331 SetLastError( ERROR_INVALID_FLAGS );
1334 ret = sizeof(UINT)/sizeof(WCHAR);
1335 if (!buffer) return ret;
1338 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1341 memcpy( buffer, &number, sizeof(number) );
1344 else ret = get_registry_locale_info( value, buffer, len );
1346 if (ret != -1) return ret;
1350 /* now load it from kernel resources */
1352 lang_id = LANGIDFROMLCID( lcid );
1354 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1355 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1356 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1358 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1359 ULongToPtr((lctype >> 4) + 1), lang_id )))
1361 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1364 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1367 p = LockResource( hmem );
1368 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1370 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1371 else if (is_genitive_name_supported( lctype ) && *p)
1373 /* genitive form's stored after a null separator from a nominative */
1374 for (i = 1; i <= *p; i++) if (!p[i]) break;
1376 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1384 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1386 if (!buffer) return ret;
1390 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1394 if (lcflags & LOCALE_RETURN_NUMBER)
1397 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1399 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1401 number = strtolW( tmp, &end, get_value_base_by_lctype( lctype ) );
1403 memcpy( buffer, &number, sizeof(number) );
1404 else /* invalid number */
1406 SetLastError( ERROR_INVALID_FLAGS );
1409 HeapFree( GetProcessHeap(), 0, tmp );
1411 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1412 lcid, lctype, buffer, len, number );
1416 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1417 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1419 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1420 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1425 /******************************************************************************
1426 * GetLocaleInfoEx (KERNEL32.@)
1428 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1430 return GetLocaleInfoW(LocaleNameToLCID(locale, 0), info, buffer, len);
1433 /******************************************************************************
1434 * SetLocaleInfoA [KERNEL32.@]
1436 * Set information about an aspect of a locale.
1439 * lcid [I] LCID of the locale
1440 * lctype [I] LCTYPE_ flags from "winnls.h"
1441 * data [I] Information to set
1444 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1445 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1446 * Failure: FALSE. Use GetLastError() to determine the cause.
1449 * - Values are only be set for the current user locale; the system locale
1450 * settings cannot be changed.
1451 * - Any settings changed by this call are lost when the locale is changed by
1452 * the control panel (in Wine, this happens every time you change LANG).
1453 * - The native implementation of this function does not check that lcid matches
1454 * the current user locale, and simply sets the new values. Wine warns you in
1455 * this case, but behaves the same.
1457 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1459 UINT codepage = CP_ACP;
1464 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1468 SetLastError( ERROR_INVALID_PARAMETER );
1471 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1472 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1474 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1477 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1478 ret = SetLocaleInfoW( lcid, lctype, strW );
1479 HeapFree( GetProcessHeap(), 0, strW );
1484 /******************************************************************************
1485 * SetLocaleInfoW (KERNEL32.@)
1487 * See SetLocaleInfoA.
1489 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1492 static const WCHAR intlW[] = {'i','n','t','l',0 };
1493 UNICODE_STRING valueW;
1498 value = get_locale_value_name( lctype );
1500 if (!data || !value)
1502 SetLastError( ERROR_INVALID_PARAMETER );
1506 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1508 SetLastError( ERROR_INVALID_FLAGS );
1512 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1514 /* FIXME: should check that data to set is sane */
1516 /* FIXME: profile functions should map to registry */
1517 WriteProfileStringW( intlW, value, data );
1519 if (!(hkey = create_registry_key())) return FALSE;
1520 RtlInitUnicodeString( &valueW, value );
1521 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1523 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1525 /* Set I-value from S value */
1526 WCHAR *lpD, *lpM, *lpY;
1529 lpD = strrchrW(data, 'd');
1530 lpM = strrchrW(data, 'M');
1531 lpY = strrchrW(data, 'y');
1535 szBuff[0] = '1'; /* D-M-Y */
1540 szBuff[0] = '2'; /* Y-M-D */
1542 szBuff[0] = '0'; /* M-D-Y */
1547 if (lctype == LOCALE_SSHORTDATE)
1548 lctype = LOCALE_IDATE;
1550 lctype = LOCALE_ILDATE;
1552 value = get_locale_value_name( lctype );
1554 WriteProfileStringW( intlW, value, szBuff );
1556 RtlInitUnicodeString( &valueW, value );
1557 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1562 if (status) SetLastError( RtlNtStatusToDosError(status) );
1567 /******************************************************************************
1568 * GetACP (KERNEL32.@)
1570 * Get the current Ansi code page Id for the system.
1576 * The current Ansi code page identifier for the system.
1578 UINT WINAPI GetACP(void)
1580 assert( ansi_cptable );
1581 return ansi_cptable->info.codepage;
1585 /******************************************************************************
1586 * SetCPGlobal (KERNEL32.@)
1588 * Set the current Ansi code page Id for the system.
1591 * acp [I] code page ID to be the new ACP.
1596 UINT WINAPI SetCPGlobal( UINT acp )
1598 UINT ret = GetACP();
1599 const union cptable *new_cptable = wine_cp_get_table( acp );
1601 if (new_cptable) ansi_cptable = new_cptable;
1606 /***********************************************************************
1607 * GetOEMCP (KERNEL32.@)
1609 * Get the current OEM code page Id for the system.
1615 * The current OEM code page identifier for the system.
1617 UINT WINAPI GetOEMCP(void)
1619 assert( oem_cptable );
1620 return oem_cptable->info.codepage;
1624 /***********************************************************************
1625 * IsValidCodePage (KERNEL32.@)
1627 * Determine if a given code page identifier is valid.
1630 * codepage [I] Code page Id to verify.
1633 * TRUE, If codepage is valid and available on the system,
1636 BOOL WINAPI IsValidCodePage( UINT codepage )
1643 return wine_cp_get_table( codepage ) != NULL;
1648 /***********************************************************************
1649 * IsDBCSLeadByteEx (KERNEL32.@)
1651 * Determine if a character is a lead byte in a given code page.
1654 * codepage [I] Code page for the test.
1655 * testchar [I] Character to test
1658 * TRUE, if testchar is a lead byte in codepage,
1661 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1663 const union cptable *table = get_codepage_table( codepage );
1664 return table && wine_is_dbcs_leadbyte( table, testchar );
1668 /***********************************************************************
1669 * IsDBCSLeadByte (KERNEL32.@)
1670 * IsDBCSLeadByte (KERNEL.207)
1672 * Determine if a character is a lead byte.
1675 * testchar [I] Character to test
1678 * TRUE, if testchar is a lead byte in the ANSI code page,
1681 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1683 if (!ansi_cptable) return FALSE;
1684 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1688 /***********************************************************************
1689 * GetCPInfo (KERNEL32.@)
1691 * Get information about a code page.
1694 * codepage [I] Code page number
1695 * cpinfo [O] Destination for code page information
1698 * Success: TRUE. cpinfo is updated with the information about codepage.
1699 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1701 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1703 const union cptable *table;
1707 SetLastError( ERROR_INVALID_PARAMETER );
1711 if (!(table = get_codepage_table( codepage )))
1717 cpinfo->DefaultChar[0] = 0x3f;
1718 cpinfo->DefaultChar[1] = 0;
1719 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1720 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1724 SetLastError( ERROR_INVALID_PARAMETER );
1727 if (table->info.def_char & 0xff00)
1729 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1730 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1734 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1735 cpinfo->DefaultChar[1] = 0;
1737 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1738 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1740 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1745 /***********************************************************************
1746 * GetCPInfoExA (KERNEL32.@)
1748 * Get extended information about a code page.
1751 * codepage [I] Code page number
1752 * dwFlags [I] Reserved, must to 0.
1753 * cpinfo [O] Destination for code page information
1756 * Success: TRUE. cpinfo is updated with the information about codepage.
1757 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1759 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1763 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1766 /* the layout is the same except for CodePageName */
1767 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1768 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1772 /***********************************************************************
1773 * GetCPInfoExW (KERNEL32.@)
1775 * Unicode version of GetCPInfoExA.
1777 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1779 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1786 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1788 cpinfo->CodePage = CP_UTF7;
1789 cpinfo->UnicodeDefaultChar = 0x3f;
1790 strcpyW(cpinfo->CodePageName, utf7);
1796 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1798 cpinfo->CodePage = CP_UTF8;
1799 cpinfo->UnicodeDefaultChar = 0x3f;
1800 strcpyW(cpinfo->CodePageName, utf8);
1806 const union cptable *table = get_codepage_table( codepage );
1808 cpinfo->CodePage = table->info.codepage;
1809 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1810 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1811 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1818 /***********************************************************************
1819 * EnumSystemCodePagesA (KERNEL32.@)
1821 * Call a user defined function for every code page installed on the system.
1824 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1825 * flags [I] Reserved, set to 0.
1828 * TRUE, If all code pages have been enumerated, or
1829 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1831 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1833 const union cptable *table;
1839 if (!(table = wine_cp_enum_table( index++ ))) break;
1840 sprintf( buffer, "%d", table->info.codepage );
1841 if (!lpfnCodePageEnum( buffer )) break;
1847 /***********************************************************************
1848 * EnumSystemCodePagesW (KERNEL32.@)
1850 * See EnumSystemCodePagesA.
1852 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1854 const union cptable *table;
1855 WCHAR buffer[10], *p;
1856 int page, index = 0;
1860 if (!(table = wine_cp_enum_table( index++ ))) break;
1861 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1863 page = table->info.codepage;
1866 *--p = '0' + (page % 10);
1869 if (!lpfnCodePageEnum( p )) break;
1875 /***********************************************************************
1876 * MultiByteToWideChar (KERNEL32.@)
1878 * Convert a multibyte character string into a Unicode string.
1881 * page [I] Codepage character set to convert from
1882 * flags [I] Character mapping flags
1883 * src [I] Source string buffer
1884 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1885 * dst [O] Destination buffer
1886 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1889 * Success: If dstlen > 0, the number of characters written to dst.
1890 * If dstlen == 0, the number of characters needed to perform the
1891 * conversion. In both cases the count includes the terminating NUL.
1892 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1893 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1894 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1895 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1898 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1899 LPWSTR dst, INT dstlen )
1901 const union cptable *table;
1904 if (!src || !srclen || (!dst && dstlen))
1906 SetLastError( ERROR_INVALID_PARAMETER );
1910 if (srclen < 0) srclen = strlen(src) + 1;
1917 SetLastError( ERROR_INVALID_FLAGS );
1920 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1925 SetLastError( ERROR_INVALID_FLAGS );
1928 FIXME("UTF-7 not supported\n");
1929 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1934 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1938 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1942 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1945 if (!(table = get_codepage_table( page )))
1947 SetLastError( ERROR_INVALID_PARAMETER );
1950 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1958 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1959 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1963 TRACE("cp %d %s -> %s, ret = %d\n",
1964 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1969 /***********************************************************************
1970 * WideCharToMultiByte (KERNEL32.@)
1972 * Convert a Unicode character string into a multibyte string.
1975 * page [I] Code page character set to convert to
1976 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
1977 * src [I] Source string buffer
1978 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1979 * dst [O] Destination buffer
1980 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
1981 * defchar [I] Default character to use for conversion if no exact
1982 * conversion can be made
1983 * used [O] Set if default character was used in the conversion
1986 * Success: If dstlen > 0, the number of characters written to dst.
1987 * If dstlen == 0, number of characters needed to perform the
1988 * conversion. In both cases the count includes the terminating NUL.
1989 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1990 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1991 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1992 * parameter was given.
1994 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1995 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1997 const union cptable *table;
2000 if (!src || !srclen || (!dst && dstlen))
2002 SetLastError( ERROR_INVALID_PARAMETER );
2006 if (srclen < 0) srclen = strlenW(src) + 1;
2011 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2014 SetLastError( ERROR_INVALID_FLAGS );
2017 if (defchar || used)
2019 SetLastError( ERROR_INVALID_PARAMETER );
2022 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2025 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2026 if (defchar || used)
2028 SetLastError( ERROR_INVALID_PARAMETER );
2033 SetLastError( ERROR_INVALID_FLAGS );
2036 FIXME("UTF-7 not supported\n");
2037 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2042 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2043 defchar, used ? &used_tmp : NULL );
2048 if (defchar || used)
2050 SetLastError( ERROR_INVALID_PARAMETER );
2053 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2056 if (!(table = get_codepage_table( page )))
2058 SetLastError( ERROR_INVALID_PARAMETER );
2061 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2062 defchar, used ? &used_tmp : NULL );
2063 if (used) *used = used_tmp;
2071 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2072 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2076 TRACE("cp %d %s -> %s, ret = %d\n",
2077 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2082 /***********************************************************************
2083 * GetThreadLocale (KERNEL32.@)
2085 * Get the current threads locale.
2091 * The LCID currently associated with the calling thread.
2093 LCID WINAPI GetThreadLocale(void)
2095 LCID ret = NtCurrentTeb()->CurrentLocale;
2096 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2100 /**********************************************************************
2101 * SetThreadLocale (KERNEL32.@)
2103 * Set the current threads locale.
2106 * lcid [I] LCID of the locale to set
2109 * Success: TRUE. The threads locale is set to lcid.
2110 * Failure: FALSE. Use GetLastError() to determine the cause.
2112 BOOL WINAPI SetThreadLocale( LCID lcid )
2114 TRACE("(0x%04X)\n", lcid);
2116 lcid = ConvertDefaultLocale(lcid);
2118 if (lcid != GetThreadLocale())
2120 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2122 SetLastError(ERROR_INVALID_PARAMETER);
2126 NtCurrentTeb()->CurrentLocale = lcid;
2131 /**********************************************************************
2132 * SetThreadUILanguage (KERNEL32.@)
2134 * Set the current threads UI language.
2137 * langid [I] LANGID of the language to set, or 0 to use
2138 * the available language which is best supported
2139 * for console applications
2142 * Success: The return value is the same as the input value.
2143 * Failure: The return value differs from the input value.
2144 * Use GetLastError() to determine the cause.
2146 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2148 TRACE("(0x%04x) stub - returning success\n", langid);
2152 /******************************************************************************
2153 * ConvertDefaultLocale (KERNEL32.@)
2155 * Convert a default locale identifier into a real identifier.
2158 * lcid [I] LCID identifier of the locale to convert
2161 * lcid unchanged, if not a default locale or its sublanguage is
2162 * not SUBLANG_NEUTRAL.
2163 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2164 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2165 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2167 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2173 case LOCALE_SYSTEM_DEFAULT:
2174 lcid = GetSystemDefaultLCID();
2176 case LOCALE_USER_DEFAULT:
2177 case LOCALE_NEUTRAL:
2178 lcid = GetUserDefaultLCID();
2181 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2182 langid = LANGIDFROMLCID(lcid);
2183 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2185 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2186 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2193 /******************************************************************************
2194 * IsValidLocale (KERNEL32.@)
2196 * Determine if a locale is valid.
2199 * lcid [I] LCID of the locale to check
2200 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2203 * TRUE, if lcid is valid,
2207 * Wine does not currently make the distinction between supported and installed. All
2208 * languages supported are installed by default.
2210 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2212 /* check if language is registered in the kernel32 resources */
2213 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2214 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2217 /******************************************************************************
2218 * IsValidLocaleName (KERNEL32.@)
2220 BOOL WINAPI IsValidLocaleName( LPCWSTR locale )
2222 struct locale_name locale_name;
2224 /* string parsing */
2225 parse_locale_name( locale, &locale_name );
2227 TRACE( "found lcid %x for %s, matches %d\n",
2228 locale_name.lcid, debugstr_w(locale), locale_name.matches );
2230 return locale_name.matches > 0;
2233 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2234 LPCSTR name, WORD LangID, LONG_PTR lParam )
2236 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2239 sprintf(buf, "%08x", (UINT)LangID);
2240 return lpfnLocaleEnum( buf );
2243 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2244 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2246 static const WCHAR formatW[] = {'%','0','8','x',0};
2247 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2249 sprintfW( buf, formatW, (UINT)LangID );
2250 return lpfnLocaleEnum( buf );
2253 /******************************************************************************
2254 * EnumSystemLocalesA (KERNEL32.@)
2256 * Call a users function for each locale available on the system.
2259 * lpfnLocaleEnum [I] Callback function to call for each locale
2260 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2264 * Failure: FALSE. Use GetLastError() to determine the cause.
2266 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2268 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2269 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2270 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2271 (LONG_PTR)lpfnLocaleEnum);
2276 /******************************************************************************
2277 * EnumSystemLocalesW (KERNEL32.@)
2279 * See EnumSystemLocalesA.
2281 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2283 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2284 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2285 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2286 (LONG_PTR)lpfnLocaleEnum);
2291 struct enum_locale_ex_data
2293 LOCALE_ENUMPROCEX proc;
2298 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2299 LPCWSTR name, WORD lang, LONG_PTR lparam )
2301 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2306 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2307 buffer, sizeof(buffer) / sizeof(WCHAR) );
2308 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2309 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2310 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2312 flags = LOCALE_WINDOWS;
2313 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2314 if (data->flags && ~(data->flags & flags)) return TRUE;
2315 return data->proc( buffer, flags, data->lparam );
2318 /******************************************************************************
2319 * EnumSystemLocalesEx (KERNEL32.@)
2321 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2323 struct enum_locale_ex_data data;
2327 SetLastError( ERROR_INVALID_PARAMETER );
2332 data.lparam = lparam;
2333 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2334 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2335 enum_locale_ex_proc, (LONG_PTR)&data );
2340 /***********************************************************************
2341 * VerLanguageNameA (KERNEL32.@)
2343 * Get the name of a language.
2346 * wLang [I] LANGID of the language
2347 * szLang [O] Destination for the language name
2350 * Success: The size of the language name. If szLang is non-NULL, it is filled
2352 * Failure: 0. Use GetLastError() to determine the cause.
2355 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2357 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2361 /***********************************************************************
2362 * VerLanguageNameW (KERNEL32.@)
2364 * See VerLanguageNameA.
2366 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2368 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2372 /******************************************************************************
2373 * GetStringTypeW (KERNEL32.@)
2375 * See GetStringTypeA.
2377 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2379 static const unsigned char type2_map[16] =
2381 C2_NOTAPPLICABLE, /* unassigned */
2382 C2_LEFTTORIGHT, /* L */
2383 C2_RIGHTTOLEFT, /* R */
2384 C2_EUROPENUMBER, /* EN */
2385 C2_EUROPESEPARATOR, /* ES */
2386 C2_EUROPETERMINATOR, /* ET */
2387 C2_ARABICNUMBER, /* AN */
2388 C2_COMMONSEPARATOR, /* CS */
2389 C2_BLOCKSEPARATOR, /* B */
2390 C2_SEGMENTSEPARATOR, /* S */
2391 C2_WHITESPACE, /* WS */
2392 C2_OTHERNEUTRAL, /* ON */
2393 C2_RIGHTTOLEFT, /* AL */
2394 C2_NOTAPPLICABLE, /* NSM */
2395 C2_NOTAPPLICABLE, /* BN */
2396 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2399 if (count == -1) count = strlenW(src) + 1;
2403 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2406 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2410 WARN("CT_CTYPE3: semi-stub.\n");
2414 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2416 type1 = get_char_typeW( *src++ ) & 0xfff;
2417 /* try to construct type3 from type1 */
2418 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2419 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2420 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2421 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2422 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2423 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2424 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2426 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2427 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2428 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2429 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2430 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2431 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2432 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2433 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2435 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2436 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2437 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2438 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2439 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2440 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2441 *chartype++ = type3;
2446 SetLastError( ERROR_INVALID_PARAMETER );
2453 /******************************************************************************
2454 * GetStringTypeExW (KERNEL32.@)
2456 * See GetStringTypeExA.
2458 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2460 /* locale is ignored for Unicode */
2461 return GetStringTypeW( type, src, count, chartype );
2465 /******************************************************************************
2466 * GetStringTypeA (KERNEL32.@)
2468 * Get characteristics of the characters making up a string.
2471 * locale [I] Locale Id for the string
2472 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2473 * src [I] String to analyse
2474 * count [I] Length of src in chars, or -1 if src is NUL terminated
2475 * chartype [O] Destination for the calculated characteristics
2478 * Success: TRUE. chartype is filled with the requested characteristics of each char
2480 * Failure: FALSE. Use GetLastError() to determine the cause.
2482 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2489 if(count == -1) count = strlen(src) + 1;
2491 if (!(cp = get_lcid_codepage( locale )))
2493 FIXME("For locale %04x using current ANSI code page\n", locale);
2497 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2498 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2500 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2502 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2503 * string, with multibyte characters there maybe be more bytes in count
2504 * than character space in the buffer!
2506 ret = GetStringTypeW(type, srcW, countW, chartype);
2507 HeapFree(GetProcessHeap(), 0, srcW);
2512 /******************************************************************************
2513 * GetStringTypeExA (KERNEL32.@)
2515 * Get characteristics of the characters making up a string.
2518 * locale [I] Locale Id for the string
2519 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2520 * src [I] String to analyse
2521 * count [I] Length of src in chars, or -1 if src is NUL terminated
2522 * chartype [O] Destination for the calculated characteristics
2525 * Success: TRUE. chartype is filled with the requested characteristics of each char
2527 * Failure: FALSE. Use GetLastError() to determine the cause.
2529 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2531 return GetStringTypeA(locale, type, src, count, chartype);
2534 /*************************************************************************
2535 * LCMapStringEx (KERNEL32.@)
2537 * Map characters in a locale sensitive string.
2540 * name [I] Locale name for the conversion.
2541 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2542 * src [I] String to map
2543 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2544 * dst [O] Destination for mapped string
2545 * dstlen [I] Length of dst in characters
2546 * version [I] reserved, must be NULL
2547 * reserved [I] reserved, must be NULL
2548 * lparam [I] reserved, must be 0
2551 * Success: The length of the mapped string in dst, including the NUL terminator.
2552 * Failure: 0. Use GetLastError() to determine the cause.
2554 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2555 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2559 if (version) FIXME("unsupported version structure %p\n", version);
2560 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2561 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2563 if (!src || !srclen || dstlen < 0)
2565 SetLastError(ERROR_INVALID_PARAMETER);
2569 /* mutually exclusive flags */
2570 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2571 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2572 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2573 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2575 SetLastError(ERROR_INVALID_FLAGS);
2579 if (!dstlen) dst = NULL;
2581 if (flags & LCMAP_SORTKEY)
2586 SetLastError(ERROR_INVALID_FLAGS);
2590 if (srclen < 0) srclen = strlenW(src);
2592 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2593 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2595 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2597 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2603 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2604 if (flags & SORT_STRINGSORT)
2606 SetLastError(ERROR_INVALID_FLAGS);
2610 if (srclen < 0) srclen = strlenW(src) + 1;
2612 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2613 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2615 if (!dst) /* return required string length */
2619 for (len = 0; srclen; src++, srclen--)
2622 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2623 * and skips white space and punctuation characters for
2624 * NORM_IGNORESYMBOLS.
2626 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2633 if (flags & LCMAP_UPPERCASE)
2635 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2638 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2640 *dst_ptr++ = toupperW(wch);
2644 else if (flags & LCMAP_LOWERCASE)
2646 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2649 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2651 *dst_ptr++ = tolowerW(wch);
2659 SetLastError(ERROR_INVALID_FLAGS);
2662 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2665 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2674 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2678 return dst_ptr - dst;
2681 /*************************************************************************
2682 * LCMapStringW (KERNEL32.@)
2686 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2687 LPWSTR dst, INT dstlen)
2689 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2690 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2692 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2695 /*************************************************************************
2696 * LCMapStringA (KERNEL32.@)
2698 * Map characters in a locale sensitive string.
2701 * lcid [I] LCID for the conversion.
2702 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2703 * src [I] String to map
2704 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2705 * dst [O] Destination for mapped string
2706 * dstlen [I] Length of dst in characters
2709 * Success: The length of the mapped string in dst, including the NUL terminator.
2710 * Failure: 0. Use GetLastError() to determine the cause.
2712 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2713 LPSTR dst, INT dstlen)
2715 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2717 INT ret = 0, srclenW, dstlenW;
2718 UINT locale_cp = CP_ACP;
2720 if (!src || !srclen || dstlen < 0)
2722 SetLastError(ERROR_INVALID_PARAMETER);
2726 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2728 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2733 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2734 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2737 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2740 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2743 if (flags & LCMAP_SORTKEY)
2747 SetLastError(ERROR_INVALID_FLAGS);
2748 goto map_string_exit;
2750 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2752 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2755 goto map_string_exit;
2758 if (flags & SORT_STRINGSORT)
2760 SetLastError(ERROR_INVALID_FLAGS);
2761 goto map_string_exit;
2764 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2766 goto map_string_exit;
2768 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2771 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2772 goto map_string_exit;
2775 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2776 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2777 HeapFree(GetProcessHeap(), 0, dstW);
2780 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2784 /*************************************************************************
2785 * FoldStringA (KERNEL32.@)
2787 * Map characters in a string.
2790 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2791 * src [I] String to map
2792 * srclen [I] Length of src, or -1 if src is NUL terminated
2793 * dst [O] Destination for mapped string
2794 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2797 * Success: The length of the string written to dst, including the terminating NUL. If
2798 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2799 * and dst may be NULL.
2800 * Failure: 0. Use GetLastError() to determine the cause.
2802 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2803 LPSTR dst, INT dstlen)
2805 INT ret = 0, srclenW = 0;
2806 WCHAR *srcW = NULL, *dstW = NULL;
2808 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2810 SetLastError(ERROR_INVALID_PARAMETER);
2814 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2815 src, srclen, NULL, 0);
2816 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2820 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2821 goto FoldStringA_exit;
2824 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2825 src, srclen, srcW, srclenW);
2827 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2829 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2832 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2836 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2837 goto FoldStringA_exit;
2840 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2841 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2844 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2848 HeapFree(GetProcessHeap(), 0, dstW);
2851 HeapFree(GetProcessHeap(), 0, srcW);
2855 /*************************************************************************
2856 * FoldStringW (KERNEL32.@)
2860 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2861 LPWSTR dst, INT dstlen)
2865 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2870 /* Fall through for dwFlags == 0 */
2871 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2872 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2873 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2874 SetLastError(ERROR_INVALID_FLAGS);
2878 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2880 SetLastError(ERROR_INVALID_PARAMETER);
2884 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2886 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2890 /******************************************************************************
2891 * CompareStringW (KERNEL32.@)
2893 * See CompareStringA.
2895 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
2896 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2898 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
2901 /******************************************************************************
2902 * CompareStringEx (KERNEL32.@)
2904 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
2905 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
2909 if (version) FIXME("unexpected version parameter\n");
2910 if (reserved) FIXME("unexpected reserved value\n");
2911 if (lParam) FIXME("unexpected lParam\n");
2915 SetLastError(ERROR_INVALID_PARAMETER);
2919 if( flags & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2920 SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2922 SetLastError(ERROR_INVALID_FLAGS);
2926 /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2927 if (flags & 0x10000000)
2928 WARN("Ignoring unknown flags 0x10000000\n");
2930 if (len1 < 0) len1 = strlenW(str1);
2931 if (len2 < 0) len2 = strlenW(str2);
2933 ret = wine_compare_string(flags, str1, len1, str2, len2);
2935 if (ret) /* need to translate result */
2936 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2940 /******************************************************************************
2941 * CompareStringA (KERNEL32.@)
2943 * Compare two locale sensitive strings.
2946 * lcid [I] LCID for the comparison
2947 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
2948 * str1 [I] First string to compare
2949 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
2950 * str2 [I] Second string to compare
2951 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
2954 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2955 * str1 is less than, equal to or greater than str2 respectively.
2956 * Failure: FALSE. Use GetLastError() to determine the cause.
2958 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
2959 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2961 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2962 WCHAR *buf2W = buf1W + 130;
2963 LPWSTR str1W, str2W;
2964 INT len1W, len2W, ret;
2965 UINT locale_cp = CP_ACP;
2969 SetLastError(ERROR_INVALID_PARAMETER);
2972 if (len1 < 0) len1 = strlen(str1);
2973 if (len2 < 0) len2 = strlen(str2);
2975 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2979 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2984 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2985 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2988 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2991 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
3002 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
3007 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
3008 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
3011 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3012 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3015 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3024 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3026 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3027 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3031 /*************************************************************************
3032 * lstrcmp (KERNEL32.@)
3033 * lstrcmpA (KERNEL32.@)
3035 * Compare two strings using the current thread locale.
3038 * str1 [I] First string to compare
3039 * str2 [I] Second string to compare
3042 * Success: A number less than, equal to or greater than 0 depending on whether
3043 * str1 is less than, equal to or greater than str2 respectively.
3044 * Failure: FALSE. Use GetLastError() to determine the cause.
3046 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3050 if ((str1 == NULL) && (str2 == NULL)) return 0;
3051 if (str1 == NULL) return -1;
3052 if (str2 == NULL) return 1;
3054 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3060 /*************************************************************************
3061 * lstrcmpi (KERNEL32.@)
3062 * lstrcmpiA (KERNEL32.@)
3064 * Compare two strings using the current thread locale, ignoring case.
3067 * str1 [I] First string to compare
3068 * str2 [I] Second string to compare
3071 * Success: A number less than, equal to or greater than 0 depending on whether
3072 * str2 is less than, equal to or greater than str1 respectively.
3073 * Failure: FALSE. Use GetLastError() to determine the cause.
3075 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3079 if ((str1 == NULL) && (str2 == NULL)) return 0;
3080 if (str1 == NULL) return -1;
3081 if (str2 == NULL) return 1;
3083 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3089 /*************************************************************************
3090 * lstrcmpW (KERNEL32.@)
3094 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3098 if ((str1 == NULL) && (str2 == NULL)) return 0;
3099 if (str1 == NULL) return -1;
3100 if (str2 == NULL) return 1;
3102 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3108 /*************************************************************************
3109 * lstrcmpiW (KERNEL32.@)
3113 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3117 if ((str1 == NULL) && (str2 == NULL)) return 0;
3118 if (str1 == NULL) return -1;
3119 if (str2 == NULL) return 1;
3121 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3127 /******************************************************************************
3130 void LOCALE_Init(void)
3132 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3133 const union cptable *unix_cp );
3135 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3138 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3139 char user_locale[50];
3141 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3142 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3143 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3144 CFStringRef user_locale_string_ref;
3146 if (user_locale_country_ref)
3148 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3149 user_locale_lang_ref, user_locale_country_ref);
3153 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3154 user_locale_lang_ref);
3157 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3159 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3160 setenv( "LANG", user_locale, 0 );
3161 TRACE( "setting locale to '%s'\n", user_locale );
3162 #endif /* __APPLE__ */
3164 setlocale( LC_ALL, "" );
3166 unix_cp = setup_unix_locales();
3167 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3170 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3171 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3173 /* Retrieve the preferred language as chosen in System Preferences. */
3174 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3176 CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3177 CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3178 CFStringRef user_language_string_ref;
3179 if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3180 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3181 !CFEqual(user_language_string_ref, user_locale_lang_ref))
3183 struct locale_name locale_name;
3185 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3186 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3187 parse_locale_name( buffer, &locale_name );
3188 lcid_LC_MESSAGES = locale_name.lcid;
3189 TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3191 CFRelease( all_locales );
3192 if (preferred_locales)
3193 CFRelease( preferred_locales );
3196 CFRelease( user_locale_ref );
3197 CFRelease( user_locale_string_ref );
3200 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3201 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3202 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3204 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3205 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3206 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3207 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3208 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3210 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3211 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3213 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3214 ansi_cptable = wine_cp_get_table( 1252 );
3215 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3216 oem_cptable = wine_cp_get_table( 437 );
3217 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3218 mac_cptable = wine_cp_get_table( 10000 );
3219 if (unix_cp != CP_UTF8)
3221 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3222 unix_cptable = wine_cp_get_table( 28591 );
3225 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3227 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3228 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3229 mac_cptable->info.codepage, unix_cp );
3231 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3234 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3236 UNICODE_STRING keyName;
3237 OBJECT_ATTRIBUTES attr;
3240 RtlInitUnicodeString( &keyName, szKeyName );
3241 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3243 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3249 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3253 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3256 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3257 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3258 info->NameLength > keyNameSize)
3263 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3265 memcpy( szKeyName, info->Name, info->NameLength);
3266 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3268 TRACE("returning %s\n", debugstr_w(szKeyName));
3272 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3273 LPWSTR szValueName, ULONG valueNameSize,
3274 LPWSTR szValueData, ULONG valueDataSize)
3277 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3280 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3281 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3282 info->NameLength > valueNameSize ||
3283 info->DataLength > valueDataSize)
3288 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3290 memcpy( szValueName, info->Name, info->NameLength);
3291 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3292 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3293 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3295 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3299 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3302 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3303 DWORD dwSize = sizeof(buffer);
3304 UNICODE_STRING valueName;
3306 RtlInitUnicodeString( &valueName, szValueName );
3308 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3309 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3310 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3311 info->DataLength == sizeof(DWORD))
3313 memcpy(lpVal, info->Data, sizeof(DWORD));
3320 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3323 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3327 /* FIXME: Is it correct to use the system default langid? */
3328 langId = GetSystemDefaultLangID();
3330 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3331 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3333 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3337 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3341 ULONG iResourceIndex = lgrpid & 0xf;
3342 LPCWSTR lpResEntry = LockResource( hResDir );
3345 for (i = 0; i < iResourceIndex; i++)
3346 lpResEntry += *lpResEntry + 1;
3348 if (*lpResEntry < nameSize)
3350 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3351 szName[*lpResEntry] = '\0';
3356 FreeResource( hResource );
3361 /* Registry keys for NLS related information */
3363 static const WCHAR szCountryListName[] = {
3364 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3365 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3366 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3367 'T','e','l','e','p','h','o','n','y','\\',
3368 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3372 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3375 LANGUAGEGROUP_ENUMPROCA procA;
3376 LANGUAGEGROUP_ENUMPROCW procW;
3379 } ENUMLANGUAGEGROUP_CALLBACKS;
3381 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3382 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3384 WCHAR szNumber[10], szValue[4];
3386 BOOL bContinue = TRUE;
3391 SetLastError(ERROR_INVALID_PARAMETER);
3395 switch (lpProcs->dwFlags)
3398 /* Default to LGRPID_INSTALLED */
3399 lpProcs->dwFlags = LGRPID_INSTALLED;
3400 /* Fall through... */
3401 case LGRPID_INSTALLED:
3402 case LGRPID_SUPPORTED:
3405 SetLastError(ERROR_INVALID_FLAGS);
3409 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3412 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3416 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3417 szValue, sizeof(szValue) ))
3419 BOOL bInstalled = szValue[0] == '1';
3420 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3422 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3423 bInstalled ? "" : "not ");
3425 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3427 WCHAR szGrpName[48];
3429 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3430 szGrpName[0] = '\0';
3433 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3437 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3438 char szGrpNameA[48];
3440 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3441 * or whether the language names are ever localised. Assume CP_ACP.
3444 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3445 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3447 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3467 /******************************************************************************
3468 * EnumSystemLanguageGroupsA (KERNEL32.@)
3470 * Call a users function for each language group available on the system.
3473 * pLangGrpEnumProc [I] Callback function to call for each language group
3474 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3475 * lParam [I] User parameter to pass to pLangGrpEnumProc
3479 * Failure: FALSE. Use GetLastError() to determine the cause.
3481 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3482 DWORD dwFlags, LONG_PTR lParam)
3484 ENUMLANGUAGEGROUP_CALLBACKS procs;
3486 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3488 procs.procA = pLangGrpEnumProc;
3490 procs.dwFlags = dwFlags;
3491 procs.lParam = lParam;
3493 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3496 /******************************************************************************
3497 * EnumSystemLanguageGroupsW (KERNEL32.@)
3499 * See EnumSystemLanguageGroupsA.
3501 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3502 DWORD dwFlags, LONG_PTR lParam)
3504 ENUMLANGUAGEGROUP_CALLBACKS procs;
3506 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3509 procs.procW = pLangGrpEnumProc;
3510 procs.dwFlags = dwFlags;
3511 procs.lParam = lParam;
3513 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3516 /******************************************************************************
3517 * IsValidLanguageGroup (KERNEL32.@)
3519 * Determine if a language group is supported and/or installed.
3522 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3523 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3526 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3529 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3531 static const WCHAR szFormat[] = { '%','x','\0' };
3532 WCHAR szValueName[16], szValue[2];
3533 BOOL bSupported = FALSE, bInstalled = FALSE;
3539 case LGRPID_INSTALLED:
3540 case LGRPID_SUPPORTED:
3542 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3544 sprintfW( szValueName, szFormat, lgrpid );
3546 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3550 if (szValue[0] == '1')
3560 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3561 (dwFlags == LGRPID_INSTALLED && bInstalled))
3567 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3570 LANGGROUPLOCALE_ENUMPROCA procA;
3571 LANGGROUPLOCALE_ENUMPROCW procW;
3575 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3577 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3578 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3580 static const WCHAR szAlternateSortsKeyName[] = {
3581 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3583 WCHAR szNumber[10], szValue[4];
3585 BOOL bContinue = TRUE, bAlternate = FALSE;
3587 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3589 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3591 SetLastError(ERROR_INVALID_PARAMETER);
3595 if (lpProcs->dwFlags)
3597 SetLastError(ERROR_INVALID_FLAGS);
3601 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3604 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3608 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3609 szValue, sizeof(szValue) ))
3611 lgrpid = strtoulW( szValue, NULL, 16 );
3613 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3614 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3616 if (lgrpid == lpProcs->lgrpid)
3620 lcid = strtoulW( szNumber, NULL, 16 );
3622 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3623 * '00000437 ;Georgian'
3624 * At present we only pass the LCID string.
3628 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3631 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3633 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3635 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3643 /* Finished enumerating this key */
3646 /* Enumerate alternate sorts also */
3647 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3652 bContinue = FALSE; /* Finished both keys */
3665 /******************************************************************************
3666 * EnumLanguageGroupLocalesA (KERNEL32.@)
3668 * Call a users function for every locale in a language group available on the system.
3671 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3672 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3673 * dwFlags [I] Reserved, set to 0
3674 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3678 * Failure: FALSE. Use GetLastError() to determine the cause.
3680 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3681 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3683 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3685 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3687 callbacks.procA = pLangGrpLcEnumProc;
3688 callbacks.procW = NULL;
3689 callbacks.dwFlags = dwFlags;
3690 callbacks.lgrpid = lgrpid;
3691 callbacks.lParam = lParam;
3693 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3696 /******************************************************************************
3697 * EnumLanguageGroupLocalesW (KERNEL32.@)
3699 * See EnumLanguageGroupLocalesA.
3701 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3702 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3704 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3706 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3708 callbacks.procA = NULL;
3709 callbacks.procW = pLangGrpLcEnumProc;
3710 callbacks.dwFlags = dwFlags;
3711 callbacks.lgrpid = lgrpid;
3712 callbacks.lParam = lParam;
3714 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3717 /******************************************************************************
3718 * EnumSystemGeoID (KERNEL32.@)
3720 * Call a users function for every location available on the system.
3723 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3724 * reserved [I] Reserved, set to 0
3725 * pGeoEnumProc [I] Callback function to call for each location
3729 * Failure: FALSE. Use GetLastError() to determine the cause.
3731 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3733 static const WCHAR szCountryCodeValueName[] = {
3734 'C','o','u','n','t','r','y','C','o','d','e','\0'
3740 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3742 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3744 SetLastError(ERROR_INVALID_PARAMETER);
3748 hKey = NLS_RegOpenKey( 0, szCountryListName );
3750 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3752 BOOL bContinue = TRUE;
3754 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3758 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3760 TRACE("Got geoid %d\n", dwGeoId);
3762 if (!pGeoEnumProc( dwGeoId ))
3781 /******************************************************************************
3782 * InvalidateNLSCache (KERNEL32.@)
3784 * Invalidate the cache of NLS values.
3793 BOOL WINAPI InvalidateNLSCache(void)
3799 /******************************************************************************
3800 * GetUserGeoID (KERNEL32.@)
3802 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3804 GEOID ret = GEOID_NOT_AVAILABLE;
3805 static const WCHAR geoW[] = {'G','e','o',0};
3806 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3807 WCHAR bufferW[40], *end;
3809 HANDLE hkey, hSubkey = 0;
3810 UNICODE_STRING keyW;
3811 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3812 RtlInitUnicodeString( &keyW, nationW );
3813 count = sizeof(bufferW);
3815 if(!(hkey = create_registry_key())) return ret;
3818 case GEOCLASS_NATION:
3819 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3821 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3822 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3823 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3826 case GEOCLASS_REGION:
3827 FIXME("GEOCLASS_REGION not handled yet\n");
3832 if (hSubkey) NtClose(hSubkey);
3836 /******************************************************************************
3837 * SetUserGeoID (KERNEL32.@)
3839 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3841 static const WCHAR geoW[] = {'G','e','o',0};
3842 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3843 static const WCHAR formatW[] = {'%','i',0};
3844 UNICODE_STRING nameW,keyW;
3846 OBJECT_ATTRIBUTES attr;
3849 if(!(hkey = create_registry_key())) return FALSE;
3851 attr.Length = sizeof(attr);
3852 attr.RootDirectory = hkey;
3853 attr.ObjectName = &nameW;
3854 attr.Attributes = 0;
3855 attr.SecurityDescriptor = NULL;
3856 attr.SecurityQualityOfService = NULL;
3857 RtlInitUnicodeString( &nameW, geoW );
3858 RtlInitUnicodeString( &keyW, nationW );
3860 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3863 NtClose(attr.RootDirectory);
3867 sprintfW(bufferW, formatW, GeoID);
3868 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3869 NtClose(attr.RootDirectory);
3878 UILANGUAGE_ENUMPROCA procA;
3879 UILANGUAGE_ENUMPROCW procW;
3883 } ENUM_UILANG_CALLBACK;
3885 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3886 LPCSTR name, WORD LangID, LONG_PTR lParam )
3888 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3891 sprintf(buf, "%08x", (UINT)LangID);
3892 return enum_uilang->u.procA( buf, enum_uilang->param );
3895 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3896 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3898 static const WCHAR formatW[] = {'%','0','8','x',0};
3899 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3902 sprintfW( buf, formatW, (UINT)LangID );
3903 return enum_uilang->u.procW( buf, enum_uilang->param );
3906 /******************************************************************************
3907 * EnumUILanguagesA (KERNEL32.@)
3909 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3911 ENUM_UILANG_CALLBACK enum_uilang;
3913 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3915 if(!pUILangEnumProc) {
3916 SetLastError(ERROR_INVALID_PARAMETER);
3920 SetLastError(ERROR_INVALID_FLAGS);
3924 enum_uilang.u.procA = pUILangEnumProc;
3925 enum_uilang.flags = dwFlags;
3926 enum_uilang.param = lParam;
3928 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3929 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3930 (LONG_PTR)&enum_uilang);
3934 /******************************************************************************
3935 * EnumUILanguagesW (KERNEL32.@)
3937 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3939 ENUM_UILANG_CALLBACK enum_uilang;
3941 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3944 if(!pUILangEnumProc) {
3945 SetLastError(ERROR_INVALID_PARAMETER);
3949 SetLastError(ERROR_INVALID_FLAGS);
3953 enum_uilang.u.procW = pUILangEnumProc;
3954 enum_uilang.flags = dwFlags;
3955 enum_uilang.param = lParam;
3957 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3958 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3959 (LONG_PTR)&enum_uilang);
3963 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
3964 int cchData, LANGID language)
3966 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3970 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
3971 int cchData, LANGID language)
3973 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3977 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
3981 TRACE("%p, %d\n", localename, buffersize);
3983 userlcid = GetUserDefaultLCID();
3984 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
3987 /******************************************************************************
3988 * NormalizeString (KERNEL32.@)
3990 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
3991 LPWSTR lpDstString, INT cwDstLength)
3993 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
3994 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3998 /******************************************************************************
3999 * IsNormalizedString (KERNEL32.@)
4001 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
4003 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
4004 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4018 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
4022 delta /= (firsttime ? DAMP : 2);
4023 delta += delta/numpoints;
4025 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4027 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4030 /******************************************************************************
4031 * IdnToAscii (KERNEL32.@)
4032 * Implementation of Punycode based on RFC 3492.
4034 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4035 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4037 static const WCHAR prefixW[] = {'x','n','-','-'};
4040 INT i, label_start, label_end, norm_len, out_label, out = 0;
4042 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4043 lpASCIICharStr, cchASCIIChar);
4045 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4048 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4050 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4053 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4054 cchUnicodeChar, norm_str, norm_len);
4056 HeapFree(GetProcessHeap(), 0, norm_str);
4060 for(label_start=0; label_start<norm_len;) {
4061 INT n = INIT_N, bias = INIT_BIAS;
4062 INT delta = 0, b = 0, h;
4065 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4066 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4067 if(norm_str[i] < 0x80)
4071 if(b == label_end-label_start) {
4072 if(label_end < norm_len)
4074 if(!lpASCIICharStr) {
4076 }else if(out+b <= cchASCIIChar) {
4077 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4080 HeapFree(GetProcessHeap(), 0, norm_str);
4081 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4084 label_start = label_end+1;
4088 if(!lpASCIICharStr) {
4089 out += 5+b; /* strlen(xn--...-) */
4090 }else if(out+5+b <= cchASCIIChar) {
4091 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4093 for(i=label_start; i<label_end; i++)
4094 if(norm_str[i] < 0x80)
4095 lpASCIICharStr[out++] = norm_str[i];
4096 lpASCIICharStr[out++] = '-';
4098 HeapFree(GetProcessHeap(), 0, norm_str);
4099 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4105 for(h=b; h<label_end-label_start;) {
4106 INT m = 0xffff, q, k;
4108 for(i=label_start; i<label_end; i++) {
4109 if(norm_str[i]>=n && m>norm_str[i])
4112 delta += (m-n)*(h+1);
4115 for(i=label_start; i<label_end; i++) {
4116 if(norm_str[i] < n) {
4118 }else if(norm_str[i] == n) {
4119 for(q=delta, k=BASE; ; k+=BASE) {
4120 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4121 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4122 if(!lpASCIICharStr) {
4124 }else if(out+1 <= cchASCIIChar) {
4125 lpASCIICharStr[out++] = disp<='z'-'a' ?
4126 'a'+disp : '0'+disp-'z'+'a'-1;
4128 HeapFree(GetProcessHeap(), 0, norm_str);
4129 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4136 bias = adapt(delta, h+1, h==b);
4145 if(out-out_label > 63) {
4146 HeapFree(GetProcessHeap(), 0, norm_str);
4147 SetLastError(ERROR_INVALID_NAME);
4151 if(label_end < norm_len) {
4152 if(!lpASCIICharStr) {
4154 }else if(out+1 <= cchASCIIChar) {
4155 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4157 HeapFree(GetProcessHeap(), 0, norm_str);
4158 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4162 label_start = label_end+1;
4165 HeapFree(GetProcessHeap(), 0, norm_str);
4169 /******************************************************************************
4170 * IdnToNameprepUnicode (KERNEL32.@)
4172 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4173 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4182 extern const unsigned short nameprep_char_type[];
4183 extern const WCHAR nameprep_mapping[];
4186 WCHAR buf[64], *map_str, norm_str[64], ch;
4187 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4188 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4190 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4191 lpNameprepCharStr, cchNameprepChar);
4193 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4194 SetLastError(ERROR_INVALID_FLAGS);
4198 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4199 SetLastError(ERROR_INVALID_PARAMETER);
4203 if(cchUnicodeChar == -1)
4204 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4205 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4206 SetLastError(ERROR_INVALID_NAME);
4210 for(label_start=0; label_start<cchUnicodeChar;) {
4212 for(i=label_start; i<cchUnicodeChar; i++) {
4213 ch = lpUnicodeCharStr[i];
4215 if(i!=cchUnicodeChar-1 && !ch) {
4216 SetLastError(ERROR_INVALID_NAME);
4219 /* check if ch is one of label separators defined in RFC3490 */
4220 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4228 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4230 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4231 || (ch>='0' && ch<='9') || ch=='-')
4234 SetLastError(ERROR_INVALID_NAME);
4238 /* last label may be empty */
4239 if(label_start==label_end && ch) {
4240 SetLastError(ERROR_INVALID_NAME);
4244 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4245 lpUnicodeCharStr[label_end-1]=='-')) {
4246 SetLastError(ERROR_INVALID_NAME);
4251 /* maximal label length is 63 characters */
4252 if(label_end-label_start > 63) {
4253 SetLastError(ERROR_INVALID_NAME);
4256 if(label_end < cchUnicodeChar)
4259 if(!lpNameprepCharStr) {
4260 out += label_end-label_start;
4261 }else if(out+label_end-label_start <= cchNameprepChar) {
4262 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4263 (label_end-label_start)*sizeof(WCHAR));
4264 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4265 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4266 out += label_end-label_start;
4268 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4272 label_start = label_end;
4277 for(i=label_start; i<label_end; i++) {
4278 ch = lpUnicodeCharStr[i];
4279 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4280 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4282 if(!ptr[0]) map_len++;
4283 else if(!ptr[1]) map_len++;
4284 else if(!ptr[2]) map_len += 2;
4285 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4287 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4288 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4290 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4297 for(i=label_start; i<label_end; i++) {
4298 ch = lpUnicodeCharStr[i];
4299 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4300 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4303 map_str[map_len++] = ch;
4305 map_str[map_len++] = ptr[0];
4307 map_str[map_len++] = ptr[0];
4308 map_str[map_len++] = ptr[1];
4309 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4310 map_str[map_len++] = ptr[0];
4311 map_str[map_len++] = ptr[1];
4312 map_str[map_len++] = ptr[2];
4316 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4317 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4319 HeapFree(GetProcessHeap(), 0, map_str);
4321 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4322 SetLastError(ERROR_INVALID_NAME);
4326 if(label_end < cchUnicodeChar) {
4327 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4331 if(!lpNameprepCharStr) {
4333 }else if(out+norm_len <= cchNameprepChar) {
4334 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4337 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4341 have_bidi_ral = prohibit_bidi_ral = FALSE;
4343 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4345 for(i=0; i<norm_len; i++) {
4347 flags = get_table_entry( nameprep_char_type, ch );
4350 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4351 : ERROR_NO_UNICODE_TRANSLATION);
4355 if(flags & BIDI_RAL)
4356 have_bidi_ral = TRUE;
4358 prohibit_bidi_ral = TRUE;
4363 flags = get_table_entry( nameprep_char_type, ch );
4364 if((flags & BIDI_RAL) == 0)
4365 prohibit_bidi_ral = TRUE;
4367 ch = norm_str[norm_len-1];
4368 flags = get_table_entry( nameprep_char_type, ch );
4369 if((flags & BIDI_RAL) == 0)
4370 prohibit_bidi_ral = TRUE;
4373 if(have_bidi_ral && prohibit_bidi_ral) {
4374 SetLastError(ERROR_INVALID_NAME);
4378 label_start = label_end;
4384 /******************************************************************************
4385 * IdnToUnicode (KERNEL32.@)
4387 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4388 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4390 extern const unsigned short nameprep_char_type[];
4392 INT i, label_start, label_end, out_label, out = 0;
4395 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4396 lpUnicodeCharStr, cchUnicodeChar);
4398 for(label_start=0; label_start<cchASCIIChar;) {
4399 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4402 for(i=label_start; i<cchASCIIChar; i++) {
4403 ch = lpASCIICharStr[i];
4405 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4406 SetLastError(ERROR_INVALID_NAME);
4415 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4417 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4418 || (ch>='0' && ch<='9') || ch=='-')
4421 SetLastError(ERROR_INVALID_NAME);
4425 /* last label may be empty */
4426 if(label_start==label_end && ch) {
4427 SetLastError(ERROR_INVALID_NAME);
4431 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4432 lpUnicodeCharStr[label_end-1]=='-')) {
4433 SetLastError(ERROR_INVALID_NAME);
4436 if(label_end-label_start > 63) {
4437 SetLastError(ERROR_INVALID_NAME);
4441 if(label_end-label_start<4 ||
4442 tolowerW(lpASCIICharStr[label_start])!='x' ||
4443 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4444 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4445 if(label_end < cchUnicodeChar)
4448 if(!lpUnicodeCharStr) {
4449 out += label_end-label_start;
4450 }else if(out+label_end-label_start <= cchUnicodeChar) {
4451 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4452 (label_end-label_start)*sizeof(WCHAR));
4453 out += label_end-label_start;
4455 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4459 label_start = label_end;
4463 if(delim == label_start+3)
4465 if(!lpUnicodeCharStr) {
4466 out += delim-label_start-4;
4467 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4468 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4469 (delim-label_start-4)*sizeof(WCHAR));
4470 out += delim-label_start-4;
4472 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4475 if(out != out_label)
4478 for(i=delim; i<label_end;) {
4481 for(k=BASE; ; k+=BASE) {
4482 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4483 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4484 SetLastError(ERROR_INVALID_NAME);
4487 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4489 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4494 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4495 n += pos/(out-out_label+1);
4496 pos %= out-out_label+1;
4498 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4499 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4500 SetLastError(ERROR_INVALID_NAME);
4503 if(!lpUnicodeCharStr) {
4505 }else if(out+1 <= cchASCIIChar) {
4506 memmove(lpUnicodeCharStr+out_label+pos+1,
4507 lpUnicodeCharStr+out_label+pos,
4508 (out-out_label-pos)*sizeof(WCHAR));
4509 lpUnicodeCharStr[out_label+pos] = n;
4512 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4518 if(out-out_label > 63) {
4519 SetLastError(ERROR_INVALID_NAME);
4523 if(label_end < cchASCIIChar) {
4524 if(!lpUnicodeCharStr) {
4526 }else if(out+1 <= cchUnicodeChar) {
4527 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4529 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4533 label_start = label_end+1;