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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "wine/port.h"
34 #include "winuser.h" /* for RT_STRINGW */
36 #include "wine/unicode.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(nls
);
44 #define LOCALE_LOCALEINFOFLAGSMASK (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP|LOCALE_RETURN_NUMBER)
46 extern void CODEPAGE_Init( UINT ansi
, UINT oem
, UINT mac
, LCID lcid
);
48 #define NLS_MAX_LANGUAGES 20
52 LANGID found_lang_id
[NLS_MAX_LANGUAGES
];
53 char found_language
[NLS_MAX_LANGUAGES
][3];
54 char found_country
[NLS_MAX_LANGUAGES
][3];
59 /***********************************************************************
62 * Retrieve the ANSI codepage for a given locale.
64 inline static UINT
get_lcid_codepage( LCID lcid
)
67 if (!GetLocaleInfoW( lcid
, LOCALE_IDEFAULTANSICODEPAGE
|LOCALE_RETURN_NUMBER
, (WCHAR
*)&ret
,
68 sizeof(ret
)/sizeof(WCHAR
) )) ret
= 0;
73 /***********************************************************************
76 * Create the Control Panel\\International registry key.
78 inline static HKEY
create_registry_key(void)
80 static const WCHAR intlW
[] = {'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
81 'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
82 OBJECT_ATTRIBUTES attr
;
86 if (RtlOpenCurrentUser( KEY_ALL_ACCESS
, &hkey
) != STATUS_SUCCESS
) return 0;
88 attr
.Length
= sizeof(attr
);
89 attr
.RootDirectory
= hkey
;
90 attr
.ObjectName
= &nameW
;
92 attr
.SecurityDescriptor
= NULL
;
93 attr
.SecurityQualityOfService
= NULL
;
94 RtlInitUnicodeString( &nameW
, intlW
);
96 if (NtCreateKey( &hkey
, KEY_ALL_ACCESS
, &attr
, 0, NULL
, 0, NULL
) != STATUS_SUCCESS
) hkey
= 0;
97 NtClose( attr
.RootDirectory
);
102 /***********************************************************************
105 * Update registry contents on startup if the user locale has changed.
106 * This simulates the action of the Windows control panel.
108 inline static void update_registry( LCID lcid
)
110 static const WCHAR LocaleW
[] = {'L','o','c','a','l','e',0};
111 UNICODE_STRING nameW
;
114 DWORD count
= sizeof(buffer
);
117 if (!(hkey
= create_registry_key()))
118 return; /* don't do anything if we can't create the registry key */
120 RtlInitUnicodeString( &nameW
, LocaleW
);
121 count
= sizeof(bufferW
);
122 if (!NtQueryValueKey(hkey
, &nameW
, KeyValuePartialInformation
, (LPBYTE
)bufferW
, count
, &count
))
124 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)bufferW
;
125 RtlUnicodeToMultiByteN( buffer
, sizeof(buffer
)-1, &count
,
126 (WCHAR
*)info
->Data
, info
->DataLength
);
128 if (strtol( buffer
, NULL
, 16 ) == lcid
) /* already set correctly */
133 TRACE( "updating registry, locale changed %s -> %08lx\n", buffer
, lcid
);
135 else TRACE( "updating registry, locale changed none -> %08lx\n", lcid
);
137 sprintf( buffer
, "%08lx", lcid
);
138 RtlMultiByteToUnicodeN( bufferW
, sizeof(bufferW
), NULL
, buffer
, strlen(buffer
)+1 );
139 NtSetValueKey( hkey
, &nameW
, 0, REG_SZ
, bufferW
, (strlenW(bufferW
)+1) * sizeof(WCHAR
) );
142 #define UPDATE_VALUE(lctype) do { \
143 GetLocaleInfoW( lcid, (lctype)|LOCALE_NOUSEROVERRIDE, bufferW, sizeof(bufferW)/sizeof(WCHAR) ); \
144 SetLocaleInfoW( lcid, (lctype), bufferW ); } while (0)
146 UPDATE_VALUE(LOCALE_SLANGUAGE
);
147 UPDATE_VALUE(LOCALE_SCOUNTRY
);
148 UPDATE_VALUE(LOCALE_ICOUNTRY
);
149 UPDATE_VALUE(LOCALE_S1159
);
150 UPDATE_VALUE(LOCALE_S2359
);
151 UPDATE_VALUE(LOCALE_STIME
);
152 UPDATE_VALUE(LOCALE_ITIME
);
153 UPDATE_VALUE(LOCALE_ITLZERO
);
154 UPDATE_VALUE(LOCALE_SSHORTDATE
);
155 UPDATE_VALUE(LOCALE_IDATE
);
156 UPDATE_VALUE(LOCALE_SLONGDATE
);
157 UPDATE_VALUE(LOCALE_SDATE
);
158 UPDATE_VALUE(LOCALE_SCURRENCY
);
159 UPDATE_VALUE(LOCALE_ICURRENCY
);
160 UPDATE_VALUE(LOCALE_INEGCURR
);
161 UPDATE_VALUE(LOCALE_ICURRDIGITS
);
162 UPDATE_VALUE(LOCALE_SDECIMAL
);
163 UPDATE_VALUE(LOCALE_SLIST
);
164 UPDATE_VALUE(LOCALE_STHOUSAND
);
165 UPDATE_VALUE(LOCALE_IDIGITS
);
166 UPDATE_VALUE(LOCALE_ILZERO
);
167 UPDATE_VALUE(LOCALE_IMEASURE
);
172 /***********************************************************************
173 * find_language_id_proc
175 static BOOL CALLBACK
find_language_id_proc( HMODULE hModule
, LPCSTR type
,
176 LPCSTR name
, WORD LangID
, LPARAM lParam
)
178 LANG_FIND_DATA
*l_data
= (LANG_FIND_DATA
*)lParam
;
179 LCID lcid
= MAKELCID(LangID
, SORT_DEFAULT
);
180 char buf_language
[128];
181 char buf_country
[128];
182 char buf_en_language
[128];
184 TRACE("%04X\n", (UINT
)LangID
);
185 if(PRIMARYLANGID(LangID
) == LANG_NEUTRAL
)
186 return TRUE
; /* continue search */
191 GetLocaleInfoA(lcid
, LOCALE_SISO639LANGNAME
|LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
,
192 buf_language
, sizeof(buf_language
));
193 TRACE("LOCALE_SISO639LANGNAME: %s\n", buf_language
);
195 GetLocaleInfoA(lcid
, LOCALE_SISO3166CTRYNAME
|LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
,
196 buf_country
, sizeof(buf_country
));
197 TRACE("LOCALE_SISO3166CTRYNAME: %s\n", buf_country
);
199 if(l_data
->lang
&& strlen(l_data
->lang
) > 0 && !strcasecmp(l_data
->lang
, buf_language
))
201 if(l_data
->country
&& strlen(l_data
->country
) > 0)
203 if(!strcasecmp(l_data
->country
, buf_country
))
205 l_data
->found_lang_id
[0] = LangID
;
207 TRACE("Found lang_id %04X for %s_%s\n", LangID
, l_data
->lang
, l_data
->country
);
208 return FALSE
; /* stop enumeration */
211 else /* l_data->country not specified */
213 if(l_data
->n_found
< NLS_MAX_LANGUAGES
)
215 l_data
->found_lang_id
[l_data
->n_found
] = LangID
;
216 strncpy(l_data
->found_country
[l_data
->n_found
], buf_country
, 3);
217 strncpy(l_data
->found_language
[l_data
->n_found
], buf_language
, 3);
219 TRACE("Found lang_id %04X for %s\n", LangID
, l_data
->lang
);
220 return TRUE
; /* continue search */
225 /* Just in case, check LOCALE_SENGLANGUAGE too,
226 * in hope that possible alias name might have that value.
228 buf_en_language
[0] = 0;
229 GetLocaleInfoA(lcid
, LOCALE_SENGLANGUAGE
|LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
,
230 buf_en_language
, sizeof(buf_en_language
));
231 TRACE("LOCALE_SENGLANGUAGE: %s\n", buf_en_language
);
233 if(l_data
->lang
&& strlen(l_data
->lang
) > 0 && !strcasecmp(l_data
->lang
, buf_en_language
))
235 l_data
->found_lang_id
[l_data
->n_found
] = LangID
;
236 strncpy(l_data
->found_country
[l_data
->n_found
], buf_country
, 3);
237 strncpy(l_data
->found_language
[l_data
->n_found
], buf_language
, 3);
239 TRACE("Found lang_id %04X for %s\n", LangID
, l_data
->lang
);
242 return TRUE
; /* continue search */
246 /***********************************************************************
250 * Lang: a string whose two first chars are the iso name of a language.
251 * Country: a string whose two first chars are the iso name of country
252 * Charset: a string defining the chosen charset encoding
253 * Dialect: a string defining a variation of the locale
255 * all those values are from the standardized format of locale
256 * name in unix which is: Lang[_Country][.Charset][@Dialect]
259 * the numeric code of the language used by Windows
261 * FIXME: Charset and Dialect are not handled
263 static LANGID
get_language_id(LPCSTR Lang
, LPCSTR Country
, LPCSTR Charset
, LPCSTR Dialect
)
265 LANG_FIND_DATA l_data
;
266 char lang_string
[256];
270 l_data
.found_lang_id
[0] = MAKELANGID(LANG_ENGLISH
, SUBLANG_DEFAULT
);
274 memset(&l_data
, 0, sizeof(LANG_FIND_DATA
));
275 strncpy(l_data
.lang
, Lang
, sizeof(l_data
.lang
));
277 if(Country
&& strlen(Country
) > 0)
278 strncpy(l_data
.country
, Country
, sizeof(l_data
.country
));
280 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), RT_STRINGA
,
281 (LPCSTR
)LOCALE_ILANGUAGE
, find_language_id_proc
, (LPARAM
)&l_data
);
283 strcpy(lang_string
, l_data
.lang
);
284 if(l_data
.country
&& strlen(l_data
.country
) > 0)
286 strcat(lang_string
, "_");
287 strcat(lang_string
, l_data
.country
);
292 if(l_data
.country
&& strlen(l_data
.country
) > 0)
294 MESSAGE("Warning: Language '%s' was not found, retrying without country name...\n", lang_string
);
295 l_data
.country
[0] = 0;
296 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), RT_STRINGA
,
297 (LPCSTR
)LOCALE_ILANGUAGE
, find_language_id_proc
, (LONG
)&l_data
);
301 /* Re-evaluate lang_string */
302 strcpy(lang_string
, l_data
.lang
);
303 if(l_data
.country
&& strlen(l_data
.country
) > 0)
305 strcat(lang_string
, "_");
306 strcat(lang_string
, l_data
.country
);
311 MESSAGE("Warning: Language '%s' was not recognized, defaulting to English\n", lang_string
);
312 l_data
.found_lang_id
[0] = MAKELANGID(LANG_ENGLISH
, SUBLANG_DEFAULT
);
316 if(l_data
.n_found
== 1)
317 TRACE("For language '%s' lang_id %04X was found\n", lang_string
, l_data
.found_lang_id
[0]);
318 else /* l_data->n_found > 1 */
321 MESSAGE("For language '%s' several language ids were found:\n", lang_string
);
322 for(i
= 0; i
< l_data
.n_found
; i
++)
323 MESSAGE("%s_%s - %04X; ", l_data
.found_language
[i
], l_data
.found_country
[i
], l_data
.found_lang_id
[i
]);
325 MESSAGE("\nInstead of using first in the list, suggest to define\n"
326 "your LANG environment variable like this: LANG=%s_%s\n",
327 l_data
.found_language
[0], l_data
.found_country
[0]);
331 TRACE("Returning %04X\n", l_data
.found_lang_id
[0]);
332 return l_data
.found_lang_id
[0];
336 /***********************************************************************
339 static LCID
init_default_lcid(void)
342 char *lang
,*country
,*charset
,*dialect
,*next
;
345 if (GetEnvironmentVariableA( "LC_ALL", buf
, sizeof(buf
) ) ||
346 GetEnvironmentVariableA( "LC_CTYPE", buf
, sizeof(buf
) ) ||
347 GetEnvironmentVariableA( "LANGUAGE", buf
, sizeof(buf
) ) ||
348 GetEnvironmentVariableA( "LC_MESSAGES", buf
, sizeof(buf
) ) ||
349 GetEnvironmentVariableA( "LANG", buf
, sizeof(buf
) ))
351 if (!strcmp(buf
,"POSIX") || !strcmp(buf
,"C")) goto done
;
356 next
=strchr(lang
,':'); if (next
) *next
++='\0';
357 dialect
=strchr(lang
,'@'); if (dialect
) *dialect
++='\0';
358 charset
=strchr(lang
,'.'); if (charset
) *charset
++='\0';
359 country
=strchr(lang
,'_'); if (country
) *country
++='\0';
361 ret
= get_language_id(lang
, country
, charset
, dialect
);
364 } while (lang
&& !ret
);
366 if (!ret
) MESSAGE("Warning: language '%s' not recognized, defaulting to English\n", buf
);
370 if (!ret
) ret
= MAKELCID( MAKELANGID(LANG_ENGLISH
,SUBLANG_DEFAULT
), SORT_DEFAULT
) ;
375 /******************************************************************************
376 * get_locale_value_name
378 * Gets the registry value name for a given lctype.
380 static const WCHAR
*get_locale_value_name( DWORD lctype
)
382 static const WCHAR iCalendarTypeW
[] = {'i','C','a','l','e','n','d','a','r','T','y','p','e',0};
383 static const WCHAR iCountryW
[] = {'i','C','o','u','n','t','r','y',0};
384 static const WCHAR iCurrDigitsW
[] = {'i','C','u','r','r','D','i','g','i','t','s',0};
385 static const WCHAR iCurrencyW
[] = {'i','C','u','r','r','e','n','c','y',0};
386 static const WCHAR iDateW
[] = {'i','D','a','t','e',0};
387 static const WCHAR iDigitsW
[] = {'i','D','i','g','i','t','s',0};
388 static const WCHAR iFirstDayOfWeekW
[] = {'i','F','i','r','s','t','D','a','y','O','f','W','e','e','k',0};
389 static const WCHAR iFirstWeekOfYearW
[] = {'i','F','i','r','s','t','W','e','e','k','O','f','Y','e','a','r',0};
390 static const WCHAR iLDateW
[] = {'i','L','D','a','t','e',0};
391 static const WCHAR iLZeroW
[] = {'i','L','Z','e','r','o',0};
392 static const WCHAR iMeasureW
[] = {'i','M','e','a','s','u','r','e',0};
393 static const WCHAR iNegCurrW
[] = {'i','N','e','g','C','u','r','r',0};
394 static const WCHAR iNegNumberW
[] = {'i','N','e','g','N','u','m','b','e','r',0};
395 static const WCHAR iPaperSizeW
[] = {'i','P','a','p','e','r','S','i','z','e',0};
396 static const WCHAR iTLZeroW
[] = {'i','T','L','Z','e','r','o',0};
397 static const WCHAR iTimeW
[] = {'i','T','i','m','e',0};
398 static const WCHAR s1159W
[] = {'s','1','1','5','9',0};
399 static const WCHAR s2359W
[] = {'s','2','3','5','9',0};
400 static const WCHAR sCountryW
[] = {'s','C','o','u','n','t','r','y',0};
401 static const WCHAR sCurrencyW
[] = {'s','C','u','r','r','e','n','c','y',0};
402 static const WCHAR sDateW
[] = {'s','D','a','t','e',0};
403 static const WCHAR sDecimalW
[] = {'s','D','e','c','i','m','a','l',0};
404 static const WCHAR sGroupingW
[] = {'s','G','r','o','u','p','i','n','g',0};
405 static const WCHAR sLanguageW
[] = {'s','L','a','n','g','u','a','g','e',0};
406 static const WCHAR sListW
[] = {'s','L','i','s','t',0};
407 static const WCHAR sLongDateW
[] = {'s','L','o','n','g','D','a','t','e',0};
408 static const WCHAR sMonDecimalSepW
[] = {'s','M','o','n','D','e','c','i','m','a','l','S','e','p',0};
409 static const WCHAR sMonGroupingW
[] = {'s','M','o','n','G','r','o','u','p','i','n','g',0};
410 static const WCHAR sMonThousandSepW
[] = {'s','M','o','n','T','h','o','u','s','a','n','d','S','e','p',0};
411 static const WCHAR sNegativeSignW
[] = {'s','N','e','g','a','t','i','v','e','S','i','g','n',0};
412 static const WCHAR sPositiveSignW
[] = {'s','P','o','s','i','t','i','v','e','S','i','g','n',0};
413 static const WCHAR sShortDateW
[] = {'s','S','h','o','r','t','D','a','t','e',0};
414 static const WCHAR sThousandW
[] = {'s','T','h','o','u','s','a','n','d',0};
415 static const WCHAR sTimeFormatW
[] = {'s','T','i','m','e','F','o','r','m','a','t',0};
416 static const WCHAR sTimeW
[] = {'s','T','i','m','e',0};
417 static const WCHAR sYearMonthW
[] = {'s','Y','e','a','r','M','o','n','t','h',0};
419 switch (lctype
& ~LOCALE_LOCALEINFOFLAGSMASK
)
421 /* These values are used by SetLocaleInfo and GetLocaleInfo, and
422 * the values are stored in the registry, confirmed under Windows.
424 case LOCALE_ICALENDARTYPE
: return iCalendarTypeW
;
425 case LOCALE_ICURRDIGITS
: return iCurrDigitsW
;
426 case LOCALE_ICURRENCY
: return iCurrencyW
;
427 case LOCALE_IDIGITS
: return iDigitsW
;
428 case LOCALE_IFIRSTDAYOFWEEK
: return iFirstDayOfWeekW
;
429 case LOCALE_IFIRSTWEEKOFYEAR
: return iFirstWeekOfYearW
;
430 case LOCALE_ILZERO
: return iLZeroW
;
431 case LOCALE_IMEASURE
: return iMeasureW
;
432 case LOCALE_INEGCURR
: return iNegCurrW
;
433 case LOCALE_INEGNUMBER
: return iNegNumberW
;
434 case LOCALE_IPAPERSIZE
: return iPaperSizeW
;
435 case LOCALE_ITIME
: return iTimeW
;
436 case LOCALE_S1159
: return s1159W
;
437 case LOCALE_S2359
: return s2359W
;
438 case LOCALE_SCURRENCY
: return sCurrencyW
;
439 case LOCALE_SDATE
: return sDateW
;
440 case LOCALE_SDECIMAL
: return sDecimalW
;
441 case LOCALE_SGROUPING
: return sGroupingW
;
442 case LOCALE_SLIST
: return sListW
;
443 case LOCALE_SLONGDATE
: return sLongDateW
;
444 case LOCALE_SMONDECIMALSEP
: return sMonDecimalSepW
;
445 case LOCALE_SMONGROUPING
: return sMonGroupingW
;
446 case LOCALE_SMONTHOUSANDSEP
: return sMonThousandSepW
;
447 case LOCALE_SNEGATIVESIGN
: return sNegativeSignW
;
448 case LOCALE_SPOSITIVESIGN
: return sPositiveSignW
;
449 case LOCALE_SSHORTDATE
: return sShortDateW
;
450 case LOCALE_STHOUSAND
: return sThousandW
;
451 case LOCALE_STIME
: return sTimeW
;
452 case LOCALE_STIMEFORMAT
: return sTimeFormatW
;
453 case LOCALE_SYEARMONTH
: return sYearMonthW
;
455 /* The following are not listed under MSDN as supported,
456 * but seem to be used and also stored in the registry.
458 case LOCALE_ICOUNTRY
: return iCountryW
;
459 case LOCALE_IDATE
: return iDateW
;
460 case LOCALE_ILDATE
: return iLDateW
;
461 case LOCALE_ITLZERO
: return iTLZeroW
;
462 case LOCALE_SCOUNTRY
: return sCountryW
;
463 case LOCALE_SLANGUAGE
: return sLanguageW
;
469 /******************************************************************************
470 * get_registry_locale_info
472 * Retrieve user-modified locale info from the registry.
473 * Return length, 0 on error, -1 if not found.
475 static INT
get_registry_locale_info( LPCWSTR value
, LPWSTR buffer
, INT len
)
481 UNICODE_STRING nameW
;
482 KEY_VALUE_PARTIAL_INFORMATION
*info
;
483 static const int info_size
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
485 if (!(hkey
= create_registry_key())) return -1;
487 RtlInitUnicodeString( &nameW
, value
);
488 size
= info_size
+ len
* sizeof(WCHAR
);
490 if (!(info
= HeapAlloc( GetProcessHeap(), 0, size
)))
493 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
497 status
= NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, info
, size
, &size
);
498 if (status
== STATUS_BUFFER_OVERFLOW
&& !buffer
) status
= 0;
502 ret
= (size
- info_size
) / sizeof(WCHAR
);
503 /* append terminating null if needed */
504 if (!ret
|| ((WCHAR
*)info
->Data
)[ret
-1])
506 if (ret
< len
|| !buffer
) ret
++;
509 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
515 memcpy( buffer
, info
->Data
, (ret
-1) * sizeof(WCHAR
) );
521 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
) ret
= -1;
524 SetLastError( RtlNtStatusToDosError(status
) );
529 HeapFree( GetProcessHeap(), 0, info
);
534 /******************************************************************************
535 * GetLocaleInfoA (KERNEL32.@)
538 * LANG_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
540 * MS online documentation states that the string returned is NULL terminated
541 * except for LOCALE_FONTSIGNATURE which "will return a non-NULL
542 * terminated string".
544 INT WINAPI
GetLocaleInfoA( LCID lcid
, LCTYPE lctype
, LPSTR buffer
, INT len
)
549 if (len
< 0 || (len
&& !buffer
))
551 SetLastError( ERROR_INVALID_PARAMETER
);
554 if (!len
) buffer
= NULL
;
556 if (!(lenW
= GetLocaleInfoW( lcid
, lctype
, NULL
, 0 ))) return 0;
558 if (!(bufferW
= HeapAlloc( GetProcessHeap(), 0, lenW
* sizeof(WCHAR
) )))
560 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
563 if ((ret
= GetLocaleInfoW( lcid
, lctype
, bufferW
, lenW
)))
565 if ((lctype
& LOCALE_RETURN_NUMBER
) ||
566 ((lctype
& ~LOCALE_LOCALEINFOFLAGSMASK
) == LOCALE_FONTSIGNATURE
))
568 /* it's not an ASCII string, just bytes */
569 ret
*= sizeof(WCHAR
);
572 if (ret
<= len
) memcpy( buffer
, bufferW
, ret
);
575 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
582 UINT codepage
= CP_ACP
;
583 if (!(lctype
& LOCALE_USE_CP_ACP
)) codepage
= get_lcid_codepage( lcid
);
584 ret
= WideCharToMultiByte( codepage
, 0, bufferW
, ret
, buffer
, len
, NULL
, NULL
);
587 HeapFree( GetProcessHeap(), 0, bufferW
);
592 /******************************************************************************
593 * GetLocaleInfoW (KERNEL32.@)
596 * LANG_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
598 * MS online documentation states that the string returned is NULL terminated
599 * except for LOCALE_FONTSIGNATURE which "will return a non-NULL
600 * terminated string".
602 INT WINAPI
GetLocaleInfoW( LCID lcid
, LCTYPE lctype
, LPWSTR buffer
, INT len
)
613 if (len
< 0 || (len
&& !buffer
))
615 SetLastError( ERROR_INVALID_PARAMETER
);
618 if (!len
) buffer
= NULL
;
620 if (lcid
== LOCALE_NEUTRAL
|| lcid
== LANG_SYSTEM_DEFAULT
) lcid
= GetSystemDefaultLCID();
621 else if (lcid
== LANG_USER_DEFAULT
) lcid
= GetUserDefaultLCID();
623 lcflags
= lctype
& LOCALE_LOCALEINFOFLAGSMASK
;
624 lctype
&= ~LOCALE_LOCALEINFOFLAGSMASK
;
626 /* first check for overrides in the registry */
628 if (!(lcflags
& LOCALE_NOUSEROVERRIDE
) && lcid
== GetUserDefaultLCID())
630 const WCHAR
*value
= get_locale_value_name(lctype
);
632 if (value
&& ((ret
= get_registry_locale_info( value
, buffer
, len
)) != -1)) return ret
;
635 /* now load it from kernel resources */
637 lang_id
= LANGIDFROMLCID( lcid
);
639 /* replace SUBLANG_NEUTRAL by SUBLANG_DEFAULT */
640 if (SUBLANGID(lang_id
) == SUBLANG_NEUTRAL
)
641 lang_id
= MAKELANGID(PRIMARYLANGID(lang_id
), SUBLANG_DEFAULT
);
643 hModule
= GetModuleHandleA( "kernel32.dll" );
644 if (!(hrsrc
= FindResourceExW( hModule
, RT_STRINGW
, (LPCWSTR
)((lctype
>> 4) + 1), lang_id
)))
646 SetLastError( ERROR_INVALID_FLAGS
); /* no such lctype */
649 if (!(hmem
= LoadResource( hModule
, hrsrc
)))
652 p
= LockResource( hmem
);
653 for (i
= 0; i
< (lctype
& 0x0f); i
++) p
+= *p
+ 1;
655 if (lcflags
& LOCALE_RETURN_NUMBER
) ret
= sizeof(UINT
)/sizeof(WCHAR
);
656 else ret
= (lctype
== LOCALE_FONTSIGNATURE
) ? *p
: *p
+ 1;
658 if (!buffer
) return ret
;
662 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
666 if (lcflags
& LOCALE_RETURN_NUMBER
)
669 WCHAR
*end
, *tmp
= HeapAlloc( GetProcessHeap(), 0, (*p
+ 1) * sizeof(WCHAR
) );
671 memcpy( tmp
, p
+ 1, *p
* sizeof(WCHAR
) );
673 number
= strtolW( tmp
, &end
, 10 );
675 memcpy( buffer
, &number
, sizeof(number
) );
676 else /* invalid number */
678 SetLastError( ERROR_INVALID_FLAGS
);
681 HeapFree( GetProcessHeap(), 0, tmp
);
683 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning number %d\n",
684 lcid
, lctype
, buffer
, len
, number
);
688 memcpy( buffer
, p
+ 1, *p
* sizeof(WCHAR
) );
689 if (lctype
!= LOCALE_FONTSIGNATURE
) buffer
[ret
-1] = 0;
691 TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d) returning %d %s\n",
692 lcid
, lctype
, buffer
, len
, ret
, debugstr_w(buffer
) );
698 /******************************************************************************
699 * SetLocaleInfoA [KERNEL32.@]
701 BOOL WINAPI
SetLocaleInfoA(LCID lcid
, LCTYPE lctype
, LPCSTR data
)
703 UINT codepage
= CP_ACP
;
708 if (lcid
== LOCALE_NEUTRAL
|| lcid
== LANG_SYSTEM_DEFAULT
) lcid
= GetSystemDefaultLCID();
709 else if (lcid
== LANG_USER_DEFAULT
) lcid
= GetUserDefaultLCID();
711 if (!(lctype
& LOCALE_USE_CP_ACP
)) codepage
= get_lcid_codepage( lcid
);
712 len
= MultiByteToWideChar( codepage
, 0, data
, -1, NULL
, 0 );
713 if (!(strW
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
715 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
718 MultiByteToWideChar( codepage
, 0, data
, -1, strW
, len
);
719 ret
= SetLocaleInfoW( lcid
, lctype
, strW
);
720 HeapFree( GetProcessHeap(), 0, strW
);
725 /******************************************************************************
726 * SetLocaleInfoW (KERNEL32.@)
728 BOOL WINAPI
SetLocaleInfoW( LCID lcid
, LCTYPE lctype
, LPCWSTR data
)
731 const WCHAR intlW
[] = {'i','n','t','l',0 };
732 UNICODE_STRING valueW
;
736 if (lcid
== LOCALE_NEUTRAL
|| lcid
== LANG_SYSTEM_DEFAULT
) lcid
= GetSystemDefaultLCID();
737 else if (lcid
== LANG_USER_DEFAULT
) lcid
= GetUserDefaultLCID();
739 if (!(value
= get_locale_value_name( lctype
)))
741 SetLastError( ERROR_INVALID_PARAMETER
);
744 if (lcid
!= GetUserDefaultLCID()) return TRUE
; /* fake success */
746 TRACE("setting %lx to %s\n", lctype
, debugstr_w(data
) );
748 /* FIXME: should check that data to set is sane */
750 /* FIXME: profile functions should map to registry */
751 WriteProfileStringW( intlW
, value
, data
);
753 if (!(hkey
= create_registry_key())) return FALSE
;
754 RtlInitUnicodeString( &valueW
, value
);
755 status
= NtSetValueKey( hkey
, &valueW
, 0, REG_SZ
, data
, (strlenW(data
)+1)*sizeof(WCHAR
) );
758 if (status
) SetLastError( RtlNtStatusToDosError(status
) );
764 /***********************************************************************
765 * GetThreadLocale (KERNEL32.@)
767 LCID WINAPI
GetThreadLocale(void)
769 LCID ret
= NtCurrentTeb()->CurrentLocale
;
770 if (!ret
) NtCurrentTeb()->CurrentLocale
= ret
= GetUserDefaultLCID();
775 /**********************************************************************
776 * SetThreadLocale (KERNEL32.@)
779 * check if lcid is a valid cp
781 BOOL WINAPI
SetThreadLocale( LCID lcid
) /* [in] Locale identifier */
783 if (lcid
== LOCALE_NEUTRAL
|| lcid
== LANG_SYSTEM_DEFAULT
) lcid
= GetSystemDefaultLCID();
784 else if (lcid
== LANG_USER_DEFAULT
) lcid
= GetUserDefaultLCID();
786 NtCurrentTeb()->CurrentLocale
= lcid
;
787 NtCurrentTeb()->code_page
= get_lcid_codepage( lcid
);
792 /******************************************************************************
793 * ConvertDefaultLocale (KERNEL32.@)
795 LCID WINAPI
ConvertDefaultLocale( LCID lcid
)
799 case LOCALE_SYSTEM_DEFAULT
:
800 return GetSystemDefaultLCID();
801 case LOCALE_USER_DEFAULT
:
802 return GetUserDefaultLCID();
804 return MAKELCID (LANG_NEUTRAL
, SUBLANG_NEUTRAL
);
806 return MAKELANGID( PRIMARYLANGID(lcid
), SUBLANG_NEUTRAL
);
810 /******************************************************************************
811 * IsValidLocale (KERNEL32.@)
813 BOOL WINAPI
IsValidLocale( LCID lcid
, DWORD flags
)
815 /* check if language is registered in the kernel32 resources */
816 return FindResourceExW( GetModuleHandleA("KERNEL32"), RT_STRINGW
,
817 (LPCWSTR
)LOCALE_ILANGUAGE
, LANGIDFROMLCID(lcid
)) != 0;
821 static BOOL CALLBACK
enum_lang_proc_a( HMODULE hModule
, LPCSTR type
,
822 LPCSTR name
, WORD LangID
, LONG lParam
)
824 LOCALE_ENUMPROCA lpfnLocaleEnum
= (LOCALE_ENUMPROCA
)lParam
;
827 sprintf(buf
, "%08x", (UINT
)LangID
);
828 return lpfnLocaleEnum( buf
);
831 static BOOL CALLBACK
enum_lang_proc_w( HMODULE hModule
, LPCWSTR type
,
832 LPCWSTR name
, WORD LangID
, LONG lParam
)
834 static const WCHAR formatW
[] = {'%','0','8','x',0};
835 LOCALE_ENUMPROCW lpfnLocaleEnum
= (LOCALE_ENUMPROCW
)lParam
;
837 sprintfW( buf
, formatW
, (UINT
)LangID
);
838 return lpfnLocaleEnum( buf
);
841 /******************************************************************************
842 * EnumSystemLocalesA (KERNEL32.@)
844 BOOL WINAPI
EnumSystemLocalesA(LOCALE_ENUMPROCA lpfnLocaleEnum
,
847 TRACE("(%p,%08lx)\n", lpfnLocaleEnum
,flags
);
848 EnumResourceLanguagesA( GetModuleHandleA("KERNEL32"), RT_STRINGA
,
849 (LPCSTR
)LOCALE_ILANGUAGE
, enum_lang_proc_a
,
850 (LONG
)lpfnLocaleEnum
);
855 /******************************************************************************
856 * EnumSystemLocalesW (KERNEL32.@)
858 BOOL WINAPI
EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum
, DWORD flags
)
860 TRACE("(%p,%08lx)\n", lpfnLocaleEnum
,flags
);
861 EnumResourceLanguagesW( GetModuleHandleA("KERNEL32"), RT_STRINGW
,
862 (LPCWSTR
)LOCALE_ILANGUAGE
, enum_lang_proc_w
,
863 (LONG
)lpfnLocaleEnum
);
868 /***********************************************************************
869 * VerLanguageNameA (KERNEL32.@)
871 DWORD WINAPI
VerLanguageNameA( UINT wLang
, LPSTR szLang
, UINT nSize
)
873 return GetLocaleInfoA( MAKELCID(wLang
, SORT_DEFAULT
), LOCALE_SENGLANGUAGE
, szLang
, nSize
);
877 /***********************************************************************
878 * VerLanguageNameW (KERNEL32.@)
880 DWORD WINAPI
VerLanguageNameW( UINT wLang
, LPWSTR szLang
, UINT nSize
)
882 return GetLocaleInfoW( MAKELCID(wLang
, SORT_DEFAULT
), LOCALE_SENGLANGUAGE
, szLang
, nSize
);
886 /******************************************************************************
887 * GetStringTypeW (KERNEL32.@)
889 BOOL WINAPI
GetStringTypeW( DWORD type
, LPCWSTR src
, INT count
, LPWORD chartype
)
891 if (count
== -1) count
= strlenW(src
) + 1;
895 while (count
--) *chartype
++ = get_char_typeW( *src
++ ) & 0xfff;
898 while (count
--) *chartype
++ = get_char_typeW( *src
++ ) >> 12;
902 WARN("CT_CTYPE3: semi-stub.\n");
906 WORD type1
, type3
= 0; /* C3_NOTAPPLICABLE */
908 type1
= get_char_typeW( *src
++ ) & 0xfff;
909 /* try to construct type3 from type1 */
910 if(type1
& C1_SPACE
) type3
|= C3_SYMBOL
;
911 if(type1
& C1_ALPHA
) type3
|= C3_ALPHA
;
912 if ((c
>=0x30A0)&&(c
<=0x30FF)) type3
|= C3_KATAKANA
;
913 if ((c
>=0x3040)&&(c
<=0x309F)) type3
|= C3_HIRAGANA
;
914 if ((c
>=0x4E00)&&(c
<=0x9FAF)) type3
|= C3_IDEOGRAPH
;
915 if ((c
>=0x0600)&&(c
<=0x06FF)) type3
|= C3_KASHIDA
;
916 if ((c
>=0x3000)&&(c
<=0x303F)) type3
|= C3_SYMBOL
;
918 if ((c
>=0xFF00)&&(c
<=0xFF60)) type3
|= C3_FULLWIDTH
;
919 if ((c
>=0xFF00)&&(c
<=0xFF20)) type3
|= C3_SYMBOL
;
920 if ((c
>=0xFF3B)&&(c
<=0xFF40)) type3
|= C3_SYMBOL
;
921 if ((c
>=0xFF5B)&&(c
<=0xFF60)) type3
|= C3_SYMBOL
;
922 if ((c
>=0xFF21)&&(c
<=0xFF3A)) type3
|= C3_ALPHA
;
923 if ((c
>=0xFF41)&&(c
<=0xFF5A)) type3
|= C3_ALPHA
;
924 if ((c
>=0xFFE0)&&(c
<=0xFFE6)) type3
|= C3_FULLWIDTH
;
925 if ((c
>=0xFFE0)&&(c
<=0xFFE6)) type3
|= C3_SYMBOL
;
927 if ((c
>=0xFF61)&&(c
<=0xFFDC)) type3
|= C3_HALFWIDTH
;
928 if ((c
>=0xFF61)&&(c
<=0xFF64)) type3
|= C3_SYMBOL
;
929 if ((c
>=0xFF65)&&(c
<=0xFF9F)) type3
|= C3_KATAKANA
;
930 if ((c
>=0xFF65)&&(c
<=0xFF9F)) type3
|= C3_ALPHA
;
931 if ((c
>=0xFFE8)&&(c
<=0xFFEE)) type3
|= C3_HALFWIDTH
;
932 if ((c
>=0xFFE8)&&(c
<=0xFFEE)) type3
|= C3_SYMBOL
;
938 SetLastError( ERROR_INVALID_PARAMETER
);
945 /******************************************************************************
946 * GetStringTypeExW (KERNEL32.@)
948 BOOL WINAPI
GetStringTypeExW( LCID locale
, DWORD type
, LPCWSTR src
, INT count
, LPWORD chartype
)
950 /* locale is ignored for Unicode */
951 return GetStringTypeW( type
, src
, count
, chartype
);
955 /******************************************************************************
956 * GetStringTypeA (KERNEL32.@)
958 BOOL WINAPI
GetStringTypeA( LCID locale
, DWORD type
, LPCSTR src
, INT count
, LPWORD chartype
)
965 if(count
== -1) count
= strlen(src
) + 1;
967 if (!(cp
= get_lcid_codepage( locale
)))
969 FIXME("For locale %04lx using current ANSI code page\n", locale
);
973 countW
= MultiByteToWideChar(cp
, 0, src
, count
, NULL
, 0);
974 if((srcW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
976 MultiByteToWideChar(cp
, 0, src
, count
, srcW
, countW
);
978 * NOTE: the target buffer has 1 word for each CHARACTER in the source
979 * string, with multibyte characters there maybe be more bytes in count
980 * than character space in the buffer!
982 ret
= GetStringTypeW(type
, srcW
, countW
, chartype
);
983 HeapFree(GetProcessHeap(), 0, srcW
);
988 /******************************************************************************
989 * GetStringTypeExA (KERNEL32.@)
991 BOOL WINAPI
GetStringTypeExA( LCID locale
, DWORD type
, LPCSTR src
, INT count
, LPWORD chartype
)
993 return GetStringTypeA(locale
, type
, src
, count
, chartype
);
997 /******************************************************************************
1000 void LOCALE_Init(void)
1002 UINT ansi
= 1252, oem
= 437, mac
= 10000;
1003 LCID lcid
= init_default_lcid();
1005 GetLocaleInfoW( lcid
, LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
1006 (LPWSTR
)&ansi
, sizeof(ansi
)/sizeof(WCHAR
) );
1007 GetLocaleInfoW( lcid
, LOCALE_IDEFAULTMACCODEPAGE
| LOCALE_RETURN_NUMBER
,
1008 (LPWSTR
)&mac
, sizeof(mac
)/sizeof(WCHAR
) );
1009 GetLocaleInfoW( lcid
, LOCALE_IDEFAULTCODEPAGE
| LOCALE_RETURN_NUMBER
,
1010 (LPWSTR
)&oem
, sizeof(oem
)/sizeof(WCHAR
) );
1012 CODEPAGE_Init( ansi
, oem
, mac
, lcid
);
1013 update_registry( lcid
);
1016 /******************************************************************************
1017 * EnumSystemLanguageGroupsA (KERNEL32.@)
1019 BOOL
EnumSystemLanguageGroupsA(
1020 LANGUAGEGROUP_ENUMPROCA pLangGroupEnumProc
, /* callback function */
1021 DWORD dwFlags
, /* language groups */
1022 LONG_PTR lParam
/* callback parameter */
1026 SetLastError( ERROR_INVALID_PARAMETER
);
1030 /******************************************************************************
1031 * EnumSystemLanguageGroupsW (KERNEL32.@)
1033 BOOL
EnumSystemLanguageGroupsW(
1034 LANGUAGEGROUP_ENUMPROCW pLangGroupEnumProc
, /* callback function */
1035 DWORD dwFlags
, /* language groups */
1036 LONG_PTR lParam
/* callback parameter */
1040 SetLastError( ERROR_INVALID_PARAMETER
);
1044 /******************************************************************************
1045 * InvalidateNLSCache (KERNEL32.@)
1047 BOOL WINAPI
InvalidateNLSCache(void)