1 /*
2 * Locale support
3 *
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
8 *
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.
13 *
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.
18 *
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
22 */
23
24 #include "config.h"
25 #include "wine/port.h"
26
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>
34
35 #ifdef __APPLE__
36 # include <CoreFoundation/CFBundle.h>
37 # include <CoreFoundation/CFLocale.h>
38 # include <CoreFoundation/CFString.h>
39 #endif
40
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"
53
54 WINE_DEFAULT_DEBUG_CHANNEL(nls);
55
56 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|LOCALE_RETURN_NUMBER)
57
58 /* current code pages */
59 static const union cptable *ansi_cptable;
60 static const union cptable *oem_cptable;
61 static const union cptable *mac_cptable;
62 static const union cptable *unix_cptable; /* NULL if UTF8 */
63
64 static HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName);
65
66 static const WCHAR szNlsKeyName[] = {
67 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
68 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
69 'C','o','n','t','r','o','l','\\','N','l','s','\0'
70 };
71
72 static const WCHAR szLocaleKeyName[] = {
73 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
74 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
75 'C','o','n','t','r','o','l','\\','N','l','s','\\','L','o','c','a','l','e',0
76 };
77
78 static const WCHAR szCodepageKeyName[] = {
79 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
80 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
81 'C','o','n','t','r','o','l','\\','N','l','s','\\','C','o','d','e','p','a','g','e',0
82 };
83
84 static const WCHAR szLangGroupsKeyName[] = {
85 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
86 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
87 'C','o','n','t','r','o','l','\\','N','l','s','\\',
88 'L','a','n','g','u','a','g','e',' ','G','r','o','u','p','s',0
89 };
90
91 /* Charset to codepage map, sorted by name. */
92 static const struct charset_entry
93 {
94 const char *charset_name;
95 UINT codepage;
96 } charset_names[] =
97 {
98 { "BIG5", 950 },
99 { "CP1250", 1250 },
100 { "CP1251", 1251 },
101 { "CP1252", 1252 },
102 { "CP1253", 1253 },
103 { "CP1254", 1254 },
104 { "CP1255", 1255 },
105 { "CP1256", 1256 },
106 { "CP1257", 1257 },
107 { "CP1258", 1258 },
108 { "CP932", 932 },
109 { "CP936", 936 },
110 { "CP949", 949 },
111 { "CP950", 950 },
112 { "EUCJP", 20932 },
113 { "GB2312", 936 },
114 { "IBM037", 37 },
115 { "IBM1026", 1026 },
116 { "IBM424", 424 },
117 { "IBM437", 437 },
118 { "IBM500", 500 },
119 { "IBM850", 850 },
120 { "IBM852", 852 },
121 { "IBM855", 855 },
122 { "IBM857", 857 },
123 { "IBM860", 860 },
124 { "IBM861", 861 },
125 { "IBM862", 862 },
126 { "IBM863", 863 },
127 { "IBM864", 864 },
128 { "IBM865", 865 },
129 { "IBM866", 866 },
130 { "IBM869", 869 },
131 { "IBM874", 874 },
132 { "IBM875", 875 },
133 { "ISO88591", 28591 },
134 { "ISO885910", 28600 },
135 { "ISO885913", 28603 },
136 { "ISO885914", 28604 },
137 { "ISO885915", 28605 },
138 { "ISO885916", 28606 },
139 { "ISO88592", 28592 },
140 { "ISO88593", 28593 },
141 { "ISO88594", 28594 },
142 { "ISO88595", 28595 },
143 { "ISO88596", 28596 },
144 { "ISO88597", 28597 },
145 { "ISO88598", 28598 },
146 { "ISO88599", 28599 },
147 { "KOI8R", 20866 },
148 { "KOI8U", 21866 },
149 { "UTF8", CP_UTF8 }
150 };
151
152
153 struct locale_name
154 {
155 WCHAR win_name[128]; /* Windows name ("en-US") */
156 WCHAR lang[128]; /* language ("en") (note: buffer contains the other strings too) */
157 WCHAR *country; /* country ("US") */
158 WCHAR *charset; /* charset ("UTF-8") for Unix format only */
159 WCHAR *script; /* script ("Latn") for Windows format only */
160 WCHAR *modifier; /* modifier or sort order */
161 LCID lcid; /* corresponding LCID */
162 int matches; /* number of elements matching LCID (0..4) */
163 UINT codepage; /* codepage corresponding to charset */
164 };
165
166 /* locale ids corresponding to the various Unix locale parameters */
167 static LCID lcid_LC_COLLATE;
168 static LCID lcid_LC_CTYPE;
169 static LCID lcid_LC_MESSAGES;
170 static LCID lcid_LC_MONETARY;
171 static LCID lcid_LC_NUMERIC;
172 static LCID lcid_LC_TIME;
173 static LCID lcid_LC_PAPER;
174 static LCID lcid_LC_MEASUREMENT;
175 static LCID lcid_LC_TELEPHONE;
176
177 /* Copy Ascii string to Unicode without using codepages */
178 static inline void strcpynAtoW( WCHAR *dst, const char *src, size_t n )
179 {
180 while (n > 1 && *src)
181 {
182 *dst++ = (unsigned char)*src++;
183 n--;
184 }
185 if (n) *dst = 0;
186 }
187
188
189 /***********************************************************************
190 * get_lcid_codepage
191 *
192 * Retrieve the ANSI codepage for a given locale.
193 */
194 static inline UINT get_lcid_codepage( LCID lcid )
195 {
196 UINT ret;
197 if (!GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (WCHAR *)&ret,
198 sizeof(ret)/sizeof(WCHAR) )) ret = 0;
199 return ret;
200 }
201
202
203 /***********************************************************************
204 * get_codepage_table
205 *
206 * Find the table for a given codepage, handling CP_ACP etc. pseudo-codepages
207 */
208 static const union cptable *get_codepage_table( unsigned int codepage )
209 {
210 const union cptable *ret = NULL;
211
212 assert( ansi_cptable ); /* init must have been done already */
213
214 switch(codepage)
215 {
216 case CP_ACP:
217 return ansi_cptable;
218 case CP_OEMCP:
219 return oem_cptable;
220 case CP_MACCP:
221 return mac_cptable;
222 case CP_UTF7:
223 case CP_UTF8:
224 break;
225 case CP_THREAD_ACP:
226 if (!(codepage = kernel_get_thread_data()->code_page)) return ansi_cptable;
227 /* fall through */
228 default:
229 if (codepage == ansi_cptable->info.codepage) return ansi_cptable;
230 if (codepage == oem_cptable->info.codepage) return oem_cptable;
231 if (codepage == mac_cptable->info.codepage) return mac_cptable;
232 ret = wine_cp_get_table( codepage );
233 break;
234 }
235 return ret;
236 }
237
238
239 /***********************************************************************
240 * charset_cmp (internal)
241 */
242 static int charset_cmp( const void *name, const void *entry )
243 {
244 const struct charset_entry *charset = (const struct charset_entry *)entry;
245 return strcasecmp( (const char *)name, charset->charset_name );
246 }
247
248 /***********************************************************************
249 * find_charset
250 */
251 static UINT find_charset( const WCHAR *name )
252 {
253 const struct charset_entry *entry;
254 char charset_name[16];
255 size_t i, j;
256
257 /* remove punctuation characters from charset name */
258 for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
259 if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
260 charset_name[j] = 0;
261
262 entry = bsearch( charset_name, charset_names,
263 sizeof(charset_names)/sizeof(charset_names[0]),
264 sizeof(charset_names[0]), charset_cmp );
265 if (entry) return entry->codepage;
266 return 0;
267 }
268
269
270 /***********************************************************************
271 * find_locale_id_callback
272 */
273 static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
274 LPCWSTR name, WORD LangID, LPARAM lParam )
275 {
276 struct locale_name *data = (struct locale_name *)lParam;
277 WCHAR buffer[128];
278 int matches = 0;
279 LCID lcid = MAKELCID( LangID, SORT_DEFAULT ); /* FIXME: handle sort order */
280
281 if (PRIMARYLANGID(LangID) == LANG_NEUTRAL) return TRUE; /* continue search */
282
283 /* first check exact name */
284 if (data->win_name[0] &&
285 GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE,
286 buffer, sizeof(buffer)/sizeof(WCHAR) ))
287 {
288 if (!strcmpW( data->win_name, buffer ))
289 {
290 matches = 4; /* everything matches */
291 goto done;
292 }
293 }
294
295 if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
296 buffer, sizeof(buffer)/sizeof(WCHAR) ))
297 return TRUE;
298 if (strcmpW( buffer, data->lang )) return TRUE;
299 matches++; /* language name matched */
300
301 if (data->country)
302 {
303 if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
304 buffer, sizeof(buffer)/sizeof(WCHAR) ))
305 {
306 if (strcmpW( buffer, data->country )) goto done;
307 matches++; /* country name matched */
308 }
309 }
310 else /* match default language */
311 {
312 if (SUBLANGID(LangID) == SUBLANG_DEFAULT) matches++;
313 }
314
315 if (data->codepage)
316 {
317 UINT unix_cp;
318 if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
319 (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
320 {
321 if (unix_cp == data->codepage) matches++;
322 }
323 }
324
325 /* FIXME: check sort order */
326
327 done:
328 if (matches > data->matches)
329 {
330 data->lcid = lcid;
331 data->matches = matches;
332 }
333 return (data->matches < 4); /* no need to continue for perfect match */
334 }
335
336
337 /***********************************************************************
338 * parse_locale_name
339 *
340 * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
341 * Unix format is: lang[_country][.charset][@modifier]
342 * Windows format is: lang[-script][-country][_modifier]
343 */
344 static void parse_locale_name( const WCHAR *str, struct locale_name *name )
345 {
346 static const WCHAR sepW[] = {'-','_','.','@',0};
347 static const WCHAR winsepW[] = {'-','_',0};
348 static const WCHAR posixW[] = {'P','O','S','I','X',0};
349 static const WCHAR cW[] = {'C',0};
350 static const WCHAR latinW[] = {'l','a','t','i','n',0};
351 static const WCHAR latnW[] = {'-','L','a','t','n',0};
352 WCHAR *p;
353
354 TRACE("%s\n", debugstr_w(str));
355
356 name->country = name->charset = name->script = name->modifier = NULL;
357 name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
358 name->matches = 0;
359 name->codepage = 0;
360 name->win_name[0] = 0;
361 lstrcpynW( name->lang, str, sizeof(name->lang)/sizeof(WCHAR) );
362
363 if (!(p = strpbrkW( name->lang, sepW )))
364 {
365 if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
366 {
367 name->matches = 4; /* perfect match for default English lcid */
368 return;
369 }
370 strcpyW( name->win_name, name->lang );
371 }
372 else if (*p == '-') /* Windows format */
373 {
374 strcpyW( name->win_name, name->lang );
375 *p++ = 0;
376 name->country = p;
377 if (!(p = strpbrkW( p, winsepW ))) goto done;
378 if (*p == '-')
379 {
380 *p++ = 0;
381 name->script = name->country;
382 name->country = p;
383 if (!(p = strpbrkW( p, winsepW ))) goto done;
384 }
385 *p++ = 0;
386 name->modifier = p;
387 }
388 else /* Unix format */
389 {
390 if (*p == '_')
391 {
392 *p++ = 0;
393 name->country = p;
394 p = strpbrkW( p, sepW + 2 );
395 }
396 if (p && *p == '.')
397 {
398 *p++ = 0;
399 name->charset = p;
400 p = strchrW( p, '@' );
401 }
402 if (p)
403 {
404 *p++ = 0;
405 name->modifier = p;
406 }
407
408 if (name->charset)
409 name->codepage = find_charset( name->charset );
410
411 /* rebuild a Windows name if possible */
412
413 if (name->charset) goto done; /* can't specify charset in Windows format */
414 if (name->modifier && strcmpW( name->modifier, latinW ))
415 goto done; /* only Latn script supported for now */
416 strcpyW( name->win_name, name->lang );
417 if (name->modifier) strcatW( name->win_name, latnW );
418 if (name->country)
419 {
420 p = name->win_name + strlenW(name->win_name);
421 *p++ = '-';
422 strcpyW( p, name->country );
423 }
424 }
425 done:
426 EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
427 find_locale_id_callback, (LPARAM)name );
428 }
429
430
431 /***********************************************************************
432 * convert_default_lcid
433 *
434 * Get the default LCID to use for a given lctype in GetLocaleInfo.
435 */
436 static LCID convert_default_lcid( LCID lcid, LCTYPE lctype )
437 {
438 if (lcid == LOCALE_SYSTEM_DEFAULT ||
439 lcid == LOCALE_USER_DEFAULT ||
440 lcid == LOCALE_NEUTRAL)
441 {
442 LCID default_id = 0;
443
444 switch(lctype & 0xffff)
445 {
446 case LOCALE_SSORTNAME:
447 default_id = lcid_LC_COLLATE;
448 break;
449
450 case LOCALE_FONTSIGNATURE:
451 case LOCALE_IDEFAULTANSICODEPAGE:
452 case LOCALE_IDEFAULTCODEPAGE:
453 case LOCALE_IDEFAULTEBCDICCODEPAGE:
454 case LOCALE_IDEFAULTMACCODEPAGE:
455 case LOCALE_IDEFAULTUNIXCODEPAGE:
456 default_id = lcid_LC_CTYPE;
457 break;
458
459 case LOCALE_ICURRDIGITS:
460 case LOCALE_ICURRENCY:
461 case LOCALE_IINTLCURRDIGITS:
462 case LOCALE_INEGCURR:
463 case LOCALE_INEGSEPBYSPACE:
464 case LOCALE_INEGSIGNPOSN:
465 case LOCALE_INEGSYMPRECEDES:
466 case LOCALE_IPOSSEPBYSPACE:
467 case LOCALE_IPOSSIGNPOSN:
468 case LOCALE_IPOSSYMPRECEDES:
469 case LOCALE_SCURRENCY:
470 case LOCALE_SINTLSYMBOL:
471 case LOCALE_SMONDECIMALSEP:
472 case LOCALE_SMONGROUPING:
473 case LOCALE_SMONTHOUSANDSEP:
474 case LOCALE_SNATIVECURRNAME:
475 default_id = lcid_LC_MONETARY;
476 break;
477
478 case LOCALE_IDIGITS:
479 case LOCALE_IDIGITSUBSTITUTION:
480 case LOCALE_ILZERO:
481 case LOCALE_INEGNUMBER:
482 case LOCALE_SDECIMAL:
483 case LOCALE_SGROUPING:
484 case LOCALE_SNAN:
485 case LOCALE_SNATIVEDIGITS:
486 case LOCALE_SNEGATIVESIGN:
487 case LOCALE_SNEGINFINITY:
488 case LOCALE_SPOSINFINITY:
489 case LOCALE_SPOSITIVESIGN:
490 case LOCALE_STHOUSAND:
491 default_id = lcid_LC_NUMERIC;
492 break;
493
494 case LOCALE_ICALENDARTYPE:
495 case LOCALE_ICENTURY:
496 case LOCALE_IDATE:
497 case LOCALE_IDAYLZERO:
498 case LOCALE_IFIRSTDAYOFWEEK:
499 case LOCALE_IFIRSTWEEKOFYEAR:
500 case LOCALE_ILDATE:
501 case LOCALE_IMONLZERO:
502 case LOCALE_IOPTIONALCALENDAR:
503 case LOCALE_ITIME:
504 case LOCALE_ITIMEMARKPOSN:
505 case LOCALE_ITLZERO:
506 case LOCALE_S1159:
507 case LOCALE_S2359:
508 case LOCALE_SABBREVDAYNAME1:
509 case LOCALE_SABBREVDAYNAME2:
510 case LOCALE_SABBREVDAYNAME3:
511 case LOCALE_SABBREVDAYNAME4:
512 case LOCALE_SABBREVDAYNAME5:
513 case LOCALE_SABBREVDAYNAME6:
514 case LOCALE_SABBREVDAYNAME7:
515 case LOCALE_SABBREVMONTHNAME1:
516 case LOCALE_SABBREVMONTHNAME2:
517 case LOCALE_SABBREVMONTHNAME3:
518 case LOCALE_SABBREVMONTHNAME4:
519 case LOCALE_SABBREVMONTHNAME5:
520 case LOCALE_SABBREVMONTHNAME6:
521 case LOCALE_SABBREVMONTHNAME7:
522 case LOCALE_SABBREVMONTHNAME8:
523 case LOCALE_SABBREVMONTHNAME9:
524 case LOCALE_SABBREVMONTHNAME10:
525 case LOCALE_SABBREVMONTHNAME11:
526 case LOCALE_SABBREVMONTHNAME12:
527 case LOCALE_SABBREVMONTHNAME13:
528 case LOCALE_SDATE:
529 case LOCALE_SDAYNAME1:
530 case LOCALE_SDAYNAME2:
531 case LOCALE_SDAYNAME3:
532 case LOCALE_SDAYNAME4:
533 case LOCALE_SDAYNAME5:
534 case LOCALE_SDAYNAME6:
535 case LOCALE_SDAYNAME7:
536 case LOCALE_SDURATION:
537 case LOCALE_SLONGDATE:
538 case LOCALE_SMONTHNAME1:
539 case LOCALE_SMONTHNAME2:
540 case LOCALE_SMONTHNAME3:
541 case LOCALE_SMONTHNAME4:
542 case LOCALE_SMONTHNAME5:
543 case LOCALE_SMONTHNAME6:
544 case LOCALE_SMONTHNAME7:
545 case LOCALE_SMONTHNAME8:
546 case LOCALE_SMONTHNAME9:
547 case LOCALE_SMONTHNAME10:
548 case LOCALE_SMONTHNAME11:
549 case LOCALE_SMONTHNAME12:
550 case LOCALE_SMONTHNAME13:
551 case LOCALE_SSHORTDATE:
552 case LOCALE_SSHORTESTDAYNAME1:
553 case LOCALE_SSHORTESTDAYNAME2:
554 case LOCALE_SSHORTESTDAYNAME3:
555 case LOCALE_SSHORTESTDAYNAME4:
556 case LOCALE_SSHORTESTDAYNAME5:
557 case LOCALE_SSHORTESTDAYNAME6:
558 case LOCALE_SSHORTESTDAYNAME7:
559 case LOCALE_STIME:
560 case LOCALE_STIMEFORMAT:
561 case LOCALE_SYEARMONTH:
562 default_id = lcid_LC_TIME;
563 break;
564
565 case LOCALE_IPAPERSIZE:
566 default_id = lcid_LC_PAPER;
567 break;
568
569 case LOCALE_IMEASURE:
570 default_id = lcid_LC_MEASUREMENT;
571 break;
572
573 case LOCALE_ICOUNTRY:
574 default_id = lcid_LC_TELEPHONE;
575 break;
576 }
577 if (default_id) lcid = default_id;
578 }
579 return ConvertDefaultLocale( lcid );
580 }
581
582
583 /***********************************************************************
584 * create_registry_key
585 *
586 * Create the Control Panel\\International registry key.
587 */
588 static inline HANDLE create_registry_key(void)
589 {
590 static const WCHAR intlW[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
591 'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
592 OBJECT_ATTRIBUTES attr;
593 UNICODE_STRING nameW;
594 HANDLE hkey;
595
596 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey ) != STATUS_SUCCESS) return 0;
597
598 attr.Length = sizeof(attr);
599 attr.RootDirectory = hkey;
600 attr.ObjectName = &nameW;
601 attr.Attributes = 0;
602 attr.SecurityDescriptor = NULL;
603 attr.SecurityQualityOfService = NULL;
604 RtlInitUnicodeString( &nameW, intlW );
605
606 if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) hkey = 0;
607 NtClose( attr.RootDirectory );
608 return hkey;
609 }
610
611
612 /* update the registry settings for a given locale parameter */
613 /* return TRUE if an update was needed */
614 static BOOL locale_update_registry( HKEY hkey, const WCHAR *name, LCID lcid,
615 const LCTYPE *values, UINT nb_values )
616 {
617 static const WCHAR formatW[] = { '%','','8','x',0 };
618 WCHAR bufferW[40];
619 UNICODE_STRING nameW;
620 DWORD count, i;
621
622 RtlInitUnicodeString( &nameW, name );
623 count = sizeof(bufferW);
624 if (!NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, (LPBYTE)bufferW, count, &count))
625 {
626 const KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)bufferW;
627 LPCWSTR text = (LPCWSTR)info->Data;
628
629 if (strtoulW( text, NULL, 16 ) == lcid) return FALSE; /* already set correctly */
630 TRACE( "updating registry, locale %s changed %s -> %08x\n",
631 debugstr_w(name), debugstr_w(text), lcid );
632 }
633 else TRACE( "updating registry, locale %s changed none -> %08x\n", debugstr_w(name), lcid );
634 sprintfW( bufferW, formatW, lcid );
635 NtSetValueKey( hkey, &nameW, 0, REG_SZ, bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR) );
636
637 for (i = 0; i < nb_values; i++)
638 {
639 GetLocaleInfoW( lcid, values[i] | LOCALE_NOUSEROVERRIDE, bufferW,
640 sizeof(bufferW)/sizeof(WCHAR) );
641 SetLocaleInfoW( lcid, values[i], bufferW );
642 }
643 return TRUE;
644 }
645
646
647 /***********************************************************************
648 * LOCALE_InitRegistry
649 *
650 * Update registry contents on startup if the user locale has changed.
651 * This simulates the action of the Windows control panel.
652 */
653 void LOCALE_InitRegistry(void)
654 {
655 static const WCHAR acpW[] = {'A','C','P',0};
656 static const WCHAR oemcpW[] = {'O','E','M','C','P',0};
657 static const WCHAR maccpW[] = {'M','A','C','C','P',0};
658 static const WCHAR localeW[] = {'L','o','c','a','l','e',0};
659 static const WCHAR lc_ctypeW[] = { 'L','C','_','C','T','Y','P','E',0 };
660 static const WCHAR lc_monetaryW[] = { 'L','C','_','M','O','N','E','T','A','R','Y',0 };
661 static const WCHAR lc_numericW[] = { 'L','C','_','N','U','M','E','R','I','C',0 };
662 static const WCHAR lc_timeW[] = { 'L','C','_','T','I','M','E',0 };
663 static const WCHAR lc_measurementW[] = { 'L','C','_','M','E','A','S','U','R','E','M','E','N','T',0 };
664 static const WCHAR lc_telephoneW[] = { 'L','C','_','T','E','L','E','P','H','O','N','E',0 };
665 static const WCHAR lc_paperW[] = { 'L','C','_','P','A','P','E','R',0};
666 static const struct
667 {
668 LPCWSTR name;
669 USHORT value;
670 } update_cp_values[] = {
671 { acpW, LOCALE_IDEFAULTANSICODEPAGE },
672 { oemcpW, LOCALE_IDEFAULTCODEPAGE },
673 { maccpW, LOCALE_IDEFAULTMACCODEPAGE }
674 };
675 static const LCTYPE lc_messages_values[] = {
676 LOCALE_SLANGUAGE,
677 LOCALE_SCOUNTRY,
678 LOCALE_SLIST };
679 static const LCTYPE lc_monetary_values[] = {
680 LOCALE_SCURRENCY,
681 LOCALE_ICURRENCY,
682 LOCALE_INEGCURR,
683 LOCALE_ICURRDIGITS,
684 LOCALE_ILZERO,
685 LOCALE_SMONDECIMALSEP,
686 LOCALE_SMONGROUPING,
687 LOCALE_SMONTHOUSANDSEP };
688 static const LCTYPE lc_numeric_values[] = {
689 LOCALE_SDECIMAL,
690 LOCALE_STHOUSAND,
691 LOCALE_IDIGITS,
692 LOCALE_IDIGITSUBSTITUTION,
693 LOCALE_SNATIVEDIGITS,
694 LOCALE_INEGNUMBER,
695 LOCALE_SNEGATIVESIGN,
696 LOCALE_SPOSITIVESIGN,
697 LOCALE_SGROUPING };
698 static const LCTYPE lc_time_values[] = {
699 LOCALE_S1159,
700 LOCALE_S2359,
701 LOCALE_STIME,
702 LOCALE_ITIME,
703 LOCALE_ITLZERO,
704 LOCALE_SSHORTDATE,
705 LOCALE_SLONGDATE,
706 LOCALE_SDATE,
707 LOCALE_ITIMEMARKPOSN,
708 LOCALE_ICALENDARTYPE,
709 LOCALE_IFIRSTDAYOFWEEK,
710 LOCALE_IFIRSTWEEKOFYEAR,
711 LOCALE_STIMEFORMAT,
712 LOCALE_SYEARMONTH,
713 LOCALE_IDATE };
714 static const LCTYPE lc_measurement_values[] = { LOCALE_IMEASURE };
715 static const LCTYPE lc_telephone_values[] = { LOCALE_ICOUNTRY };
716 static const LCTYPE lc_paper_values[] = { LOCALE_IPAPERSIZE };
717
718 UNICODE_STRING nameW;
719 WCHAR bufferW[80];
720 DWORD count, i;
721 HANDLE hkey;
722 LCID lcid = GetUserDefaultLCID();
723
724 if (!(hkey = create_registry_key()))
725 return; /* don't do anything if we can't create the registry key */
726
727 locale_update_registry( hkey, localeW, lcid_LC_MESSAGES, lc_messages_values,
728 sizeof(lc_messages_values)/sizeof(lc_messages_values[0]) );
729 locale_update_registry( hkey, lc_monetaryW, lcid_LC_MONETARY, lc_monetary_values,
730 sizeof(lc_monetary_values)/sizeof(lc_monetary_values[0]) );
731 locale_update_registry( hkey, lc_numericW, lcid_LC_NUMERIC, lc_numeric_values,
732 sizeof(lc_numeric_values)/sizeof(lc_numeric_values[0]) );
733 locale_update_registry( hkey, lc_timeW, lcid_LC_TIME, lc_time_values,
734 sizeof(lc_time_values)/sizeof(lc_time_values[0]) );
735 locale_update_registry( hkey, lc_measurementW, lcid_LC_MEASUREMENT, lc_measurement_values,
736 sizeof(lc_measurement_values)/sizeof(lc_measurement_values[0]) );
737 locale_update_registry( hkey, lc_telephoneW, lcid_LC_TELEPHONE, lc_telephone_values,
738 sizeof(lc_telephone_values)/sizeof(lc_telephone_values[0]) );
739 locale_update_registry( hkey, lc_paperW, lcid_LC_PAPER, lc_paper_values,
740 sizeof(lc_paper_values)/sizeof(lc_paper_values[0]) );
741
742 if (locale_update_registry( hkey, lc_ctypeW, lcid_LC_CTYPE, NULL, 0 ))
743 {
744 HKEY nls_key = NLS_RegOpenKey( 0, szCodepageKeyName );
745
746 for (i = 0; i < sizeof(update_cp_values)/sizeof(update_cp_values[0]); i++)
747 {
748 count = GetLocaleInfoW( lcid, update_cp_values[i].value | LOCALE_NOUSEROVERRIDE,
749 bufferW, sizeof(bufferW)/sizeof(WCHAR) );
750 RtlInitUnicodeString( &nameW, update_cp_values[i].name );
751 NtSetValueKey( nls_key, &nameW, 0, REG_SZ, bufferW, count * sizeof(WCHAR) );
752 }
753 NtClose( nls_key );
754 }
755
756 NtClose( hkey );
757 }
758
759
760 /***********************************************************************
761 * setup_unix_locales
762 */
763 static UINT setup_unix_locales(void)
764 {
765 struct locale_name locale_name;
766 WCHAR buffer[128], ctype_buff[128];
767 char *locale;
768 UINT unix_cp = 0;
769
770 if ((locale = setlocale( LC_CTYPE, NULL )))
771 {
772 strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
773 parse_locale_name( ctype_buff, &locale_name );
774 lcid_LC_CTYPE = locale_name.lcid;
775 unix_cp = locale_name.codepage;
776 }
777 if (!lcid_LC_CTYPE) /* this one needs a default value */
778 lcid_LC_CTYPE = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
779
780 TRACE( "got lcid %04x (%d matches) for LC_CTYPE=%s\n",
781 locale_name.lcid, locale_name.matches, debugstr_a(locale) );
782
783 #define GET_UNIX_LOCALE(cat) do \
784 if ((locale = setlocale( cat, NULL ))) \
785 { \
786 strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
787 if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
788 else { \
789 parse_locale_name( buffer, &locale_name ); \
790 lcid_##cat = locale_name.lcid; \
791 TRACE( "got lcid %04x (%d matches) for " #cat "=%s\n", \
792 locale_name.lcid, locale_name.matches, debugstr_a(locale) ); \
793 } \
794 } while (0)
795
796 GET_UNIX_LOCALE( LC_COLLATE );
797 GET_UNIX_LOCALE( LC_MESSAGES );
798 GET_UNIX_LOCALE( LC_MONETARY );
799 GET_UNIX_LOCALE( LC_NUMERIC );
800 GET_UNIX_LOCALE( LC_TIME );
801 #ifdef LC_PAPER
802 GET_UNIX_LOCALE( LC_PAPER );
803 #endif
804 #ifdef LC_MEASUREMENT
805 GET_UNIX_LOCALE( LC_MEASUREMENT );
806 #endif
807 #ifdef LC_TELEPHONE
808 GET_UNIX_LOCALE( LC_TELEPHONE );
809 #endif
810
811 #undef GET_UNIX_LOCALE
812
813 return unix_cp;
814 }
815
816
817 /***********************************************************************
818 * GetUserDefaultLangID (KERNEL32.@)
819 *
820 * Get the default language Id for the current user.
821 *
822 * PARAMS
823 * None.
824 *
825 * RETURNS
826 * The current LANGID of the default language for the current user.
827 */
828 LANGID WINAPI GetUserDefaultLangID(void)
829 {
830 return LANGIDFROMLCID(GetUserDefaultLCID());
831 }
832
833
834 /***********************************************************************
835 * GetSystemDefaultLangID (KERNEL32.@)
836 *
837 * Get the default language Id for the system.
838 *
839 * PARAMS
840 * None.
841 *
842 * RETURNS
843 * The current LANGID of the default language for the system.
844 */
845 LANGID WINAPI GetSystemDefaultLangID(void)
846 {
847 return LANGIDFROMLCID(GetSystemDefaultLCID());
848 }
849
850
851 /***********************************************************************
852 * GetUserDefaultLCID (KERNEL32.@)
853 *
854 * Get the default locale Id for the current user.
855 *
856 * PARAMS
857 * None.
858 *
859 * RETURNS
860 * The current LCID of the default locale for the current user.
861 */
862 LCID WINAPI GetUserDefaultLCID(void)
863 {
864 LCID lcid;
865 NtQueryDefaultLocale( TRUE, &lcid );
866 return lcid;
867 }
868
869
870 /***********************************************************************
871 * GetSystemDefaultLCID (KERNEL32.@)
872 *
873 * Get the default locale Id for the system.
874 *
875 * PARAMS
876 * None.
877 *
878 * RETURNS
879 * The current LCID of the default locale for the system.
880 */
881 LCID WINAPI GetSystemDefaultLCID(void)
882 {
883 LCID lcid;
884 NtQueryDefaultLocale( FALSE, &lcid );
885 return lcid;
886 }
887
888
889 /***********************************************************************
890 * GetUserDefaultUILanguage (KERNEL32.@)
891 *
892 * Get the default user interface language Id for the current user.
893 *
894 * PARAMS
895 * None.
896 *
897 * RETURNS
898 * The current LANGID of the default UI language for the current user.
899 */
900 LANGID WINAPI GetUserDefaultUILanguage(void)
901 {
902 LANGID lang;
903 NtQueryDefaultUILanguage( &lang );
904 return lang;
905 }
906
907
908 /***********************************************************************
909 * GetSystemDefaultUILanguage (KERNEL32.@)
910 *
911 * Get the default user interface language Id for the system.
912 *
913 * PARAMS
914 * None.
915 *
916 * RETURNS
917 * The current LANGID of the default UI language for the system. This is
918 * typically the same language used during the installation process.
919 */
920 LANGID WINAPI GetSystemDefaultUILanguage(void)
921 {
922 LANGID lang;
923 NtQueryInstallUILanguage( &lang );
924 return lang;
925 }
926
927
928 /***********************************************************************
929 * LocaleNameToLCID (KERNEL32.@)
930 */
931 LCID WINAPI LocaleNameToLCID( LPCWSTR name,