4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2002 Alexandre Julliard for CodeWeavers
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "wine/port.h"
36 # include <CoreFoundation/CFBundle.h>
37 # include <CoreFoundation/CFLocale.h>
38 # include <CoreFoundation/CFString.h>
42 #define WIN32_NO_STATUS
45 #include "winuser.h" /* for RT_STRINGW */
47 #include "wine/unicode.h"
51 #include "kernel_private.h"
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(nls);
56 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|\
57 LOCALE_RETURN_NUMBER|LOCALE_RETURN_GENITIVE_NAMES)
59 /* current code pages */
60 static const union cptable *ansi_cptable;
61 static const union cptable *oem_cptable;
62 static const union cptable *mac_cptable;
63 static const union cptable *unix_cptable; /* NULL if UTF8 */
65 static const WCHAR szLocaleKeyName[] = {
66 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
67 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
68 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
71 static const WCHAR szLangGroupsKeyName[] = {
72 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
73 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
74 'C','o','n','t','r','o','l','\\','N','l','s','\\',
75 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
78 /* Charset to codepage map, sorted by name. */
79 static const struct charset_entry
81 const char *charset_name;
120 { "ISO88591", 28591 },
121 { "ISO885910", 28600 },
122 { "ISO885913", 28603 },
123 { "ISO885914", 28604 },
124 { "ISO885915", 28605 },
125 { "ISO885916", 28606 },
126 { "ISO88592", 28592 },
127 { "ISO88593", 28593 },
128 { "ISO88594", 28594 },
129 { "ISO88595", 28595 },
130 { "ISO88596", 28596 },
131 { "ISO88597", 28597 },
132 { "ISO88598", 28598 },
133 { "ISO88599", 28599 },
142 WCHAR win_name[128]; /* Windows name ("en-US") */
143 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
144 WCHAR *country; /* country ("US") */
145 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
146 WCHAR *script; /* script ("Latn") for Windows format only */
147 WCHAR *modifier; /* modifier or sort order */
148 LCID lcid; /* corresponding LCID */
149 int matches; /* number of elements matching LCID (0..4) */
150 UINT codepage; /* codepage corresponding to charset */
153 /* locale ids corresponding to the various Unix locale parameters */
154 static LCID lcid_LC_COLLATE;
155 static LCID lcid_LC_CTYPE;
156 static LCID lcid_LC_MESSAGES;
157 static LCID lcid_LC_MONETARY;
158 static LCID lcid_LC_NUMERIC;
159 static LCID lcid_LC_TIME;
160 static LCID lcid_LC_PAPER;
161 static LCID lcid_LC_MEASUREMENT;
162 static LCID lcid_LC_TELEPHONE;
164 /* Copy Ascii string to Unicode without using codepages */
165 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
167 while (n > 1 && *src)
169 *dst++ = (unsigned char)*src++;
175 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
177 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
180 /***********************************************************************
183 * Retrieve the ANSI codepage for a given locale.
185 static inline UINT get_lcid_codepage( LCID lcid )
188 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
189 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
194 /***********************************************************************
197 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
199 static const union cptable *get_codepage_table( unsigned int codepage )
201 const union cptable *ret = NULL;
203 assert( ansi_cptable ); /* init must have been done already */
217 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
218 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
221 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
222 if (codepage == oem_cptable->info.codepage) return oem_cptable;
223 if (codepage == mac_cptable->info.codepage) return mac_cptable;
224 ret = wine_cp_get_table( codepage );
231 /***********************************************************************
232 * charset_cmp (internal)
234 static int charset_cmp( const void *name, const void *entry )
236 const struct charset_entry *charset = entry;
237 return strcasecmp( name, charset->charset_name );
240 /***********************************************************************
243 static UINT find_charset( const WCHAR *name )
245 const struct charset_entry *entry;
246 char charset_name[16];
249 /* remove punctuation characters from charset name */
250 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
251 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
254 entry = bsearch( charset_name, charset_names,
255 sizeof(charset_names)/sizeof(charset_names[0]),
256 sizeof(charset_names[0]), charset_cmp );
257 if (entry) return entry->codepage;
262 /***********************************************************************
263 * find_locale_id_callback
265 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
266 LPCWSTR name, WORD LangID, LPARAM lParam )
268 struct locale_name *data = (struct locale_name *)lParam;
271 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
273 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
275 /* first check exact name */
276 if (data->win_name[0] &&
277 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
278 buffer, sizeof(buffer)/sizeof(WCHAR) ))
280 if (!strcmpW( data->win_name, buffer ))
282 matches = 4; /* everything matches */
287 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
288 buffer, sizeof(buffer)/sizeof(WCHAR) ))
290 if (strcmpW( buffer, data->lang )) return TRUE;
291 matches++; /* language name matched */
295 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
296 buffer, sizeof(buffer)/sizeof(WCHAR) ))
298 if (strcmpW( buffer, data->country )) goto done;
299 matches++; /* country name matched */
302 else /* match default language */
304 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
310 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
311 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
313 if (unix_cp == data->codepage) matches++;
317 /* FIXME: check sort order */
320 if (matches > data->matches)
323 data->matches = matches;
325 return (data->matches < 4); /* no need to continue for perfect match */
329 /***********************************************************************
332 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
333 * Unix format is: lang[_country][.charset][@modifier]
334 * Windows format is: lang[-script][-country][_modifier]
336 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
338 static const WCHAR sepW[] = {'-','_','.','@',0};
339 static const WCHAR winsepW[] = {'-','_',0};
340 static const WCHAR posixW[] = {'P','O','S','I','X',0};
341 static const WCHAR cW[] = {'C',0};
342 static const WCHAR latinW[] = {'l','a','t','i','n',0};
343 static const WCHAR latnW[] = {'-','L','a','t','n',0};
346 TRACE("%s\n", debugstr_w(str));
348 name->country = name->charset = name->script = name->modifier = NULL;
349 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
352 name->win_name[0] = 0;
353 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
355 if (!(p = strpbrkW( name->lang, sepW )))
357 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
359 name->matches = 4; /* perfect match for default English lcid */
362 strcpyW( name->win_name, name->lang );
364 else if (*p == '-') /* Windows format */
366 strcpyW( name->win_name, name->lang );
369 if (!(p = strpbrkW( p, winsepW ))) goto done;
373 name->script = name->country;
375 if (!(p = strpbrkW( p, winsepW ))) goto done;
380 else /* Unix format */
386 p = strpbrkW( p, sepW + 2 );
392 p = strchrW( p, '@' );
401 name->codepage = find_charset( name->charset );
403 /* rebuild a Windows name if possible */
405 if (name->charset) goto done; /* can't specify charset in Windows format */
406 if (name->modifier && strcmpW( name->modifier, latinW ))
407 goto done; /* only Latn script supported for now */
408 strcpyW( name->win_name, name->lang );
409 if (name->modifier) strcatW( name->win_name, latnW );
412 p = name->win_name + strlenW(name->win_name);
414 strcpyW( p, name->country );
418 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
419 find_locale_id_callback, (LPARAM)name );
423 /***********************************************************************
424 * convert_default_lcid
426 * Get the default LCID to use for a given lctype in GetLocaleInfo.
428 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
430 if (lcid == LOCALE_SYSTEM_DEFAULT ||
431 lcid == LOCALE_USER_DEFAULT ||
432 lcid == LOCALE_NEUTRAL)
436 switch(lctype & 0xffff)
438 case LOCALE_SSORTNAME:
439 default_id = lcid_LC_COLLATE;
442 case LOCALE_FONTSIGNATURE:
443 case LOCALE_IDEFAULTANSICODEPAGE:
444 case LOCALE_IDEFAULTCODEPAGE:
445 case LOCALE_IDEFAULTEBCDICCODEPAGE:
446 case LOCALE_IDEFAULTMACCODEPAGE:
447 case LOCALE_IDEFAULTUNIXCODEPAGE:
448 default_id = lcid_LC_CTYPE;
451 case LOCALE_ICURRDIGITS:
452 case LOCALE_ICURRENCY:
453 case LOCALE_IINTLCURRDIGITS:
454 case LOCALE_INEGCURR:
455 case LOCALE_INEGSEPBYSPACE:
456 case LOCALE_INEGSIGNPOSN:
457 case LOCALE_INEGSYMPRECEDES:
458 case LOCALE_IPOSSEPBYSPACE:
459 case LOCALE_IPOSSIGNPOSN:
460 case LOCALE_IPOSSYMPRECEDES:
461 case LOCALE_SCURRENCY:
462 case LOCALE_SINTLSYMBOL:
463 case LOCALE_SMONDECIMALSEP:
464 case LOCALE_SMONGROUPING:
465 case LOCALE_SMONTHOUSANDSEP:
466 case LOCALE_SNATIVECURRNAME:
467 default_id = lcid_LC_MONETARY;
471 case LOCALE_IDIGITSUBSTITUTION:
473 case LOCALE_INEGNUMBER:
474 case LOCALE_SDECIMAL:
475 case LOCALE_SGROUPING:
477 case LOCALE_SNATIVEDIGITS:
478 case LOCALE_SNEGATIVESIGN:
479 case LOCALE_SNEGINFINITY:
480 case LOCALE_SPOSINFINITY:
481 case LOCALE_SPOSITIVESIGN:
482 case LOCALE_STHOUSAND:
483 default_id = lcid_LC_NUMERIC;
486 case LOCALE_ICALENDARTYPE:
487 case LOCALE_ICENTURY:
489 case LOCALE_IDAYLZERO:
490 case LOCALE_IFIRSTDAYOFWEEK:
491 case LOCALE_IFIRSTWEEKOFYEAR:
493 case LOCALE_IMONLZERO:
494 case LOCALE_IOPTIONALCALENDAR:
496 case LOCALE_ITIMEMARKPOSN:
500 case LOCALE_SABBREVDAYNAME1:
501 case LOCALE_SABBREVDAYNAME2:
502 case LOCALE_SABBREVDAYNAME3:
503 case LOCALE_SABBREVDAYNAME4:
504 case LOCALE_SABBREVDAYNAME5:
505 case LOCALE_SABBREVDAYNAME6:
506 case LOCALE_SABBREVDAYNAME7:
507 case LOCALE_SABBREVMONTHNAME1:
508 case LOCALE_SABBREVMONTHNAME2:
509 case LOCALE_SABBREVMONTHNAME3:
510 case LOCALE_SABBREVMONTHNAME4:
511 case LOCALE_SABBREVMONTHNAME5:
512 case LOCALE_SABBREVMONTHNAME6:
513 case LOCALE_SABBREVMONTHNAME7:
514 case LOCALE_SABBREVMONTHNAME8:
515 case LOCALE_SABBREVMONTHNAME9:
516 case LOCALE_SABBREVMONTHNAME10:
517 case LOCALE_SABBREVMONTHNAME11:
518 case LOCALE_SABBREVMONTHNAME12:
519 case LOCALE_SABBREVMONTHNAME13:
521 case LOCALE_SDAYNAME1:
522 case LOCALE_SDAYNAME2:
523 case LOCALE_SDAYNAME3:
524 case LOCALE_SDAYNAME4:
525 case LOCALE_SDAYNAME5:
526 case LOCALE_SDAYNAME6:
527 case LOCALE_SDAYNAME7:
528 case LOCALE_SDURATION:
529 case LOCALE_SLONGDATE:
530 case LOCALE_SMONTHNAME1:
531 case LOCALE_SMONTHNAME2:
532 case LOCALE_SMONTHNAME3:
533 case LOCALE_SMONTHNAME4:
534 case LOCALE_SMONTHNAME5:
535 case LOCALE_SMONTHNAME6:
536 case LOCALE_SMONTHNAME7:
537 case LOCALE_SMONTHNAME8:
538 case LOCALE_SMONTHNAME9:
539 case LOCALE_SMONTHNAME10:
540 case LOCALE_SMONTHNAME11:
541 case LOCALE_SMONTHNAME12:
542 case LOCALE_SMONTHNAME13:
543 case LOCALE_SSHORTDATE:
544 case LOCALE_SSHORTESTDAYNAME1:
545 case LOCALE_SSHORTESTDAYNAME2:
546 case LOCALE_SSHORTESTDAYNAME3:
547 case LOCALE_SSHORTESTDAYNAME4:
548 case LOCALE_SSHORTESTDAYNAME5:
549 case LOCALE_SSHORTESTDAYNAME6:
550 case LOCALE_SSHORTESTDAYNAME7:
552 case LOCALE_STIMEFORMAT:
553 case LOCALE_SYEARMONTH:
554 default_id = lcid_LC_TIME;
557 case LOCALE_IPAPERSIZE:
558 default_id = lcid_LC_PAPER;
561 case LOCALE_IMEASURE:
562 default_id = lcid_LC_MEASUREMENT;
565 case LOCALE_ICOUNTRY:
566 default_id = lcid_LC_TELEPHONE;
569 if (default_id) lcid = default_id;
571 return ConvertDefaultLocale( lcid );
574 /***********************************************************************
575 * is_genitive_name_supported
577 * Determine could LCTYPE basically support genitive name form or not.
579 static BOOL is_genitive_name_supported( LCTYPE lctype )
581 switch(lctype & 0xffff)
583 case LOCALE_SMONTHNAME1:
584 case LOCALE_SMONTHNAME2:
585 case LOCALE_SMONTHNAME3:
586 case LOCALE_SMONTHNAME4:
587 case LOCALE_SMONTHNAME5:
588 case LOCALE_SMONTHNAME6:
589 case LOCALE_SMONTHNAME7:
590 case LOCALE_SMONTHNAME8:
591 case LOCALE_SMONTHNAME9:
592 case LOCALE_SMONTHNAME10:
593 case LOCALE_SMONTHNAME11:
594 case LOCALE_SMONTHNAME12:
595 case LOCALE_SMONTHNAME13:
602 /***********************************************************************
603 * create_registry_key
605 * Create the Control Panel\\International registry key.
607 static inline HANDLE create_registry_key(void)
609 static const WCHAR cplW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l',0};
610 static const WCHAR intlW[] = {'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
611 OBJECT_ATTRIBUTES attr;
612 UNICODE_STRING nameW;
613 HANDLE cpl_key, hkey = 0;
615 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
617 attr.Length = sizeof(attr);
618 attr.RootDirectory = hkey;
619 attr.ObjectName = &nameW;
621 attr.SecurityDescriptor = NULL;
622 attr.SecurityQualityOfService = NULL;
623 RtlInitUnicodeString( &nameW, cplW );
625 if (!NtCreateKey( &cpl_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
627 NtClose( attr.RootDirectory );
628 attr.RootDirectory = cpl_key;
629 RtlInitUnicodeString( &nameW, intlW );
630 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
632 NtClose( attr.RootDirectory );
637 /* update the registry settings for a given locale parameter */
638 /* return TRUE if an update was needed */
639 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
640 const LCTYPE *values, UINT nb_values )
642 static const WCHAR formatW[] = { '%','0','8','x',0 };
644 UNICODE_STRING nameW;
647 RtlInitUnicodeString( &nameW, name );
648 count = sizeof(bufferW);
649 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, bufferW, count, &count))
651 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
652 LPCWSTR text = (LPCWSTR)info->Data;
654 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
655 TRACE( "updating registry, locale %s changed %s -> %08x\n",
656 debugstr_w(name), debugstr_w(text), lcid );
658 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
659 sprintfW( bufferW, formatW, lcid );
660 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
662 for (i = 0; i < nb_values; i++)
664 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
665 sizeof(bufferW)/sizeof(WCHAR) );
666 SetLocaleInfoW( lcid, values[i], bufferW );
672 /***********************************************************************
673 * LOCALE_InitRegistry
675 * Update registry contents on startup if the user locale has changed.
676 * This simulates the action of the Windows control panel.
678 void LOCALE_InitRegistry(void)
680 static const WCHAR acpW[] = {'A','C','P',0};
681 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
682 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
683 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
684 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
685 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
686 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
687 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
688 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
689 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
690 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
695 } update_cp_values[] = {
696 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
697 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
698 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
700 static const LCTYPE lc_messages_values[] = {
701 LOCALE_SABBREVLANGNAME,
704 static const LCTYPE lc_monetary_values[] = {
710 LOCALE_SMONDECIMALSEP,
712 LOCALE_SMONTHOUSANDSEP };
713 static const LCTYPE lc_numeric_values[] = {
717 LOCALE_IDIGITSUBSTITUTION,
718 LOCALE_SNATIVEDIGITS,
720 LOCALE_SNEGATIVESIGN,
721 LOCALE_SPOSITIVESIGN,
723 static const LCTYPE lc_time_values[] = {
732 LOCALE_ITIMEMARKPOSN,
733 LOCALE_ICALENDARTYPE,
734 LOCALE_IFIRSTDAYOFWEEK,
735 LOCALE_IFIRSTWEEKOFYEAR,
739 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
740 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
741 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
743 UNICODE_STRING nameW;
747 LCID lcid = GetUserDefaultLCID();
749 if (!(hkey = create_registry_key()))
750 return; /* don't do anything if we can't create the registry key */
752 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
753 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
754 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
755 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
756 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
757 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
758 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
759 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
760 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
761 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
762 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
763 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
764 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
765 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
767 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
769 static const WCHAR codepageW[] =
770 {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
771 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
772 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0};
774 OBJECT_ATTRIBUTES attr;
778 RtlInitUnicodeString( &nameW, codepageW );
779 InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL );
780 while (codepageW[len])
782 nameW.Length = len * sizeof(WCHAR);
783 if (NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) break;
786 while (codepageW[len] && codepageW[len] != '\\') len++;
788 nameW.Length = len * sizeof(WCHAR);
789 if (!NtCreateKey( &nls_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))
791 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
793 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
794 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
795 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
796 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
806 /***********************************************************************
809 static UINT setup_unix_locales(void)
811 struct locale_name locale_name;
812 WCHAR buffer[128], ctype_buff[128];
816 if ((locale = setlocale( LC_CTYPE, NULL )))
818 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
819 parse_locale_name( ctype_buff, &locale_name );
820 lcid_LC_CTYPE = locale_name.lcid;
821 unix_cp = locale_name.codepage;
823 if (!lcid_LC_CTYPE) /* this one needs a default value */
824 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
826 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
827 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
829 #define GET_UNIX_LOCALE(cat) do \
830 if ((locale = setlocale( cat, NULL ))) \
832 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
833 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
835 parse_locale_name( buffer, &locale_name ); \
836 lcid_##cat = locale_name.lcid; \
837 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
838 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
842 GET_UNIX_LOCALE( LC_COLLATE );
843 GET_UNIX_LOCALE( LC_MESSAGES );
844 GET_UNIX_LOCALE( LC_MONETARY );
845 GET_UNIX_LOCALE( LC_NUMERIC );
846 GET_UNIX_LOCALE( LC_TIME );
848 GET_UNIX_LOCALE( LC_PAPER );
850 #ifdef LC_MEASUREMENT
851 GET_UNIX_LOCALE( LC_MEASUREMENT );
854 GET_UNIX_LOCALE( LC_TELEPHONE );
857 #undef GET_UNIX_LOCALE
863 /***********************************************************************
864 * GetUserDefaultLangID (KERNEL32.@)
866 * Get the default language Id for the current user.
872 * The current LANGID of the default language for the current user.
874 LANGID WINAPI GetUserDefaultLangID(void)
876 return LANGIDFROMLCID(GetUserDefaultLCID());
880 /***********************************************************************
881 * GetSystemDefaultLangID (KERNEL32.@)
883 * Get the default language Id for the system.
889 * The current LANGID of the default language for the system.
891 LANGID WINAPI GetSystemDefaultLangID(void)
893 return LANGIDFROMLCID(GetSystemDefaultLCID());
897 /***********************************************************************
898 * GetUserDefaultLCID (KERNEL32.@)
900 * Get the default locale Id for the current user.
906 * The current LCID of the default locale for the current user.
908 LCID WINAPI GetUserDefaultLCID(void)
911 NtQueryDefaultLocale( TRUE, &lcid );
916 /***********************************************************************
917 * GetSystemDefaultLCID (KERNEL32.@)
919 * Get the default locale Id for the system.
925 * The current LCID of the default locale for the system.
927 LCID WINAPI GetSystemDefaultLCID(void)
930 NtQueryDefaultLocale( FALSE, &lcid );
934 /***********************************************************************
935 * GetSystemDefaultLocaleName (KERNEL32.@)
937 INT WINAPI GetSystemDefaultLocaleName(LPWSTR localename, INT len)
939 LCID lcid = GetSystemDefaultLCID();
940 return LCIDToLocaleName(lcid, localename, len, 0);
943 /***********************************************************************
944 * GetUserDefaultUILanguage (KERNEL32.@)
946 * Get the default user interface language Id for the current user.
952 * The current LANGID of the default UI language for the current user.
954 LANGID WINAPI GetUserDefaultUILanguage(void)
957 NtQueryDefaultUILanguage( &lang );
962 /***********************************************************************
963 * GetSystemDefaultUILanguage (KERNEL32.@)
965 * Get the default user interface language Id for the system.
971 * The current LANGID of the default UI language for the system. This is
972 * typically the same language used during the installation process.
974 LANGID WINAPI GetSystemDefaultUILanguage(void)
977 NtQueryInstallUILanguage( &lang );
982 /***********************************************************************
983 * LocaleNameToLCID (KERNEL32.@)
985 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
987 struct locale_name locale_name;
989 if (flags) FIXME( "unsupported flags %x\n", flags );
991 if (name == LOCALE_NAME_USER_DEFAULT)
992 return GetUserDefaultLCID();
995 parse_locale_name( name, &locale_name );
997 TRACE( "found lcid %x for %s, matches %d\n",
998 locale_name.lcid, debugstr_w(name), locale_name.matches );
1000 if (!locale_name.matches)
1001 WARN( "locale %s not recognized, defaulting to English\n", debugstr_w(name) );
1002 else if (locale_name.matches == 1)
1003 WARN( "locale %s not recognized, defaulting to %s\n",
1004 debugstr_w(name), debugstr_w(locale_name.lang) );
1006 return locale_name.lcid;
1010 /***********************************************************************
1011 * LCIDToLocaleName (KERNEL32.@)
1013 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1015 if (flags) FIXME( "unsupported flags %x\n", flags );
1017 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1021 /******************************************************************************
1022 * get_locale_value_name
1024 * Gets the registry value name for a given lctype.
1026 static const WCHAR *get_locale_value_name( DWORD lctype )
1028 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1029 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1030 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1031 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1032 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1033 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1034 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1035 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1036 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1037 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1038 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1039 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1040 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1041 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1042 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1043 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1044 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1045 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1046 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1047 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1048 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1049 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1050 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1051 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1052 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1053 static const WCHAR sListW[] = {'s','L','i','s','t',0};
1054 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1055 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1056 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1057 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1058 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1059 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1060 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1061 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1062 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1063 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1064 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1065 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1066 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1070 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1071 * the values are stored in the registry, confirmed under Windows.
1073 case LOCALE_ICALENDARTYPE: return iCalendarTypeW;
1074 case LOCALE_ICURRDIGITS: return iCurrDigitsW;
1075 case LOCALE_ICURRENCY: return iCurrencyW;
1076 case LOCALE_IDIGITS: return iDigitsW;
1077 case LOCALE_IFIRSTDAYOFWEEK: return iFirstDayOfWeekW;
1078 case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1079 case LOCALE_ILZERO: return iLZeroW;
1080 case LOCALE_IMEASURE: return iMeasureW;
1081 case LOCALE_INEGCURR: return iNegCurrW;
1082 case LOCALE_INEGNUMBER: return iNegNumberW;
1083 case LOCALE_IPAPERSIZE: return iPaperSizeW;
1084 case LOCALE_ITIME: return iTimeW;
1085 case LOCALE_S1159: return s1159W;
1086 case LOCALE_S2359: return s2359W;
1087 case LOCALE_SCURRENCY: return sCurrencyW;
1088 case LOCALE_SDATE: return sDateW;
1089 case LOCALE_SDECIMAL: return sDecimalW;
1090 case LOCALE_SGROUPING: return sGroupingW;
1091 case LOCALE_SLIST: return sListW;
1092 case LOCALE_SLONGDATE: return sLongDateW;
1093 case LOCALE_SMONDECIMALSEP: return sMonDecimalSepW;
1094 case LOCALE_SMONGROUPING: return sMonGroupingW;
1095 case LOCALE_SMONTHOUSANDSEP: return sMonThousandSepW;
1096 case LOCALE_SNEGATIVESIGN: return sNegativeSignW;
1097 case LOCALE_SPOSITIVESIGN: return sPositiveSignW;
1098 case LOCALE_SSHORTDATE: return sShortDateW;
1099 case LOCALE_STHOUSAND: return sThousandW;
1100 case LOCALE_STIME: return sTimeW;
1101 case LOCALE_STIMEFORMAT: return sTimeFormatW;
1102 case LOCALE_SYEARMONTH: return sYearMonthW;
1104 /* The following are not listed under MSDN as supported,
1105 * but seem to be used and also stored in the registry.
1107 case LOCALE_ICOUNTRY: return iCountryW;
1108 case LOCALE_IDATE: return iDateW;
1109 case LOCALE_ILDATE: return iLDateW;
1110 case LOCALE_ITLZERO: return iTLZeroW;
1111 case LOCALE_SCOUNTRY: return sCountryW;
1112 case LOCALE_SABBREVLANGNAME: return sLanguageW;
1114 /* The following are used in XP and later */
1115 case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1116 case LOCALE_SNATIVEDIGITS: return sNativeDigitsW;
1117 case LOCALE_ITIMEMARKPOSN: return iTimePrefixW;
1123 /******************************************************************************
1124 * get_registry_locale_info
1126 * Retrieve user-modified locale info from the registry.
1127 * Return length, 0 on error, -1 if not found.
1129 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1135 UNICODE_STRING nameW;
1136 KEY_VALUE_PARTIAL_INFORMATION *info;
1137 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1139 if (!(hkey = create_registry_key())) return -1;
1141 RtlInitUnicodeString( &nameW, value );
1142 size = info_size + len * sizeof(WCHAR);
1144 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1147 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1151 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1155 ret = (size - info_size) / sizeof(WCHAR);
1156 /* append terminating null if needed */
1157 if (!ret || ((WCHAR *)info->Data)[ret-1])
1159 if (ret < len || !buffer) ret++;
1162 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1168 memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1172 else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1174 ret = (size - info_size) / sizeof(WCHAR) + 1;
1176 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1182 SetLastError( RtlNtStatusToDosError(status) );
1186 HeapFree( GetProcessHeap(), 0, info );
1191 /******************************************************************************
1192 * GetLocaleInfoA (KERNEL32.@)
1194 * Get information about an aspect of a locale.
1197 * lcid [I] LCID of the locale
1198 * lctype [I] LCTYPE_ flags from "winnls.h"
1199 * buffer [O] Destination for the information
1200 * len [I] Length of buffer in characters
1203 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1204 * with the information.
1205 * Failure: 0. Use GetLastError() to determine the cause.
1208 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1209 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1210 * which is a bit string.
1212 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1217 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1219 if (len < 0 || (len && !buffer))
1221 SetLastError( ERROR_INVALID_PARAMETER );
1224 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1226 SetLastError( ERROR_INVALID_FLAGS );
1230 if (!len) buffer = NULL;
1232 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1234 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1236 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1239 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1241 if ((lctype & LOCALE_RETURN_NUMBER) ||
1242 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1244 /* it's not an ASCII string, just bytes */
1245 ret *= sizeof(WCHAR);
1248 if (ret <= len) memcpy( buffer, bufferW, ret );
1251 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1258 UINT codepage = CP_ACP;
1259 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1260 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1263 HeapFree( GetProcessHeap(), 0, bufferW );
1268 /******************************************************************************
1269 * GetLocaleInfoW (KERNEL32.@)
1271 * See GetLocaleInfoA.
1273 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1283 if (len < 0 || (len && !buffer))
1285 SetLastError( ERROR_INVALID_PARAMETER );
1288 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1289 !is_genitive_name_supported( lctype ))
1291 SetLastError( ERROR_INVALID_FLAGS );
1295 if (!len) buffer = NULL;
1297 lcid = convert_default_lcid( lcid, lctype );
1299 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1302 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1304 /* first check for overrides in the registry */
1306 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1307 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1309 const WCHAR *value = get_locale_value_name(lctype);
1313 if (lcflags & LOCALE_RETURN_NUMBER)
1316 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1320 UINT number = strtolW( tmp, &end, 10 );
1321 if (*end) /* invalid number */
1323 SetLastError( ERROR_INVALID_FLAGS );
1326 ret = sizeof(UINT)/sizeof(WCHAR);
1327 if (!buffer) return ret;
1330 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1333 memcpy( buffer, &number, sizeof(number) );
1336 else ret = get_registry_locale_info( value, buffer, len );
1338 if (ret != -1) return ret;
1342 /* now load it from kernel resources */
1344 lang_id = LANGIDFROMLCID( lcid );
1346 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1347 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1348 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1350 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1351 ULongToPtr((lctype >> 4) + 1), lang_id )))
1353 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1356 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1359 p = LockResource( hmem );
1360 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1362 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1363 else if (is_genitive_name_supported( lctype ) && *p)
1365 /* genitive form's stored after a null separator from a nominative */
1366 for (i = 1; i <= *p; i++) if (!p[i]) break;
1368 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1376 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1378 if (!buffer) return ret;
1382 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1386 if (lcflags & LOCALE_RETURN_NUMBER)
1389 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1391 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1393 number = strtolW( tmp, &end, 10 );
1395 memcpy( buffer, &number, sizeof(number) );
1396 else /* invalid number */
1398 SetLastError( ERROR_INVALID_FLAGS );
1401 HeapFree( GetProcessHeap(), 0, tmp );
1403 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1404 lcid, lctype, buffer, len, number );
1408 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1409 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1411 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1412 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1417 /******************************************************************************
1418 * GetLocaleInfoEx (KERNEL32.@)
1420 INT WINAPI GetLocaleInfoEx(LPCWSTR locale, LCTYPE info, LPWSTR buffer, INT len)
1422 return GetLocaleInfoW(LocaleNameToLCID(locale, 0), info, buffer, len);
1425 /******************************************************************************
1426 * SetLocaleInfoA [KERNEL32.@]
1428 * Set information about an aspect of a locale.
1431 * lcid [I] LCID of the locale
1432 * lctype [I] LCTYPE_ flags from "winnls.h"
1433 * data [I] Information to set
1436 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1437 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1438 * Failure: FALSE. Use GetLastError() to determine the cause.
1441 * - Values are only be set for the current user locale; the system locale
1442 * settings cannot be changed.
1443 * - Any settings changed by this call are lost when the locale is changed by
1444 * the control panel (in Wine, this happens every time you change LANG).
1445 * - The native implementation of this function does not check that lcid matches
1446 * the current user locale, and simply sets the new values. Wine warns you in
1447 * this case, but behaves the same.
1449 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1451 UINT codepage = CP_ACP;
1456 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1460 SetLastError( ERROR_INVALID_PARAMETER );
1463 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1464 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1466 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1469 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1470 ret = SetLocaleInfoW( lcid, lctype, strW );
1471 HeapFree( GetProcessHeap(), 0, strW );
1476 /******************************************************************************
1477 * SetLocaleInfoW (KERNEL32.@)
1479 * See SetLocaleInfoA.
1481 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1484 static const WCHAR intlW[] = {'i','n','t','l',0 };
1485 UNICODE_STRING valueW;
1490 value = get_locale_value_name( lctype );
1492 if (!data || !value)
1494 SetLastError( ERROR_INVALID_PARAMETER );
1498 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1500 SetLastError( ERROR_INVALID_FLAGS );
1504 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1506 /* FIXME: should check that data to set is sane */
1508 /* FIXME: profile functions should map to registry */
1509 WriteProfileStringW( intlW, value, data );
1511 if (!(hkey = create_registry_key())) return FALSE;
1512 RtlInitUnicodeString( &valueW, value );
1513 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1515 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1517 /* Set I-value from S value */
1518 WCHAR *lpD, *lpM, *lpY;
1521 lpD = strrchrW(data, 'd');
1522 lpM = strrchrW(data, 'M');
1523 lpY = strrchrW(data, 'y');
1527 szBuff[0] = '1'; /* D-M-Y */
1532 szBuff[0] = '2'; /* Y-M-D */
1534 szBuff[0] = '0'; /* M-D-Y */
1539 if (lctype == LOCALE_SSHORTDATE)
1540 lctype = LOCALE_IDATE;
1542 lctype = LOCALE_ILDATE;
1544 value = get_locale_value_name( lctype );
1546 WriteProfileStringW( intlW, value, szBuff );
1548 RtlInitUnicodeString( &valueW, value );
1549 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1554 if (status) SetLastError( RtlNtStatusToDosError(status) );
1559 /******************************************************************************
1560 * GetACP (KERNEL32.@)
1562 * Get the current Ansi code page Id for the system.
1568 * The current Ansi code page identifier for the system.
1570 UINT WINAPI GetACP(void)
1572 assert( ansi_cptable );
1573 return ansi_cptable->info.codepage;
1577 /******************************************************************************
1578 * SetCPGlobal (KERNEL32.@)
1580 * Set the current Ansi code page Id for the system.
1583 * acp [I] code page ID to be the new ACP.
1588 UINT WINAPI SetCPGlobal( UINT acp )
1590 UINT ret = GetACP();
1591 const union cptable *new_cptable = wine_cp_get_table( acp );
1593 if (new_cptable) ansi_cptable = new_cptable;
1598 /***********************************************************************
1599 * GetOEMCP (KERNEL32.@)
1601 * Get the current OEM code page Id for the system.
1607 * The current OEM code page identifier for the system.
1609 UINT WINAPI GetOEMCP(void)
1611 assert( oem_cptable );
1612 return oem_cptable->info.codepage;
1616 /***********************************************************************
1617 * IsValidCodePage (KERNEL32.@)
1619 * Determine if a given code page identifier is valid.
1622 * codepage [I] Code page Id to verify.
1625 * TRUE, If codepage is valid and available on the system,
1628 BOOL WINAPI IsValidCodePage( UINT codepage )
1635 return wine_cp_get_table( codepage ) != NULL;
1640 /***********************************************************************
1641 * IsDBCSLeadByteEx (KERNEL32.@)
1643 * Determine if a character is a lead byte in a given code page.
1646 * codepage [I] Code page for the test.
1647 * testchar [I] Character to test
1650 * TRUE, if testchar is a lead byte in codepage,
1653 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1655 const union cptable *table = get_codepage_table( codepage );
1656 return table && wine_is_dbcs_leadbyte( table, testchar );
1660 /***********************************************************************
1661 * IsDBCSLeadByte (KERNEL32.@)
1662 * IsDBCSLeadByte (KERNEL.207)
1664 * Determine if a character is a lead byte.
1667 * testchar [I] Character to test
1670 * TRUE, if testchar is a lead byte in the ANSI code page,
1673 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1675 if (!ansi_cptable) return FALSE;
1676 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1680 /***********************************************************************
1681 * GetCPInfo (KERNEL32.@)
1683 * Get information about a code page.
1686 * codepage [I] Code page number
1687 * cpinfo [O] Destination for code page information
1690 * Success: TRUE. cpinfo is updated with the information about codepage.
1691 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1693 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1695 const union cptable *table;
1699 SetLastError( ERROR_INVALID_PARAMETER );
1703 if (!(table = get_codepage_table( codepage )))
1709 cpinfo->DefaultChar[0] = 0x3f;
1710 cpinfo->DefaultChar[1] = 0;
1711 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1712 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1716 SetLastError( ERROR_INVALID_PARAMETER );
1719 if (table->info.def_char & 0xff00)
1721 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1722 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1726 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1727 cpinfo->DefaultChar[1] = 0;
1729 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1730 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1732 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1737 /***********************************************************************
1738 * GetCPInfoExA (KERNEL32.@)
1740 * Get extended information about a code page.
1743 * codepage [I] Code page number
1744 * dwFlags [I] Reserved, must to 0.
1745 * cpinfo [O] Destination for code page information
1748 * Success: TRUE. cpinfo is updated with the information about codepage.
1749 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1751 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1755 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1758 /* the layout is the same except for CodePageName */
1759 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1760 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1764 /***********************************************************************
1765 * GetCPInfoExW (KERNEL32.@)
1767 * Unicode version of GetCPInfoExA.
1769 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1771 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1778 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1780 cpinfo->CodePage = CP_UTF7;
1781 cpinfo->UnicodeDefaultChar = 0x3f;
1782 strcpyW(cpinfo->CodePageName, utf7);
1788 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1790 cpinfo->CodePage = CP_UTF8;
1791 cpinfo->UnicodeDefaultChar = 0x3f;
1792 strcpyW(cpinfo->CodePageName, utf8);
1798 const union cptable *table = get_codepage_table( codepage );
1800 cpinfo->CodePage = table->info.codepage;
1801 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1802 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1803 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1810 /***********************************************************************
1811 * EnumSystemCodePagesA (KERNEL32.@)
1813 * Call a user defined function for every code page installed on the system.
1816 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1817 * flags [I] Reserved, set to 0.
1820 * TRUE, If all code pages have been enumerated, or
1821 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1823 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1825 const union cptable *table;
1831 if (!(table = wine_cp_enum_table( index++ ))) break;
1832 sprintf( buffer, "%d", table->info.codepage );
1833 if (!lpfnCodePageEnum( buffer )) break;
1839 /***********************************************************************
1840 * EnumSystemCodePagesW (KERNEL32.@)
1842 * See EnumSystemCodePagesA.
1844 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1846 const union cptable *table;
1847 WCHAR buffer[10], *p;
1848 int page, index = 0;
1852 if (!(table = wine_cp_enum_table( index++ ))) break;
1853 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1855 page = table->info.codepage;
1858 *--p = '0' + (page % 10);
1861 if (!lpfnCodePageEnum( p )) break;
1867 /***********************************************************************
1868 * MultiByteToWideChar (KERNEL32.@)
1870 * Convert a multibyte character string into a Unicode string.
1873 * page [I] Codepage character set to convert from
1874 * flags [I] Character mapping flags
1875 * src [I] Source string buffer
1876 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1877 * dst [O] Destination buffer
1878 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1881 * Success: If dstlen > 0, the number of characters written to dst.
1882 * If dstlen == 0, the number of characters needed to perform the
1883 * conversion. In both cases the count includes the terminating NUL.
1884 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1885 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1886 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1887 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1890 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1891 LPWSTR dst, INT dstlen )
1893 const union cptable *table;
1896 if (!src || !srclen || (!dst && dstlen))
1898 SetLastError( ERROR_INVALID_PARAMETER );
1902 if (srclen < 0) srclen = strlen(src) + 1;
1909 SetLastError( ERROR_INVALID_FLAGS );
1912 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1917 SetLastError( ERROR_INVALID_FLAGS );
1920 FIXME("UTF-7 not supported\n");
1921 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1926 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1930 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1934 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1937 if (!(table = get_codepage_table( page )))
1939 SetLastError( ERROR_INVALID_PARAMETER );
1942 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1950 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1951 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1955 TRACE("cp %d %s -> %s, ret = %d\n",
1956 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1961 /***********************************************************************
1962 * WideCharToMultiByte (KERNEL32.@)
1964 * Convert a Unicode character string into a multibyte string.
1967 * page [I] Code page character set to convert to
1968 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
1969 * src [I] Source string buffer
1970 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1971 * dst [O] Destination buffer
1972 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
1973 * defchar [I] Default character to use for conversion if no exact
1974 * conversion can be made
1975 * used [O] Set if default character was used in the conversion
1978 * Success: If dstlen > 0, the number of characters written to dst.
1979 * If dstlen == 0, number of characters needed to perform the
1980 * conversion. In both cases the count includes the terminating NUL.
1981 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1982 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1983 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1984 * parameter was given.
1986 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1987 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1989 const union cptable *table;
1992 if (!src || !srclen || (!dst && dstlen))
1994 SetLastError( ERROR_INVALID_PARAMETER );
1998 if (srclen < 0) srclen = strlenW(src) + 1;
2003 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
2006 SetLastError( ERROR_INVALID_FLAGS );
2009 if (defchar || used)
2011 SetLastError( ERROR_INVALID_PARAMETER );
2014 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2017 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2018 if (defchar || used)
2020 SetLastError( ERROR_INVALID_PARAMETER );
2025 SetLastError( ERROR_INVALID_FLAGS );
2028 FIXME("UTF-7 not supported\n");
2029 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2034 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2035 defchar, used ? &used_tmp : NULL );
2040 if (defchar || used)
2042 SetLastError( ERROR_INVALID_PARAMETER );
2045 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2048 if (!(table = get_codepage_table( page )))
2050 SetLastError( ERROR_INVALID_PARAMETER );
2053 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2054 defchar, used ? &used_tmp : NULL );
2055 if (used) *used = used_tmp;
2063 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2064 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2068 TRACE("cp %d %s -> %s, ret = %d\n",
2069 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2074 /***********************************************************************
2075 * GetThreadLocale (KERNEL32.@)
2077 * Get the current threads locale.
2083 * The LCID currently associated with the calling thread.
2085 LCID WINAPI GetThreadLocale(void)
2087 LCID ret = NtCurrentTeb()->CurrentLocale;
2088 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2092 /**********************************************************************
2093 * SetThreadLocale (KERNEL32.@)
2095 * Set the current threads locale.
2098 * lcid [I] LCID of the locale to set
2101 * Success: TRUE. The threads locale is set to lcid.
2102 * Failure: FALSE. Use GetLastError() to determine the cause.
2104 BOOL WINAPI SetThreadLocale( LCID lcid )
2106 TRACE("(0x%04X)\n", lcid);
2108 lcid = ConvertDefaultLocale(lcid);
2110 if (lcid != GetThreadLocale())
2112 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2114 SetLastError(ERROR_INVALID_PARAMETER);
2118 NtCurrentTeb()->CurrentLocale = lcid;
2123 /**********************************************************************
2124 * SetThreadUILanguage (KERNEL32.@)
2126 * Set the current threads UI language.
2129 * langid [I] LANGID of the language to set, or 0 to use
2130 * the available language which is best supported
2131 * for console applications
2134 * Success: The return value is the same as the input value.
2135 * Failure: The return value differs from the input value.
2136 * Use GetLastError() to determine the cause.
2138 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2140 TRACE("(0x%04x) stub - returning success\n", langid);
2144 /******************************************************************************
2145 * ConvertDefaultLocale (KERNEL32.@)
2147 * Convert a default locale identifier into a real identifier.
2150 * lcid [I] LCID identifier of the locale to convert
2153 * lcid unchanged, if not a default locale or its sublanguage is
2154 * not SUBLANG_NEUTRAL.
2155 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2156 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2157 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2159 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2165 case LOCALE_SYSTEM_DEFAULT:
2166 lcid = GetSystemDefaultLCID();
2168 case LOCALE_USER_DEFAULT:
2169 case LOCALE_NEUTRAL:
2170 lcid = GetUserDefaultLCID();
2173 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2174 langid = LANGIDFROMLCID(lcid);
2175 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2177 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2178 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2185 /******************************************************************************
2186 * IsValidLocale (KERNEL32.@)
2188 * Determine if a locale is valid.
2191 * lcid [I] LCID of the locale to check
2192 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2195 * TRUE, if lcid is valid,
2199 * Wine does not currently make the distinction between supported and installed. All
2200 * languages supported are installed by default.
2202 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2204 /* check if language is registered in the kernel32 resources */
2205 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2206 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2210 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2211 LPCSTR name, WORD LangID, LONG_PTR lParam )
2213 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2216 sprintf(buf, "%08x", (UINT)LangID);
2217 return lpfnLocaleEnum( buf );
2220 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2221 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2223 static const WCHAR formatW[] = {'%','0','8','x',0};
2224 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2226 sprintfW( buf, formatW, (UINT)LangID );
2227 return lpfnLocaleEnum( buf );
2230 /******************************************************************************
2231 * EnumSystemLocalesA (KERNEL32.@)
2233 * Call a users function for each locale available on the system.
2236 * lpfnLocaleEnum [I] Callback function to call for each locale
2237 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2241 * Failure: FALSE. Use GetLastError() to determine the cause.
2243 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2245 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2246 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2247 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2248 (LONG_PTR)lpfnLocaleEnum);
2253 /******************************************************************************
2254 * EnumSystemLocalesW (KERNEL32.@)
2256 * See EnumSystemLocalesA.
2258 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2260 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2261 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2262 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2263 (LONG_PTR)lpfnLocaleEnum);
2268 struct enum_locale_ex_data
2270 LOCALE_ENUMPROCEX proc;
2275 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2276 LPCWSTR name, WORD lang, LONG_PTR lparam )
2278 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2283 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2284 buffer, sizeof(buffer) / sizeof(WCHAR) );
2285 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2286 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2287 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2289 flags = LOCALE_WINDOWS;
2290 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2291 if (data->flags && ~(data->flags & flags)) return TRUE;
2292 return data->proc( buffer, flags, data->lparam );
2295 /******************************************************************************
2296 * EnumSystemLocalesEx (KERNEL32.@)
2298 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2300 struct enum_locale_ex_data data;
2304 SetLastError( ERROR_INVALID_PARAMETER );
2309 data.lparam = lparam;
2310 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2311 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2312 enum_locale_ex_proc, (LONG_PTR)&data );
2317 /***********************************************************************
2318 * VerLanguageNameA (KERNEL32.@)
2320 * Get the name of a language.
2323 * wLang [I] LANGID of the language
2324 * szLang [O] Destination for the language name
2327 * Success: The size of the language name. If szLang is non-NULL, it is filled
2329 * Failure: 0. Use GetLastError() to determine the cause.
2332 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2334 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2338 /***********************************************************************
2339 * VerLanguageNameW (KERNEL32.@)
2341 * See VerLanguageNameA.
2343 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2345 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2349 /******************************************************************************
2350 * GetStringTypeW (KERNEL32.@)
2352 * See GetStringTypeA.
2354 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2356 static const unsigned char type2_map[16] =
2358 C2_NOTAPPLICABLE, /* unassigned */
2359 C2_LEFTTORIGHT, /* L */
2360 C2_RIGHTTOLEFT, /* R */
2361 C2_EUROPENUMBER, /* EN */
2362 C2_EUROPESEPARATOR, /* ES */
2363 C2_EUROPETERMINATOR, /* ET */
2364 C2_ARABICNUMBER, /* AN */
2365 C2_COMMONSEPARATOR, /* CS */
2366 C2_BLOCKSEPARATOR, /* B */
2367 C2_SEGMENTSEPARATOR, /* S */
2368 C2_WHITESPACE, /* WS */
2369 C2_OTHERNEUTRAL, /* ON */
2370 C2_RIGHTTOLEFT, /* AL */
2371 C2_NOTAPPLICABLE, /* NSM */
2372 C2_NOTAPPLICABLE, /* BN */
2373 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2376 if (count == -1) count = strlenW(src) + 1;
2380 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2383 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2387 WARN("CT_CTYPE3: semi-stub.\n");
2391 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2393 type1 = get_char_typeW( *src++ ) & 0xfff;
2394 /* try to construct type3 from type1 */
2395 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2396 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2397 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2398 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2399 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2400 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2401 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2403 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2404 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2405 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2406 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2407 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2408 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2409 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2410 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2412 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2413 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2414 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2415 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2416 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2417 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2418 *chartype++ = type3;
2423 SetLastError( ERROR_INVALID_PARAMETER );
2430 /******************************************************************************
2431 * GetStringTypeExW (KERNEL32.@)
2433 * See GetStringTypeExA.
2435 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2437 /* locale is ignored for Unicode */
2438 return GetStringTypeW( type, src, count, chartype );
2442 /******************************************************************************
2443 * GetStringTypeA (KERNEL32.@)
2445 * Get characteristics of the characters making up a string.
2448 * locale [I] Locale Id for the string
2449 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2450 * src [I] String to analyse
2451 * count [I] Length of src in chars, or -1 if src is NUL terminated
2452 * chartype [O] Destination for the calculated characteristics
2455 * Success: TRUE. chartype is filled with the requested characteristics of each char
2457 * Failure: FALSE. Use GetLastError() to determine the cause.
2459 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2466 if(count == -1) count = strlen(src) + 1;
2468 if (!(cp = get_lcid_codepage( locale )))
2470 FIXME("For locale %04x using current ANSI code page\n", locale);
2474 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2475 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2477 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2479 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2480 * string, with multibyte characters there maybe be more bytes in count
2481 * than character space in the buffer!
2483 ret = GetStringTypeW(type, srcW, countW, chartype);
2484 HeapFree(GetProcessHeap(), 0, srcW);
2489 /******************************************************************************
2490 * GetStringTypeExA (KERNEL32.@)
2492 * Get characteristics of the characters making up a string.
2495 * locale [I] Locale Id for the string
2496 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2497 * src [I] String to analyse
2498 * count [I] Length of src in chars, or -1 if src is NUL terminated
2499 * chartype [O] Destination for the calculated characteristics
2502 * Success: TRUE. chartype is filled with the requested characteristics of each char
2504 * Failure: FALSE. Use GetLastError() to determine the cause.
2506 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2508 return GetStringTypeA(locale, type, src, count, chartype);
2511 /*************************************************************************
2512 * LCMapStringEx (KERNEL32.@)
2514 * Map characters in a locale sensitive string.
2517 * name [I] Locale name for the conversion.
2518 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2519 * src [I] String to map
2520 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2521 * dst [O] Destination for mapped string
2522 * dstlen [I] Length of dst in characters
2523 * version [I] reserved, must be NULL
2524 * reserved [I] reserved, must be NULL
2525 * lparam [I] reserved, must be 0
2528 * Success: The length of the mapped string in dst, including the NUL terminator.
2529 * Failure: 0. Use GetLastError() to determine the cause.
2531 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2532 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2536 if (version) FIXME("unsupported version structure %p\n", version);
2537 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2538 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2540 if (!src || !srclen || dstlen < 0)
2542 SetLastError(ERROR_INVALID_PARAMETER);
2546 /* mutually exclusive flags */
2547 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2548 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2549 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2550 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2552 SetLastError(ERROR_INVALID_FLAGS);
2556 if (!dstlen) dst = NULL;
2558 if (flags & LCMAP_SORTKEY)
2563 SetLastError(ERROR_INVALID_FLAGS);
2567 if (srclen < 0) srclen = strlenW(src);
2569 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2570 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2572 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2574 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2580 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2581 if (flags & SORT_STRINGSORT)
2583 SetLastError(ERROR_INVALID_FLAGS);
2587 if (srclen < 0) srclen = strlenW(src) + 1;
2589 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2590 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2592 if (!dst) /* return required string length */
2596 for (len = 0; srclen; src++, srclen--)
2599 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2600 * and skips white space and punctuation characters for
2601 * NORM_IGNORESYMBOLS.
2603 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2610 if (flags & LCMAP_UPPERCASE)
2612 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2615 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2617 *dst_ptr++ = toupperW(wch);
2621 else if (flags & LCMAP_LOWERCASE)
2623 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2626 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2628 *dst_ptr++ = tolowerW(wch);
2636 SetLastError(ERROR_INVALID_FLAGS);
2639 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2642 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2651 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2655 return dst_ptr - dst;
2658 /*************************************************************************
2659 * LCMapStringW (KERNEL32.@)
2663 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2664 LPWSTR dst, INT dstlen)
2666 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2667 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2669 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2672 /*************************************************************************
2673 * LCMapStringA (KERNEL32.@)
2675 * Map characters in a locale sensitive string.
2678 * lcid [I] LCID for the conversion.
2679 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2680 * src [I] String to map
2681 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2682 * dst [O] Destination for mapped string
2683 * dstlen [I] Length of dst in characters
2686 * Success: The length of the mapped string in dst, including the NUL terminator.
2687 * Failure: 0. Use GetLastError() to determine the cause.
2689 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2690 LPSTR dst, INT dstlen)
2692 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2694 INT ret = 0, srclenW, dstlenW;
2695 UINT locale_cp = CP_ACP;
2697 if (!src || !srclen || dstlen < 0)
2699 SetLastError(ERROR_INVALID_PARAMETER);
2703 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2705 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2710 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2711 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2714 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2717 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2720 if (flags & LCMAP_SORTKEY)
2724 SetLastError(ERROR_INVALID_FLAGS);
2725 goto map_string_exit;
2727 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2729 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2732 goto map_string_exit;
2735 if (flags & SORT_STRINGSORT)
2737 SetLastError(ERROR_INVALID_FLAGS);
2738 goto map_string_exit;
2741 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2743 goto map_string_exit;
2745 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2748 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2749 goto map_string_exit;
2752 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2753 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2754 HeapFree(GetProcessHeap(), 0, dstW);
2757 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2761 /*************************************************************************
2762 * FoldStringA (KERNEL32.@)
2764 * Map characters in a string.
2767 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2768 * src [I] String to map
2769 * srclen [I] Length of src, or -1 if src is NUL terminated
2770 * dst [O] Destination for mapped string
2771 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2774 * Success: The length of the string written to dst, including the terminating NUL. If
2775 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2776 * and dst may be NULL.
2777 * Failure: 0. Use GetLastError() to determine the cause.
2779 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2780 LPSTR dst, INT dstlen)
2782 INT ret = 0, srclenW = 0;
2783 WCHAR *srcW = NULL, *dstW = NULL;
2785 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2787 SetLastError(ERROR_INVALID_PARAMETER);
2791 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2792 src, srclen, NULL, 0);
2793 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2797 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2798 goto FoldStringA_exit;
2801 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2802 src, srclen, srcW, srclenW);
2804 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2806 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2809 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2813 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2814 goto FoldStringA_exit;
2817 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2818 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2821 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2825 HeapFree(GetProcessHeap(), 0, dstW);
2828 HeapFree(GetProcessHeap(), 0, srcW);
2832 /*************************************************************************
2833 * FoldStringW (KERNEL32.@)
2837 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2838 LPWSTR dst, INT dstlen)
2842 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2847 /* Fall through for dwFlags == 0 */
2848 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2849 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2850 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2851 SetLastError(ERROR_INVALID_FLAGS);
2855 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2857 SetLastError(ERROR_INVALID_PARAMETER);
2861 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2863 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2867 /******************************************************************************
2868 * CompareStringW (KERNEL32.@)
2870 * See CompareStringA.
2872 INT WINAPI CompareStringW(LCID lcid, DWORD flags,
2873 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2875 return CompareStringEx(NULL, flags, str1, len1, str2, len2, NULL, NULL, 0);
2878 /******************************************************************************
2879 * CompareStringEx (KERNEL32.@)
2881 INT WINAPI CompareStringEx(LPCWSTR locale, DWORD flags, LPCWSTR str1, INT len1,
2882 LPCWSTR str2, INT len2, LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lParam)
2886 if (version) FIXME("unexpected version parameter\n");
2887 if (reserved) FIXME("unexpected reserved value\n");
2888 if (lParam) FIXME("unexpected lParam\n");
2892 SetLastError(ERROR_INVALID_PARAMETER);
2896 if( flags & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2897 SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2899 SetLastError(ERROR_INVALID_FLAGS);
2903 /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2904 if (flags & 0x10000000)
2905 WARN("Ignoring unknown flags 0x10000000\n");
2907 if (len1 < 0) len1 = strlenW(str1);
2908 if (len2 < 0) len2 = strlenW(str2);
2910 ret = wine_compare_string(flags, str1, len1, str2, len2);
2912 if (ret) /* need to translate result */
2913 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2917 /******************************************************************************
2918 * CompareStringA (KERNEL32.@)
2920 * Compare two locale sensitive strings.
2923 * lcid [I] LCID for the comparison
2924 * flags [I] Flags for the comparison (NORM_ constants from "winnls.h").
2925 * str1 [I] First string to compare
2926 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
2927 * str2 [I] Second string to compare
2928 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
2931 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2932 * str1 is less than, equal to or greater than str2 respectively.
2933 * Failure: FALSE. Use GetLastError() to determine the cause.
2935 INT WINAPI CompareStringA(LCID lcid, DWORD flags,
2936 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2938 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2939 WCHAR *buf2W = buf1W + 130;
2940 LPWSTR str1W, str2W;
2941 INT len1W, len2W, ret;
2942 UINT locale_cp = CP_ACP;
2946 SetLastError(ERROR_INVALID_PARAMETER);
2949 if (len1 < 0) len1 = strlen(str1);
2950 if (len2 < 0) len2 = strlen(str2);
2952 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2956 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2961 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2962 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2965 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2968 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2979 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2984 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2985 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2988 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2989 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2992 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
3001 ret = CompareStringEx(NULL, flags, str1W, len1W, str2W, len2W, NULL, NULL, 0);
3003 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
3004 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
3008 /*************************************************************************
3009 * lstrcmp (KERNEL32.@)
3010 * lstrcmpA (KERNEL32.@)
3012 * Compare two strings using the current thread locale.
3015 * str1 [I] First string to compare
3016 * str2 [I] Second string to compare
3019 * Success: A number less than, equal to or greater than 0 depending on whether
3020 * str1 is less than, equal to or greater than str2 respectively.
3021 * Failure: FALSE. Use GetLastError() to determine the cause.
3023 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
3027 if ((str1 == NULL) && (str2 == NULL)) return 0;
3028 if (str1 == NULL) return -1;
3029 if (str2 == NULL) return 1;
3031 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3037 /*************************************************************************
3038 * lstrcmpi (KERNEL32.@)
3039 * lstrcmpiA (KERNEL32.@)
3041 * Compare two strings using the current thread locale, ignoring case.
3044 * str1 [I] First string to compare
3045 * str2 [I] Second string to compare
3048 * Success: A number less than, equal to or greater than 0 depending on whether
3049 * str2 is less than, equal to or greater than str1 respectively.
3050 * Failure: FALSE. Use GetLastError() to determine the cause.
3052 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3056 if ((str1 == NULL) && (str2 == NULL)) return 0;
3057 if (str1 == NULL) return -1;
3058 if (str2 == NULL) return 1;
3060 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3066 /*************************************************************************
3067 * lstrcmpW (KERNEL32.@)
3071 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3075 if ((str1 == NULL) && (str2 == NULL)) return 0;
3076 if (str1 == NULL) return -1;
3077 if (str2 == NULL) return 1;
3079 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3085 /*************************************************************************
3086 * lstrcmpiW (KERNEL32.@)
3090 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3094 if ((str1 == NULL) && (str2 == NULL)) return 0;
3095 if (str1 == NULL) return -1;
3096 if (str2 == NULL) return 1;
3098 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3104 /******************************************************************************
3107 void LOCALE_Init(void)
3109 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3110 const union cptable *unix_cp );
3112 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3115 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3116 char user_locale[50];
3118 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3119 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3120 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3121 CFStringRef user_locale_string_ref;
3123 if (user_locale_country_ref)
3125 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3126 user_locale_lang_ref, user_locale_country_ref);
3130 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3131 user_locale_lang_ref);
3134 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3136 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3137 setenv( "LANG", user_locale, 0 );
3138 TRACE( "setting locale to '%s'\n", user_locale );
3139 #endif /* __APPLE__ */
3141 setlocale( LC_ALL, "" );
3143 unix_cp = setup_unix_locales();
3144 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3147 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3148 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3150 /* Retrieve the preferred language as chosen in System Preferences. */
3151 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3153 CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3154 CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3155 CFStringRef user_language_string_ref;
3156 if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3157 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3158 !CFEqual(user_language_string_ref, user_locale_lang_ref))
3160 struct locale_name locale_name;
3162 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3163 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3164 parse_locale_name( buffer, &locale_name );
3165 lcid_LC_MESSAGES = locale_name.lcid;
3166 TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3168 CFRelease( all_locales );
3169 if (preferred_locales)
3170 CFRelease( preferred_locales );
3173 CFRelease( user_locale_ref );
3174 CFRelease( user_locale_string_ref );
3177 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3178 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3179 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3181 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3182 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3183 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3184 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3185 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3187 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3188 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3190 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3191 ansi_cptable = wine_cp_get_table( 1252 );
3192 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3193 oem_cptable = wine_cp_get_table( 437 );
3194 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3195 mac_cptable = wine_cp_get_table( 10000 );
3196 if (unix_cp != CP_UTF8)
3198 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3199 unix_cptable = wine_cp_get_table( 28591 );
3202 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3204 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3205 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3206 mac_cptable->info.codepage, unix_cp );
3208 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3211 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3213 UNICODE_STRING keyName;
3214 OBJECT_ATTRIBUTES attr;
3217 RtlInitUnicodeString( &keyName, szKeyName );
3218 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3220 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3226 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3230 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3233 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3234 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3235 info->NameLength > keyNameSize)
3240 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3242 memcpy( szKeyName, info->Name, info->NameLength);
3243 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3245 TRACE("returning %s\n", debugstr_w(szKeyName));
3249 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3250 LPWSTR szValueName, ULONG valueNameSize,
3251 LPWSTR szValueData, ULONG valueDataSize)
3254 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3257 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3258 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3259 info->NameLength > valueNameSize ||
3260 info->DataLength > valueDataSize)
3265 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3267 memcpy( szValueName, info->Name, info->NameLength);
3268 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3269 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3270 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3272 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3276 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3279 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3280 DWORD dwSize = sizeof(buffer);
3281 UNICODE_STRING valueName;
3283 RtlInitUnicodeString( &valueName, szValueName );
3285 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3286 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3287 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3288 info->DataLength == sizeof(DWORD))
3290 memcpy(lpVal, info->Data, sizeof(DWORD));
3297 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3300 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3304 /* FIXME: Is it correct to use the system default langid? */
3305 langId = GetSystemDefaultLangID();
3307 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3308 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3310 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3314 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3318 ULONG iResourceIndex = lgrpid & 0xf;
3319 LPCWSTR lpResEntry = LockResource( hResDir );
3322 for (i = 0; i < iResourceIndex; i++)
3323 lpResEntry += *lpResEntry + 1;
3325 if (*lpResEntry < nameSize)
3327 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3328 szName[*lpResEntry] = '\0';
3333 FreeResource( hResource );
3338 /* Registry keys for NLS related information */
3340 static const WCHAR szCountryListName[] = {
3341 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3342 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3343 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3344 'T','e','l','e','p','h','o','n','y','\\',
3345 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3349 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3352 LANGUAGEGROUP_ENUMPROCA procA;
3353 LANGUAGEGROUP_ENUMPROCW procW;
3356 } ENUMLANGUAGEGROUP_CALLBACKS;
3358 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3359 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3361 WCHAR szNumber[10], szValue[4];
3363 BOOL bContinue = TRUE;
3368 SetLastError(ERROR_INVALID_PARAMETER);
3372 switch (lpProcs->dwFlags)
3375 /* Default to LGRPID_INSTALLED */
3376 lpProcs->dwFlags = LGRPID_INSTALLED;
3377 /* Fall through... */
3378 case LGRPID_INSTALLED:
3379 case LGRPID_SUPPORTED:
3382 SetLastError(ERROR_INVALID_FLAGS);
3386 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3389 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3393 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3394 szValue, sizeof(szValue) ))
3396 BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
3397 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3399 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3400 bInstalled ? "" : "not ");
3402 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3404 WCHAR szGrpName[48];
3406 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3407 szGrpName[0] = '\0';
3410 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3414 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3415 char szGrpNameA[48];
3417 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3418 * or whether the language names are ever localised. Assume CP_ACP.
3421 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3422 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3424 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3444 /******************************************************************************
3445 * EnumSystemLanguageGroupsA (KERNEL32.@)
3447 * Call a users function for each language group available on the system.
3450 * pLangGrpEnumProc [I] Callback function to call for each language group
3451 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3452 * lParam [I] User parameter to pass to pLangGrpEnumProc
3456 * Failure: FALSE. Use GetLastError() to determine the cause.
3458 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3459 DWORD dwFlags, LONG_PTR lParam)
3461 ENUMLANGUAGEGROUP_CALLBACKS procs;
3463 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3465 procs.procA = pLangGrpEnumProc;
3467 procs.dwFlags = dwFlags;
3468 procs.lParam = lParam;
3470 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3473 /******************************************************************************
3474 * EnumSystemLanguageGroupsW (KERNEL32.@)
3476 * See EnumSystemLanguageGroupsA.
3478 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3479 DWORD dwFlags, LONG_PTR lParam)
3481 ENUMLANGUAGEGROUP_CALLBACKS procs;
3483 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3486 procs.procW = pLangGrpEnumProc;
3487 procs.dwFlags = dwFlags;
3488 procs.lParam = lParam;
3490 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3493 /******************************************************************************
3494 * IsValidLanguageGroup (KERNEL32.@)
3496 * Determine if a language group is supported and/or installed.
3499 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3500 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3503 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3506 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3508 static const WCHAR szFormat[] = { '%','x','\0' };
3509 WCHAR szValueName[16], szValue[2];
3510 BOOL bSupported = FALSE, bInstalled = FALSE;
3516 case LGRPID_INSTALLED:
3517 case LGRPID_SUPPORTED:
3519 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3521 sprintfW( szValueName, szFormat, lgrpid );
3523 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3527 if (szValue[0] == '1')
3537 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3538 (dwFlags == LGRPID_INSTALLED && bInstalled))
3544 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3547 LANGGROUPLOCALE_ENUMPROCA procA;
3548 LANGGROUPLOCALE_ENUMPROCW procW;
3552 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3554 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3555 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3557 static const WCHAR szAlternateSortsKeyName[] = {
3558 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3560 WCHAR szNumber[10], szValue[4];
3562 BOOL bContinue = TRUE, bAlternate = FALSE;
3564 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3566 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3568 SetLastError(ERROR_INVALID_PARAMETER);
3572 if (lpProcs->dwFlags)
3574 SetLastError(ERROR_INVALID_FLAGS);
3578 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3581 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3585 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3586 szValue, sizeof(szValue) ))
3588 lgrpid = strtoulW( szValue, NULL, 16 );
3590 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3591 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3593 if (lgrpid == lpProcs->lgrpid)
3597 lcid = strtoulW( szNumber, NULL, 16 );
3599 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3600 * '00000437 ;Georgian'
3601 * At present we only pass the LCID string.
3605 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3608 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3610 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3612 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3620 /* Finished enumerating this key */
3623 /* Enumerate alternate sorts also */
3624 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3629 bContinue = FALSE; /* Finished both keys */
3642 /******************************************************************************
3643 * EnumLanguageGroupLocalesA (KERNEL32.@)
3645 * Call a users function for every locale in a language group available on the system.
3648 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3649 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3650 * dwFlags [I] Reserved, set to 0
3651 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3655 * Failure: FALSE. Use GetLastError() to determine the cause.
3657 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3658 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3660 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3662 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3664 callbacks.procA = pLangGrpLcEnumProc;
3665 callbacks.procW = NULL;
3666 callbacks.dwFlags = dwFlags;
3667 callbacks.lgrpid = lgrpid;
3668 callbacks.lParam = lParam;
3670 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3673 /******************************************************************************
3674 * EnumLanguageGroupLocalesW (KERNEL32.@)
3676 * See EnumLanguageGroupLocalesA.
3678 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3679 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3681 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3683 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3685 callbacks.procA = NULL;
3686 callbacks.procW = pLangGrpLcEnumProc;
3687 callbacks.dwFlags = dwFlags;
3688 callbacks.lgrpid = lgrpid;
3689 callbacks.lParam = lParam;
3691 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3694 /******************************************************************************
3695 * EnumSystemGeoID (KERNEL32.@)
3697 * Call a users function for every location available on the system.
3700 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3701 * reserved [I] Reserved, set to 0
3702 * pGeoEnumProc [I] Callback function to call for each location
3706 * Failure: FALSE. Use GetLastError() to determine the cause.
3708 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3710 static const WCHAR szCountryCodeValueName[] = {
3711 'C','o','u','n','t','r','y','C','o','d','e','\0'
3717 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3719 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3721 SetLastError(ERROR_INVALID_PARAMETER);
3725 hKey = NLS_RegOpenKey( 0, szCountryListName );
3727 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3729 BOOL bContinue = TRUE;
3731 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3735 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3737 TRACE("Got geoid %d\n", dwGeoId);
3739 if (!pGeoEnumProc( dwGeoId ))
3758 /******************************************************************************
3759 * InvalidateNLSCache (KERNEL32.@)
3761 * Invalidate the cache of NLS values.
3770 BOOL WINAPI InvalidateNLSCache(void)
3776 /******************************************************************************
3777 * GetUserGeoID (KERNEL32.@)
3779 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3781 GEOID ret = GEOID_NOT_AVAILABLE;
3782 static const WCHAR geoW[] = {'G','e','o',0};
3783 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3784 WCHAR bufferW[40], *end;
3786 HANDLE hkey, hSubkey = 0;
3787 UNICODE_STRING keyW;
3788 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3789 RtlInitUnicodeString( &keyW, nationW );
3790 count = sizeof(bufferW);
3792 if(!(hkey = create_registry_key())) return ret;
3795 case GEOCLASS_NATION:
3796 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3798 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3799 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3800 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3803 case GEOCLASS_REGION:
3804 FIXME("GEOCLASS_REGION not handled yet\n");
3809 if (hSubkey) NtClose(hSubkey);
3813 /******************************************************************************
3814 * SetUserGeoID (KERNEL32.@)
3816 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3818 static const WCHAR geoW[] = {'G','e','o',0};
3819 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3820 static const WCHAR formatW[] = {'%','i',0};
3821 UNICODE_STRING nameW,keyW;
3823 OBJECT_ATTRIBUTES attr;
3826 if(!(hkey = create_registry_key())) return FALSE;
3828 attr.Length = sizeof(attr);
3829 attr.RootDirectory = hkey;
3830 attr.ObjectName = &nameW;
3831 attr.Attributes = 0;
3832 attr.SecurityDescriptor = NULL;
3833 attr.SecurityQualityOfService = NULL;
3834 RtlInitUnicodeString( &nameW, geoW );
3835 RtlInitUnicodeString( &keyW, nationW );
3837 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3840 NtClose(attr.RootDirectory);
3844 sprintfW(bufferW, formatW, GeoID);
3845 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3846 NtClose(attr.RootDirectory);
3855 UILANGUAGE_ENUMPROCA procA;
3856 UILANGUAGE_ENUMPROCW procW;
3860 } ENUM_UILANG_CALLBACK;
3862 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3863 LPCSTR name, WORD LangID, LONG_PTR lParam )
3865 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3868 sprintf(buf, "%08x", (UINT)LangID);
3869 return enum_uilang->u.procA( buf, enum_uilang->param );
3872 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3873 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3875 static const WCHAR formatW[] = {'%','0','8','x',0};
3876 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3879 sprintfW( buf, formatW, (UINT)LangID );
3880 return enum_uilang->u.procW( buf, enum_uilang->param );
3883 /******************************************************************************
3884 * EnumUILanguagesA (KERNEL32.@)
3886 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3888 ENUM_UILANG_CALLBACK enum_uilang;
3890 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3892 if(!pUILangEnumProc) {
3893 SetLastError(ERROR_INVALID_PARAMETER);
3897 SetLastError(ERROR_INVALID_FLAGS);
3901 enum_uilang.u.procA = pUILangEnumProc;
3902 enum_uilang.flags = dwFlags;
3903 enum_uilang.param = lParam;
3905 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3906 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3907 (LONG_PTR)&enum_uilang);
3911 /******************************************************************************
3912 * EnumUILanguagesW (KERNEL32.@)
3914 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3916 ENUM_UILANG_CALLBACK enum_uilang;
3918 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3921 if(!pUILangEnumProc) {
3922 SetLastError(ERROR_INVALID_PARAMETER);
3926 SetLastError(ERROR_INVALID_FLAGS);
3930 enum_uilang.u.procW = pUILangEnumProc;
3931 enum_uilang.flags = dwFlags;
3932 enum_uilang.param = lParam;
3934 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3935 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3936 (LONG_PTR)&enum_uilang);
3940 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
3941 int cchData, LANGID language)
3943 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3947 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
3948 int cchData, LANGID language)
3950 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3954 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
3958 TRACE("%p, %d\n", localename, buffersize);
3960 userlcid = GetUserDefaultLCID();
3961 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
3964 /******************************************************************************
3965 * NormalizeString (KERNEL32.@)
3967 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
3968 LPWSTR lpDstString, INT cwDstLength)
3970 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
3971 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3975 /******************************************************************************
3976 * IsNormalizedString (KERNEL32.@)
3978 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
3980 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
3981 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3995 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
3999 delta /= (firsttime ? DAMP : 2);
4000 delta += delta/numpoints;
4002 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
4004 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
4007 /******************************************************************************
4008 * IdnToAscii (KERNEL32.@)
4009 * Implementation of Punycode based on RFC 3492.
4011 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4012 LPWSTR lpASCIICharStr, INT cchASCIIChar)
4014 static const WCHAR prefixW[] = {'x','n','-','-'};
4017 INT i, label_start, label_end, norm_len, out_label, out = 0;
4019 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4020 lpASCIICharStr, cchASCIIChar);
4022 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
4025 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
4027 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4030 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4031 cchUnicodeChar, norm_str, norm_len);
4033 HeapFree(GetProcessHeap(), 0, norm_str);
4037 for(label_start=0; label_start<norm_len;) {
4038 INT n = INIT_N, bias = INIT_BIAS;
4039 INT delta = 0, b = 0, h;
4042 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4043 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4044 if(norm_str[i] < 0x80)
4048 if(b == label_end-label_start) {
4049 if(label_end < norm_len)
4051 if(!lpASCIICharStr) {
4053 }else if(out+b <= cchASCIIChar) {
4054 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4057 HeapFree(GetProcessHeap(), 0, norm_str);
4058 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4061 label_start = label_end+1;
4065 if(!lpASCIICharStr) {
4066 out += 5+b; /* strlen(xn--...-) */
4067 }else if(out+5+b <= cchASCIIChar) {
4068 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4070 for(i=label_start; i<label_end; i++)
4071 if(norm_str[i] < 0x80)
4072 lpASCIICharStr[out++] = norm_str[i];
4073 lpASCIICharStr[out++] = '-';
4075 HeapFree(GetProcessHeap(), 0, norm_str);
4076 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4082 for(h=b; h<label_end-label_start;) {
4083 INT m = 0xffff, q, k;
4085 for(i=label_start; i<label_end; i++) {
4086 if(norm_str[i]>=n && m>norm_str[i])
4089 delta += (m-n)*(h+1);
4092 for(i=label_start; i<label_end; i++) {
4093 if(norm_str[i] < n) {
4095 }else if(norm_str[i] == n) {
4096 for(q=delta, k=BASE; ; k+=BASE) {
4097 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4098 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4099 if(!lpASCIICharStr) {
4101 }else if(out+1 <= cchASCIIChar) {
4102 lpASCIICharStr[out++] = disp<='z'-'a' ?
4103 'a'+disp : '0'+disp-'z'+'a'-1;
4105 HeapFree(GetProcessHeap(), 0, norm_str);
4106 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4113 bias = adapt(delta, h+1, h==b);
4122 if(out-out_label > 63) {
4123 HeapFree(GetProcessHeap(), 0, norm_str);
4124 SetLastError(ERROR_INVALID_NAME);
4128 if(label_end < norm_len) {
4129 if(!lpASCIICharStr) {
4131 }else if(out+1 <= cchASCIIChar) {
4132 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4134 HeapFree(GetProcessHeap(), 0, norm_str);
4135 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4139 label_start = label_end+1;
4142 HeapFree(GetProcessHeap(), 0, norm_str);
4146 /******************************************************************************
4147 * IdnToNameprepUnicode (KERNEL32.@)
4149 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4150 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4159 extern const unsigned short nameprep_char_type[];
4160 extern const WCHAR nameprep_mapping[];
4163 WCHAR buf[64], *map_str, norm_str[64], ch;
4164 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4165 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4167 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4168 lpNameprepCharStr, cchNameprepChar);
4170 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4171 SetLastError(ERROR_INVALID_FLAGS);
4175 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4176 SetLastError(ERROR_INVALID_PARAMETER);
4180 if(cchUnicodeChar == -1)
4181 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4182 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4183 SetLastError(ERROR_INVALID_NAME);
4187 for(label_start=0; label_start<cchUnicodeChar;) {
4189 for(i=label_start; i<cchUnicodeChar; i++) {
4190 ch = lpUnicodeCharStr[i];
4192 if(i!=cchUnicodeChar-1 && !ch) {
4193 SetLastError(ERROR_INVALID_NAME);
4196 /* check if ch is one of label separators defined in RFC3490 */
4197 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4205 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4207 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4208 || (ch>='0' && ch<='9') || ch=='-')
4211 SetLastError(ERROR_INVALID_NAME);
4215 /* last label may be empty */
4216 if(label_start==label_end && ch) {
4217 SetLastError(ERROR_INVALID_NAME);
4221 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4222 lpUnicodeCharStr[label_end-1]=='-')) {
4223 SetLastError(ERROR_INVALID_NAME);
4228 /* maximal label length is 63 characters */
4229 if(label_end-label_start > 63) {
4230 SetLastError(ERROR_INVALID_NAME);
4233 if(label_end < cchUnicodeChar)
4236 if(!lpNameprepCharStr) {
4237 out += label_end-label_start;
4238 }else if(out+label_end-label_start <= cchNameprepChar) {
4239 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4240 (label_end-label_start)*sizeof(WCHAR));
4241 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4242 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4243 out += label_end-label_start;
4245 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4249 label_start = label_end;
4254 for(i=label_start; i<label_end; i++) {
4255 ch = lpUnicodeCharStr[i];
4256 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4257 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4259 if(!ptr[0]) map_len++;
4260 else if(!ptr[1]) map_len++;
4261 else if(!ptr[2]) map_len += 2;
4262 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4264 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4265 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4267 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4274 for(i=label_start; i<label_end; i++) {
4275 ch = lpUnicodeCharStr[i];
4276 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4277 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4280 map_str[map_len++] = ch;
4282 map_str[map_len++] = ptr[0];
4284 map_str[map_len++] = ptr[0];
4285 map_str[map_len++] = ptr[1];
4286 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4287 map_str[map_len++] = ptr[0];
4288 map_str[map_len++] = ptr[1];
4289 map_str[map_len++] = ptr[2];
4293 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4294 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4296 HeapFree(GetProcessHeap(), 0, map_str);
4298 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4299 SetLastError(ERROR_INVALID_NAME);
4303 if(label_end < cchUnicodeChar) {
4304 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4308 if(!lpNameprepCharStr) {
4310 }else if(out+norm_len <= cchNameprepChar) {
4311 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4314 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4318 have_bidi_ral = prohibit_bidi_ral = FALSE;
4320 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4322 for(i=0; i<norm_len; i++) {
4324 flags = get_table_entry( nameprep_char_type, ch );
4327 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4328 : ERROR_NO_UNICODE_TRANSLATION);
4332 if(flags & BIDI_RAL)
4333 have_bidi_ral = TRUE;
4335 prohibit_bidi_ral = TRUE;
4340 flags = get_table_entry( nameprep_char_type, ch );
4341 if((flags & BIDI_RAL) == 0)
4342 prohibit_bidi_ral = TRUE;
4344 ch = norm_str[norm_len-1];
4345 flags = get_table_entry( nameprep_char_type, ch );
4346 if((flags & BIDI_RAL) == 0)
4347 prohibit_bidi_ral = TRUE;
4350 if(have_bidi_ral && prohibit_bidi_ral) {
4351 SetLastError(ERROR_INVALID_NAME);
4355 label_start = label_end;
4361 /******************************************************************************
4362 * IdnToUnicode (KERNEL32.@)
4364 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4365 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4367 extern const unsigned short nameprep_char_type[];
4369 INT i, label_start, label_end, out_label, out = 0;
4372 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4373 lpUnicodeCharStr, cchUnicodeChar);
4375 for(label_start=0; label_start<cchASCIIChar;) {
4376 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4379 for(i=label_start; i<cchASCIIChar; i++) {
4380 ch = lpASCIICharStr[i];
4382 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4383 SetLastError(ERROR_INVALID_NAME);
4392 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4394 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4395 || (ch>='0' && ch<='9') || ch=='-')
4398 SetLastError(ERROR_INVALID_NAME);
4402 /* last label may be empty */
4403 if(label_start==label_end && ch) {
4404 SetLastError(ERROR_INVALID_NAME);
4408 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4409 lpUnicodeCharStr[label_end-1]=='-')) {
4410 SetLastError(ERROR_INVALID_NAME);
4413 if(label_end-label_start > 63) {
4414 SetLastError(ERROR_INVALID_NAME);
4418 if(label_end-label_start<4 ||
4419 tolowerW(lpASCIICharStr[label_start])!='x' ||
4420 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4421 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4422 if(label_end < cchUnicodeChar)
4425 if(!lpUnicodeCharStr) {
4426 out += label_end-label_start;
4427 }else if(out+label_end-label_start <= cchUnicodeChar) {
4428 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4429 (label_end-label_start)*sizeof(WCHAR));
4430 out += label_end-label_start;
4432 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4436 label_start = label_end;
4440 if(delim == label_start+3)
4442 if(!lpUnicodeCharStr) {
4443 out += delim-label_start-4;
4444 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4445 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4446 (delim-label_start-4)*sizeof(WCHAR));
4447 out += delim-label_start-4;
4449 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4452 if(out != out_label)
4455 for(i=delim; i<label_end;) {
4458 for(k=BASE; ; k+=BASE) {
4459 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4460 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4461 SetLastError(ERROR_INVALID_NAME);
4464 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4466 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4471 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4472 n += pos/(out-out_label+1);
4473 pos %= out-out_label+1;
4475 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4476 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4477 SetLastError(ERROR_INVALID_NAME);
4480 if(!lpUnicodeCharStr) {
4482 }else if(out+1 <= cchASCIIChar) {
4483 memmove(lpUnicodeCharStr+out_label+pos+1,
4484 lpUnicodeCharStr+out_label+pos,
4485 (out-out_label-pos)*sizeof(WCHAR));
4486 lpUnicodeCharStr[out_label+pos] = n;
4489 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4495 if(out-out_label > 63) {
4496 SetLastError(ERROR_INVALID_NAME);
4500 if(label_end < cchASCIIChar) {
4501 if(!lpUnicodeCharStr) {
4503 }else if(out+1 <= cchUnicodeChar) {
4504 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4506 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4510 label_start = label_end+1;