windowscodecs: Implement MetadataReaderInfo_GetSpecVersion.
[wine/multimedia.git] / dlls / kernel32 / locale.c
blobe467735647cafcc2b16c421fc122c470819b4625
1 /*
2 * Locale support
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
7 * Copyright 2002 Alexandre Julliard for CodeWeavers
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFBundle.h>
37 # include <CoreFoundation/CFLocale.h>
38 # include <CoreFoundation/CFString.h>
39 #endif
41 #include "ntstatus.h"
42 #define WIN32_NO_STATUS
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winuser.h" /* for RT_STRINGW */
46 #include "winternl.h"
47 #include "wine/unicode.h"
48 #include "winnls.h"
49 #include "winerror.h"
50 #include "winver.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;
82 UINT codepage;
83 } charset_names[] =
85 { "BIG5", 950 },
86 { "CP1250", 1250 },
87 { "CP1251", 1251 },
88 { "CP1252", 1252 },
89 { "CP1253", 1253 },
90 { "CP1254", 1254 },
91 { "CP1255", 1255 },
92 { "CP1256", 1256 },
93 { "CP1257", 1257 },
94 { "CP1258", 1258 },
95 { "CP932", 932 },
96 { "CP936", 936 },
97 { "CP949", 949 },
98 { "CP950", 950 },
99 { "EUCJP", 20932 },
100 { "GB2312", 936 },
101 { "IBM037", 37 },
102 { "IBM1026", 1026 },
103 { "IBM424", 424 },
104 { "IBM437", 437 },
105 { "IBM500", 500 },
106 { "IBM850", 850 },
107 { "IBM852", 852 },
108 { "IBM855", 855 },
109 { "IBM857", 857 },
110 { "IBM860", 860 },
111 { "IBM861", 861 },
112 { "IBM862", 862 },
113 { "IBM863", 863 },
114 { "IBM864", 864 },
115 { "IBM865", 865 },
116 { "IBM866", 866 },
117 { "IBM869", 869 },
118 { "IBM874", 874 },
119 { "IBM875", 875 },
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 },
134 { "KOI8R", 20866 },
135 { "KOI8U", 21866 },
136 { "UTF8", CP_UTF8 }
140 struct locale_name
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++;
170 n--;
172 if (n) *dst = 0;
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 /***********************************************************************
181 * get_lcid_codepage
183 * Retrieve the ANSI codepage for a given locale.
185 static inline UINT get_lcid_codepage( LCID lcid )
187 UINT ret;
188 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
189 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
190 return ret;
194 /***********************************************************************
195 * get_codepage_table
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 */
205 switch(codepage)
207 case CP_ACP:
208 return ansi_cptable;
209 case CP_OEMCP:
210 return oem_cptable;
211 case CP_MACCP:
212 return mac_cptable;
213 case CP_UTF7:
214 case CP_UTF8:
215 break;
216 case CP_THREAD_ACP:
217 if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return ansi_cptable;
218 codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale );
219 /* fall through */
220 default:
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 );
225 break;
227 return ret;
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 /***********************************************************************
241 * find_charset
243 static UINT find_charset( const WCHAR *name )
245 const struct charset_entry *entry;
246 char charset_name[16];
247 size_t i, j;
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];
252 charset_name[j] = 0;
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;
258 return 0;
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;
269 WCHAR buffer[128];
270 int matches = 0;
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 */
283 goto done;
287 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
288 buffer, sizeof(buffer)/sizeof(WCHAR) ))
289 return TRUE;
290 if (strcmpW( buffer, data->lang )) return TRUE;
291 matches++; /* language name matched */
293 if (data->country)
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++;
307 if (data->codepage)
309 UINT unix_cp;
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 */
319 done:
320 if (matches > data->matches)
322 data->lcid = lcid;
323 data->matches = matches;
325 return (data->matches < 4); /* no need to continue for perfect match */
329 /***********************************************************************
330 * parse_locale_name
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};
344 WCHAR *p;
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 );
350 name->matches = 0;
351 name->codepage = 0;
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 */
360 return;
362 strcpyW( name->win_name, name->lang );
364 else if (*p == '-') /* Windows format */
366 strcpyW( name->win_name, name->lang );
367 *p++ = 0;
368 name->country = p;
369 if (!(p = strpbrkW( p, winsepW ))) goto done;
370 if (*p == '-')
372 *p++ = 0;
373 name->script = name->country;
374 name->country = p;
375 if (!(p = strpbrkW( p, winsepW ))) goto done;
377 *p++ = 0;
378 name->modifier = p;
380 else /* Unix format */
382 if (*p == '_')
384 *p++ = 0;
385 name->country = p;
386 p = strpbrkW( p, sepW + 2 );
388 if (p && *p == '.')
390 *p++ = 0;
391 name->charset = p;
392 p = strchrW( p, '@' );
394 if (p)
396 *p++ = 0;
397 name->modifier = p;
400 if (name->charset)
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 );
410 if (name->country)
412 p = name->win_name + strlenW(name->win_name);
413 *p++ = '-';
414 strcpyW( p, name->country );
417 done:
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)
434 LCID default_id = 0;
436 switch(lctype & 0xffff)
438 case LOCALE_SSORTNAME:
439 default_id = lcid_LC_COLLATE;
440 break;
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;
449 break;
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;
468 break;
470 case LOCALE_IDIGITS:
471 case LOCALE_IDIGITSUBSTITUTION:
472 case LOCALE_ILZERO:
473 case LOCALE_INEGNUMBER:
474 case LOCALE_SDECIMAL:
475 case LOCALE_SGROUPING:
476 case LOCALE_SNAN:
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;
484 break;
486 case LOCALE_ICALENDARTYPE:
487 case LOCALE_ICENTURY:
488 case LOCALE_IDATE:
489 case LOCALE_IDAYLZERO:
490 case LOCALE_IFIRSTDAYOFWEEK:
491 case LOCALE_IFIRSTWEEKOFYEAR:
492 case LOCALE_ILDATE:
493 case LOCALE_IMONLZERO:
494 case LOCALE_IOPTIONALCALENDAR:
495 case LOCALE_ITIME:
496 case LOCALE_ITIMEMARKPOSN:
497 case LOCALE_ITLZERO:
498 case LOCALE_S1159:
499 case LOCALE_S2359:
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:
520 case LOCALE_SDATE:
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:
551 case LOCALE_STIME:
552 case LOCALE_STIMEFORMAT:
553 case LOCALE_SYEARMONTH:
554 default_id = lcid_LC_TIME;
555 break;
557 case LOCALE_IPAPERSIZE:
558 default_id = lcid_LC_PAPER;
559 break;
561 case LOCALE_IMEASURE:
562 default_id = lcid_LC_MEASUREMENT;
563 break;
565 case LOCALE_ICOUNTRY:
566 default_id = lcid_LC_TELEPHONE;
567 break;
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:
596 return TRUE;
597 default:
598 return FALSE;
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;
620 attr.Attributes = 0;
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 );
633 return hkey;
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 };
643 WCHAR bufferW[40];
644 UNICODE_STRING nameW;
645 DWORD count, i;
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 );
668 return TRUE;
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};
691 static const struct
693 LPCWSTR name;
694 USHORT value;
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,
702 LOCALE_SCOUNTRY,
703 LOCALE_SLIST };
704 static const LCTYPE lc_monetary_values[] = {
705 LOCALE_SCURRENCY,
706 LOCALE_ICURRENCY,
707 LOCALE_INEGCURR,
708 LOCALE_ICURRDIGITS,
709 LOCALE_ILZERO,
710 LOCALE_SMONDECIMALSEP,
711 LOCALE_SMONGROUPING,
712 LOCALE_SMONTHOUSANDSEP };
713 static const LCTYPE lc_numeric_values[] = {
714 LOCALE_SDECIMAL,
715 LOCALE_STHOUSAND,
716 LOCALE_IDIGITS,
717 LOCALE_IDIGITSUBSTITUTION,
718 LOCALE_SNATIVEDIGITS,
719 LOCALE_INEGNUMBER,
720 LOCALE_SNEGATIVESIGN,
721 LOCALE_SPOSITIVESIGN,
722 LOCALE_SGROUPING };
723 static const LCTYPE lc_time_values[] = {
724 LOCALE_S1159,
725 LOCALE_S2359,
726 LOCALE_STIME,
727 LOCALE_ITIME,
728 LOCALE_ITLZERO,
729 LOCALE_SSHORTDATE,
730 LOCALE_SLONGDATE,
731 LOCALE_SDATE,
732 LOCALE_ITIMEMARKPOSN,
733 LOCALE_ICALENDARTYPE,
734 LOCALE_IFIRSTDAYOFWEEK,
735 LOCALE_IFIRSTWEEKOFYEAR,
736 LOCALE_STIMEFORMAT,
737 LOCALE_SYEARMONTH,
738 LOCALE_IDATE };
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;
744 WCHAR bufferW[80];
745 DWORD count, i;
746 HANDLE hkey;
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;
775 HANDLE nls_key;
776 DWORD len = 14;
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;
784 NtClose( nls_key );
785 len++;
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) );
798 NtClose( nls_key );
802 NtClose( hkey );
806 /***********************************************************************
807 * setup_unix_locales
809 static UINT setup_unix_locales(void)
811 struct locale_name locale_name;
812 WCHAR buffer[128], ctype_buff[128];
813 char *locale;
814 UINT unix_cp = 0;
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; \
834 else { \
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) ); \
840 } while (0)
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 );
847 #ifdef LC_PAPER
848 GET_UNIX_LOCALE( LC_PAPER );
849 #endif
850 #ifdef LC_MEASUREMENT
851 GET_UNIX_LOCALE( LC_MEASUREMENT );
852 #endif
853 #ifdef LC_TELEPHONE
854 GET_UNIX_LOCALE( LC_TELEPHONE );
855 #endif
857 #undef GET_UNIX_LOCALE
859 return unix_cp;
863 /***********************************************************************
864 * GetUserDefaultLangID (KERNEL32.@)
866 * Get the default language Id for the current user.
868 * PARAMS
869 * None.
871 * RETURNS
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.
885 * PARAMS
886 * None.
888 * RETURNS
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.
902 * PARAMS
903 * None.
905 * RETURNS
906 * The current LCID of the default locale for the current user.
908 LCID WINAPI GetUserDefaultLCID(void)
910 LCID lcid;
911 NtQueryDefaultLocale( TRUE, &lcid );
912 return lcid;
916 /***********************************************************************
917 * GetSystemDefaultLCID (KERNEL32.@)
919 * Get the default locale Id for the system.
921 * PARAMS
922 * None.
924 * RETURNS
925 * The current LCID of the default locale for the system.
927 LCID WINAPI GetSystemDefaultLCID(void)
929 LCID lcid;
930 NtQueryDefaultLocale( FALSE, &lcid );
931 return lcid;
935 /***********************************************************************
936 * GetUserDefaultUILanguage (KERNEL32.@)
938 * Get the default user interface language Id for the current user.
940 * PARAMS
941 * None.
943 * RETURNS
944 * The current LANGID of the default UI language for the current user.
946 LANGID WINAPI GetUserDefaultUILanguage(void)
948 LANGID lang;
949 NtQueryDefaultUILanguage( &lang );
950 return lang;
954 /***********************************************************************
955 * GetSystemDefaultUILanguage (KERNEL32.@)
957 * Get the default user interface language Id for the system.
959 * PARAMS
960 * None.
962 * RETURNS
963 * The current LANGID of the default UI language for the system. This is
964 * typically the same language used during the installation process.
966 LANGID WINAPI GetSystemDefaultUILanguage(void)
968 LANGID lang;
969 NtQueryInstallUILanguage( &lang );
970 return lang;
974 /***********************************************************************
975 * LocaleNameToLCID (KERNEL32.@)
977 LCID WINAPI LocaleNameToLCID( LPCWSTR name, DWORD flags )
979 struct locale_name locale_name;
981 if (flags) FIXME( "unsupported flags %x\n", flags );
983 if (name == LOCALE_NAME_USER_DEFAULT)
984 return GetUserDefaultLCID();
986 /* string parsing */
987 parse_locale_name( name, &locale_name );
989 TRACE( "found lcid %x for %s, matches %d\n",
990 locale_name.lcid, debugstr_w(name), locale_name.matches );
992 if (!locale_name.matches)
993 WARN( "locale %s not recognized, defaulting to English\n", debugstr_w(name) );
994 else if (locale_name.matches == 1)
995 WARN( "locale %s not recognized, defaulting to %s\n",
996 debugstr_w(name), debugstr_w(locale_name.lang) );
998 return locale_name.lcid;
1002 /***********************************************************************
1003 * LCIDToLocaleName (KERNEL32.@)
1005 INT WINAPI LCIDToLocaleName( LCID lcid, LPWSTR name, INT count, DWORD flags )
1007 if (flags) FIXME( "unsupported flags %x\n", flags );
1009 return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
1013 /******************************************************************************
1014 * get_locale_value_name
1016 * Gets the registry value name for a given lctype.
1018 static const WCHAR *get_locale_value_name( DWORD lctype )
1020 static const WCHAR iCalendarTypeW[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
1021 static const WCHAR iCountryW[] = {'i','C','o','u','n','t','r','y',0};
1022 static const WCHAR iCurrDigitsW[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
1023 static const WCHAR iCurrencyW[] = {'i','C','u','r','r','e','n','c','y',0};
1024 static const WCHAR iDateW[] = {'i','D','a','t','e',0};
1025 static const WCHAR iDigitsW[] = {'i','D','i','g','i','t','s',0};
1026 static const WCHAR iFirstDayOfWeekW[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
1027 static const WCHAR iFirstWeekOfYearW[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
1028 static const WCHAR iLDateW[] = {'i','L','D','a','t','e',0};
1029 static const WCHAR iLZeroW[] = {'i','L','Z','e','r','o',0};
1030 static const WCHAR iMeasureW[] = {'i','M','e','a','s','u','r','e',0};
1031 static const WCHAR iNegCurrW[] = {'i','N','e','g','C','u','r','r',0};
1032 static const WCHAR iNegNumberW[] = {'i','N','e','g','N','u','m','b','e','r',0};
1033 static const WCHAR iPaperSizeW[] = {'i','P','a','p','e','r','S','i','z','e',0};
1034 static const WCHAR iTLZeroW[] = {'i','T','L','Z','e','r','o',0};
1035 static const WCHAR iTimePrefixW[] = {'i','T','i','m','e','P','r','e','f','i','x',0};
1036 static const WCHAR iTimeW[] = {'i','T','i','m','e',0};
1037 static const WCHAR s1159W[] = {'s','1','1','5','9',0};
1038 static const WCHAR s2359W[] = {'s','2','3','5','9',0};
1039 static const WCHAR sCountryW[] = {'s','C','o','u','n','t','r','y',0};
1040 static const WCHAR sCurrencyW[] = {'s','C','u','r','r','e','n','c','y',0};
1041 static const WCHAR sDateW[] = {'s','D','a','t','e',0};
1042 static const WCHAR sDecimalW[] = {'s','D','e','c','i','m','a','l',0};
1043 static const WCHAR sGroupingW[] = {'s','G','r','o','u','p','i','n','g',0};
1044 static const WCHAR sLanguageW[] = {'s','L','a','n','g','u','a','g','e',0};
1045 static const WCHAR sListW[] = {'s','L','i','s','t',0};
1046 static const WCHAR sLongDateW[] = {'s','L','o','n','g','D','a','t','e',0};
1047 static const WCHAR sMonDecimalSepW[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
1048 static const WCHAR sMonGroupingW[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
1049 static const WCHAR sMonThousandSepW[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
1050 static const WCHAR sNativeDigitsW[] = {'s','N','a','t','i','v','e','D','i','g','i','t','s',0};
1051 static const WCHAR sNegativeSignW[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
1052 static const WCHAR sPositiveSignW[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
1053 static const WCHAR sShortDateW[] = {'s','S','h','o','r','t','D','a','t','e',0};
1054 static const WCHAR sThousandW[] = {'s','T','h','o','u','s','a','n','d',0};
1055 static const WCHAR sTimeFormatW[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
1056 static const WCHAR sTimeW[] = {'s','T','i','m','e',0};
1057 static const WCHAR sYearMonthW[] = {'s','Y','e','a','r','M','o','n','t','h',0};
1058 static const WCHAR NumShapeW[] = {'N','u','m','s','h','a','p','e',0};
1060 switch (lctype)
1062 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
1063 * the values are stored in the registry, confirmed under Windows.
1065 case LOCALE_ICALENDARTYPE: return iCalendarTypeW;
1066 case LOCALE_ICURRDIGITS: return iCurrDigitsW;
1067 case LOCALE_ICURRENCY: return iCurrencyW;
1068 case LOCALE_IDIGITS: return iDigitsW;
1069 case LOCALE_IFIRSTDAYOFWEEK: return iFirstDayOfWeekW;
1070 case LOCALE_IFIRSTWEEKOFYEAR: return iFirstWeekOfYearW;
1071 case LOCALE_ILZERO: return iLZeroW;
1072 case LOCALE_IMEASURE: return iMeasureW;
1073 case LOCALE_INEGCURR: return iNegCurrW;
1074 case LOCALE_INEGNUMBER: return iNegNumberW;
1075 case LOCALE_IPAPERSIZE: return iPaperSizeW;
1076 case LOCALE_ITIME: return iTimeW;
1077 case LOCALE_S1159: return s1159W;
1078 case LOCALE_S2359: return s2359W;
1079 case LOCALE_SCURRENCY: return sCurrencyW;
1080 case LOCALE_SDATE: return sDateW;
1081 case LOCALE_SDECIMAL: return sDecimalW;
1082 case LOCALE_SGROUPING: return sGroupingW;
1083 case LOCALE_SLIST: return sListW;
1084 case LOCALE_SLONGDATE: return sLongDateW;
1085 case LOCALE_SMONDECIMALSEP: return sMonDecimalSepW;
1086 case LOCALE_SMONGROUPING: return sMonGroupingW;
1087 case LOCALE_SMONTHOUSANDSEP: return sMonThousandSepW;
1088 case LOCALE_SNEGATIVESIGN: return sNegativeSignW;
1089 case LOCALE_SPOSITIVESIGN: return sPositiveSignW;
1090 case LOCALE_SSHORTDATE: return sShortDateW;
1091 case LOCALE_STHOUSAND: return sThousandW;
1092 case LOCALE_STIME: return sTimeW;
1093 case LOCALE_STIMEFORMAT: return sTimeFormatW;
1094 case LOCALE_SYEARMONTH: return sYearMonthW;
1096 /* The following are not listed under MSDN as supported,
1097 * but seem to be used and also stored in the registry.
1099 case LOCALE_ICOUNTRY: return iCountryW;
1100 case LOCALE_IDATE: return iDateW;
1101 case LOCALE_ILDATE: return iLDateW;
1102 case LOCALE_ITLZERO: return iTLZeroW;
1103 case LOCALE_SCOUNTRY: return sCountryW;
1104 case LOCALE_SABBREVLANGNAME: return sLanguageW;
1106 /* The following are used in XP and later */
1107 case LOCALE_IDIGITSUBSTITUTION: return NumShapeW;
1108 case LOCALE_SNATIVEDIGITS: return sNativeDigitsW;
1109 case LOCALE_ITIMEMARKPOSN: return iTimePrefixW;
1111 return NULL;
1115 /******************************************************************************
1116 * get_registry_locale_info
1118 * Retrieve user-modified locale info from the registry.
1119 * Return length, 0 on error, -1 if not found.
1121 static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
1123 DWORD size;
1124 INT ret;
1125 HANDLE hkey;
1126 NTSTATUS status;
1127 UNICODE_STRING nameW;
1128 KEY_VALUE_PARTIAL_INFORMATION *info;
1129 static const int info_size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
1131 if (!(hkey = create_registry_key())) return -1;
1133 RtlInitUnicodeString( &nameW, value );
1134 size = info_size + len * sizeof(WCHAR);
1136 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1138 NtClose( hkey );
1139 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1140 return 0;
1143 status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, info, size, &size );
1145 if (!status)
1147 ret = (size - info_size) / sizeof(WCHAR);
1148 /* append terminating null if needed */
1149 if (!ret || ((WCHAR *)info->Data)[ret-1])
1151 if (ret < len || !buffer) ret++;
1152 else
1154 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1155 ret = 0;
1158 if (ret && buffer)
1160 memcpy( buffer, info->Data, (ret-1) * sizeof(WCHAR) );
1161 buffer[ret-1] = 0;
1164 else if (status == STATUS_BUFFER_OVERFLOW && !buffer)
1166 ret = (size - info_size) / sizeof(WCHAR) + 1;
1168 else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1170 ret = -1;
1172 else
1174 SetLastError( RtlNtStatusToDosError(status) );
1175 ret = 0;
1177 NtClose( hkey );
1178 HeapFree( GetProcessHeap(), 0, info );
1179 return ret;
1183 /******************************************************************************
1184 * GetLocaleInfoA (KERNEL32.@)
1186 * Get information about an aspect of a locale.
1188 * PARAMS
1189 * lcid [I] LCID of the locale
1190 * lctype [I] LCTYPE_ flags from "winnls.h"
1191 * buffer [O] Destination for the information
1192 * len [I] Length of buffer in characters
1194 * RETURNS
1195 * Success: The size of the data requested. If buffer is non-NULL, it is filled
1196 * with the information.
1197 * Failure: 0. Use GetLastError() to determine the cause.
1199 * NOTES
1200 * - LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
1201 * - The string returned is NUL terminated, except for LOCALE_FONTSIGNATURE,
1202 * which is a bit string.
1204 INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
1206 WCHAR *bufferW;
1207 INT lenW, ret;
1209 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1211 if (len < 0 || (len && !buffer))
1213 SetLastError( ERROR_INVALID_PARAMETER );
1214 return 0;
1216 if (lctype & LOCALE_RETURN_GENITIVE_NAMES )
1218 SetLastError( ERROR_INVALID_FLAGS );
1219 return 0;
1222 if (!len) buffer = NULL;
1224 if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
1226 if (!(bufferW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
1228 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1229 return 0;
1231 if ((ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW )))
1233 if ((lctype & LOCALE_RETURN_NUMBER) ||
1234 ((lctype & ~LOCALE_LOCALEINFOFLAGSMASK) == LOCALE_FONTSIGNATURE))
1236 /* it's not an ASCII string, just bytes */
1237 ret *= sizeof(WCHAR);
1238 if (buffer)
1240 if (ret <= len) memcpy( buffer, bufferW, ret );
1241 else
1243 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1244 ret = 0;
1248 else
1250 UINT codepage = CP_ACP;
1251 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1252 ret = WideCharToMultiByte( codepage, 0, bufferW, ret, buffer, len, NULL, NULL );
1255 HeapFree( GetProcessHeap(), 0, bufferW );
1256 return ret;
1260 /******************************************************************************
1261 * GetLocaleInfoW (KERNEL32.@)
1263 * See GetLocaleInfoA.
1265 INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
1267 LANGID lang_id;
1268 HRSRC hrsrc;
1269 HGLOBAL hmem;
1270 INT ret;
1271 UINT lcflags;
1272 const WCHAR *p;
1273 unsigned int i;
1275 if (len < 0 || (len && !buffer))
1277 SetLastError( ERROR_INVALID_PARAMETER );
1278 return 0;
1280 if (lctype & LOCALE_RETURN_GENITIVE_NAMES &&
1281 !is_genitive_name_supported( lctype ))
1283 SetLastError( ERROR_INVALID_FLAGS );
1284 return 0;
1287 if (!len) buffer = NULL;
1289 lcid = convert_default_lcid( lcid, lctype );
1291 lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
1292 lctype &= 0xffff;
1294 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
1296 /* first check for overrides in the registry */
1298 if (!(lcflags & LOCALE_NOUSEROVERRIDE) &&
1299 lcid == convert_default_lcid( LOCALE_USER_DEFAULT, lctype ))
1301 const WCHAR *value = get_locale_value_name(lctype);
1303 if (value)
1305 if (lcflags & LOCALE_RETURN_NUMBER)
1307 WCHAR tmp[16];
1308 ret = get_registry_locale_info( value, tmp, sizeof(tmp)/sizeof(WCHAR) );
1309 if (ret > 0)
1311 WCHAR *end;
1312 UINT number = strtolW( tmp, &end, 10 );
1313 if (*end) /* invalid number */
1315 SetLastError( ERROR_INVALID_FLAGS );
1316 return 0;
1318 ret = sizeof(UINT)/sizeof(WCHAR);
1319 if (!buffer) return ret;
1320 if (ret > len)
1322 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1323 return 0;
1325 memcpy( buffer, &number, sizeof(number) );
1328 else ret = get_registry_locale_info( value, buffer, len );
1330 if (ret != -1) return ret;
1334 /* now load it from kernel resources */
1336 lang_id = LANGIDFROMLCID( lcid );
1338 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
1339 if (SUBLANGID(lang_id) == SUBLANG_NEUTRAL)
1340 lang_id = MAKELANGID(PRIMARYLANGID(lang_id), SUBLANG_DEFAULT);
1342 if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
1343 ULongToPtr((lctype >> 4) + 1), lang_id )))
1345 SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
1346 return 0;
1348 if (!(hmem = LoadResource( kernel32_handle, hrsrc )))
1349 return 0;
1351 p = LockResource( hmem );
1352 for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
1354 if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT)/sizeof(WCHAR);
1355 else if (is_genitive_name_supported( lctype ) && *p)
1357 /* genitive form's stored after a null separator from a nominative */
1358 for (i = 1; i <= *p; i++) if (!p[i]) break;
1360 if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
1362 ret = *p - i + 1;
1363 p += i;
1365 else ret = i;
1367 else
1368 ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
1370 if (!buffer) return ret;
1372 if (ret > len)
1374 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1375 return 0;
1378 if (lcflags & LOCALE_RETURN_NUMBER)
1380 UINT number;
1381 WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
1382 if (!tmp) return 0;
1383 memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
1384 tmp[*p] = 0;
1385 number = strtolW( tmp, &end, 10 );
1386 if (!*end)
1387 memcpy( buffer, &number, sizeof(number) );
1388 else /* invalid number */
1390 SetLastError( ERROR_INVALID_FLAGS );
1391 ret = 0;
1393 HeapFree( GetProcessHeap(), 0, tmp );
1395 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
1396 lcid, lctype, buffer, len, number );
1398 else
1400 memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
1401 if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
1403 TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
1404 lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
1406 return ret;
1410 /******************************************************************************
1411 * SetLocaleInfoA [KERNEL32.@]
1413 * Set information about an aspect of a locale.
1415 * PARAMS
1416 * lcid [I] LCID of the locale
1417 * lctype [I] LCTYPE_ flags from "winnls.h"
1418 * data [I] Information to set
1420 * RETURNS
1421 * Success: TRUE. The information given will be returned by GetLocaleInfoA()
1422 * whenever it is called without LOCALE_NOUSEROVERRIDE.
1423 * Failure: FALSE. Use GetLastError() to determine the cause.
1425 * NOTES
1426 * - Values are only be set for the current user locale; the system locale
1427 * settings cannot be changed.
1428 * - Any settings changed by this call are lost when the locale is changed by
1429 * the control panel (in Wine, this happens every time you change LANG).
1430 * - The native implementation of this function does not check that lcid matches
1431 * the current user locale, and simply sets the new values. Wine warns you in
1432 * this case, but behaves the same.
1434 BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
1436 UINT codepage = CP_ACP;
1437 WCHAR *strW;
1438 DWORD len;
1439 BOOL ret;
1441 if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
1443 if (!data)
1445 SetLastError( ERROR_INVALID_PARAMETER );
1446 return FALSE;
1448 len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
1449 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1451 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1452 return FALSE;
1454 MultiByteToWideChar( codepage, 0, data, -1, strW, len );
1455 ret = SetLocaleInfoW( lcid, lctype, strW );
1456 HeapFree( GetProcessHeap(), 0, strW );
1457 return ret;
1461 /******************************************************************************
1462 * SetLocaleInfoW (KERNEL32.@)
1464 * See SetLocaleInfoA.
1466 BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
1468 const WCHAR *value;
1469 static const WCHAR intlW[] = {'i','n','t','l',0 };
1470 UNICODE_STRING valueW;
1471 NTSTATUS status;
1472 HANDLE hkey;
1474 lctype &= 0xffff;
1475 value = get_locale_value_name( lctype );
1477 if (!data || !value)
1479 SetLastError( ERROR_INVALID_PARAMETER );
1480 return FALSE;
1483 if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
1485 SetLastError( ERROR_INVALID_FLAGS );
1486 return FALSE;
1489 TRACE("setting %x (%s) to %s\n", lctype, debugstr_w(value), debugstr_w(data) );
1491 /* FIXME: should check that data to set is sane */
1493 /* FIXME: profile functions should map to registry */
1494 WriteProfileStringW( intlW, value, data );
1496 if (!(hkey = create_registry_key())) return FALSE;
1497 RtlInitUnicodeString( &valueW, value );
1498 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, data, (strlenW(data)+1)*sizeof(WCHAR) );
1500 if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
1502 /* Set I-value from S value */
1503 WCHAR *lpD, *lpM, *lpY;
1504 WCHAR szBuff[2];
1506 lpD = strrchrW(data, 'd');
1507 lpM = strrchrW(data, 'M');
1508 lpY = strrchrW(data, 'y');
1510 if (lpD <= lpM)
1512 szBuff[0] = '1'; /* D-M-Y */
1514 else
1516 if (lpY <= lpM)
1517 szBuff[0] = '2'; /* Y-M-D */
1518 else
1519 szBuff[0] = '0'; /* M-D-Y */
1522 szBuff[1] = '\0';
1524 if (lctype == LOCALE_SSHORTDATE)
1525 lctype = LOCALE_IDATE;
1526 else
1527 lctype = LOCALE_ILDATE;
1529 value = get_locale_value_name( lctype );
1531 WriteProfileStringW( intlW, value, szBuff );
1533 RtlInitUnicodeString( &valueW, value );
1534 status = NtSetValueKey( hkey, &valueW, 0, REG_SZ, szBuff, sizeof(szBuff) );
1537 NtClose( hkey );
1539 if (status) SetLastError( RtlNtStatusToDosError(status) );
1540 return !status;
1544 /******************************************************************************
1545 * GetACP (KERNEL32.@)
1547 * Get the current Ansi code page Id for the system.
1549 * PARAMS
1550 * None.
1552 * RETURNS
1553 * The current Ansi code page identifier for the system.
1555 UINT WINAPI GetACP(void)
1557 assert( ansi_cptable );
1558 return ansi_cptable->info.codepage;
1562 /******************************************************************************
1563 * SetCPGlobal (KERNEL32.@)
1565 * Set the current Ansi code page Id for the system.
1567 * PARAMS
1568 * acp [I] code page ID to be the new ACP.
1570 * RETURNS
1571 * The previous ACP.
1573 UINT WINAPI SetCPGlobal( UINT acp )
1575 UINT ret = GetACP();
1576 const union cptable *new_cptable = wine_cp_get_table( acp );
1578 if (new_cptable) ansi_cptable = new_cptable;
1579 return ret;
1583 /***********************************************************************
1584 * GetOEMCP (KERNEL32.@)
1586 * Get the current OEM code page Id for the system.
1588 * PARAMS
1589 * None.
1591 * RETURNS
1592 * The current OEM code page identifier for the system.
1594 UINT WINAPI GetOEMCP(void)
1596 assert( oem_cptable );
1597 return oem_cptable->info.codepage;
1601 /***********************************************************************
1602 * IsValidCodePage (KERNEL32.@)
1604 * Determine if a given code page identifier is valid.
1606 * PARAMS
1607 * codepage [I] Code page Id to verify.
1609 * RETURNS
1610 * TRUE, If codepage is valid and available on the system,
1611 * FALSE otherwise.
1613 BOOL WINAPI IsValidCodePage( UINT codepage )
1615 switch(codepage) {
1616 case CP_UTF7:
1617 case CP_UTF8:
1618 return TRUE;
1619 default:
1620 return wine_cp_get_table( codepage ) != NULL;
1625 /***********************************************************************
1626 * IsDBCSLeadByteEx (KERNEL32.@)
1628 * Determine if a character is a lead byte in a given code page.
1630 * PARAMS
1631 * codepage [I] Code page for the test.
1632 * testchar [I] Character to test
1634 * RETURNS
1635 * TRUE, if testchar is a lead byte in codepage,
1636 * FALSE otherwise.
1638 BOOL WINAPI IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
1640 const union cptable *table = get_codepage_table( codepage );
1641 return table && wine_is_dbcs_leadbyte( table, testchar );
1645 /***********************************************************************
1646 * IsDBCSLeadByte (KERNEL32.@)
1647 * IsDBCSLeadByte (KERNEL.207)
1649 * Determine if a character is a lead byte.
1651 * PARAMS
1652 * testchar [I] Character to test
1654 * RETURNS
1655 * TRUE, if testchar is a lead byte in the ANSI code page,
1656 * FALSE otherwise.
1658 BOOL WINAPI IsDBCSLeadByte( BYTE testchar )
1660 if (!ansi_cptable) return FALSE;
1661 return wine_is_dbcs_leadbyte( ansi_cptable, testchar );
1665 /***********************************************************************
1666 * GetCPInfo (KERNEL32.@)
1668 * Get information about a code page.
1670 * PARAMS
1671 * codepage [I] Code page number
1672 * cpinfo [O] Destination for code page information
1674 * RETURNS
1675 * Success: TRUE. cpinfo is updated with the information about codepage.
1676 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1678 BOOL WINAPI GetCPInfo( UINT codepage, LPCPINFO cpinfo )
1680 const union cptable *table;
1682 if (!cpinfo)
1684 SetLastError( ERROR_INVALID_PARAMETER );
1685 return FALSE;
1688 if (!(table = get_codepage_table( codepage )))
1690 switch(codepage)
1692 case CP_UTF7:
1693 case CP_UTF8:
1694 cpinfo->DefaultChar[0] = 0x3f;
1695 cpinfo->DefaultChar[1] = 0;
1696 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1697 cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
1698 return TRUE;
1701 SetLastError( ERROR_INVALID_PARAMETER );
1702 return FALSE;
1704 if (table->info.def_char & 0xff00)
1706 cpinfo->DefaultChar[0] = (table->info.def_char & 0xff00) >> 8;
1707 cpinfo->DefaultChar[1] = table->info.def_char & 0x00ff;
1709 else
1711 cpinfo->DefaultChar[0] = table->info.def_char & 0xff;
1712 cpinfo->DefaultChar[1] = 0;
1714 if ((cpinfo->MaxCharSize = table->info.char_size) == 2)
1715 memcpy( cpinfo->LeadByte, table->dbcs.lead_bytes, sizeof(cpinfo->LeadByte) );
1716 else
1717 cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
1719 return TRUE;
1722 /***********************************************************************
1723 * GetCPInfoExA (KERNEL32.@)
1725 * Get extended information about a code page.
1727 * PARAMS
1728 * codepage [I] Code page number
1729 * dwFlags [I] Reserved, must to 0.
1730 * cpinfo [O] Destination for code page information
1732 * RETURNS
1733 * Success: TRUE. cpinfo is updated with the information about codepage.
1734 * Failure: FALSE, if codepage is invalid or cpinfo is NULL.
1736 BOOL WINAPI GetCPInfoExA( UINT codepage, DWORD dwFlags, LPCPINFOEXA cpinfo )
1738 CPINFOEXW cpinfoW;
1740 if (!GetCPInfoExW( codepage, dwFlags, &cpinfoW ))
1741 return FALSE;
1743 /* the layout is the same except for CodePageName */
1744 memcpy(cpinfo, &cpinfoW, sizeof(CPINFOEXA));
1745 WideCharToMultiByte(CP_ACP, 0, cpinfoW.CodePageName, -1, cpinfo->CodePageName, sizeof(cpinfo->CodePageName), NULL, NULL);
1746 return TRUE;
1749 /***********************************************************************
1750 * GetCPInfoExW (KERNEL32.@)
1752 * Unicode version of GetCPInfoExA.
1754 BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD dwFlags, LPCPINFOEXW cpinfo )
1756 if (!GetCPInfo( codepage, (LPCPINFO)cpinfo ))
1757 return FALSE;
1759 switch(codepage)
1761 case CP_UTF7:
1763 static const WCHAR utf7[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','7',')',0};
1765 cpinfo->CodePage = CP_UTF7;
1766 cpinfo->UnicodeDefaultChar = 0x3f;
1767 strcpyW(cpinfo->CodePageName, utf7);
1768 break;
1771 case CP_UTF8:
1773 static const WCHAR utf8[] = {'U','n','i','c','o','d','e',' ','(','U','T','F','-','8',')',0};
1775 cpinfo->CodePage = CP_UTF8;
1776 cpinfo->UnicodeDefaultChar = 0x3f;
1777 strcpyW(cpinfo->CodePageName, utf8);
1778 break;
1781 default:
1783 const union cptable *table = get_codepage_table( codepage );
1785 cpinfo->CodePage = table->info.codepage;
1786 cpinfo->UnicodeDefaultChar = table->info.def_unicode_char;
1787 MultiByteToWideChar( CP_ACP, 0, table->info.name, -1, cpinfo->CodePageName,
1788 sizeof(cpinfo->CodePageName)/sizeof(WCHAR));
1789 break;
1792 return TRUE;
1795 /***********************************************************************
1796 * EnumSystemCodePagesA (KERNEL32.@)
1798 * Call a user defined function for every code page installed on the system.
1800 * PARAMS
1801 * lpfnCodePageEnum [I] User CODEPAGE_ENUMPROC to call with each found code page
1802 * flags [I] Reserved, set to 0.
1804 * RETURNS
1805 * TRUE, If all code pages have been enumerated, or
1806 * FALSE if lpfnCodePageEnum returned FALSE to stop the enumeration.
1808 BOOL WINAPI EnumSystemCodePagesA( CODEPAGE_ENUMPROCA lpfnCodePageEnum, DWORD flags )
1810 const union cptable *table;
1811 char buffer[10];
1812 int index = 0;
1814 for (;;)
1816 if (!(table = wine_cp_enum_table( index++ ))) break;
1817 sprintf( buffer, "%d", table->info.codepage );
1818 if (!lpfnCodePageEnum( buffer )) break;
1820 return TRUE;
1824 /***********************************************************************
1825 * EnumSystemCodePagesW (KERNEL32.@)
1827 * See EnumSystemCodePagesA.
1829 BOOL WINAPI EnumSystemCodePagesW( CODEPAGE_ENUMPROCW lpfnCodePageEnum, DWORD flags )
1831 const union cptable *table;
1832 WCHAR buffer[10], *p;
1833 int page, index = 0;
1835 for (;;)
1837 if (!(table = wine_cp_enum_table( index++ ))) break;
1838 p = buffer + sizeof(buffer)/sizeof(WCHAR);
1839 *--p = 0;
1840 page = table->info.codepage;
1843 *--p = '0' + (page % 10);
1844 page /= 10;
1845 } while( page );
1846 if (!lpfnCodePageEnum( p )) break;
1848 return TRUE;
1852 /***********************************************************************
1853 * MultiByteToWideChar (KERNEL32.@)
1855 * Convert a multibyte character string into a Unicode string.
1857 * PARAMS
1858 * page [I] Codepage character set to convert from
1859 * flags [I] Character mapping flags
1860 * src [I] Source string buffer
1861 * srclen [I] Length of src (in bytes), or -1 if src is NUL terminated
1862 * dst [O] Destination buffer
1863 * dstlen [I] Length of dst (in WCHARs), or 0 to compute the required length
1865 * RETURNS
1866 * Success: If dstlen > 0, the number of characters written to dst.
1867 * If dstlen == 0, the number of characters needed to perform the
1868 * conversion. In both cases the count includes the terminating NUL.
1869 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1870 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1871 * and dstlen != 0; ERROR_INVALID_PARAMETER, if an invalid parameter
1872 * is passed, and ERROR_NO_UNICODE_TRANSLATION if no translation is
1873 * possible for src.
1875 INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
1876 LPWSTR dst, INT dstlen )
1878 const union cptable *table;
1879 int ret;
1881 if (!src || !srclen || (!dst && dstlen))
1883 SetLastError( ERROR_INVALID_PARAMETER );
1884 return 0;
1887 if (srclen < 0) srclen = strlen(src) + 1;
1889 switch(page)
1891 case CP_SYMBOL:
1892 if (flags)
1894 SetLastError( ERROR_INVALID_FLAGS );
1895 return 0;
1897 ret = wine_cpsymbol_mbstowcs( src, srclen, dst, dstlen );
1898 break;
1899 case CP_UTF7:
1900 if (flags)
1902 SetLastError( ERROR_INVALID_FLAGS );
1903 return 0;
1905 FIXME("UTF-7 not supported\n");
1906 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1907 return 0;
1908 case CP_UNIXCP:
1909 if (unix_cptable)
1911 ret = wine_cp_mbstowcs( unix_cptable, flags, src, srclen, dst, dstlen );
1912 break;
1914 #ifdef __APPLE__
1915 flags |= MB_COMPOSITE; /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
1916 #endif
1917 /* fall through */
1918 case CP_UTF8:
1919 ret = wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
1920 break;
1921 default:
1922 if (!(table = get_codepage_table( page )))
1924 SetLastError( ERROR_INVALID_PARAMETER );
1925 return 0;
1927 ret = wine_cp_mbstowcs( table, flags, src, srclen, dst, dstlen );
1928 break;
1931 if (ret < 0)
1933 switch(ret)
1935 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
1936 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
1938 ret = 0;
1940 TRACE("cp %d %s -> %s, ret = %d\n",
1941 page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
1942 return ret;
1946 /***********************************************************************
1947 * WideCharToMultiByte (KERNEL32.@)
1949 * Convert a Unicode character string into a multibyte string.
1951 * PARAMS
1952 * page [I] Code page character set to convert to
1953 * flags [I] Mapping Flags (MB_ constants from "winnls.h").
1954 * src [I] Source string buffer
1955 * srclen [I] Length of src (in WCHARs), or -1 if src is NUL terminated
1956 * dst [O] Destination buffer
1957 * dstlen [I] Length of dst (in bytes), or 0 to compute the required length
1958 * defchar [I] Default character to use for conversion if no exact
1959 * conversion can be made
1960 * used [O] Set if default character was used in the conversion
1962 * RETURNS
1963 * Success: If dstlen > 0, the number of characters written to dst.
1964 * If dstlen == 0, number of characters needed to perform the
1965 * conversion. In both cases the count includes the terminating NUL.
1966 * Failure: 0. Use GetLastError() to determine the cause. Possible errors are
1967 * ERROR_INSUFFICIENT_BUFFER, if not enough space is available in dst
1968 * and dstlen != 0, and ERROR_INVALID_PARAMETER, if an invalid
1969 * parameter was given.
1971 INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
1972 LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
1974 const union cptable *table;
1975 int ret, used_tmp;
1977 if (!src || !srclen || (!dst && dstlen))
1979 SetLastError( ERROR_INVALID_PARAMETER );
1980 return 0;
1983 if (srclen < 0) srclen = strlenW(src) + 1;
1985 switch(page)
1987 case CP_SYMBOL:
1988 /* when using CP_SYMBOL, ERROR_INVALID_FLAGS takes precedence */
1989 if (flags)
1991 SetLastError( ERROR_INVALID_FLAGS );
1992 return 0;
1994 if (defchar || used)
1996 SetLastError( ERROR_INVALID_PARAMETER );
1997 return 0;
1999 ret = wine_cpsymbol_wcstombs( src, srclen, dst, dstlen );
2000 break;
2001 case CP_UTF7:
2002 /* when using CP_UTF7, ERROR_INVALID_PARAMETER takes precedence */
2003 if (defchar || used)
2005 SetLastError( ERROR_INVALID_PARAMETER );
2006 return 0;
2008 if (flags)
2010 SetLastError( ERROR_INVALID_FLAGS );
2011 return 0;
2013 FIXME("UTF-7 not supported\n");
2014 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2015 return 0;
2016 case CP_UNIXCP:
2017 if (unix_cptable)
2019 ret = wine_cp_wcstombs( unix_cptable, flags, src, srclen, dst, dstlen,
2020 defchar, used ? &used_tmp : NULL );
2021 break;
2023 /* fall through */
2024 case CP_UTF8:
2025 if (defchar || used)
2027 SetLastError( ERROR_INVALID_PARAMETER );
2028 return 0;
2030 ret = wine_utf8_wcstombs( flags, src, srclen, dst, dstlen );
2031 break;
2032 default:
2033 if (!(table = get_codepage_table( page )))
2035 SetLastError( ERROR_INVALID_PARAMETER );
2036 return 0;
2038 ret = wine_cp_wcstombs( table, flags, src, srclen, dst, dstlen,
2039 defchar, used ? &used_tmp : NULL );
2040 if (used) *used = used_tmp;
2041 break;
2044 if (ret < 0)
2046 switch(ret)
2048 case -1: SetLastError( ERROR_INSUFFICIENT_BUFFER ); break;
2049 case -2: SetLastError( ERROR_NO_UNICODE_TRANSLATION ); break;
2051 ret = 0;
2053 TRACE("cp %d %s -> %s, ret = %d\n",
2054 page, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret);
2055 return ret;
2059 /***********************************************************************
2060 * GetThreadLocale (KERNEL32.@)
2062 * Get the current threads locale.
2064 * PARAMS
2065 * None.
2067 * RETURNS
2068 * The LCID currently associated with the calling thread.
2070 LCID WINAPI GetThreadLocale(void)
2072 LCID ret = NtCurrentTeb()->CurrentLocale;
2073 if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID();
2074 return ret;
2077 /**********************************************************************
2078 * SetThreadLocale (KERNEL32.@)
2080 * Set the current threads locale.
2082 * PARAMS
2083 * lcid [I] LCID of the locale to set
2085 * RETURNS
2086 * Success: TRUE. The threads locale is set to lcid.
2087 * Failure: FALSE. Use GetLastError() to determine the cause.
2089 BOOL WINAPI SetThreadLocale( LCID lcid )
2091 TRACE("(0x%04X)\n", lcid);
2093 lcid = ConvertDefaultLocale(lcid);
2095 if (lcid != GetThreadLocale())
2097 if (!IsValidLocale(lcid, LCID_SUPPORTED))
2099 SetLastError(ERROR_INVALID_PARAMETER);
2100 return FALSE;
2103 NtCurrentTeb()->CurrentLocale = lcid;
2105 return TRUE;
2108 /**********************************************************************
2109 * SetThreadUILanguage (KERNEL32.@)
2111 * Set the current threads UI language.
2113 * PARAMS
2114 * langid [I] LANGID of the language to set, or 0 to use
2115 * the available language which is best supported
2116 * for console applications
2118 * RETURNS
2119 * Success: The return value is the same as the input value.
2120 * Failure: The return value differs from the input value.
2121 * Use GetLastError() to determine the cause.
2123 LANGID WINAPI SetThreadUILanguage( LANGID langid )
2125 TRACE("(0x%04x) stub - returning success\n", langid);
2126 return langid;
2129 /******************************************************************************
2130 * ConvertDefaultLocale (KERNEL32.@)
2132 * Convert a default locale identifier into a real identifier.
2134 * PARAMS
2135 * lcid [I] LCID identifier of the locale to convert
2137 * RETURNS
2138 * lcid unchanged, if not a default locale or its sublanguage is
2139 * not SUBLANG_NEUTRAL.
2140 * GetSystemDefaultLCID(), if lcid == LOCALE_SYSTEM_DEFAULT.
2141 * GetUserDefaultLCID(), if lcid == LOCALE_USER_DEFAULT or LOCALE_NEUTRAL.
2142 * Otherwise, lcid with sublanguage changed to SUBLANG_DEFAULT.
2144 LCID WINAPI ConvertDefaultLocale( LCID lcid )
2146 LANGID langid;
2148 switch (lcid)
2150 case LOCALE_SYSTEM_DEFAULT:
2151 lcid = GetSystemDefaultLCID();
2152 break;
2153 case LOCALE_USER_DEFAULT:
2154 case LOCALE_NEUTRAL:
2155 lcid = GetUserDefaultLCID();
2156 break;
2157 default:
2158 /* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
2159 langid = LANGIDFROMLCID(lcid);
2160 if (SUBLANGID(langid) == SUBLANG_NEUTRAL)
2162 langid = MAKELANGID(PRIMARYLANGID(langid), SUBLANG_DEFAULT);
2163 lcid = MAKELCID(langid, SORTIDFROMLCID(lcid));
2166 return lcid;
2170 /******************************************************************************
2171 * IsValidLocale (KERNEL32.@)
2173 * Determine if a locale is valid.
2175 * PARAMS
2176 * lcid [I] LCID of the locale to check
2177 * flags [I] LCID_SUPPORTED = Valid, LCID_INSTALLED = Valid and installed on the system
2179 * RETURNS
2180 * TRUE, if lcid is valid,
2181 * FALSE, otherwise.
2183 * NOTES
2184 * Wine does not currently make the distinction between supported and installed. All
2185 * languages supported are installed by default.
2187 BOOL WINAPI IsValidLocale( LCID lcid, DWORD flags )
2189 /* check if language is registered in the kernel32 resources */
2190 return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
2191 (LPCWSTR)LOCALE_ILANGUAGE, LANGIDFROMLCID(lcid)) != 0;
2195 static BOOL CALLBACK enum_lang_proc_a( HMODULE hModule, LPCSTR type,
2196 LPCSTR name, WORD LangID, LONG_PTR lParam )
2198 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
2199 char buf[20];
2201 sprintf(buf, "%08x", (UINT)LangID);
2202 return lpfnLocaleEnum( buf );
2205 static BOOL CALLBACK enum_lang_proc_w( HMODULE hModule, LPCWSTR type,
2206 LPCWSTR name, WORD LangID, LONG_PTR lParam )
2208 static const WCHAR formatW[] = {'%','0','8','x',0};
2209 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
2210 WCHAR buf[20];
2211 sprintfW( buf, formatW, (UINT)LangID );
2212 return lpfnLocaleEnum( buf );
2215 /******************************************************************************
2216 * EnumSystemLocalesA (KERNEL32.@)
2218 * Call a users function for each locale available on the system.
2220 * PARAMS
2221 * lpfnLocaleEnum [I] Callback function to call for each locale
2222 * dwFlags [I] LOCALE_SUPPORTED=All supported, LOCALE_INSTALLED=Installed only
2224 * RETURNS
2225 * Success: TRUE.
2226 * Failure: FALSE. Use GetLastError() to determine the cause.
2228 BOOL WINAPI EnumSystemLocalesA( LOCALE_ENUMPROCA lpfnLocaleEnum, DWORD dwFlags )
2230 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2231 EnumResourceLanguagesA( kernel32_handle, (LPSTR)RT_STRING,
2232 (LPCSTR)LOCALE_ILANGUAGE, enum_lang_proc_a,
2233 (LONG_PTR)lpfnLocaleEnum);
2234 return TRUE;
2238 /******************************************************************************
2239 * EnumSystemLocalesW (KERNEL32.@)
2241 * See EnumSystemLocalesA.
2243 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum, DWORD dwFlags )
2245 TRACE("(%p,%08x)\n", lpfnLocaleEnum, dwFlags);
2246 EnumResourceLanguagesW( kernel32_handle, (LPWSTR)RT_STRING,
2247 (LPCWSTR)LOCALE_ILANGUAGE, enum_lang_proc_w,
2248 (LONG_PTR)lpfnLocaleEnum);
2249 return TRUE;
2253 struct enum_locale_ex_data
2255 LOCALE_ENUMPROCEX proc;
2256 DWORD flags;
2257 LPARAM lparam;
2260 static BOOL CALLBACK enum_locale_ex_proc( HMODULE module, LPCWSTR type,
2261 LPCWSTR name, WORD lang, LONG_PTR lparam )
2263 struct enum_locale_ex_data *data = (struct enum_locale_ex_data *)lparam;
2264 WCHAR buffer[256];
2265 DWORD neutral;
2266 unsigned int flags;
2268 GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
2269 buffer, sizeof(buffer) / sizeof(WCHAR) );
2270 if (!GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ),
2271 LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
2272 (LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
2273 neutral = 0;
2274 flags = LOCALE_WINDOWS;
2275 flags |= neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA;
2276 if (data->flags && ~(data->flags & flags)) return TRUE;
2277 return data->proc( buffer, flags, data->lparam );
2280 /******************************************************************************
2281 * EnumSystemLocalesEx (KERNEL32.@)
2283 BOOL WINAPI EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD flags, LPARAM lparam, LPVOID reserved )
2285 struct enum_locale_ex_data data;
2287 if (reserved)
2289 SetLastError( ERROR_INVALID_PARAMETER );
2290 return FALSE;
2292 data.proc = proc;
2293 data.flags = flags;
2294 data.lparam = lparam;
2295 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
2296 (LPCWSTR)MAKEINTRESOURCE((LOCALE_SNAME >> 4) + 1),
2297 enum_locale_ex_proc, (LONG_PTR)&data );
2298 return TRUE;
2302 /***********************************************************************
2303 * VerLanguageNameA (KERNEL32.@)
2305 * Get the name of a language.
2307 * PARAMS
2308 * wLang [I] LANGID of the language
2309 * szLang [O] Destination for the language name
2311 * RETURNS
2312 * Success: The size of the language name. If szLang is non-NULL, it is filled
2313 * with the name.
2314 * Failure: 0. Use GetLastError() to determine the cause.
2317 DWORD WINAPI VerLanguageNameA( DWORD wLang, LPSTR szLang, DWORD nSize )
2319 return GetLocaleInfoA( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2323 /***********************************************************************
2324 * VerLanguageNameW (KERNEL32.@)
2326 * See VerLanguageNameA.
2328 DWORD WINAPI VerLanguageNameW( DWORD wLang, LPWSTR szLang, DWORD nSize )
2330 return GetLocaleInfoW( MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize );
2334 /******************************************************************************
2335 * GetStringTypeW (KERNEL32.@)
2337 * See GetStringTypeA.
2339 BOOL WINAPI GetStringTypeW( DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2341 static const unsigned char type2_map[16] =
2343 C2_NOTAPPLICABLE, /* unassigned */
2344 C2_LEFTTORIGHT, /* L */
2345 C2_RIGHTTOLEFT, /* R */
2346 C2_EUROPENUMBER, /* EN */
2347 C2_EUROPESEPARATOR, /* ES */
2348 C2_EUROPETERMINATOR, /* ET */
2349 C2_ARABICNUMBER, /* AN */
2350 C2_COMMONSEPARATOR, /* CS */
2351 C2_BLOCKSEPARATOR, /* B */
2352 C2_SEGMENTSEPARATOR, /* S */
2353 C2_WHITESPACE, /* WS */
2354 C2_OTHERNEUTRAL, /* ON */
2355 C2_RIGHTTOLEFT, /* AL */
2356 C2_NOTAPPLICABLE, /* NSM */
2357 C2_NOTAPPLICABLE, /* BN */
2358 C2_OTHERNEUTRAL /* LRE, LRO, RLE, RLO, PDF */
2361 if (count == -1) count = strlenW(src) + 1;
2362 switch(type)
2364 case CT_CTYPE1:
2365 while (count--) *chartype++ = get_char_typeW( *src++ ) & 0xfff;
2366 break;
2367 case CT_CTYPE2:
2368 while (count--) *chartype++ = type2_map[get_char_typeW( *src++ ) >> 12];
2369 break;
2370 case CT_CTYPE3:
2372 WARN("CT_CTYPE3: semi-stub.\n");
2373 while (count--)
2375 int c = *src;
2376 WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
2378 type1 = get_char_typeW( *src++ ) & 0xfff;
2379 /* try to construct type3 from type1 */
2380 if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
2381 if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
2382 if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
2383 if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
2384 if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
2385 if ((c>=0x0600)&&(c<=0x06FF)) type3 |= C3_KASHIDA;
2386 if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
2388 if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
2389 if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
2390 if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
2391 if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
2392 if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
2393 if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
2394 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
2395 if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
2397 if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
2398 if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
2399 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
2400 if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
2401 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
2402 if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
2403 *chartype++ = type3;
2405 break;
2407 default:
2408 SetLastError( ERROR_INVALID_PARAMETER );
2409 return FALSE;
2411 return TRUE;
2415 /******************************************************************************
2416 * GetStringTypeExW (KERNEL32.@)
2418 * See GetStringTypeExA.
2420 BOOL WINAPI GetStringTypeExW( LCID locale, DWORD type, LPCWSTR src, INT count, LPWORD chartype )
2422 /* locale is ignored for Unicode */
2423 return GetStringTypeW( type, src, count, chartype );
2427 /******************************************************************************
2428 * GetStringTypeA (KERNEL32.@)
2430 * Get characteristics of the characters making up a string.
2432 * PARAMS
2433 * locale [I] Locale Id for the string
2434 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2435 * src [I] String to analyse
2436 * count [I] Length of src in chars, or -1 if src is NUL terminated
2437 * chartype [O] Destination for the calculated characteristics
2439 * RETURNS
2440 * Success: TRUE. chartype is filled with the requested characteristics of each char
2441 * in src.
2442 * Failure: FALSE. Use GetLastError() to determine the cause.
2444 BOOL WINAPI GetStringTypeA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2446 UINT cp;
2447 INT countW;
2448 LPWSTR srcW;
2449 BOOL ret = FALSE;
2451 if(count == -1) count = strlen(src) + 1;
2453 if (!(cp = get_lcid_codepage( locale )))
2455 FIXME("For locale %04x using current ANSI code page\n", locale);
2456 cp = GetACP();
2459 countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
2460 if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2462 MultiByteToWideChar(cp, 0, src, count, srcW, countW);
2464 * NOTE: the target buffer has 1 word for each CHARACTER in the source
2465 * string, with multibyte characters there maybe be more bytes in count
2466 * than character space in the buffer!
2468 ret = GetStringTypeW(type, srcW, countW, chartype);
2469 HeapFree(GetProcessHeap(), 0, srcW);
2471 return ret;
2474 /******************************************************************************
2475 * GetStringTypeExA (KERNEL32.@)
2477 * Get characteristics of the characters making up a string.
2479 * PARAMS
2480 * locale [I] Locale Id for the string
2481 * type [I] CT_CTYPE1 = classification, CT_CTYPE2 = directionality, CT_CTYPE3 = typographic info
2482 * src [I] String to analyse
2483 * count [I] Length of src in chars, or -1 if src is NUL terminated
2484 * chartype [O] Destination for the calculated characteristics
2486 * RETURNS
2487 * Success: TRUE. chartype is filled with the requested characteristics of each char
2488 * in src.
2489 * Failure: FALSE. Use GetLastError() to determine the cause.
2491 BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LPWORD chartype )
2493 return GetStringTypeA(locale, type, src, count, chartype);
2496 /*************************************************************************
2497 * LCMapStringEx (KERNEL32.@)
2499 * Map characters in a locale sensitive string.
2501 * PARAMS
2502 * name [I] Locale name for the conversion.
2503 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h")
2504 * src [I] String to map
2505 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2506 * dst [O] Destination for mapped string
2507 * dstlen [I] Length of dst in characters
2508 * version [I] reserved, must be NULL
2509 * reserved [I] reserved, must be NULL
2510 * lparam [I] reserved, must be 0
2512 * RETURNS
2513 * Success: The length of the mapped string in dst, including the NUL terminator.
2514 * Failure: 0. Use GetLastError() to determine the cause.
2516 INT WINAPI LCMapStringEx(LPCWSTR name, DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen,
2517 LPNLSVERSIONINFO version, LPVOID reserved, LPARAM lparam)
2519 LPWSTR dst_ptr;
2521 if (version) FIXME("unsupported version structure %p\n", version);
2522 if (reserved) FIXME("unsupported reserved pointer %p\n", reserved);
2523 if (lparam) FIXME("unsupported lparam %lx\n", lparam);
2525 if (!src || !srclen || dstlen < 0)
2527 SetLastError(ERROR_INVALID_PARAMETER);
2528 return 0;
2531 /* mutually exclusive flags */
2532 if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
2533 (flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
2534 (flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
2535 (flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE))
2537 SetLastError(ERROR_INVALID_FLAGS);
2538 return 0;
2541 if (!dstlen) dst = NULL;
2543 if (flags & LCMAP_SORTKEY)
2545 INT ret;
2546 if (src == dst)
2548 SetLastError(ERROR_INVALID_FLAGS);
2549 return 0;
2552 if (srclen < 0) srclen = strlenW(src);
2554 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2555 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2557 ret = wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
2558 if (ret == 0)
2559 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2560 else
2561 ret++;
2562 return ret;
2565 /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
2566 if (flags & SORT_STRINGSORT)
2568 SetLastError(ERROR_INVALID_FLAGS);
2569 return 0;
2572 if (srclen < 0) srclen = strlenW(src) + 1;
2574 TRACE("(%s,0x%08x,%s,%d,%p,%d)\n",
2575 debugstr_w(name), flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2577 if (!dst) /* return required string length */
2579 INT len;
2581 for (len = 0; srclen; src++, srclen--)
2583 WCHAR wch = *src;
2584 /* tests show that win2k just ignores NORM_IGNORENONSPACE,
2585 * and skips white space and punctuation characters for
2586 * NORM_IGNORESYMBOLS.
2588 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2589 continue;
2590 len++;
2592 return len;
2595 if (flags & LCMAP_UPPERCASE)
2597 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2599 WCHAR wch = *src;
2600 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2601 continue;
2602 *dst_ptr++ = toupperW(wch);
2603 dstlen--;
2606 else if (flags & LCMAP_LOWERCASE)
2608 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2610 WCHAR wch = *src;
2611 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2612 continue;
2613 *dst_ptr++ = tolowerW(wch);
2614 dstlen--;
2617 else
2619 if (src == dst)
2621 SetLastError(ERROR_INVALID_FLAGS);
2622 return 0;
2624 for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
2626 WCHAR wch = *src;
2627 if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
2628 continue;
2629 *dst_ptr++ = wch;
2630 dstlen--;
2634 if (srclen)
2636 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2637 return 0;
2640 return dst_ptr - dst;
2643 /*************************************************************************
2644 * LCMapStringW (KERNEL32.@)
2646 * See LCMapStringA.
2648 INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
2649 LPWSTR dst, INT dstlen)
2651 TRACE("(0x%04x,0x%08x,%s,%d,%p,%d)\n",
2652 lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
2654 return LCMapStringEx(NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
2657 /*************************************************************************
2658 * LCMapStringA (KERNEL32.@)
2660 * Map characters in a locale sensitive string.
2662 * PARAMS
2663 * lcid [I] LCID for the conversion.
2664 * flags [I] Flags controlling the mapping (LCMAP_ constants from "winnls.h").
2665 * src [I] String to map
2666 * srclen [I] Length of src in chars, or -1 if src is NUL terminated
2667 * dst [O] Destination for mapped string
2668 * dstlen [I] Length of dst in characters
2670 * RETURNS
2671 * Success: The length of the mapped string in dst, including the NUL terminator.
2672 * Failure: 0. Use GetLastError() to determine the cause.
2674 INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
2675 LPSTR dst, INT dstlen)
2677 WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
2678 LPWSTR srcW, dstW;
2679 INT ret = 0, srclenW, dstlenW;
2680 UINT locale_cp = CP_ACP;
2682 if (!src || !srclen || dstlen < 0)
2684 SetLastError(ERROR_INVALID_PARAMETER);
2685 return 0;
2688 if (!(flags & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2690 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 260);
2691 if (srclenW)
2692 srcW = bufW;
2693 else
2695 srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, NULL, 0);
2696 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2697 if (!srcW)
2699 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2700 return 0;
2702 MultiByteToWideChar(locale_cp, 0, src, srclen, srcW, srclenW);
2705 if (flags & LCMAP_SORTKEY)
2707 if (src == dst)
2709 SetLastError(ERROR_INVALID_FLAGS);
2710 goto map_string_exit;
2712 ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
2713 if (ret == 0)
2714 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2715 else
2716 ret++;
2717 goto map_string_exit;
2720 if (flags & SORT_STRINGSORT)
2722 SetLastError(ERROR_INVALID_FLAGS);
2723 goto map_string_exit;
2726 dstlenW = LCMapStringEx(NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0);
2727 if (!dstlenW)
2728 goto map_string_exit;
2730 dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
2731 if (!dstW)
2733 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2734 goto map_string_exit;
2737 LCMapStringEx(NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0);
2738 ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
2739 HeapFree(GetProcessHeap(), 0, dstW);
2741 map_string_exit:
2742 if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
2743 return ret;
2746 /*************************************************************************
2747 * FoldStringA (KERNEL32.@)
2749 * Map characters in a string.
2751 * PARAMS
2752 * dwFlags [I] Flags controlling chars to map (MAP_ constants from "winnls.h")
2753 * src [I] String to map
2754 * srclen [I] Length of src, or -1 if src is NUL terminated
2755 * dst [O] Destination for mapped string
2756 * dstlen [I] Length of dst, or 0 to find the required length for the mapped string
2758 * RETURNS
2759 * Success: The length of the string written to dst, including the terminating NUL. If
2760 * dstlen is 0, the value returned is the same, but nothing is written to dst,
2761 * and dst may be NULL.
2762 * Failure: 0. Use GetLastError() to determine the cause.
2764 INT WINAPI FoldStringA(DWORD dwFlags, LPCSTR src, INT srclen,
2765 LPSTR dst, INT dstlen)
2767 INT ret = 0, srclenW = 0;
2768 WCHAR *srcW = NULL, *dstW = NULL;
2770 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2772 SetLastError(ERROR_INVALID_PARAMETER);
2773 return 0;
2776 srclenW = MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2777 src, srclen, NULL, 0);
2778 srcW = HeapAlloc(GetProcessHeap(), 0, srclenW * sizeof(WCHAR));
2780 if (!srcW)
2782 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2783 goto FoldStringA_exit;
2786 MultiByteToWideChar(CP_ACP, dwFlags & MAP_COMPOSITE ? MB_COMPOSITE : 0,
2787 src, srclen, srcW, srclenW);
2789 dwFlags = (dwFlags & ~MAP_PRECOMPOSED) | MAP_FOLDCZONE;
2791 ret = FoldStringW(dwFlags, srcW, srclenW, NULL, 0);
2792 if (ret && dstlen)
2794 dstW = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(WCHAR));
2796 if (!dstW)
2798 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2799 goto FoldStringA_exit;
2802 ret = FoldStringW(dwFlags, srcW, srclenW, dstW, ret);
2803 if (!WideCharToMultiByte(CP_ACP, 0, dstW, ret, dst, dstlen, NULL, NULL))
2805 ret = 0;
2806 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2810 HeapFree(GetProcessHeap(), 0, dstW);
2812 FoldStringA_exit:
2813 HeapFree(GetProcessHeap(), 0, srcW);
2814 return ret;
2817 /*************************************************************************
2818 * FoldStringW (KERNEL32.@)
2820 * See FoldStringA.
2822 INT WINAPI FoldStringW(DWORD dwFlags, LPCWSTR src, INT srclen,
2823 LPWSTR dst, INT dstlen)
2825 int ret;
2827 switch (dwFlags & (MAP_COMPOSITE|MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES))
2829 case 0:
2830 if (dwFlags)
2831 break;
2832 /* Fall through for dwFlags == 0 */
2833 case MAP_PRECOMPOSED|MAP_COMPOSITE:
2834 case MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES:
2835 case MAP_COMPOSITE|MAP_EXPAND_LIGATURES:
2836 SetLastError(ERROR_INVALID_FLAGS);
2837 return 0;
2840 if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
2842 SetLastError(ERROR_INVALID_PARAMETER);
2843 return 0;
2846 ret = wine_fold_string(dwFlags, src, srclen, dst, dstlen);
2847 if (!ret)
2848 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2849 return ret;
2852 /******************************************************************************
2853 * CompareStringW (KERNEL32.@)
2855 * See CompareStringA.
2857 INT WINAPI CompareStringW(LCID lcid, DWORD style,
2858 LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
2860 INT ret;
2862 if (!str1 || !str2)
2864 SetLastError(ERROR_INVALID_PARAMETER);
2865 return 0;
2868 if( style & ~(NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS|
2869 SORT_STRINGSORT|NORM_IGNOREKANATYPE|NORM_IGNOREWIDTH|LOCALE_USE_CP_ACP|0x10000000) )
2871 SetLastError(ERROR_INVALID_FLAGS);
2872 return 0;
2875 /* this style is related to diacritics in Arabic, Japanese, and Hebrew */
2876 if (style & 0x10000000)
2877 WARN("Ignoring unknown style 0x10000000\n");
2879 if (len1 < 0) len1 = strlenW(str1);
2880 if (len2 < 0) len2 = strlenW(str2);
2882 ret = wine_compare_string(style, str1, len1, str2, len2);
2884 if (ret) /* need to translate result */
2885 return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
2886 return CSTR_EQUAL;
2889 /******************************************************************************
2890 * CompareStringA (KERNEL32.@)
2892 * Compare two locale sensitive strings.
2894 * PARAMS
2895 * lcid [I] LCID for the comparison
2896 * style [I] Flags for the comparison (NORM_ constants from "winnls.h").
2897 * str1 [I] First string to compare
2898 * len1 [I] Length of str1, or -1 if str1 is NUL terminated
2899 * str2 [I] Second string to compare
2900 * len2 [I] Length of str2, or -1 if str2 is NUL terminated
2902 * RETURNS
2903 * Success: CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN depending on whether
2904 * str1 is less than, equal to or greater than str2 respectively.
2905 * Failure: FALSE. Use GetLastError() to determine the cause.
2907 INT WINAPI CompareStringA(LCID lcid, DWORD style,
2908 LPCSTR str1, INT len1, LPCSTR str2, INT len2)
2910 WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer;
2911 WCHAR *buf2W = buf1W + 130;
2912 LPWSTR str1W, str2W;
2913 INT len1W, len2W, ret;
2914 UINT locale_cp = CP_ACP;
2916 if (!str1 || !str2)
2918 SetLastError(ERROR_INVALID_PARAMETER);
2919 return 0;
2921 if (len1 < 0) len1 = strlen(str1);
2922 if (len2 < 0) len2 = strlen(str2);
2924 if (!(style & LOCALE_USE_CP_ACP)) locale_cp = get_lcid_codepage( lcid );
2926 if (len1)
2928 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 130);
2929 if (len1W)
2930 str1W = buf1W;
2931 else
2933 len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
2934 str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
2935 if (!str1W)
2937 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2938 return 0;
2940 MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
2943 else
2945 len1W = 0;
2946 str1W = buf1W;
2949 if (len2)
2951 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 130);
2952 if (len2W)
2953 str2W = buf2W;
2954 else
2956 len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
2957 str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
2958 if (!str2W)
2960 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2961 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2962 return 0;
2964 MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
2967 else
2969 len2W = 0;
2970 str2W = buf2W;
2973 ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
2975 if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
2976 if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
2977 return ret;
2980 /*************************************************************************
2981 * lstrcmp (KERNEL32.@)
2982 * lstrcmpA (KERNEL32.@)
2984 * Compare two strings using the current thread locale.
2986 * PARAMS
2987 * str1 [I] First string to compare
2988 * str2 [I] Second string to compare
2990 * RETURNS
2991 * Success: A number less than, equal to or greater than 0 depending on whether
2992 * str1 is less than, equal to or greater than str2 respectively.
2993 * Failure: FALSE. Use GetLastError() to determine the cause.
2995 int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
2997 int ret;
2999 if ((str1 == NULL) && (str2 == NULL)) return 0;
3000 if (str1 == NULL) return -1;
3001 if (str2 == NULL) return 1;
3003 ret = CompareStringA(GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3004 if (ret) ret -= 2;
3006 return ret;
3009 /*************************************************************************
3010 * lstrcmpi (KERNEL32.@)
3011 * lstrcmpiA (KERNEL32.@)
3013 * Compare two strings using the current thread locale, ignoring case.
3015 * PARAMS
3016 * str1 [I] First string to compare
3017 * str2 [I] Second string to compare
3019 * RETURNS
3020 * Success: A number less than, equal to or greater than 0 depending on whether
3021 * str2 is less than, equal to or greater than str1 respectively.
3022 * Failure: FALSE. Use GetLastError() to determine the cause.
3024 int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
3026 int ret;
3028 if ((str1 == NULL) && (str2 == NULL)) return 0;
3029 if (str1 == NULL) return -1;
3030 if (str2 == NULL) return 1;
3032 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1);
3033 if (ret) ret -= 2;
3035 return ret;
3038 /*************************************************************************
3039 * lstrcmpW (KERNEL32.@)
3041 * See lstrcmpA.
3043 int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
3045 int ret;
3047 if ((str1 == NULL) && (str2 == NULL)) return 0;
3048 if (str1 == NULL) return -1;
3049 if (str2 == NULL) return 1;
3051 ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
3052 if (ret) ret -= 2;
3054 return ret;
3057 /*************************************************************************
3058 * lstrcmpiW (KERNEL32.@)
3060 * See lstrcmpiA.
3062 int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
3064 int ret;
3066 if ((str1 == NULL) && (str2 == NULL)) return 0;
3067 if (str1 == NULL) return -1;
3068 if (str2 == NULL) return 1;
3070 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
3071 if (ret) ret -= 2;
3073 return ret;
3076 /******************************************************************************
3077 * LOCALE_Init
3079 void LOCALE_Init(void)
3081 extern void CDECL __wine_init_codepages( const union cptable *ansi_cp, const union cptable *oem_cp,
3082 const union cptable *unix_cp );
3084 UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
3086 #ifdef __APPLE__
3087 /* MacOS doesn't set the locale environment variables so we have to do it ourselves */
3088 char user_locale[50];
3090 CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
3091 CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
3092 CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
3093 CFStringRef user_locale_string_ref;
3095 if (user_locale_country_ref)
3097 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@.UTF-8"),
3098 user_locale_lang_ref, user_locale_country_ref);
3100 else
3102 user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.UTF-8"),
3103 user_locale_lang_ref);
3106 CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3108 unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
3109 setenv( "LANG", user_locale, 0 );
3110 TRACE( "setting locale to '%s'\n", user_locale );
3111 #endif /* __APPLE__ */
3113 setlocale( LC_ALL, "" );
3115 unix_cp = setup_unix_locales();
3116 if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
3118 #ifdef __APPLE__
3119 /* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
3120 if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
3122 /* Retrieve the preferred language as chosen in System Preferences. */
3123 /* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
3124 leave things be. */
3125 CFArrayRef all_locales = CFLocaleCopyAvailableLocaleIdentifiers();
3126 CFArrayRef preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL );
3127 CFStringRef user_language_string_ref;
3128 if (preferred_locales && CFArrayGetCount( preferred_locales ) &&
3129 (user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 )) &&
3130 !CFEqual(user_language_string_ref, user_locale_lang_ref))
3132 struct locale_name locale_name;
3133 WCHAR buffer[128];
3134 CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
3135 strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
3136 parse_locale_name( buffer, &locale_name );
3137 lcid_LC_MESSAGES = locale_name.lcid;
3138 TRACE( "setting lcid_LC_MESSAGES to '%s'\n", user_locale );
3140 CFRelease( all_locales );
3141 if (preferred_locales)
3142 CFRelease( preferred_locales );
3145 CFRelease( user_locale_ref );
3146 CFRelease( user_locale_string_ref );
3147 #endif
3149 NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
3150 NtSetDefaultLocale( TRUE, lcid_LC_MESSAGES );
3151 NtSetDefaultLocale( FALSE, lcid_LC_CTYPE );
3153 ansi_cp = get_lcid_codepage( LOCALE_USER_DEFAULT );
3154 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
3155 (LPWSTR)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
3156 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
3157 (LPWSTR)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
3158 if (!unix_cp)
3159 GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
3160 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) );
3162 if (!(ansi_cptable = wine_cp_get_table( ansi_cp )))
3163 ansi_cptable = wine_cp_get_table( 1252 );
3164 if (!(oem_cptable = wine_cp_get_table( oem_cp )))
3165 oem_cptable = wine_cp_get_table( 437 );
3166 if (!(mac_cptable = wine_cp_get_table( mac_cp )))
3167 mac_cptable = wine_cp_get_table( 10000 );
3168 if (unix_cp != CP_UTF8)
3170 if (!(unix_cptable = wine_cp_get_table( unix_cp )))
3171 unix_cptable = wine_cp_get_table( 28591 );
3174 __wine_init_codepages( ansi_cptable, oem_cptable, unix_cptable );
3176 TRACE( "ansi=%03d oem=%03d mac=%03d unix=%03d\n",
3177 ansi_cptable->info.codepage, oem_cptable->info.codepage,
3178 mac_cptable->info.codepage, unix_cp );
3180 setlocale(LC_NUMERIC, "C"); /* FIXME: oleaut32 depends on this */
3183 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName)
3185 UNICODE_STRING keyName;
3186 OBJECT_ATTRIBUTES attr;
3187 HANDLE hkey;
3189 RtlInitUnicodeString( &keyName, szKeyName );
3190 InitializeObjectAttributes(&attr, &keyName, 0, hRootKey, NULL);
3192 if (NtOpenKey( &hkey, KEY_READ, &attr ) != STATUS_SUCCESS)
3193 hkey = 0;
3195 return hkey;
3198 static BOOL NLS_RegEnumSubKey(HANDLE hKey, UINT ulIndex, LPWSTR szKeyName,
3199 ULONG keyNameSize)
3201 BYTE buffer[80];
3202 KEY_BASIC_INFORMATION *info = (KEY_BASIC_INFORMATION *)buffer;
3203 DWORD dwLen;
3205 if (NtEnumerateKey( hKey, ulIndex, KeyBasicInformation, buffer,
3206 sizeof(buffer), &dwLen) != STATUS_SUCCESS ||
3207 info->NameLength > keyNameSize)
3209 return FALSE;
3212 TRACE("info->Name %s info->NameLength %d\n", debugstr_w(info->Name), info->NameLength);
3214 memcpy( szKeyName, info->Name, info->NameLength);
3215 szKeyName[info->NameLength / sizeof(WCHAR)] = '\0';
3217 TRACE("returning %s\n", debugstr_w(szKeyName));
3218 return TRUE;
3221 static BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
3222 LPWSTR szValueName, ULONG valueNameSize,
3223 LPWSTR szValueData, ULONG valueDataSize)
3225 BYTE buffer[80];
3226 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
3227 DWORD dwLen;
3229 if (NtEnumerateValueKey( hKey, ulIndex, KeyValueFullInformation,
3230 buffer, sizeof(buffer), &dwLen ) != STATUS_SUCCESS ||
3231 info->NameLength > valueNameSize ||
3232 info->DataLength > valueDataSize)
3234 return FALSE;
3237 TRACE("info->Name %s info->DataLength %d\n", debugstr_w(info->Name), info->DataLength);
3239 memcpy( szValueName, info->Name, info->NameLength);
3240 szValueName[info->NameLength / sizeof(WCHAR)] = '\0';
3241 memcpy( szValueData, buffer + info->DataOffset, info->DataLength );
3242 szValueData[info->DataLength / sizeof(WCHAR)] = '\0';
3244 TRACE("returning %s %s\n", debugstr_w(szValueName), debugstr_w(szValueData));
3245 return TRUE;
3248 static BOOL NLS_RegGetDword(HANDLE hKey, LPCWSTR szValueName, DWORD *lpVal)
3250 BYTE buffer[128];
3251 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
3252 DWORD dwSize = sizeof(buffer);
3253 UNICODE_STRING valueName;
3255 RtlInitUnicodeString( &valueName, szValueName );
3257 TRACE("%p, %s\n", hKey, debugstr_w(szValueName));
3258 if (NtQueryValueKey( hKey, &valueName, KeyValuePartialInformation,
3259 buffer, dwSize, &dwSize ) == STATUS_SUCCESS &&
3260 info->DataLength == sizeof(DWORD))
3262 memcpy(lpVal, info->Data, sizeof(DWORD));
3263 return TRUE;
3266 return FALSE;
3269 static BOOL NLS_GetLanguageGroupName(LGRPID lgrpid, LPWSTR szName, ULONG nameSize)
3271 LANGID langId;
3272 LPCWSTR szResourceName = MAKEINTRESOURCEW(((lgrpid + 0x2000) >> 4) + 1);
3273 HRSRC hResource;
3274 BOOL bRet = FALSE;
3276 /* FIXME: Is it correct to use the system default langid? */
3277 langId = GetSystemDefaultLangID();
3279 if (SUBLANGID(langId) == SUBLANG_NEUTRAL)
3280 langId = MAKELANGID( PRIMARYLANGID(langId), SUBLANG_DEFAULT );
3282 hResource = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING, szResourceName, langId );
3284 if (hResource)
3286 HGLOBAL hResDir = LoadResource( kernel32_handle, hResource );
3288 if (hResDir)
3290 ULONG iResourceIndex = lgrpid & 0xf;
3291 LPCWSTR lpResEntry = LockResource( hResDir );
3292 ULONG i;
3294 for (i = 0; i < iResourceIndex; i++)
3295 lpResEntry += *lpResEntry + 1;
3297 if (*lpResEntry < nameSize)
3299 memcpy( szName, lpResEntry + 1, *lpResEntry * sizeof(WCHAR) );
3300 szName[*lpResEntry] = '\0';
3301 bRet = TRUE;
3305 FreeResource( hResource );
3307 return bRet;
3310 /* Registry keys for NLS related information */
3312 static const WCHAR szCountryListName[] = {
3313 'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
3314 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
3315 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3316 'T','e','l','e','p','h','o','n','y','\\',
3317 'C','o','u','n','t','r','y',' ','L','i','s','t','\0'
3321 /* Callback function ptrs for EnumSystemLanguageGroupsA/W */
3322 typedef struct
3324 LANGUAGEGROUP_ENUMPROCA procA;
3325 LANGUAGEGROUP_ENUMPROCW procW;
3326 DWORD dwFlags;
3327 LONG_PTR lParam;
3328 } ENUMLANGUAGEGROUP_CALLBACKS;
3330 /* Internal implementation of EnumSystemLanguageGroupsA/W */
3331 static BOOL NLS_EnumSystemLanguageGroups(ENUMLANGUAGEGROUP_CALLBACKS *lpProcs)
3333 WCHAR szNumber[10], szValue[4];
3334 HANDLE hKey;
3335 BOOL bContinue = TRUE;
3336 ULONG ulIndex = 0;
3338 if (!lpProcs)
3340 SetLastError(ERROR_INVALID_PARAMETER);
3341 return FALSE;
3344 switch (lpProcs->dwFlags)
3346 case 0:
3347 /* Default to LGRPID_INSTALLED */
3348 lpProcs->dwFlags = LGRPID_INSTALLED;
3349 /* Fall through... */
3350 case LGRPID_INSTALLED:
3351 case LGRPID_SUPPORTED:
3352 break;
3353 default:
3354 SetLastError(ERROR_INVALID_FLAGS);
3355 return FALSE;
3358 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3360 if (!hKey)
3361 FIXME("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3363 while (bContinue)
3365 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3366 szValue, sizeof(szValue) ))
3368 BOOL bInstalled = szValue[0] == '1' ? TRUE : FALSE;
3369 LGRPID lgrpid = strtoulW( szNumber, NULL, 16 );
3371 TRACE("grpid %s (%sinstalled)\n", debugstr_w(szNumber),
3372 bInstalled ? "" : "not ");
3374 if (lpProcs->dwFlags == LGRPID_SUPPORTED || bInstalled)
3376 WCHAR szGrpName[48];
3378 if (!NLS_GetLanguageGroupName( lgrpid, szGrpName, sizeof(szGrpName) / sizeof(WCHAR) ))
3379 szGrpName[0] = '\0';
3381 if (lpProcs->procW)
3382 bContinue = lpProcs->procW( lgrpid, szNumber, szGrpName, lpProcs->dwFlags,
3383 lpProcs->lParam );
3384 else
3386 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3387 char szGrpNameA[48];
3389 /* FIXME: MSDN doesn't say which code page the W->A translation uses,
3390 * or whether the language names are ever localised. Assume CP_ACP.
3393 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3394 WideCharToMultiByte(CP_ACP, 0, szGrpName, -1, szGrpNameA, sizeof(szGrpNameA), 0, 0);
3396 bContinue = lpProcs->procA( lgrpid, szNumberA, szGrpNameA, lpProcs->dwFlags,
3397 lpProcs->lParam );
3401 ulIndex++;
3403 else
3404 bContinue = FALSE;
3406 if (!bContinue)
3407 break;
3410 if (hKey)
3411 NtClose( hKey );
3413 return TRUE;
3416 /******************************************************************************
3417 * EnumSystemLanguageGroupsA (KERNEL32.@)
3419 * Call a users function for each language group available on the system.
3421 * PARAMS
3422 * pLangGrpEnumProc [I] Callback function to call for each language group
3423 * dwFlags [I] LGRPID_SUPPORTED=All Supported, LGRPID_INSTALLED=Installed only
3424 * lParam [I] User parameter to pass to pLangGrpEnumProc
3426 * RETURNS
3427 * Success: TRUE.
3428 * Failure: FALSE. Use GetLastError() to determine the cause.
3430 BOOL WINAPI EnumSystemLanguageGroupsA(LANGUAGEGROUP_ENUMPROCA pLangGrpEnumProc,
3431 DWORD dwFlags, LONG_PTR lParam)
3433 ENUMLANGUAGEGROUP_CALLBACKS procs;
3435 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3437 procs.procA = pLangGrpEnumProc;
3438 procs.procW = NULL;
3439 procs.dwFlags = dwFlags;
3440 procs.lParam = lParam;
3442 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3445 /******************************************************************************
3446 * EnumSystemLanguageGroupsW (KERNEL32.@)
3448 * See EnumSystemLanguageGroupsA.
3450 BOOL WINAPI EnumSystemLanguageGroupsW(LANGUAGEGROUP_ENUMPROCW pLangGrpEnumProc,
3451 DWORD dwFlags, LONG_PTR lParam)
3453 ENUMLANGUAGEGROUP_CALLBACKS procs;
3455 TRACE("(%p,0x%08X,0x%08lX)\n", pLangGrpEnumProc, dwFlags, lParam);
3457 procs.procA = NULL;
3458 procs.procW = pLangGrpEnumProc;
3459 procs.dwFlags = dwFlags;
3460 procs.lParam = lParam;
3462 return NLS_EnumSystemLanguageGroups( pLangGrpEnumProc ? &procs : NULL);
3465 /******************************************************************************
3466 * IsValidLanguageGroup (KERNEL32.@)
3468 * Determine if a language group is supported and/or installed.
3470 * PARAMS
3471 * lgrpid [I] Language Group Id (LGRPID_ values from "winnls.h")
3472 * dwFlags [I] LGRPID_SUPPORTED=Supported, LGRPID_INSTALLED=Installed
3474 * RETURNS
3475 * TRUE, if lgrpid is supported and/or installed, according to dwFlags.
3476 * FALSE otherwise.
3478 BOOL WINAPI IsValidLanguageGroup(LGRPID lgrpid, DWORD dwFlags)
3480 static const WCHAR szFormat[] = { '%','x','\0' };
3481 WCHAR szValueName[16], szValue[2];
3482 BOOL bSupported = FALSE, bInstalled = FALSE;
3483 HANDLE hKey;
3486 switch (dwFlags)
3488 case LGRPID_INSTALLED:
3489 case LGRPID_SUPPORTED:
3491 hKey = NLS_RegOpenKey( 0, szLangGroupsKeyName );
3493 sprintfW( szValueName, szFormat, lgrpid );
3495 if (NLS_RegGetDword( hKey, szValueName, (LPDWORD)szValue ))
3497 bSupported = TRUE;
3499 if (szValue[0] == '1')
3500 bInstalled = TRUE;
3503 if (hKey)
3504 NtClose( hKey );
3506 break;
3509 if ((dwFlags == LGRPID_SUPPORTED && bSupported) ||
3510 (dwFlags == LGRPID_INSTALLED && bInstalled))
3511 return TRUE;
3513 return FALSE;
3516 /* Callback function ptrs for EnumLanguageGrouplocalesA/W */
3517 typedef struct
3519 LANGGROUPLOCALE_ENUMPROCA procA;
3520 LANGGROUPLOCALE_ENUMPROCW procW;
3521 DWORD dwFlags;
3522 LGRPID lgrpid;
3523 LONG_PTR lParam;
3524 } ENUMLANGUAGEGROUPLOCALE_CALLBACKS;
3526 /* Internal implementation of EnumLanguageGrouplocalesA/W */
3527 static BOOL NLS_EnumLanguageGroupLocales(ENUMLANGUAGEGROUPLOCALE_CALLBACKS *lpProcs)
3529 static const WCHAR szAlternateSortsKeyName[] = {
3530 'A','l','t','e','r','n','a','t','e',' ','S','o','r','t','s','\0'
3532 WCHAR szNumber[10], szValue[4];
3533 HANDLE hKey;
3534 BOOL bContinue = TRUE, bAlternate = FALSE;
3535 LGRPID lgrpid;
3536 ULONG ulIndex = 1; /* Ignore default entry of 1st key */
3538 if (!lpProcs || !lpProcs->lgrpid || lpProcs->lgrpid > LGRPID_ARMENIAN)
3540 SetLastError(ERROR_INVALID_PARAMETER);
3541 return FALSE;
3544 if (lpProcs->dwFlags)
3546 SetLastError(ERROR_INVALID_FLAGS);
3547 return FALSE;
3550 hKey = NLS_RegOpenKey( 0, szLocaleKeyName );
3552 if (!hKey)
3553 WARN("NLS registry key not found. Please apply the default registry file 'wine.inf'\n");
3555 while (bContinue)
3557 if (NLS_RegEnumValue( hKey, ulIndex, szNumber, sizeof(szNumber),
3558 szValue, sizeof(szValue) ))
3560 lgrpid = strtoulW( szValue, NULL, 16 );
3562 TRACE("lcid %s, grpid %d (%smatched)\n", debugstr_w(szNumber),
3563 lgrpid, lgrpid == lpProcs->lgrpid ? "" : "not ");
3565 if (lgrpid == lpProcs->lgrpid)
3567 LCID lcid;
3569 lcid = strtoulW( szNumber, NULL, 16 );
3571 /* FIXME: native returns extra text for a few (17/150) locales, e.g:
3572 * '00000437 ;Georgian'
3573 * At present we only pass the LCID string.
3576 if (lpProcs->procW)
3577 bContinue = lpProcs->procW( lgrpid, lcid, szNumber, lpProcs->lParam );
3578 else
3580 char szNumberA[sizeof(szNumber)/sizeof(WCHAR)];
3582 WideCharToMultiByte(CP_ACP, 0, szNumber, -1, szNumberA, sizeof(szNumberA), 0, 0);
3584 bContinue = lpProcs->procA( lgrpid, lcid, szNumberA, lpProcs->lParam );
3588 ulIndex++;
3590 else
3592 /* Finished enumerating this key */
3593 if (!bAlternate)
3595 /* Enumerate alternate sorts also */
3596 hKey = NLS_RegOpenKey( hKey, szAlternateSortsKeyName );
3597 bAlternate = TRUE;
3598 ulIndex = 0;
3600 else
3601 bContinue = FALSE; /* Finished both keys */
3604 if (!bContinue)
3605 break;
3608 if (hKey)
3609 NtClose( hKey );
3611 return TRUE;
3614 /******************************************************************************
3615 * EnumLanguageGroupLocalesA (KERNEL32.@)
3617 * Call a users function for every locale in a language group available on the system.
3619 * PARAMS
3620 * pLangGrpLcEnumProc [I] Callback function to call for each locale
3621 * lgrpid [I] Language group (LGRPID_ values from "winnls.h")
3622 * dwFlags [I] Reserved, set to 0
3623 * lParam [I] User parameter to pass to pLangGrpLcEnumProc
3625 * RETURNS
3626 * Success: TRUE.
3627 * Failure: FALSE. Use GetLastError() to determine the cause.
3629 BOOL WINAPI EnumLanguageGroupLocalesA(LANGGROUPLOCALE_ENUMPROCA pLangGrpLcEnumProc,
3630 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3632 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3634 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3636 callbacks.procA = pLangGrpLcEnumProc;
3637 callbacks.procW = NULL;
3638 callbacks.dwFlags = dwFlags;
3639 callbacks.lgrpid = lgrpid;
3640 callbacks.lParam = lParam;
3642 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3645 /******************************************************************************
3646 * EnumLanguageGroupLocalesW (KERNEL32.@)
3648 * See EnumLanguageGroupLocalesA.
3650 BOOL WINAPI EnumLanguageGroupLocalesW(LANGGROUPLOCALE_ENUMPROCW pLangGrpLcEnumProc,
3651 LGRPID lgrpid, DWORD dwFlags, LONG_PTR lParam)
3653 ENUMLANGUAGEGROUPLOCALE_CALLBACKS callbacks;
3655 TRACE("(%p,0x%08X,0x%08X,0x%08lX)\n", pLangGrpLcEnumProc, lgrpid, dwFlags, lParam);
3657 callbacks.procA = NULL;
3658 callbacks.procW = pLangGrpLcEnumProc;
3659 callbacks.dwFlags = dwFlags;
3660 callbacks.lgrpid = lgrpid;
3661 callbacks.lParam = lParam;
3663 return NLS_EnumLanguageGroupLocales( pLangGrpLcEnumProc ? &callbacks : NULL );
3666 /******************************************************************************
3667 * EnumSystemGeoID (KERNEL32.@)
3669 * Call a users function for every location available on the system.
3671 * PARAMS
3672 * geoclass [I] Type of information desired (SYSGEOTYPE enum from "winnls.h")
3673 * reserved [I] Reserved, set to 0
3674 * pGeoEnumProc [I] Callback function to call for each location
3676 * RETURNS
3677 * Success: TRUE.
3678 * Failure: FALSE. Use GetLastError() to determine the cause.
3680 BOOL WINAPI EnumSystemGeoID(GEOCLASS geoclass, GEOID reserved, GEO_ENUMPROC pGeoEnumProc)
3682 static const WCHAR szCountryCodeValueName[] = {
3683 'C','o','u','n','t','r','y','C','o','d','e','\0'
3685 WCHAR szNumber[10];
3686 HANDLE hKey;
3687 ULONG ulIndex = 0;
3689 TRACE("(0x%08X,0x%08X,%p)\n", geoclass, reserved, pGeoEnumProc);
3691 if (geoclass != GEOCLASS_NATION || reserved || !pGeoEnumProc)
3693 SetLastError(ERROR_INVALID_PARAMETER);
3694 return FALSE;
3697 hKey = NLS_RegOpenKey( 0, szCountryListName );
3699 while (NLS_RegEnumSubKey( hKey, ulIndex, szNumber, sizeof(szNumber) ))
3701 BOOL bContinue = TRUE;
3702 DWORD dwGeoId;
3703 HANDLE hSubKey = NLS_RegOpenKey( hKey, szNumber );
3705 if (hSubKey)
3707 if (NLS_RegGetDword( hSubKey, szCountryCodeValueName, &dwGeoId ))
3709 TRACE("Got geoid %d\n", dwGeoId);
3711 if (!pGeoEnumProc( dwGeoId ))
3712 bContinue = FALSE;
3715 NtClose( hSubKey );
3718 if (!bContinue)
3719 break;
3721 ulIndex++;
3724 if (hKey)
3725 NtClose( hKey );
3727 return TRUE;
3730 /******************************************************************************
3731 * InvalidateNLSCache (KERNEL32.@)
3733 * Invalidate the cache of NLS values.
3735 * PARAMS
3736 * None.
3738 * RETURNS
3739 * Success: TRUE.
3740 * Failure: FALSE.
3742 BOOL WINAPI InvalidateNLSCache(void)
3744 FIXME("() stub\n");
3745 return FALSE;
3748 /******************************************************************************
3749 * GetUserGeoID (KERNEL32.@)
3751 GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass )
3753 GEOID ret = GEOID_NOT_AVAILABLE;
3754 static const WCHAR geoW[] = {'G','e','o',0};
3755 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3756 WCHAR bufferW[40], *end;
3757 DWORD count;
3758 HANDLE hkey, hSubkey = 0;
3759 UNICODE_STRING keyW;
3760 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
3761 RtlInitUnicodeString( &keyW, nationW );
3762 count = sizeof(bufferW);
3764 if(!(hkey = create_registry_key())) return ret;
3766 switch( GeoClass ){
3767 case GEOCLASS_NATION:
3768 if ((hSubkey = NLS_RegOpenKey(hkey, geoW)))
3770 if((NtQueryValueKey(hSubkey, &keyW, KeyValuePartialInformation,
3771 bufferW, count, &count) == STATUS_SUCCESS ) && info->DataLength)
3772 ret = strtolW((LPCWSTR)info->Data, &end, 10);
3774 break;
3775 case GEOCLASS_REGION:
3776 FIXME("GEOCLASS_REGION not handled yet\n");
3777 break;
3780 NtClose(hkey);
3781 if (hSubkey) NtClose(hSubkey);
3782 return ret;
3785 /******************************************************************************
3786 * SetUserGeoID (KERNEL32.@)
3788 BOOL WINAPI SetUserGeoID( GEOID GeoID )
3790 static const WCHAR geoW[] = {'G','e','o',0};
3791 static const WCHAR nationW[] = {'N','a','t','i','o','n',0};
3792 static const WCHAR formatW[] = {'%','i',0};
3793 UNICODE_STRING nameW,keyW;
3794 WCHAR bufferW[10];
3795 OBJECT_ATTRIBUTES attr;
3796 HANDLE hkey;
3798 if(!(hkey = create_registry_key())) return FALSE;
3800 attr.Length = sizeof(attr);
3801 attr.RootDirectory = hkey;
3802 attr.ObjectName = &nameW;
3803 attr.Attributes = 0;
3804 attr.SecurityDescriptor = NULL;
3805 attr.SecurityQualityOfService = NULL;
3806 RtlInitUnicodeString( &nameW, geoW );
3807 RtlInitUnicodeString( &keyW, nationW );
3809 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
3812 NtClose(attr.RootDirectory);
3813 return FALSE;
3816 sprintfW(bufferW, formatW, GeoID);
3817 NtSetValueKey(hkey, &keyW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR));
3818 NtClose(attr.RootDirectory);
3819 NtClose(hkey);
3820 return TRUE;
3823 typedef struct
3825 union
3827 UILANGUAGE_ENUMPROCA procA;
3828 UILANGUAGE_ENUMPROCW procW;
3829 } u;
3830 DWORD flags;
3831 LONG_PTR param;
3832 } ENUM_UILANG_CALLBACK;
3834 static BOOL CALLBACK enum_uilang_proc_a( HMODULE hModule, LPCSTR type,
3835 LPCSTR name, WORD LangID, LONG_PTR lParam )
3837 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3838 char buf[20];
3840 sprintf(buf, "%08x", (UINT)LangID);
3841 return enum_uilang->u.procA( buf, enum_uilang->param );
3844 static BOOL CALLBACK enum_uilang_proc_w( HMODULE hModule, LPCWSTR type,
3845 LPCWSTR name, WORD LangID, LONG_PTR lParam )
3847 static const WCHAR formatW[] = {'%','0','8','x',0};
3848 ENUM_UILANG_CALLBACK *enum_uilang = (ENUM_UILANG_CALLBACK *)lParam;
3849 WCHAR buf[20];
3851 sprintfW( buf, formatW, (UINT)LangID );
3852 return enum_uilang->u.procW( buf, enum_uilang->param );
3855 /******************************************************************************
3856 * EnumUILanguagesA (KERNEL32.@)
3858 BOOL WINAPI EnumUILanguagesA(UILANGUAGE_ENUMPROCA pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3860 ENUM_UILANG_CALLBACK enum_uilang;
3862 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3864 if(!pUILangEnumProc) {
3865 SetLastError(ERROR_INVALID_PARAMETER);
3866 return FALSE;
3868 if(dwFlags) {
3869 SetLastError(ERROR_INVALID_FLAGS);
3870 return FALSE;
3873 enum_uilang.u.procA = pUILangEnumProc;
3874 enum_uilang.flags = dwFlags;
3875 enum_uilang.param = lParam;
3877 EnumResourceLanguagesA( kernel32_handle, (LPCSTR)RT_STRING,
3878 (LPCSTR)LOCALE_ILANGUAGE, enum_uilang_proc_a,
3879 (LONG_PTR)&enum_uilang);
3880 return TRUE;
3883 /******************************************************************************
3884 * EnumUILanguagesW (KERNEL32.@)
3886 BOOL WINAPI EnumUILanguagesW(UILANGUAGE_ENUMPROCW pUILangEnumProc, DWORD dwFlags, LONG_PTR lParam)
3888 ENUM_UILANG_CALLBACK enum_uilang;
3890 TRACE("%p, %x, %lx\n", pUILangEnumProc, dwFlags, lParam);
3893 if(!pUILangEnumProc) {
3894 SetLastError(ERROR_INVALID_PARAMETER);
3895 return FALSE;
3897 if(dwFlags) {
3898 SetLastError(ERROR_INVALID_FLAGS);
3899 return FALSE;
3902 enum_uilang.u.procW = pUILangEnumProc;
3903 enum_uilang.flags = dwFlags;
3904 enum_uilang.param = lParam;
3906 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING,
3907 (LPCWSTR)LOCALE_ILANGUAGE, enum_uilang_proc_w,
3908 (LONG_PTR)&enum_uilang);
3909 return TRUE;
3912 INT WINAPI GetGeoInfoW(GEOID GeoId, GEOTYPE GeoType, LPWSTR lpGeoData,
3913 int cchData, LANGID language)
3915 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3916 return 0;
3919 INT WINAPI GetGeoInfoA(GEOID GeoId, GEOTYPE GeoType, LPSTR lpGeoData,
3920 int cchData, LANGID language)
3922 FIXME("%d %d %p %d %d\n", GeoId, GeoType, lpGeoData, cchData, language);
3923 return 0;
3926 INT WINAPI GetUserDefaultLocaleName(LPWSTR localename, int buffersize)
3928 LCID userlcid;
3930 TRACE("%p, %d\n", localename, buffersize);
3932 userlcid = GetUserDefaultLCID();
3933 return LCIDToLocaleName(userlcid, localename, buffersize, 0);
3936 /******************************************************************************
3937 * NormalizeString (KERNEL32.@)
3939 INT WINAPI NormalizeString(NORM_FORM NormForm, LPCWSTR lpSrcString, INT cwSrcLength,
3940 LPWSTR lpDstString, INT cwDstLength)
3942 FIXME("%x %p %d %p %d\n", NormForm, lpSrcString, cwSrcLength, lpDstString, cwDstLength);
3943 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3944 return 0;
3947 /******************************************************************************
3948 * IsNormalizedString (KERNEL32.@)
3950 BOOL WINAPI IsNormalizedString(NORM_FORM NormForm, LPCWSTR lpString, INT cwLength)
3952 FIXME("%x %p %d\n", NormForm, lpString, cwLength);
3953 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3954 return FALSE;
3957 enum {
3958 BASE = 36,
3959 TMIN = 1,
3960 TMAX = 26,
3961 SKEW = 38,
3962 DAMP = 700,
3963 INIT_BIAS = 72,
3964 INIT_N = 128
3967 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
3969 INT k;
3971 delta /= (firsttime ? DAMP : 2);
3972 delta += delta/numpoints;
3974 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
3975 delta /= BASE-TMIN;
3976 return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
3979 /******************************************************************************
3980 * IdnToAscii (KERNEL32.@)
3981 * Implementation of Punycode based on RFC 3492.
3983 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
3984 LPWSTR lpASCIICharStr, INT cchASCIIChar)
3986 static const WCHAR prefixW[] = {'x','n','-','-'};
3988 WCHAR *norm_str;
3989 INT i, label_start, label_end, norm_len, out_label, out = 0;
3991 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
3992 lpASCIICharStr, cchASCIIChar);
3994 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0);
3995 if(!norm_len)
3996 return 0;
3997 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR));
3998 if(!norm_str) {
3999 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4000 return 0;
4002 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr,
4003 cchUnicodeChar, norm_str, norm_len);
4004 if(!norm_len) {
4005 HeapFree(GetProcessHeap(), 0, norm_str);
4006 return 0;
4009 for(label_start=0; label_start<norm_len;) {
4010 INT n = INIT_N, bias = INIT_BIAS;
4011 INT delta = 0, b = 0, h;
4013 out_label = out;
4014 for(i=label_start; i<norm_len && norm_str[i]!='.' &&
4015 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++)
4016 if(norm_str[i] < 0x80)
4017 b++;
4018 label_end = i;
4020 if(b == label_end-label_start) {
4021 if(label_end < norm_len)
4022 b++;
4023 if(!lpASCIICharStr) {
4024 out += b;
4025 }else if(out+b <= cchASCIIChar) {
4026 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR));
4027 out += b;
4028 }else {
4029 HeapFree(GetProcessHeap(), 0, norm_str);
4030 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4031 return 0;
4033 label_start = label_end+1;
4034 continue;
4037 if(!lpASCIICharStr) {
4038 out += 5+b; /* strlen(xn--...-) */
4039 }else if(out+5+b <= cchASCIIChar) {
4040 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW));
4041 out += 4;
4042 for(i=label_start; i<label_end; i++)
4043 if(norm_str[i] < 0x80)
4044 lpASCIICharStr[out++] = norm_str[i];
4045 lpASCIICharStr[out++] = '-';
4046 }else {
4047 HeapFree(GetProcessHeap(), 0, norm_str);
4048 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4049 return 0;
4051 if(!b)
4052 out--;
4054 for(h=b; h<label_end-label_start;) {
4055 INT m = 0xffff, q, k;
4057 for(i=label_start; i<label_end; i++) {
4058 if(norm_str[i]>=n && m>norm_str[i])
4059 m = norm_str[i];
4061 delta += (m-n)*(h+1);
4062 n = m;
4064 for(i=label_start; i<label_end; i++) {
4065 if(norm_str[i] < n) {
4066 delta++;
4067 }else if(norm_str[i] == n) {
4068 for(q=delta, k=BASE; ; k+=BASE) {
4069 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4070 INT disp = q<t ? q : t+(q-t)%(BASE-t);
4071 if(!lpASCIICharStr) {
4072 out++;
4073 }else if(out+1 <= cchASCIIChar) {
4074 lpASCIICharStr[out++] = disp<='z'-'a' ?
4075 'a'+disp : '0'+disp-'z'+'a'-1;
4076 }else {
4077 HeapFree(GetProcessHeap(), 0, norm_str);
4078 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4079 return 0;
4081 if(q < t)
4082 break;
4083 q = (q-t)/(BASE-t);
4085 bias = adapt(delta, h+1, h==b);
4086 delta = 0;
4087 h++;
4090 delta++;
4091 n++;
4094 if(out-out_label > 63) {
4095 HeapFree(GetProcessHeap(), 0, norm_str);
4096 SetLastError(ERROR_INVALID_NAME);
4097 return 0;
4100 if(label_end < norm_len) {
4101 if(!lpASCIICharStr) {
4102 out++;
4103 }else if(out+1 <= cchASCIIChar) {
4104 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0;
4105 }else {
4106 HeapFree(GetProcessHeap(), 0, norm_str);
4107 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4108 return 0;
4111 label_start = label_end+1;
4114 HeapFree(GetProcessHeap(), 0, norm_str);
4115 return out;
4118 /******************************************************************************
4119 * IdnToNameprepUnicode (KERNEL32.@)
4121 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar,
4122 LPWSTR lpNameprepCharStr, INT cchNameprepChar)
4124 enum {
4125 UNASSIGNED = 0x1,
4126 PROHIBITED = 0x2,
4127 BIDI_RAL = 0x4,
4128 BIDI_L = 0x8
4131 extern const unsigned short nameprep_char_type[];
4132 extern const WCHAR nameprep_mapping[];
4133 const WCHAR *ptr;
4134 WORD flags;
4135 WCHAR buf[64], *map_str, norm_str[64], ch;
4136 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0;
4137 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only;
4139 TRACE("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar,
4140 lpNameprepCharStr, cchNameprepChar);
4142 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) {
4143 SetLastError(ERROR_INVALID_FLAGS);
4144 return 0;
4147 if(!lpUnicodeCharStr || cchUnicodeChar<-1) {
4148 SetLastError(ERROR_INVALID_PARAMETER);
4149 return 0;
4152 if(cchUnicodeChar == -1)
4153 cchUnicodeChar = strlenW(lpUnicodeCharStr)+1;
4154 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) {
4155 SetLastError(ERROR_INVALID_NAME);
4156 return 0;
4159 for(label_start=0; label_start<cchUnicodeChar;) {
4160 ascii_only = TRUE;
4161 for(i=label_start; i<cchUnicodeChar; i++) {
4162 ch = lpUnicodeCharStr[i];
4164 if(i!=cchUnicodeChar-1 && !ch) {
4165 SetLastError(ERROR_INVALID_NAME);
4166 return 0;
4168 /* check if ch is one of label separators defined in RFC3490 */
4169 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61)
4170 break;
4172 if(ch > 0x7f) {
4173 ascii_only = FALSE;
4174 continue;
4177 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4178 continue;
4179 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4180 || (ch>='0' && ch<='9') || ch=='-')
4181 continue;
4183 SetLastError(ERROR_INVALID_NAME);
4184 return 0;
4186 label_end = i;
4187 /* last label may be empty */
4188 if(label_start==label_end && ch) {
4189 SetLastError(ERROR_INVALID_NAME);
4190 return 0;
4193 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4194 lpUnicodeCharStr[label_end-1]=='-')) {
4195 SetLastError(ERROR_INVALID_NAME);
4196 return 0;
4199 if(ascii_only) {
4200 /* maximal label length is 63 characters */
4201 if(label_end-label_start > 63) {
4202 SetLastError(ERROR_INVALID_NAME);
4203 return 0;
4205 if(label_end < cchUnicodeChar)
4206 label_end++;
4208 if(!lpNameprepCharStr) {
4209 out += label_end-label_start;
4210 }else if(out+label_end-label_start <= cchNameprepChar) {
4211 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start,
4212 (label_end-label_start)*sizeof(WCHAR));
4213 if(lpUnicodeCharStr[label_end-1] > 0x7f)
4214 lpNameprepCharStr[out+label_end-label_start-1] = '.';
4215 out += label_end-label_start;
4216 }else {
4217 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4218 return 0;
4221 label_start = label_end;
4222 continue;
4225 map_len = 0;
4226 for(i=label_start; i<label_end; i++) {
4227 ch = lpUnicodeCharStr[i];
4228 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4229 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4231 if(!ptr[0]) map_len++;
4232 else if(!ptr[1]) map_len++;
4233 else if(!ptr[2]) map_len += 2;
4234 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3;
4236 if(map_len*sizeof(WCHAR) > sizeof(buf)) {
4237 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR));
4238 if(!map_str) {
4239 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4240 return 0;
4242 }else {
4243 map_str = buf;
4245 map_len = 0;
4246 for(i=label_start; i<label_end; i++) {
4247 ch = lpUnicodeCharStr[i];
4248 ptr = nameprep_mapping + nameprep_mapping[ch>>8];
4249 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f);
4251 if(!ptr[0]) {
4252 map_str[map_len++] = ch;
4253 }else if(!ptr[1]) {
4254 map_str[map_len++] = ptr[0];
4255 }else if(!ptr[2]) {
4256 map_str[map_len++] = ptr[0];
4257 map_str[map_len++] = ptr[1];
4258 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) {
4259 map_str[map_len++] = ptr[0];
4260 map_str[map_len++] = ptr[1];
4261 map_str[map_len++] = ptr[2];
4265 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len,
4266 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1);
4267 if(map_str != buf)
4268 HeapFree(GetProcessHeap(), 0, map_str);
4269 if(!norm_len) {
4270 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
4271 SetLastError(ERROR_INVALID_NAME);
4272 return 0;
4275 if(label_end < cchUnicodeChar) {
4276 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0;
4277 label_end++;
4280 if(!lpNameprepCharStr) {
4281 out += norm_len;
4282 }else if(out+norm_len <= cchNameprepChar) {
4283 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR));
4284 out += norm_len;
4285 }else {
4286 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4287 return 0;
4290 have_bidi_ral = prohibit_bidi_ral = FALSE;
4291 mask = PROHIBITED;
4292 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0)
4293 mask |= UNASSIGNED;
4294 for(i=0; i<norm_len; i++) {
4295 ch = norm_str[i];
4296 flags = get_table_entry( nameprep_char_type, ch );
4298 if(flags & mask) {
4299 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME
4300 : ERROR_NO_UNICODE_TRANSLATION);
4301 return 0;
4304 if(flags & BIDI_RAL)
4305 have_bidi_ral = TRUE;
4306 if(flags & BIDI_L)
4307 prohibit_bidi_ral = TRUE;
4310 if(have_bidi_ral) {
4311 ch = norm_str[0];
4312 flags = get_table_entry( nameprep_char_type, ch );
4313 if((flags & BIDI_RAL) == 0)
4314 prohibit_bidi_ral = TRUE;
4316 ch = norm_str[norm_len-1];
4317 flags = get_table_entry( nameprep_char_type, ch );
4318 if((flags & BIDI_RAL) == 0)
4319 prohibit_bidi_ral = TRUE;
4322 if(have_bidi_ral && prohibit_bidi_ral) {
4323 SetLastError(ERROR_INVALID_NAME);
4324 return 0;
4327 label_start = label_end;
4330 return out;
4333 /******************************************************************************
4334 * IdnToUnicode (KERNEL32.@)
4336 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
4337 LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
4339 extern const unsigned short nameprep_char_type[];
4341 INT i, label_start, label_end, out_label, out = 0;
4342 WCHAR ch;
4344 TRACE("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
4345 lpUnicodeCharStr, cchUnicodeChar);
4347 for(label_start=0; label_start<cchASCIIChar;) {
4348 INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
4350 out_label = out;
4351 for(i=label_start; i<cchASCIIChar; i++) {
4352 ch = lpASCIICharStr[i];
4354 if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
4355 SetLastError(ERROR_INVALID_NAME);
4356 return 0;
4359 if(!ch || ch=='.')
4360 break;
4361 if(ch == '-')
4362 delim = i;
4364 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
4365 continue;
4366 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
4367 || (ch>='0' && ch<='9') || ch=='-')
4368 continue;
4370 SetLastError(ERROR_INVALID_NAME);
4371 return 0;
4373 label_end = i;
4374 /* last label may be empty */
4375 if(label_start==label_end && ch) {
4376 SetLastError(ERROR_INVALID_NAME);
4377 return 0;
4380 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' ||
4381 lpUnicodeCharStr[label_end-1]=='-')) {
4382 SetLastError(ERROR_INVALID_NAME);
4383 return 0;
4385 if(label_end-label_start > 63) {
4386 SetLastError(ERROR_INVALID_NAME);
4387 return 0;
4390 if(label_end-label_start<4 ||
4391 tolowerW(lpASCIICharStr[label_start])!='x' ||
4392 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
4393 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
4394 if(label_end < cchUnicodeChar)
4395 label_end++;
4397 if(!lpUnicodeCharStr) {
4398 out += label_end-label_start;
4399 }else if(out+label_end-label_start <= cchUnicodeChar) {
4400 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
4401 (label_end-label_start)*sizeof(WCHAR));
4402 out += label_end-label_start;
4403 }else {
4404 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4405 return 0;
4408 label_start = label_end;
4409 continue;
4412 if(delim == label_start+3)
4413 delim++;
4414 if(!lpUnicodeCharStr) {
4415 out += delim-label_start-4;
4416 }else if(out+delim-label_start-4 <= cchUnicodeChar) {
4417 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
4418 (delim-label_start-4)*sizeof(WCHAR));
4419 out += delim-label_start-4;
4420 }else {
4421 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4422 return 0;
4424 if(out != out_label)
4425 delim++;
4427 for(i=delim; i<label_end;) {
4428 old_pos = pos;
4429 w = 1;
4430 for(k=BASE; ; k+=BASE) {
4431 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
4432 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
4433 SetLastError(ERROR_INVALID_NAME);
4434 return 0;
4436 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
4437 pos += digit*w;
4438 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
4439 if(digit < t)
4440 break;
4441 w *= BASE-t;
4443 bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
4444 n += pos/(out-out_label+1);
4445 pos %= out-out_label+1;
4447 if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
4448 get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
4449 SetLastError(ERROR_INVALID_NAME);
4450 return 0;
4452 if(!lpUnicodeCharStr) {
4453 out++;
4454 }else if(out+1 <= cchASCIIChar) {
4455 memmove(lpUnicodeCharStr+out_label+pos+1,
4456 lpUnicodeCharStr+out_label+pos,
4457 (out-out_label-pos)*sizeof(WCHAR));
4458 lpUnicodeCharStr[out_label+pos] = n;
4459 out++;
4460 }else {
4461 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4462 return 0;
4464 pos++;
4467 if(out-out_label > 63) {
4468 SetLastError(ERROR_INVALID_NAME);
4469 return 0;
4472 if(label_end < cchASCIIChar) {
4473 if(!lpUnicodeCharStr) {
4474 out++;
4475 }else if(out+1 <= cchUnicodeChar) {
4476 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
4477 }else {
4478 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4479 return 0;
4482 label_start = label_end+1;
4485 return out;