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 );
935 /***********************************************************************
936 * GetUserDefaultUILanguage (KERNEL32.@)
938 * Get the default user interface language Id for the current user.
944 * The current LANGID of the default UI language for the current user.
946 LANGID WINAPI GetUserDefaultUILanguage(void)
949 NtQueryDefaultUILanguage( &lang );
954 /***********************************************************************
955 * GetSystemDefaultUILanguage (KERNEL32.@)
957 * Get the default user interface language Id for the system.
963 * The current LANGID of the default UI language for the system. This is
964 * typically the same language used during the installation process.
966 LANGID WINAPI GetSystemDefaultUILanguage(void)
969 NtQueryInstallUILanguage( &lang );
974 /***********************************************************************
975 * LocaleNameToLCID (KERNEL32.@)
977 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
979 struct locale_name locale_name;
981 if (flags) FIXME( "unsupported flags %x\n", flags );
983 if (name == LOCALE_NAME_USER_DEFAULT)
984 return GetUserDefaultLCID();
987 parse_locale_name( name, &locale_name );
989 TRACE( "found lcid %x for %s, matches %d\n",
990 locale_name.lcid, debugstr_w(name), locale_name.matches );
992 if (!locale_name.matches)
993 WARN( "locale %s not recognized, defaulting to English\n", debugstr_w(name) );
994 else if (locale_name.matches == 1)
995 WARN( "locale %s not recognized, defaulting to %s\n",
996 debugstr_w(name), debugstr_w(locale_name.lang) );
998 return locale_name.lcid;
1002 /***********************************************************************
1003 * LCIDToLocaleName (KERNEL32.@)
1005 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1007 if (flags) FIXME( "unsupported flags %x\n", flags );
1009 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1013 /******************************************************************************
1014 * get_locale_value_name
1016 * Gets the registry value name for a given lctype.
1018 static const WCHAR *get_locale_value_name( DWORD lctype )
1020 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1021 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1022 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1023 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1024 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1025 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1026 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1027 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1028 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1029 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1030 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1031 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1032 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1033 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1034 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1035 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1036 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1037 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1038 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1039 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1040 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1041 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1042 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1043 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1044 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1045 static const WCHAR sListW[] = {'s','L','i','s','t',0};
1046 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1047 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1048 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1049 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1050 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1051 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1052 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1053 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1054 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1055 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1056 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1057 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1058 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1062 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1063 * the values are stored in the registry, confirmed under Windows.
1065 case LOCALE_ICALENDARTYPE: return iCalendarTypeW;
1066 case LOCALE_ICURRDIGITS: return iCurrDigitsW;
1067 case LOCALE_ICURRENCY: return iCurrencyW;
1068 case LOCALE_IDIGITS: return iDigitsW;
1069 case LOCALE_IFIRSTDAYOFWEEK: return iFirstDayOfWeekW;
1070 case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1071 case LOCALE_ILZERO: return iLZeroW;
1072 case LOCALE_IMEASURE: return iMeasureW;
1073 case LOCALE_INEGCURR: return iNegCurrW;
1074 case LOCALE_INEGNUMBER: return iNegNumberW;
1075 case LOCALE_IPAPERSIZE: return iPaperSizeW;
1076 case LOCALE_ITIME: return iTimeW;
1077 case LOCALE_S1159: return s1159W;
1078 case LOCALE_S2359: return s2359W;
1079 case LOCALE_SCURRENCY: return sCurrencyW;
1080 case LOCALE_SDATE: return sDateW;
1081 case LOCALE_SDECIMAL: return sDecimalW;
1082 case LOCALE_SGROUPING: return sGroupingW;
1083 case LOCALE_SLIST: return sListW;
1084 case LOCALE_SLONGDATE: return sLongDateW;
1085 case LOCALE_SMONDECIMALSEP: return sMonDecimalSepW;
1086 case LOCALE_SMONGROUPING: return sMonGroupingW;
1087 case LOCALE_SMONTHOUSANDSEP: return sMonThousandSepW;
1088 case LOCALE_SNEGATIVESIGN: return sNegativeSignW;
1089 case LOCALE_SPOSITIVESIGN: return sPositiveSignW;
1090 case LOCALE_SSHORTDATE: return sShortDateW;
1091 case LOCALE_STHOUSAND: return sThousandW;
1092 case LOCALE_STIME: return sTimeW;
1093 case LOCALE_STIMEFORMAT: return sTimeFormatW;
1094 case LOCALE_SYEARMONTH: return sYearMonthW;
1096 /* The following are not listed under MSDN as supported,
1097 * but seem to be used and also stored in the registry.
1099 case LOCALE_ICOUNTRY: return iCountryW;
1100 case LOCALE_IDATE: return iDateW;
1101 case LOCALE_ILDATE: return iLDateW;
1102 case LOCALE_ITLZERO: return iTLZeroW;
1103 case LOCALE_SCOUNTRY: return sCountryW;
1104 case LOCALE_SABBREVLANGNAME: return sLanguageW;
1106 /* The following are used in XP and later */
1107 case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1108 case LOCALE_SNATIVEDIGITS: return sNativeDigitsW;
1109 case LOCALE_ITIMEMARKPOSN: return iTimePrefixW;
1115 /******************************************************************************
1116 * get_registry_locale_info
1118 * Retrieve user-modified locale info from the registry.
1119 * Return length, 0 on error, -1 if not found.
1121 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1127 UNICODE_STRING nameW;
1128 KEY_VALUE_PARTIAL_INFORMATION *info;
1129 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1131 if (!(hkey = create_registry_key())) return -1;
1133 RtlInitUnicodeString( &nameW, value );
1134 size = info_size + len * sizeof(WCHAR);
1136 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1139 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1143 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1147 ret = (size - info_size) / sizeof(WCHAR);
1148 /* append terminating null if needed */
1149 if (!ret || ((WCHAR *)info->Data)[ret-1])
1151 if (ret < len || !buffer) ret++;
1154 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1160 memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1164 else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1166 ret = (size - info_size) / sizeof(WCHAR) + 1;
1168 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1174 SetLastError( RtlNtStatusToDosError(status) );
1178 HeapFree( GetProcessHeap(), 0, info );
1183 /******************************************************************************
1184 * GetLocaleInfoA (KERNEL32.@)
1186 * Get information about an aspect of a locale.
1189 * lcid [I] LCID of the locale
1190 * lctype [I] LCTYPE_ flags from "winnls.h"
1191 * buffer [O] Destination for the information
1192 * len [I] Length of buffer in characters
1195 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1196 * with the information.
1197 * Failure: 0. Use GetLastError() to determine the cause.
1200 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1201 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1202 * which is a bit string.
1204 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1209 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1211 if (len < 0 || (len && !buffer))
1213 SetLastError( ERROR_INVALID_PARAMETER );
1216 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1218 SetLastError( ERROR_INVALID_FLAGS );
1222 if (!len) buffer = NULL;
1224 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1226 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1228 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1231 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1233 if ((lctype & LOCALE_RETURN_NUMBER) ||
1234 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1236 /* it's not an ASCII string, just bytes */
1237 ret *= sizeof(WCHAR);
1240 if (ret <= len) memcpy( buffer, bufferW, ret );
1243 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1250 UINT codepage = CP_ACP;
1251 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1252 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1255 HeapFree( GetProcessHeap(), 0, bufferW );
1260 /******************************************************************************
1261 * GetLocaleInfoW (KERNEL32.@)
1263 * See GetLocaleInfoA.
1265 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1275 if (len < 0 || (len && !buffer))
1277 SetLastError( ERROR_INVALID_PARAMETER );
1280 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1281 !is_genitive_name_supported( lctype ))
1283 SetLastError( ERROR_INVALID_FLAGS );
1287 if (!len) buffer = NULL;
1289 lcid = convert_default_lcid( lcid, lctype );
1291 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1294 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1296 /* first check for overrides in the registry */
1298 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1299 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1301 const WCHAR *value = get_locale_value_name(lctype);
1305 if (lcflags & LOCALE_RETURN_NUMBER)
1308 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1312 UINT number = strtolW( tmp, &end, 10 );
1313 if (*end) /* invalid number */
1315 SetLastError( ERROR_INVALID_FLAGS );
1318 ret = sizeof(UINT)/sizeof(WCHAR);
1319 if (!buffer) return ret;
1322 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1325 memcpy( buffer, &number, sizeof(number) );
1328 else ret = get_registry_locale_info( value, buffer, len );
1330 if (ret != -1) return ret;
1334 /* now load it from kernel resources */
1336 lang_id = LANGIDFROMLCID( lcid );
1338 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1339 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1340 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1342 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1343 ULongToPtr((lctype >> 4) + 1), lang_id )))
1345 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1348 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1351 p = LockResource( hmem );
1352 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1354 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1355 else if (is_genitive_name_supported( lctype ) && *p)
1357 /* genitive form's stored after a null separator from a nominative */
1358 for (i = 1; i <= *p; i++) if (!p[i]) break;
1360 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1368 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1370 if (!buffer) return ret;
1374 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1378 if (lcflags & LOCALE_RETURN_NUMBER)
1381 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1383 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1385 number = strtolW( tmp, &end, 10 );
1387 memcpy( buffer, &number, sizeof(number) );
1388 else /* invalid number */
1390 SetLastError( ERROR_INVALID_FLAGS );
1393 HeapFree( GetProcessHeap(), 0, tmp );
1395 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1396 lcid, lctype, buffer, len, number );
1400 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1401 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1403 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1404 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1409 /******************************************************************************
1410 * GetLocaleInfoEx (KERNEL32.@)
1412 * FIXME: Should probably be a wrapper around GetLocaleInfo() (or vice-versa).
1414 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1416 FIXME("(locale=%s,info=0x%x,%p,%d): stub!\n", debugstr_w(locale), info, buffer, len);
1417 SetLastError(ERROR_INVALID_PARAMETER);
1421 /******************************************************************************
1422 * SetLocaleInfoA [KERNEL32.@]
1424 * Set information about an aspect of a locale.
1427 * lcid [I] LCID of the locale
1428 * lctype [I] LCTYPE_ flags from "winnls.h"
1429 * data [I] Information to set
1432 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1433 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1434 * Failure: FALSE. Use GetLastError() to determine the cause.
1437 * - Values are only be set for the current user locale; the system locale
1438 * settings cannot be changed.
1439 * - Any settings changed by this call are lost when the locale is changed by
1440 * the control panel (in Wine, this happens every time you change LANG).
1441 * - The native implementation of this function does not check that lcid matches
1442 * the current user locale, and simply sets the new values. Wine warns you in
1443 * this case, but behaves the same.
1445 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1447 UINT codepage = CP_ACP;
1452 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1456 SetLastError( ERROR_INVALID_PARAMETER );
1459 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1460 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1462 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1465 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1466 ret = SetLocaleInfoW( lcid, lctype, strW );
1467 HeapFree( GetProcessHeap(), 0, strW );
1472 /******************************************************************************
1473 * SetLocaleInfoW (KERNEL32.@)
1475 * See SetLocaleInfoA.
1477 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1480 static const WCHAR intlW[] = {'i','n','t','l',0 };
1481 UNICODE_STRING valueW;
1486 value = get_locale_value_name( lctype );
1488 if (!data || !value)
1490 SetLastError( ERROR_INVALID_PARAMETER );
1494 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1496 SetLastError( ERROR_INVALID_FLAGS );
1500 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1502 /* FIXME: should check that data to set is sane */
1504 /* FIXME: profile functions should map to registry */
1505 WriteProfileStringW( intlW, value, data );
1507 if (!(hkey = create_registry_key())) return FALSE;
1508 RtlInitUnicodeString( &valueW, value );
1509 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1511 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1513 /* Set I-value from S value */
1514 WCHAR *lpD, *lpM, *lpY;
1517 lpD = strrchrW(data, 'd');
1518 lpM = strrchrW(data, 'M');
1519 lpY = strrchrW(data, 'y');
1523 szBuff[0] = '1'; /* D-M-Y */
1528 szBuff[0] = '2'; /* Y-M-D */
1530 szBuff[0] = '0'; /* M-D-Y */
1535 if (lctype == LOCALE_SSHORTDATE)
1536 lctype = LOCALE_IDATE;
1538 lctype = LOCALE_ILDATE;
1540 value = get_locale_value_name( lctype );
1542 WriteProfileStringW( intlW, value, szBuff );
1544 RtlInitUnicodeString( &valueW, value );
1545 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1550 if (status) SetLastError( RtlNtStatusToDosError(status) );
1555 /******************************************************************************
1556 * GetACP (KERNEL32.@)
1558 * Get the current Ansi code page Id for the system.
1564 * The current Ansi code page identifier for the system.
1566 UINT WINAPI GetACP(void)
1568 assert( ansi_cptable );
1569 return ansi_cptable->info.codepage;
1573 /******************************************************************************
1574 * SetCPGlobal (KERNEL32.@)
1576 * Set the current Ansi code page Id for the system.
1579 * acp [I] code page ID to be the new ACP.
1584 UINT WINAPI SetCPGlobal( UINT acp )
1586 UINT ret = GetACP();
1587 const union cptable *new_cptable = wine_cp_get_table( acp );
1589 if (new_cptable) ansi_cptable = new_cptable;
1594 /***********************************************************************
1595 * GetOEMCP (KERNEL32.@)
1597 * Get the current OEM code page Id for the system.
1603 * The current OEM code page identifier for the system.
1605 UINT WINAPI GetOEMCP(void)
1607 assert( oem_cptable );
1608 return oem_cptable->info.codepage;
1612 /***********************************************************************
1613 * IsValidCodePage (KERNEL32.@)
1615 * Determine if a given code page identifier is valid.
1618 * codepage [I] Code page Id to verify.
1621 * TRUE, If codepage is valid and available on the system,
1624 BOOL WINAPI IsValidCodePage( UINT codepage )
1631 return wine_cp_get_table( codepage ) != NULL;
1636 /***********************************************************************
1637 * IsDBCSLeadByteEx (KERNEL32.@)
1639 * Determine if a character is a lead byte in a given code page.
1642 * codepage [I] Code page for the test.
1643 * testchar [I] Character to test
1646 * TRUE, if testchar is a lead byte in codepage,
1649 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1651 const union cptable *table = get_codepage_table( codepage );
1652 return table && wine_is_dbcs_leadbyte( table, testchar );
1656 /***********************************************************************
1657 * IsDBCSLeadByte (KERNEL32.@)
1658 * IsDBCSLeadByte (KERNEL.207)
1660 * Determine if a character is a lead byte.
1663 * testchar [I] Character to test
1666 * TRUE, if testchar is a lead byte in the ANSI code page,
1669 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1671 if (!ansi_cptable) return FALSE;
1672 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1676 /***********************************************************************
1677 * GetCPInfo (KERNEL32.@)
1679 * Get information about a code page.
1682 * codepage [I] Code page number
1683 * cpinfo [O] Destination for code page information
1686 * Success: TRUE. cpinfo is updated with the information about codepage.
1687 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1689 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1691 const union cptable *table;
1695 SetLastError( ERROR_INVALID_PARAMETER );
1699 if (!(table = get_codepage_table( codepage )))
1705 cpinfo->DefaultChar[0] = 0x3f;
1706 cpinfo->DefaultChar[1] = 0;
1707 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1708 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1712 SetLastError( ERROR_INVALID_PARAMETER );
1715 if (table->info.def_char & 0xff00)
1717 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1718 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1722 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1723 cpinfo->DefaultChar[1] = 0;
1725 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1726 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1728 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1733 /***********************************************************************
1734 * GetCPInfoExA (KERNEL32.@)
1736 * Get extended information about a code page.
1739 * codepage [I] Code page number
1740 * dwFlags [I] Reserved, must to 0.
1741 * cpinfo [O] Destination for code page information
1744 * Success: TRUE. cpinfo is updated with the information about codepage.
1745 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1747 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1751 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1754 /* the layout is the same except for CodePageName */
1755 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1756 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1760 /***********************************************************************
1761 * GetCPInfoExW (KERNEL32.@)
1763 * Unicode version of GetCPInfoExA.
1765 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1767 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1774 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1776 cpinfo->CodePage = CP_UTF7;
1777 cpinfo->UnicodeDefaultChar = 0x3f;
1778 strcpyW(cpinfo->CodePageName, utf7);
1784 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1786 cpinfo->CodePage = CP_UTF8;
1787 cpinfo->UnicodeDefaultChar = 0x3f;
1788 strcpyW(cpinfo->CodePageName, utf8);
1794 const union cptable *table = get_codepage_table( codepage );
1796 cpinfo->CodePage = table->info.codepage;
1797 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1798 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1799 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1806 /***********************************************************************
1807 * EnumSystemCodePagesA (KERNEL32.@)
1809 * Call a user defined function for every code page installed on the system.
1812 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1813 * flags [I] Reserved, set to 0.
1816 * TRUE, If all code pages have been enumerated, or
1817 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1819 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1821 const union cptable *table;
1827 if (!(table = wine_cp_enum_table( index++ ))) break;
1828 sprintf( buffer, "%d", table->info.codepage );
1829 if (!lpfnCodePageEnum( buffer )) break;
1835 /***********************************************************************
1836 * EnumSystemCodePagesW (KERNEL32.@)
1838 * See EnumSystemCodePagesA.
1840 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1842 const union cptable *table;
1843 WCHAR buffer[10], *p;
1844 int page, index = 0;
1848 if (!(table = wine_cp_enum_table( index++ ))) break;
1849 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1851 page = table->info.codepage;
1854 *--p = '0' + (page % 10);
1857 if (!lpfnCodePageEnum( p )) break;
1863 /***********************************************************************
1864 * MultiByteToWideChar (KERNEL32.@)
1866 * Convert a multibyte character string into a Unicode string.
1869 * page [I] Codepage character set to convert from
1870 * flags [I] Character mapping flags
1871 * src [I] Source string buffer
1872 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1873 * dst [O] Destination buffer
1874 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1877 * Success: If dstlen > 0, the number of characters written to dst.
1878 * If dstlen == 0, the number of characters needed to perform the
1879 * conversion. In both cases the count includes the terminating NUL.
1880 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1881 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1882 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1883 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1886 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1887 LPWSTR dst, INT dstlen )
1889 const union cptable *table;
1892 if (!src || !srclen || (!dst && dstlen))
1894 SetLastError( ERROR_INVALID_PARAMETER );
1898 if (srclen < 0) srclen = strlen(src) + 1;
1905 SetLastError( ERROR_INVALID_FLAGS );
1908 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1913 SetLastError( ERROR_INVALID_FLAGS );
1916 FIXME("UTF-7 not supported\n");
1917 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1922 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1926 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1930 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1933 if (!(table = get_codepage_table( page )))
1935 SetLastError( ERROR_INVALID_PARAMETER );
1938 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1946 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1947 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1951 TRACE("cp %d %s -> %s, ret = %d\n",
1952 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1957 /***********************************************************************
1958 * WideCharToMultiByte (KERNEL32.@)
1960 * Convert a Unicode character string into a multibyte string.
1963 * page [I] Code page character set to convert to
1964 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
1965 * src [I] Source string buffer
1966 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1967 * dst [O] Destination buffer
1968 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
1969 * defchar [I] Default character to use for conversion if no exact
1970 * conversion can be made
1971 * used [O] Set if default character was used in the conversion
1974 * Success: If dstlen > 0, the number of characters written to dst.
1975 * If dstlen == 0, number of characters needed to perform the
1976 * conversion. In both cases the count includes the terminating NUL.
1977 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1978 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1979 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1980 * parameter was given.
1982 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1983 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1985 const union cptable *table;
1988 if (!src || !srclen || (!dst && dstlen))
1990 SetLastError( ERROR_INVALID_PARAMETER );
1994 if (srclen < 0) srclen = strlenW(src) + 1;
1999 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2002 SetLastError( ERROR_INVALID_FLAGS );
2005 if (defchar || used)
2007 SetLastError( ERROR_INVALID_PARAMETER );
2010 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2013 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2014 if (defchar || used)
2016 SetLastError( ERROR_INVALID_PARAMETER );
2021 SetLastError( ERROR_INVALID_FLAGS );
2024 FIXME("UTF-7 not supported\n");
2025 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2030 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2031 defchar, used ? &used_tmp : NULL );
2036 if (defchar || used)
2038 SetLastError( ERROR_INVALID_PARAMETER );
2041 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2044 if (!(table = get_codepage_table( page )))
2046 SetLastError( ERROR_INVALID_PARAMETER );
2049 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2050 defchar, used ? &used_tmp : NULL );
2051 if (used) *used = used_tmp;
2059 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2060 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2064 TRACE("cp %d %s -> %s, ret = %d\n",
2065 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2070 /***********************************************************************
2071 * GetThreadLocale (KERNEL32.@)
2073 * Get the current threads locale.
2079 * The LCID currently associated with the calling thread.
2081 LCID WINAPI GetThreadLocale(void)
2083 LCID ret = NtCurrentTeb()->CurrentLocale;
2084 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2088 /**********************************************************************
2089 * SetThreadLocale (KERNEL32.@)
2091 * Set the current threads locale.
2094 * lcid [I] LCID of the locale to set
2097 * Success: TRUE. The threads locale is set to lcid.
2098 * Failure: FALSE. Use GetLastError() to determine the cause.
2100 BOOL WINAPI SetThreadLocale( LCID lcid )
2102 TRACE("(0x%04X)\n", lcid);
2104 lcid = ConvertDefaultLocale(lcid);
2106 if (lcid != GetThreadLocale())
2108 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2110 SetLastError(ERROR_INVALID_PARAMETER);
2114 NtCurrentTeb()->CurrentLocale = lcid;
2119 /**********************************************************************
2120 * SetThreadUILanguage (KERNEL32.@)
2122 * Set the current threads UI language.
2125 * langid [I] LANGID of the language to set, or 0 to use
2126 * the available language which is best supported
2127 * for console applications
2130 * Success: The return value is the same as the input value.
2131 * Failure: The return value differs from the input value.
2132 * Use GetLastError() to determine the cause.
2134 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2136 TRACE("(0x%04x) stub - returning success\n", langid);
2140 /******************************************************************************
2141 * ConvertDefaultLocale (KERNEL32.@)
2143 * Convert a default locale identifier into a real identifier.
2146 * lcid [I] LCID identifier of the locale to convert
2149 * lcid unchanged, if not a default locale or its sublanguage is
2150 * not SUBLANG_NEUTRAL.
2151 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2152 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2153 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2155 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2161 case LOCALE_SYSTEM_DEFAULT:
2162 lcid = GetSystemDefaultLCID();
2164 case LOCALE_USER_DEFAULT:
2165 case LOCALE_NEUTRAL:
2166 lcid = GetUserDefaultLCID();
2169 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2170 langid = LANGIDFROMLCID(lcid);
2171 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2173 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2174 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2181 /******************************************************************************
2182 * IsValidLocale (KERNEL32.@)
2184 * Determine if a locale is valid.
2187 * lcid [I] LCID of the locale to check
2188 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2191 * TRUE, if lcid is valid,
2195 * Wine does not currently make the distinction between supported and installed. All
2196 * languages supported are installed by default.
2198 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2200 /* check if language is registered in the kernel32 resources */
2201 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2202 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2206 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2207 LPCSTR name, WORD LangID, LONG_PTR lParam )
2209 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2212 sprintf(buf, "%08x", (UINT)LangID);
2213 return lpfnLocaleEnum( buf );
2216 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2217 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2219 static const WCHAR formatW[] = {'%','0','8','x',0};
2220 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2222 sprintfW( buf, formatW, (UINT)LangID );
2223 return lpfnLocaleEnum( buf );
2226 /******************************************************************************
2227 * EnumSystemLocalesA (KERNEL32.@)
2229 * Call a users function for each locale available on the system.
2232 * lpfnLocaleEnum [I] Callback function to call for each locale
2233 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2237 * Failure: FALSE. Use GetLastError() to determine the cause.
2239 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2241 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2242 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2243 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2244 (LONG_PTR)lpfnLocaleEnum);
2249 /******************************************************************************
2250 * EnumSystemLocalesW (KERNEL32.@)
2252 * See EnumSystemLocalesA.
2254 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2256 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2257 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2258 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2259 (LONG_PTR)lpfnLocaleEnum);
2264 struct enum_locale_ex_data
2266 LOCALE_ENUMPROCEX proc;
2271 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2272 LPCWSTR name, WORD lang, LONG_PTR lparam )
2274 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2279 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2280 buffer, sizeof(buffer) / sizeof(WCHAR) );
2281 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2282 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2283 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2285 flags = LOCALE_WINDOWS;
2286 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2287 if (data->flags && ~(data->flags & flags)) return TRUE;
2288 return data->proc( buffer, flags, data->lparam );
2291 /******************************************************************************
2292 * EnumSystemLocalesEx (KERNEL32.@)
2294 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2296 struct enum_locale_ex_data data;
2300 SetLastError( ERROR_INVALID_PARAMETER );
2305 data.lparam = lparam;
2306 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2307 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2308 enum_locale_ex_proc, (LONG_PTR)&data );
2313 /***********************************************************************
2314 * VerLanguageNameA (KERNEL32.@)
2316 * Get the name of a language.
2319 * wLang [I] LANGID of the language
2320 * szLang [O] Destination for the language name
2323 * Success: The size of the language name. If szLang is non-NULL, it is filled
2325 * Failure: 0. Use GetLastError() to determine the cause.
2328 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2330 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2334 /***********************************************************************
2335 * VerLanguageNameW (KERNEL32.@)
2337 * See VerLanguageNameA.
2339 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2341 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2345 /******************************************************************************
2346 * GetStringTypeW (KERNEL32.@)
2348 * See GetStringTypeA.
2350 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2352 static const unsigned char type2_map[16] =
2354 C2_NOTAPPLICABLE, /* unassigned */
2355 C2_LEFTTORIGHT, /* L */
2356 C2_RIGHTTOLEFT, /* R */
2357 C2_EUROPENUMBER, /* EN */
2358 C2_EUROPESEPARATOR, /* ES */
2359 C2_EUROPETERMINATOR, /* ET */
2360 C2_ARABICNUMBER, /* AN */
2361 C2_COMMONSEPARATOR, /* CS */
2362 C2_BLOCKSEPARATOR, /* B */
2363 C2_SEGMENTSEPARATOR, /* S */
2364 C2_WHITESPACE, /* WS */
2365 C2_OTHERNEUTRAL, /* ON */
2366 C2_RIGHTTOLEFT, /* AL */
2367 C2_NOTAPPLICABLE, /* NSM */
2368 C2_NOTAPPLICABLE, /* BN */
2369 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2372 if (count == -1) count = strlenW(src) + 1;
2376 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2379 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2383 WARN("CT_CTYPE3: semi-stub.\n");
2387 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2389 type1 = get_char_typeW( *src++ ) & 0xfff;
2390 /* try to construct type3 from type1 */
2391 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2392 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2393 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2394 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2395 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2396 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2397 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2399 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2400 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2401 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2402 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2403 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2404 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2405 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2406 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2408 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2409 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2410 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2411 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2412 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2413 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2414 *chartype++ = type3;
2419 SetLastError( ERROR_INVALID_PARAMETER );
2426 /******************************************************************************
2427 * GetStringTypeExW (KERNEL32.@)
2429 * See GetStringTypeExA.
2431 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2433 /* locale is ignored for Unicode */
2434 return GetStringTypeW( type, src, count, chartype );
2438 /******************************************************************************
2439 * GetStringTypeA (KERNEL32.@)
2441 * Get characteristics of the characters making up a string.
2444 * locale [I] Locale Id for the string
2445 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2446 * src [I] String to analyse
2447 * count [I] Length of src in chars, or -1 if src is NUL terminated
2448 * chartype [O] Destination for the calculated characteristics
2451 * Success: TRUE. chartype is filled with the requested characteristics of each char
2453 * Failure: FALSE. Use GetLastError() to determine the cause.
2455 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2462 if(count == -1) count = strlen(src) + 1;
2464 if (!(cp = get_lcid_codepage( locale )))
2466 FIXME("For locale %04x using current ANSI code page\n", locale);
2470 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2471 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2473 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2475 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2476 * string, with multibyte characters there maybe be more bytes in count
2477 * than character space in the buffer!
2479 ret = GetStringTypeW(type, srcW, countW, chartype);
2480 HeapFree(GetProcessHeap(), 0, srcW);
2485 /******************************************************************************
2486 * GetStringTypeExA (KERNEL32.@)
2488 * Get characteristics of the characters making up a string.
2491 * locale [I] Locale Id for the string
2492 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2493 * src [I] String to analyse
2494 * count [I] Length of src in chars, or -1 if src is NUL terminated
2495 * chartype [O] Destination for the calculated characteristics
2498 * Success: TRUE. chartype is filled with the requested characteristics of each char
2500 * Failure: FALSE. Use GetLastError() to determine the cause.
2502 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2504 return GetStringTypeA(locale, type, src, count, chartype);
2507 /*************************************************************************
2508 * LCMapStringEx (KERNEL32.@)
2510 * Map characters in a locale sensitive string.
2513 * name [I] Locale name for the conversion.
2514 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2515 * src [I] String to map
2516 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2517 * dst [O] Destination for mapped string
2518 * dstlen [I] Length of dst in characters
2519 * version [I] reserved, must be NULL
2520 * reserved [I] reserved, must be NULL
2521 * lparam [I] reserved, must be 0
2524 * Success: The length of the mapped string in dst, including the NUL terminator.
2525 * Failure: 0. Use GetLastError() to determine the cause.
2527 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2528 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2532 if (version) FIXME("unsupported version structure %p\n", version);
2533 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2534 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2536 if (!src || !srclen || dstlen < 0)
2538 SetLastError(ERROR_INVALID_PARAMETER);
2542 /* mutually exclusive flags */
2543 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2544 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2545 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2546 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2548 SetLastError(ERROR_INVALID_FLAGS);
2552 if (!dstlen) dst = NULL;
2554 if (flags & LCMAP_SORTKEY)
2559 SetLastError(ERROR_INVALID_FLAGS);
2563 if (srclen < 0) srclen = strlenW(src);
2565 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2566 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2568 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2570 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2576 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2577 if (flags & SORT_STRINGSORT)
2579 SetLastError(ERROR_INVALID_FLAGS);
2583 if (srclen < 0) srclen = strlenW(src) + 1;
2585 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2586 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2588 if (!dst) /* return required string length */
2592 for (len = 0; srclen; src++, srclen--)
2595 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2596 * and skips white space and punctuation characters for
2597 * NORM_IGNORESYMBOLS.
2599 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2606 if (flags & LCMAP_UPPERCASE)
2608 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2611 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2613 *dst_ptr++ = toupperW(wch);
2617 else if (flags & LCMAP_LOWERCASE)
2619 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2622 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2624 *dst_ptr++ = tolowerW(wch);
2632 SetLastError(ERROR_INVALID_FLAGS);
2635 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2638 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2647 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2651 return dst_ptr - dst;
2654 /*************************************************************************
2655 * LCMapStringW (KERNEL32.@)
2659 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2660 LPWSTR dst, INT dstlen)
2662 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2663 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2665 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2668 /*************************************************************************
2669 * LCMapStringA (KERNEL32.@)
2671 * Map characters in a locale sensitive string.
2674 * lcid [I] LCID for the conversion.
2675 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2676 * src [I] String to map
2677 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2678 * dst [O] Destination for mapped string
2679 * dstlen [I] Length of dst in characters
2682 * Success: The length of the mapped string in dst, including the NUL terminator.
2683 * Failure: 0. Use GetLastError() to determine the cause.
2685 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2686 LPSTR dst, INT dstlen)
2688 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2690 INT ret = 0, srclenW, dstlenW;
2691 UINT locale_cp = CP_ACP;
2693 if (!src || !srclen || dstlen < 0)
2695 SetLastError(ERROR_INVALID_PARAMETER);
2699 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2701 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2706 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2707 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2710 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2713 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2716 if (flags & LCMAP_SORTKEY)
2720 SetLastError(ERROR_INVALID_FLAGS);
2721 goto map_string_exit;
2723 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2725 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2728 goto map_string_exit;
2731 if (flags & SORT_STRINGSORT)
2733 SetLastError(ERROR_INVALID_FLAGS);
2734 goto map_string_exit;
2737 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2739 goto map_string_exit;
2741 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2744 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2745 goto map_string_exit;
2748 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2749 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2750 HeapFree(GetProcessHeap(), 0, dstW);
2753 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2757 /*************************************************************************
2758 * FoldStringA (KERNEL32.@)
2760 * Map characters in a string.
2763 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2764 * src [I] String to map
2765 * srclen [I] Length of src, or -1 if src is NUL terminated
2766 * dst [O] Destination for mapped string
2767 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2770 * Success: The length of the string written to dst, including the terminating NUL. If
2771 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2772 * and dst may be NULL.
2773 * Failure: 0. Use GetLastError() to determine the cause.
2775 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2776 LPSTR dst, INT dstlen)
2778 INT ret = 0, srclenW = 0;
2779 WCHAR *srcW = NULL, *dstW = NULL;
2781 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2783 SetLastError(ERROR_INVALID_PARAMETER);
2787 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2788 src, srclen, NULL, 0);
2789 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2793 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2794 goto FoldStringA_exit;
2797 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2798 src, srclen, srcW, srclenW);
2800 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2802 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2805 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2809 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2810 goto FoldStringA_exit;
2813 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2814 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2817 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2821 HeapFree(GetProcessHeap(), 0, dstW);
2824 HeapFree(GetProcessHeap(), 0, srcW);
2828 /*************************************************************************
2829 * FoldStringW (KERNEL32.@)
2833 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2834 LPWSTR dst, INT dstlen)
2838 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2843 /* Fall through for dwFlags == 0 */
2844 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2845 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2846 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2847 SetLastError(ERROR_INVALID_FLAGS);
2851 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2853 SetLastError(ERROR_INVALID_PARAMETER);
2857 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2859 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2863 /******************************************************************************
2864 * CompareStringW (KERNEL32.@)
2866 * See CompareStringA.
2868 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2869 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2875 SetLastError(ERROR_INVALID_PARAMETER);
2879 if( style & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2880 SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2882 SetLastError(ERROR_INVALID_FLAGS);
2886 /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2887 if (style & 0x10000000)
2888 WARN("Ignoring unknown style 0x10000000\n");
2890 if (len1 < 0) len1 = strlenW(str1);
2891 if (len2 < 0) len2 = strlenW(str2);
2893 ret = wine_compare_string(style, str1, len1, str2, len2);
2895 if (ret) /* need to translate result */
2896 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2900 /******************************************************************************
2901 * CompareStringA (KERNEL32.@)
2903 * Compare two locale sensitive strings.
2906 * lcid [I] LCID for the comparison
2907 * style [I] Flags for the comparison (NORM_ constants from "winnls.h").
2908 * str1 [I] First string to compare
2909 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
2910 * str2 [I] Second string to compare
2911 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
2914 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2915 * str1 is less than, equal to or greater than str2 respectively.
2916 * Failure: FALSE. Use GetLastError() to determine the cause.
2918 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2919 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2921 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2922 WCHAR *buf2W = buf1W + 130;
2923 LPWSTR str1W, str2W;
2924 INT len1W, len2W, ret;
2925 UINT locale_cp = CP_ACP;
2929 SetLastError(ERROR_INVALID_PARAMETER);
2932 if (len1 < 0) len1 = strlen(str1);
2933 if (len2 < 0) len2 = strlen(str2);
2935 if (!(style & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2939 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2944 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2945 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2948 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2951 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2962 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2967 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2968 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2971 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2972 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2975 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2984 ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2986 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2987 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2991 /*************************************************************************
2992 * lstrcmp (KERNEL32.@)
2993 * lstrcmpA (KERNEL32.@)
2995 * Compare two strings using the current thread locale.
2998 * str1 [I] First string to compare
2999 * str2 [I] Second string to compare
3002 * Success: A number less than, equal to or greater than 0 depending on whether
3003 * str1 is less than, equal to or greater than str2 respectively.
3004 * Failure: FALSE. Use GetLastError() to determine the cause.
3006 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3010 if ((str1 == NULL) && (str2 == NULL)) return 0;
3011 if (str1 == NULL) return -1;
3012 if (str2 == NULL) return 1;
3014 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3020 /*************************************************************************
3021 * lstrcmpi (KERNEL32.@)
3022 * lstrcmpiA (KERNEL32.@)
3024 * Compare two strings using the current thread locale, ignoring case.
3027 * str1 [I] First string to compare
3028 * str2 [I] Second string to compare
3031 * Success: A number less than, equal to or greater than 0 depending on whether
3032 * str2 is less than, equal to or greater than str1 respectively.
3033 * Failure: FALSE. Use GetLastError() to determine the cause.
3035 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3039 if ((str1 == NULL) && (str2 == NULL)) return 0;
3040 if (str1 == NULL) return -1;
3041 if (str2 == NULL) return 1;
3043 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3049 /*************************************************************************
3050 * lstrcmpW (KERNEL32.@)
3054 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3058 if ((str1 == NULL) && (str2 == NULL)) return 0;
3059 if (str1 == NULL) return -1;
3060 if (str2 == NULL) return 1;
3062 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3068 /*************************************************************************
3069 * lstrcmpiW (KERNEL32.@)
3073 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3077 if ((str1 == NULL) && (str2 == NULL)) return 0;
3078 if (str1 == NULL) return -1;
3079 if (str2 == NULL) return 1;
3081 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3087 /******************************************************************************
3090 void LOCALE_Init(void)
3092 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3093 const union cptable *unix_cp );
3095 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3098 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3099 char user_locale[50];
3101 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3102 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3103 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3104 CFStringRef user_locale_string_ref;
3106 if (user_locale_country_ref)
3108 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3109 user_locale_lang_ref, user_locale_country_ref);
3113 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3114 user_locale_lang_ref);
3117 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3119 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3120 setenv( "LANG", user_locale, 0 );
3121 TRACE( "setting locale to '%s'\n", user_locale );
3122 #endif /* __APPLE__ */
3124 setlocale( LC_ALL, "" );
3126 unix_cp = setup_unix_locales();
3127 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3130 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3131 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3133 /* Retrieve the preferred language as chosen in System Preferences. */
3134 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3136 CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3137 CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3138 CFStringRef user_language_string_ref;
3139 if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3140 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3141 !CFEqual(user_language_string_ref, user_locale_lang_ref))
3143 struct locale_name locale_name;
3145 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3146 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3147 parse_locale_name( buffer, &locale_name );
3148 lcid_LC_MESSAGES = locale_name.lcid;
3149 TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3151 CFRelease( all_locales );
3152 if (preferred_locales)
3153 CFRelease( preferred_locales );
3156 CFRelease( user_locale_ref );
3157 CFRelease( user_locale_string_ref );
3160 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3161 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3162 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3164 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3165 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3166 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3167 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3168 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3170 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3171 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3173 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3174 ansi_cptable = wine_cp_get_table( 1252 );
3175 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3176 oem_cptable = wine_cp_get_table( 437 );
3177 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3178 mac_cptable = wine_cp_get_table( 10000 );
3179 if (unix_cp != CP_UTF8)
3181 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3182 unix_cptable = wine_cp_get_table( 28591 );
3185 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3187 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3188 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3189 mac_cptable->info.codepage, unix_cp );
3191 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3194 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3196 UNICODE_STRING keyName;
3197 OBJECT_ATTRIBUTES attr;
3200 RtlInitUnicodeString( &keyName, szKeyName );
3201 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3203 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3209 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3213 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3216 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3217 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3218 info->NameLength > keyNameSize)
3223 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3225 memcpy( szKeyName, info->Name, info->NameLength);
3226 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3228 TRACE("returning %s\n", debugstr_w(szKeyName));
3232 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3233 LPWSTR szValueName, ULONG valueNameSize,
3234 LPWSTR szValueData, ULONG valueDataSize)
3237 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3240 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3241 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3242 info->NameLength > valueNameSize ||
3243 info->DataLength > valueDataSize)
3248 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3250 memcpy( szValueName, info->Name, info->NameLength);
3251 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3252 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3253 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3255 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3259 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3262 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3263 DWORD dwSize = sizeof(buffer);
3264 UNICODE_STRING valueName;
3266 RtlInitUnicodeString( &valueName, szValueName );
3268 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3269 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3270 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3271 info->DataLength == sizeof(DWORD))
3273 memcpy(lpVal, info->Data, sizeof(DWORD));
3280 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3283 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3287 /* FIXME: Is it correct to use the system default langid? */
3288 langId = GetSystemDefaultLangID();
3290 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3291 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3293 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3297 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3301 ULONG iResourceIndex = lgrpid & 0xf;
3302 LPCWSTR lpResEntry = LockResource( hResDir );
3305 for (i = 0; i < iResourceIndex; i++)
3306 lpResEntry += *lpResEntry + 1;
3308 if (*lpResEntry < nameSize)
3310 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3311 szName[*lpResEntry] = '\0';
3316 FreeResource( hResource );
3321 /* Registry keys for NLS related information */
3323 static const WCHAR szCountryListName[] = {
3324 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3325 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3326 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3327 'T','e','l','e','p','h','o','n','y','\\',
3328 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3332 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3335 LANGUAGEGROUP_ENUMPROCA procA;
3336 LANGUAGEGROUP_ENUMPROCW procW;
3339 } ENUMLANGUAGEGROUP_CALLBACKS;
3341 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3342 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3344 WCHAR szNumber[10], szValue[4];
3346 BOOL bContinue = TRUE;
3351 SetLastError(ERROR_INVALID_PARAMETER);
3355 switch (lpProcs->dwFlags)
3358 /* Default to LGRPID_INSTALLED */
3359 lpProcs->dwFlags = LGRPID_INSTALLED;
3360 /* Fall through... */
3361 case LGRPID_INSTALLED:
3362 case LGRPID_SUPPORTED:
3365 SetLastError(ERROR_INVALID_FLAGS);
3369 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3372 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3376 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3377 szValue, sizeof(szValue) ))
3379 BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
3380 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3382 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3383 bInstalled ? "" : "not ");
3385 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3387 WCHAR szGrpName[48];
3389 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3390 szGrpName[0] = '\0';
3393 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3397 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3398 char szGrpNameA[48];
3400 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3401 * or whether the language names are ever localised. Assume CP_ACP.
3404 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3405 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3407 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3427 /******************************************************************************
3428 * EnumSystemLanguageGroupsA (KERNEL32.@)
3430 * Call a users function for each language group available on the system.
3433 * pLangGrpEnumProc [I] Callback function to call for each language group
3434 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3435 * lParam [I] User parameter to pass to pLangGrpEnumProc
3439 * Failure: FALSE. Use GetLastError() to determine the cause.
3441 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3442 DWORD dwFlags, LONG_PTR lParam)
3444 ENUMLANGUAGEGROUP_CALLBACKS procs;
3446 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3448 procs.procA = pLangGrpEnumProc;
3450 procs.dwFlags = dwFlags;
3451 procs.lParam = lParam;
3453 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3456 /******************************************************************************
3457 * EnumSystemLanguageGroupsW (KERNEL32.@)
3459 * See EnumSystemLanguageGroupsA.
3461 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3462 DWORD dwFlags, LONG_PTR lParam)
3464 ENUMLANGUAGEGROUP_CALLBACKS procs;
3466 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3469 procs.procW = pLangGrpEnumProc;
3470 procs.dwFlags = dwFlags;
3471 procs.lParam = lParam;
3473 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3476 /******************************************************************************
3477 * IsValidLanguageGroup (KERNEL32.@)
3479 * Determine if a language group is supported and/or installed.
3482 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3483 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3486 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3489 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3491 static const WCHAR szFormat[] = { '%','x','\0' };
3492 WCHAR szValueName[16], szValue[2];
3493 BOOL bSupported = FALSE, bInstalled = FALSE;
3499 case LGRPID_INSTALLED:
3500 case LGRPID_SUPPORTED:
3502 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3504 sprintfW( szValueName, szFormat, lgrpid );
3506 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3510 if (szValue[0] == '1')
3520 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3521 (dwFlags == LGRPID_INSTALLED && bInstalled))
3527 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3530 LANGGROUPLOCALE_ENUMPROCA procA;
3531 LANGGROUPLOCALE_ENUMPROCW procW;
3535 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3537 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3538 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3540 static const WCHAR szAlternateSortsKeyName[] = {
3541 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3543 WCHAR szNumber[10], szValue[4];
3545 BOOL bContinue = TRUE, bAlternate = FALSE;
3547 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3549 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3551 SetLastError(ERROR_INVALID_PARAMETER);
3555 if (lpProcs->dwFlags)
3557 SetLastError(ERROR_INVALID_FLAGS);
3561 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3564 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3568 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3569 szValue, sizeof(szValue) ))
3571 lgrpid = strtoulW( szValue, NULL, 16 );
3573 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3574 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3576 if (lgrpid == lpProcs->lgrpid)
3580 lcid = strtoulW( szNumber, NULL, 16 );
3582 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3583 * '00000437 ;Georgian'
3584 * At present we only pass the LCID string.
3588 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3591 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3593 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3595 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3603 /* Finished enumerating this key */
3606 /* Enumerate alternate sorts also */
3607 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3612 bContinue = FALSE; /* Finished both keys */
3625 /******************************************************************************
3626 * EnumLanguageGroupLocalesA (KERNEL32.@)
3628 * Call a users function for every locale in a language group available on the system.
3631 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3632 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3633 * dwFlags [I] Reserved, set to 0
3634 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3638 * Failure: FALSE. Use GetLastError() to determine the cause.
3640 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3641 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3643 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3645 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3647 callbacks.procA = pLangGrpLcEnumProc;
3648 callbacks.procW = NULL;
3649 callbacks.dwFlags = dwFlags;
3650 callbacks.lgrpid = lgrpid;
3651 callbacks.lParam = lParam;
3653 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3656 /******************************************************************************
3657 * EnumLanguageGroupLocalesW (KERNEL32.@)
3659 * See EnumLanguageGroupLocalesA.
3661 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3662 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3664 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3666 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3668 callbacks.procA = NULL;
3669 callbacks.procW = pLangGrpLcEnumProc;
3670 callbacks.dwFlags = dwFlags;
3671 callbacks.lgrpid = lgrpid;
3672 callbacks.lParam = lParam;
3674 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3677 /******************************************************************************
3678 * EnumSystemGeoID (KERNEL32.@)
3680 * Call a users function for every location available on the system.
3683 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3684 * reserved [I] Reserved, set to 0
3685 * pGeoEnumProc [I] Callback function to call for each location
3689 * Failure: FALSE. Use GetLastError() to determine the cause.
3691 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3693 static const WCHAR szCountryCodeValueName[] = {
3694 'C','o','u','n','t','r','y','C','o','d','e','\0'
3700 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3702 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3704 SetLastError(ERROR_INVALID_PARAMETER);
3708 hKey = NLS_RegOpenKey( 0, szCountryListName );
3710 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3712 BOOL bContinue = TRUE;
3714 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3718 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3720 TRACE("Got geoid %d\n", dwGeoId);
3722 if (!pGeoEnumProc( dwGeoId ))
3741 /******************************************************************************
3742 * InvalidateNLSCache (KERNEL32.@)
3744 * Invalidate the cache of NLS values.
3753 BOOL WINAPI InvalidateNLSCache(void)
3759 /******************************************************************************
3760 * GetUserGeoID (KERNEL32.@)
3762 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3764 GEOID ret = GEOID_NOT_AVAILABLE;
3765 static const WCHAR geoW[] = {'G','e','o',0};
3766 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3767 WCHAR bufferW[40], *end;
3769 HANDLE hkey, hSubkey = 0;
3770 UNICODE_STRING keyW;
3771 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3772 RtlInitUnicodeString( &keyW, nationW );
3773 count = sizeof(bufferW);
3775 if(!(hkey = create_registry_key())) return ret;
3778 case GEOCLASS_NATION:
3779 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3781 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3782 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3783 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3786 case GEOCLASS_REGION:
3787 FIXME("GEOCLASS_REGION not handled yet\n");
3792 if (hSubkey) NtClose(hSubkey);
3796 /******************************************************************************
3797 * SetUserGeoID (KERNEL32.@)
3799 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3801 static const WCHAR geoW[] = {'G','e','o',0};
3802 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3803 static const WCHAR formatW[] = {'%','i',0};
3804 UNICODE_STRING nameW,keyW;
3806 OBJECT_ATTRIBUTES attr;
3809 if(!(hkey = create_registry_key())) return FALSE;
3811 attr.Length = sizeof(attr);
3812 attr.RootDirectory = hkey;
3813 attr.ObjectName = &nameW;
3814 attr.Attributes = 0;
3815 attr.SecurityDescriptor = NULL;
3816 attr.SecurityQualityOfService = NULL;
3817 RtlInitUnicodeString( &nameW, geoW );
3818 RtlInitUnicodeString( &keyW, nationW );
3820 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3823 NtClose(attr.RootDirectory);
3827 sprintfW(bufferW, formatW, GeoID);
3828 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3829 NtClose(attr.RootDirectory);
3838 UILANGUAGE_ENUMPROCA procA;
3839 UILANGUAGE_ENUMPROCW procW;
3843 } ENUM_UILANG_CALLBACK;
3845 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3846 LPCSTR name, WORD LangID, LONG_PTR lParam )
3848 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3851 sprintf(buf, "%08x", (UINT)LangID);
3852 return enum_uilang->u.procA( buf, enum_uilang->param );
3855 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3856 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3858 static const WCHAR formatW[] = {'%','0','8','x',0};
3859 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3862 sprintfW( buf, formatW, (UINT)LangID );
3863 return enum_uilang->u.procW( buf, enum_uilang->param );
3866 /******************************************************************************
3867 * EnumUILanguagesA (KERNEL32.@)
3869 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3871 ENUM_UILANG_CALLBACK enum_uilang;
3873 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3875 if(!pUILangEnumProc) {
3876 SetLastError(ERROR_INVALID_PARAMETER);
3880 SetLastError(ERROR_INVALID_FLAGS);
3884 enum_uilang.u.procA = pUILangEnumProc;
3885 enum_uilang.flags = dwFlags;
3886 enum_uilang.param = lParam;
3888 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3889 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3890 (LONG_PTR)&enum_uilang);
3894 /******************************************************************************
3895 * EnumUILanguagesW (KERNEL32.@)
3897 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3899 ENUM_UILANG_CALLBACK enum_uilang;
3901 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3904 if(!pUILangEnumProc) {
3905 SetLastError(ERROR_INVALID_PARAMETER);
3909 SetLastError(ERROR_INVALID_FLAGS);
3913 enum_uilang.u.procW = pUILangEnumProc;
3914 enum_uilang.flags = dwFlags;
3915 enum_uilang.param = lParam;
3917 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3918 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3919 (LONG_PTR)&enum_uilang);
3923 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
3924 int cchData, LANGID language)
3926 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3930 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
3931 int cchData, LANGID language)
3933 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3937 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
3941 TRACE("%p, %d\n", localename, buffersize);
3943 userlcid = GetUserDefaultLCID();
3944 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
3947 /******************************************************************************
3948 * NormalizeString (KERNEL32.@)
3950 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
3951 LPWSTR lpDstString, INT cwDstLength)
3953 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
3954 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3958 /******************************************************************************
3959 * IsNormalizedString (KERNEL32.@)
3961 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
3963 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
3964 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3978 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
3982 delta /= (firsttime ? DAMP : 2);
3983 delta += delta/numpoints;
3985 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
3987 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
3990 /******************************************************************************
3991 * IdnToAscii (KERNEL32.@)
3992 * Implementation of Punycode based on RFC 3492.
3994 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
3995 LPWSTR lpASCIICharStr, INT cchASCIIChar)
3997 static const WCHAR prefixW[] = {'x','n','-','-'};
4000 INT i, label_start, label_end, norm_len, out_label, out = 0;
4002 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4003 lpASCIICharStr, cchASCIIChar);
4005 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4008 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4010 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4013 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4014 cchUnicodeChar, norm_str, norm_len);
4016 HeapFree(GetProcessHeap(), 0, norm_str);
4020 for(label_start=0; label_start<norm_len;) {
4021 INT n = INIT_N, bias = INIT_BIAS;
4022 INT delta = 0, b = 0, h;
4025 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4026 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4027 if(norm_str[i] < 0x80)
4031 if(b == label_end-label_start) {
4032 if(label_end < norm_len)
4034 if(!lpASCIICharStr) {
4036 }else if(out+b <= cchASCIIChar) {
4037 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4040 HeapFree(GetProcessHeap(), 0, norm_str);
4041 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4044 label_start = label_end+1;
4048 if(!lpASCIICharStr) {
4049 out += 5+b; /* strlen(xn--...-) */
4050 }else if(out+5+b <= cchASCIIChar) {
4051 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4053 for(i=label_start; i<label_end; i++)
4054 if(norm_str[i] < 0x80)
4055 lpASCIICharStr[out++] = norm_str[i];
4056 lpASCIICharStr[out++] = '-';
4058 HeapFree(GetProcessHeap(), 0, norm_str);
4059 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4065 for(h=b; h<label_end-label_start;) {
4066 INT m = 0xffff, q, k;
4068 for(i=label_start; i<label_end; i++) {
4069 if(norm_str[i]>=n && m>norm_str[i])
4072 delta += (m-n)*(h+1);
4075 for(i=label_start; i<label_end; i++) {
4076 if(norm_str[i] < n) {
4078 }else if(norm_str[i] == n) {
4079 for(q=delta, k=BASE; ; k+=BASE) {
4080 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4081 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4082 if(!lpASCIICharStr) {
4084 }else if(out+1 <= cchASCIIChar) {
4085 lpASCIICharStr[out++] = disp<='z'-'a' ?
4086 'a'+disp : '0'+disp-'z'+'a'-1;
4088 HeapFree(GetProcessHeap(), 0, norm_str);
4089 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4096 bias = adapt(delta, h+1, h==b);
4105 if(out-out_label > 63) {
4106 HeapFree(GetProcessHeap(), 0, norm_str);
4107 SetLastError(ERROR_INVALID_NAME);
4111 if(label_end < norm_len) {
4112 if(!lpASCIICharStr) {
4114 }else if(out+1 <= cchASCIIChar) {
4115 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4117 HeapFree(GetProcessHeap(), 0, norm_str);
4118 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4122 label_start = label_end+1;
4125 HeapFree(GetProcessHeap(), 0, norm_str);
4129 /******************************************************************************
4130 * IdnToNameprepUnicode (KERNEL32.@)
4132 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4133 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4142 extern const unsigned short nameprep_char_type[];
4143 extern const WCHAR nameprep_mapping[];
4146 WCHAR buf[64], *map_str, norm_str[64], ch;
4147 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4148 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4150 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4151 lpNameprepCharStr, cchNameprepChar);
4153 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4154 SetLastError(ERROR_INVALID_FLAGS);
4158 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4159 SetLastError(ERROR_INVALID_PARAMETER);
4163 if(cchUnicodeChar == -1)
4164 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4165 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4166 SetLastError(ERROR_INVALID_NAME);
4170 for(label_start=0; label_start<cchUnicodeChar;) {
4172 for(i=label_start; i<cchUnicodeChar; i++) {
4173 ch = lpUnicodeCharStr[i];
4175 if(i!=cchUnicodeChar-1 && !ch) {
4176 SetLastError(ERROR_INVALID_NAME);
4179 /* check if ch is one of label separators defined in RFC3490 */
4180 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4188 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4190 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4191 || (ch>='0' && ch<='9') || ch=='-')
4194 SetLastError(ERROR_INVALID_NAME);
4198 /* last label may be empty */
4199 if(label_start==label_end && ch) {
4200 SetLastError(ERROR_INVALID_NAME);
4204 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4205 lpUnicodeCharStr[label_end-1]=='-')) {
4206 SetLastError(ERROR_INVALID_NAME);
4211 /* maximal label length is 63 characters */
4212 if(label_end-label_start > 63) {
4213 SetLastError(ERROR_INVALID_NAME);
4216 if(label_end < cchUnicodeChar)
4219 if(!lpNameprepCharStr) {
4220 out += label_end-label_start;
4221 }else if(out+label_end-label_start <= cchNameprepChar) {
4222 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4223 (label_end-label_start)*sizeof(WCHAR));
4224 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4225 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4226 out += label_end-label_start;
4228 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4232 label_start = label_end;
4237 for(i=label_start; i<label_end; i++) {
4238 ch = lpUnicodeCharStr[i];
4239 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4240 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4242 if(!ptr[0]) map_len++;
4243 else if(!ptr[1]) map_len++;
4244 else if(!ptr[2]) map_len += 2;
4245 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4247 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4248 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4250 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4257 for(i=label_start; i<label_end; i++) {
4258 ch = lpUnicodeCharStr[i];
4259 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4260 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4263 map_str[map_len++] = ch;
4265 map_str[map_len++] = ptr[0];
4267 map_str[map_len++] = ptr[0];
4268 map_str[map_len++] = ptr[1];
4269 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4270 map_str[map_len++] = ptr[0];
4271 map_str[map_len++] = ptr[1];
4272 map_str[map_len++] = ptr[2];
4276 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4277 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4279 HeapFree(GetProcessHeap(), 0, map_str);
4281 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4282 SetLastError(ERROR_INVALID_NAME);
4286 if(label_end < cchUnicodeChar) {
4287 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4291 if(!lpNameprepCharStr) {
4293 }else if(out+norm_len <= cchNameprepChar) {
4294 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4297 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4301 have_bidi_ral = prohibit_bidi_ral = FALSE;
4303 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4305 for(i=0; i<norm_len; i++) {
4307 flags = get_table_entry( nameprep_char_type, ch );
4310 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4311 : ERROR_NO_UNICODE_TRANSLATION);
4315 if(flags & BIDI_RAL)
4316 have_bidi_ral = TRUE;
4318 prohibit_bidi_ral = TRUE;
4323 flags = get_table_entry( nameprep_char_type, ch );
4324 if((flags & BIDI_RAL) == 0)
4325 prohibit_bidi_ral = TRUE;
4327 ch = norm_str[norm_len-1];
4328 flags = get_table_entry( nameprep_char_type, ch );
4329 if((flags & BIDI_RAL) == 0)
4330 prohibit_bidi_ral = TRUE;
4333 if(have_bidi_ral && prohibit_bidi_ral) {
4334 SetLastError(ERROR_INVALID_NAME);
4338 label_start = label_end;
4344 /******************************************************************************
4345 * IdnToUnicode (KERNEL32.@)
4347 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4348 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4350 extern const unsigned short nameprep_char_type[];
4352 INT i, label_start, label_end, out_label, out = 0;
4355 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4356 lpUnicodeCharStr, cchUnicodeChar);
4358 for(label_start=0; label_start<cchASCIIChar;) {
4359 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4362 for(i=label_start; i<cchASCIIChar; i++) {
4363 ch = lpASCIICharStr[i];
4365 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4366 SetLastError(ERROR_INVALID_NAME);
4375 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4377 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4378 || (ch>='0' && ch<='9') || ch=='-')
4381 SetLastError(ERROR_INVALID_NAME);
4385 /* last label may be empty */
4386 if(label_start==label_end && ch) {
4387 SetLastError(ERROR_INVALID_NAME);
4391 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4392 lpUnicodeCharStr[label_end-1]=='-')) {
4393 SetLastError(ERROR_INVALID_NAME);
4396 if(label_end-label_start > 63) {
4397 SetLastError(ERROR_INVALID_NAME);
4401 if(label_end-label_start<4 ||
4402 tolowerW(lpASCIICharStr[label_start])!='x' ||
4403 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4404 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4405 if(label_end < cchUnicodeChar)
4408 if(!lpUnicodeCharStr) {
4409 out += label_end-label_start;
4410 }else if(out+label_end-label_start <= cchUnicodeChar) {
4411 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4412 (label_end-label_start)*sizeof(WCHAR));
4413 out += label_end-label_start;
4415 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4419 label_start = label_end;
4423 if(delim == label_start+3)
4425 if(!lpUnicodeCharStr) {
4426 out += delim-label_start-4;
4427 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4428 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4429 (delim-label_start-4)*sizeof(WCHAR));
4430 out += delim-label_start-4;
4432 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4435 if(out != out_label)
4438 for(i=delim; i<label_end;) {
4441 for(k=BASE; ; k+=BASE) {
4442 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4443 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4444 SetLastError(ERROR_INVALID_NAME);
4447 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4449 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4454 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4455 n += pos/(out-out_label+1);
4456 pos %= out-out_label+1;
4458 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4459 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4460 SetLastError(ERROR_INVALID_NAME);
4463 if(!lpUnicodeCharStr) {
4465 }else if(out+1 <= cchASCIIChar) {
4466 memmove(lpUnicodeCharStr+out_label+pos+1,
4467 lpUnicodeCharStr+out_label+pos,
4468 (out-out_label-pos)*sizeof(WCHAR));
4469 lpUnicodeCharStr[out_label+pos] = n;
4472 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4478 if(out-out_label > 63) {
4479 SetLastError(ERROR_INVALID_NAME);
4483 if(label_end < cchASCIIChar) {
4484 if(!lpUnicodeCharStr) {
4486 }else if(out+1 <= cchUnicodeChar) {
4487 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4489 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4493 label_start = label_end+1;