1 /***
2 *getqloc_downlevel.c - get qualified locale for downlevel OS
3 *
4 * Copyright (c) Microsoft Corporation. All rights reserved.
5 *
6 *Purpose:
7 * defines __acrt_get_qualified_locale_downlevel - get complete locale information
8 *
9 *******************************************************************************/
10 #include <corecrt_internal.h>
11 #include <locale.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 extern "C" {
16
17 // local defines
18 #define __LCID_DEFAULT 0x1 // default language locale for country
19 #define __LCID_PRIMARY 0x2 // primary language locale for country
20 #define __LCID_FULL 0x4 // fully matched language locale for country
21 #define __LCID_LANGUAGE 0x100 // language default seen
22 #define __LCID_EXISTS 0x200 // language is installed
23
24 typedef struct tagRGLOCINFO
25 {
26 LCID lcid;
27 wchar_t chILanguage[8];
28 wchar_t * pchSEngLanguage;
29 wchar_t chSAbbrevLangName[4];
30 wchar_t * pchSEngCountry;
31 wchar_t chSAbbrevCtryName[4];
32 wchar_t chIDefaultCodepage[8];
33 wchar_t chIDefaultAnsiCodepage[8];
34 } RGLOCINFO;
35
36 static bool TranslateName(const __crt_locale_string_table *, int, const wchar_t **);
37
38 static void GetLcidFromLangCountry (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
39 static BOOL CALLBACK LangCountryEnumProc(_In_z_ PWSTR);
40
41 static void GetLcidFromLanguage (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
42 static BOOL CALLBACK LanguageEnumProc(_In_z_ PWSTR);
43
44 static void GetLcidFromCountry (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
45 static BOOL CALLBACK CountryEnumProc(_In_z_ PWSTR);
46
47 static void GetLcidFromDefault (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
48
49 static int ProcessCodePage (LPCWSTR lpCodePageStr, __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
50 static BOOL TestDefaultCountry(LCID);
51 static BOOL TestDefaultLanguage (LCID lcid, BOOL bTestPrimary, __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
52
53 static LCID LcidFromHexString(_In_z_ PCWSTR);
54 static int GetPrimaryLen(wchar_t const*);
55
56 // LANGID's of locales of nondefault languages
57 extern __declspec(selectany) LANGID const __rglangidNotDefault[] =
58 {
59 MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_CANADIAN),
60 MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC),
61 MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_LUXEMBOURG),
62 MAKELANGID(LANG_AFRIKAANS, SUBLANG_DEFAULT),
63 MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN),
64 MAKELANGID(LANG_BASQUE, SUBLANG_DEFAULT),
65 MAKELANGID(LANG_CATALAN, SUBLANG_DEFAULT),
66 MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_SWISS),
67 MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN_SWISS),
68 MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH_FINLAND)
69 };
70
71 /***
72 *BOOL __acrt_get_qualified_locale_downlevel - return fully qualified locale
73 *
74 *Purpose:
75 * get default locale, qualify partially complete locales
76 *
77 *Entry:
78 * lpInStr - input strings to be qualified
79 * lpOutId - pointer to numeric LCIDs and codepage output
80 * lpOutStr - pointer to string LCIDs and codepage output
81 *
82 *Exit:
83 * TRUE if success, qualified locale is valid
84 * FALSE if failure
85 *
86 *Exceptions:
87 *
88 *******************************************************************************/
__acrt_get_qualified_locale_downlevel(const __crt_locale_strings * lpInStr,UINT * lpOutCodePage,__crt_locale_strings * lpOutStr)89 BOOL __cdecl __acrt_get_qualified_locale_downlevel(const __crt_locale_strings* lpInStr, UINT* lpOutCodePage, __crt_locale_strings* lpOutStr)
90 {
91 int iCodePage;
92
93 // Get setloc data from per thread data struct
94 __crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
95
96 // Set downlevel setloc data in per thread data struct for use by LCID downlevel APIs
97 __crt_qualified_locale_data_downlevel downlevel_setloc;
98 __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data;
99
100 memset(&downlevel_setloc, '\0', sizeof(__crt_qualified_locale_data_downlevel));
101 _psetloc_downlevel_data = __acrt_getptd()->_setloc_downlevel_data = &downlevel_setloc;
102
103 // initialize pointer to call locale info routine based on operating system
104
105 _psetloc_data->pchLanguage = lpInStr->szLanguage;
106
107 // convert non-NLS country strings to three-letter abbreviations
108 _psetloc_data->pchCountry = lpInStr->szCountry;
109 if (_psetloc_data->pchCountry && *_psetloc_data->pchCountry)
110 TranslateName(__acrt_rg_country,
111 static_cast<int>(__acrt_rg_country_count - 1),
112 &_psetloc_data->pchCountry);
113
114 _psetloc_downlevel_data->iLcidState = 0;
115
116 if (_psetloc_data->pchLanguage && *_psetloc_data->pchLanguage)
117 {
118 if (_psetloc_data->pchCountry && *_psetloc_data->pchCountry)
119 {
120 // both language and country strings defined
121 GetLcidFromLangCountry(_psetloc_downlevel_data);
122 }
123 else
124 {
125 // language string defined, but country string undefined
126 GetLcidFromLanguage(_psetloc_downlevel_data);
127 }
128
129 if (!_psetloc_downlevel_data->iLcidState) {
130 // first attempt failed, try substituting the language name
131 // convert non-NLS language strings to three-letter abbrevs
132 if (TranslateName(__acrt_rg_language,
133 static_cast<int>(__acrt_rg_language_count - 1),
134 &_psetloc_data->pchLanguage))
135 {
136 if (_psetloc_data->pchCountry && *_psetloc_data->pchCountry)
137 {
138 GetLcidFromLangCountry(_psetloc_downlevel_data);
139 }
140 else
141 {
142 GetLcidFromLanguage(_psetloc_downlevel_data);
143 }
144 }
145 }
146 }
147 else
148 {
149 if (_psetloc_data->pchCountry && *_psetloc_data->pchCountry)
150 {
151 // country string defined, but language string undefined
152 GetLcidFromCountry(_psetloc_downlevel_data);
153 }
154 else
155 {
156 // both language and country strings undefined
157 GetLcidFromDefault(_psetloc_downlevel_data);
158 }
159 }
160
161 // test for error in LCID processing
162 if (!_psetloc_downlevel_data->iLcidState)
163 return FALSE;
164
165 // process codepage value
166 iCodePage = ProcessCodePage(lpInStr ? lpInStr->szCodePage: nullptr, _psetloc_downlevel_data);
167
168 // verify codepage validity
169 if (!iCodePage || !IsValidCodePage((WORD)iCodePage))
170 return FALSE;
171
172 // verify locale is installed
173 if (!IsValidLocale(_psetloc_downlevel_data->lcidLanguage, LCID_INSTALLED))
174 return FALSE;
175
176 // set codepage
177 if (lpOutCodePage)
178 {
179 *lpOutCodePage = (UINT)iCodePage;
180 }
181
182 // store locale name in cache
183 __acrt_LCIDToLocaleName(
184 _psetloc_downlevel_data->lcidLanguage,
185 _psetloc_data->_cacheLocaleName,
186 (int)_countof(_psetloc_data->_cacheLocaleName),
187 0);
188
189 // set locale name and codepage results
190 if (lpOutStr)
191 {
192 __acrt_LCIDToLocaleName(
193 _psetloc_downlevel_data->lcidLanguage,
194 lpOutStr->szLocaleName,
195 (int)_countof(lpOutStr->szLocaleName),
196 0);
197
198 if (GetLocaleInfoW(_psetloc_downlevel_data->lcidLanguage, LOCALE_SENGLANGUAGE,
199 lpOutStr->szLanguage, MAX_LANG_LEN) == 0)
200 return FALSE;
201
202 if (GetLocaleInfoW(_psetloc_downlevel_data->lcidCountry, LOCALE_SENGCOUNTRY,
203 lpOutStr->szCountry, MAX_CTRY_LEN) == 0)
204 return FALSE;
205
206 _itow_s((int)iCodePage, (wchar_t *)lpOutStr->szCodePage, MAX_CP_LEN, 10);
207 }
208
209 return TRUE;
210 }
211
212 /***
213 *BOOL TranslateName - convert known non-NLS string to NLS equivalent
214 *
215 *Purpose:
216 * Provide compatibility with existing code for non-NLS strings
217 *
218 *Entry:
219 * lpTable - pointer to __crt_locale_string_table used for translation
220 * high - maximum index of table (size - 1)
221 * ppchName - pointer to pointer of string to translate
222 *
223 *Exit:
224 * ppchName - pointer to pointer of string possibly translated
225 * TRUE if string translated, FALSE if unchanged
226 *
227 *Exceptions:
228 *
229 *******************************************************************************/
TranslateName(const __crt_locale_string_table * lpTable,int high,const wchar_t ** ppchName)230 static bool TranslateName (
231 const __crt_locale_string_table* lpTable,
232 int high,
233 const wchar_t ** ppchName)
234 {
235 int low = 0;
236
237 // typical binary search - do until no more to search or match
238 while (low <= high)
239 {
240 int const i = (low + high) / 2;
241 int const cmp = _wcsicmp(*ppchName, lpTable[i].szName);
242
243 if (cmp == 0)
244 {
245 *ppchName = lpTable[i].chAbbrev;
246 return true;
247 }
248 else if (cmp < 0)
249 high = i - 1;
250 else
251 low = i + 1;
252 }
253
254 return false;
255 }
256
257 /***
258 *void GetLcidFromLangCountry - get LCIDs from language and country strings
259 *
260 *Purpose:
261 * Match the best LCIDs to the language and country string given.
262 * After global variables are initialized, the LangCountryEnumProc
263 * routine is registered as an EnumSystemLocalesA callback to actually
264 * perform the matching as the LCIDs are enumerated.
265 *
266 *Entry:
267 * pchLanguage - language string
268 * bAbbrevLanguage - language string is a three-letter abbreviation
269 * pchCountry - country string
270 * bAbbrevCountry - country string ia a three-letter abbreviation
271 * iPrimaryLen - length of language string with primary name
272 *
273 *Exit:
274 * lcidLanguage - LCID of language string
275 * lcidCountry - LCID of country string
276 *
277 *Exceptions:
278 *
279 *******************************************************************************/
GetLcidFromLangCountry(__crt_qualified_locale_data_downlevel * _psetloc_downlevel_data)280 static void GetLcidFromLangCountry (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
281 {
282 __crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
283
284 // initialize static variables for callback use
285 _psetloc_data->bAbbrevLanguage = wcslen(_psetloc_data->pchLanguage) == 3;
286 _psetloc_data->bAbbrevCountry = wcslen(_psetloc_data->pchCountry) == 3;
287 _psetloc_downlevel_data->lcidLanguage = 0;
288 _psetloc_data->iPrimaryLen = _psetloc_data->bAbbrevLanguage ?
289 2 : GetPrimaryLen(_psetloc_data->pchLanguage);
290
291 EnumSystemLocalesW(LangCountryEnumProc, LCID_INSTALLED);
292
293 // locale value is invalid if the language was not installed or the language
294 // was not available for the country specified
295 if (!(_psetloc_downlevel_data->iLcidState & __LCID_LANGUAGE) ||
296 !(_psetloc_downlevel_data->iLcidState & __LCID_EXISTS) ||
297 !(_psetloc_downlevel_data->iLcidState & (__LCID_FULL |
298 __LCID_PRIMARY |
299 __LCID_DEFAULT)))
300 _psetloc_downlevel_data->iLcidState = 0;
301 }
302
303 /***
304 *BOOL CALLBACK LangCountryEnumProc - callback routine for GetLcidFromLangCountry
305 *
306 *Purpose:
307 * Determine if LCID given matches the language in pchLanguage
308 * and country in pchCountry.
309 *
310 *Entry:
311 * lpLcidString - pointer to string with decimal LCID
312 * pchCountry - pointer to country name
313 * bAbbrevCountry - set if country is three-letter abbreviation
314 *
315 *Exit:
316 * iLcidState - status of match
317 * __LCID_FULL - both language and country match (best match)
318 * __LCID_PRIMARY - primary language and country match (better)
319 * __LCID_DEFAULT - default language and country match (good)
320 * __LCID_LANGUAGE - default primary language exists
321 * __LCID_EXISTS - full match of language string exists
322 * (Overall match occurs for the best of FULL/PRIMARY/DEFAULT
323 * and LANGUAGE/EXISTS both set.)
324 * lcidLanguage - LCID matched
325 * lcidCountry - LCID matched
326 * FALSE if match occurred to terminate enumeration, else TRUE.
327 *
328 *Exceptions:
329 *
330 *******************************************************************************/
LangCountryEnumProc(_In_z_ PWSTR lpLcidString)331 static BOOL CALLBACK LangCountryEnumProc (_In_z_ PWSTR lpLcidString)
332 {
333 __crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
334 __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data = __acrt_getptd()->_setloc_downlevel_data;
335 LCID lcid = LcidFromHexString(lpLcidString);
336 wchar_t rgcInfo[120];
337
338 // test locale country against input value
339 if (GetLocaleInfoW(lcid,
340 _psetloc_data->bAbbrevCountry ?
341 LOCALE_SABBREVCTRYNAME : LOCALE_SENGCOUNTRY,
342 rgcInfo, _countof(rgcInfo)) == 0)
343 {
344 // set error condition and exit
345 _psetloc_downlevel_data->iLcidState = 0;
346 return TRUE;
347 }
348 if (!_wcsicmp(_psetloc_data->pchCountry, rgcInfo))
349 {
350 // country matched - test for language match
351 if (GetLocaleInfoW(lcid,
352 _psetloc_data->bAbbrevLanguage ?
353 LOCALE_SABBREVLANGNAME : LOCALE_SENGLANGUAGE,
354 rgcInfo, _countof(rgcInfo)) == 0)
355 {
356 // set error condition and exit
357 _psetloc_downlevel_data->iLcidState = 0;
358 return TRUE;
359 }
360 if (!_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
361 {
362 // language matched also - set state and value
363 _psetloc_downlevel_data->iLcidState |= (__LCID_FULL |
364 __LCID_LANGUAGE |
365 __LCID_EXISTS);
366 _psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = lcid;
367 }
368
369 // test if match already for primary langauage
370 else if (!(_psetloc_downlevel_data->iLcidState & __LCID_PRIMARY))
371 {
372 // if not, use _psetloc_data->iPrimaryLen to partial match language string
373 if (_psetloc_data->iPrimaryLen && !_wcsnicmp(_psetloc_data->pchLanguage, rgcInfo, _psetloc_data->iPrimaryLen))
374 {
375 // primary language matched - set state and country LCID
376 _psetloc_downlevel_data->iLcidState |= __LCID_PRIMARY;
377 _psetloc_downlevel_data->lcidCountry = lcid;
378
379 // if language is primary only (no subtype), set language LCID
380 if ((int)wcslen(_psetloc_data->pchLanguage) == _psetloc_data->iPrimaryLen)
381 _psetloc_downlevel_data->lcidLanguage = lcid;
382 }
383
384 // test if default language already defined
385 else if (!(_psetloc_downlevel_data->iLcidState & __LCID_DEFAULT))
386 {
387 // if not, test if locale language is default for country
388 if (TestDefaultCountry(lcid))
389 {
390 // default language for country - set state, value
391 _psetloc_downlevel_data->iLcidState |= __LCID_DEFAULT;
392 _psetloc_downlevel_data->lcidCountry = lcid;
393 }
394 }
395 }
396 }
397 // test if input language both exists and default primary language defined
398 if ((_psetloc_downlevel_data->iLcidState & (__LCID_LANGUAGE | __LCID_EXISTS)) !=
399 (__LCID_LANGUAGE | __LCID_EXISTS))
400 {
401 // test language match to determine whether it is installed
402 if (GetLocaleInfoW(lcid, _psetloc_data->bAbbrevLanguage ? LOCALE_SABBREVLANGNAME
403 : LOCALE_SENGLANGUAGE,
404 rgcInfo, _countof(rgcInfo)) == 0)
405 {
406 // set error condition and exit
407 _psetloc_downlevel_data->iLcidState = 0;
408 return TRUE;
409 }
410
411 if (!_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
412 {
413 // language matched - set bit for existance
414 _psetloc_downlevel_data->iLcidState |= __LCID_EXISTS;
415
416 if (_psetloc_data->bAbbrevLanguage)
417 {
418 // abbreviation - set state
419 // also set language LCID if not set already
420 _psetloc_downlevel_data->iLcidState |= __LCID_LANGUAGE;
421 if (!_psetloc_downlevel_data->lcidLanguage)
422 _psetloc_downlevel_data->lcidLanguage = lcid;
423 }
424
425 // test if language is primary only (no sublanguage)
426 else if (_psetloc_data->iPrimaryLen && ((int)wcslen(_psetloc_data->pchLanguage) == _psetloc_data->iPrimaryLen))
427 {
428 // primary language only - test if default LCID
429 if (TestDefaultLanguage(lcid, TRUE, _psetloc_downlevel_data))
430 {
431 // default primary language - set state
432 // also set LCID if not set already
433 _psetloc_downlevel_data->iLcidState |= __LCID_LANGUAGE;
434 if (!_psetloc_downlevel_data->lcidLanguage)
435 _psetloc_downlevel_data->lcidLanguage = lcid;
436 }
437 }
438 else
439 {
440 // language with sublanguage - set state
441 // also set LCID if not set already
442 _psetloc_downlevel_data->iLcidState |= __LCID_LANGUAGE;
443 if (!_psetloc_downlevel_data->lcidLanguage)
444 _psetloc_downlevel_data->lcidLanguage = lcid;
445 }
446 }
447 else if (!_psetloc_data->bAbbrevLanguage && _psetloc_data->iPrimaryLen
448 && !_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
449 {
450 // primary language match - test for default language only
451 if (TestDefaultLanguage(lcid, FALSE, _psetloc_downlevel_data))
452 {
453 // default primary language - set state
454 // also set LCID if not set already
455 _psetloc_downlevel_data->iLcidState |= __LCID_LANGUAGE;
456 if (!_psetloc_downlevel_data->lcidLanguage)
457 _psetloc_downlevel_data->lcidLanguage = lcid;
458 }
459 }
460 }
461
462 // if LOCALE_FULL set, return FALSE to stop enumeration,
463 // else return TRUE to continue
464 return (_psetloc_downlevel_data->iLcidState & __LCID_FULL) == 0;
465 }
466
467 /***
468 *void GetLcidFromLanguage - get LCIDs from language string
469 *
470 *Purpose:
471 * Match the best LCIDs to the language string given. After global
472 * variables are initialized, the LanguageEnumProc routine is
473 * registered as an EnumSystemLocalesA callback to actually perform
474 * the matching as the LCIDs are enumerated.
475 *
476 *Entry:
477 * pchLanguage - language string
478 * bAbbrevLanguage - language string is a three-letter abbreviation
479 * iPrimaryLen - length of language string with primary name
480 *
481 *Exit:
482 * lcidLanguage - lcidCountry - LCID of language with default
483 * country
484 *
485 *Exceptions:
486 *
487 *******************************************************************************/
GetLcidFromLanguage(__crt_qualified_locale_data_downlevel * _psetloc_downlevel_data)488 static void GetLcidFromLanguage (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
489 {
490 __crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
491
492 // initialize static variables for callback use
493 _psetloc_data->bAbbrevLanguage = wcslen(_psetloc_data->pchLanguage) == 3;
494 _psetloc_data->iPrimaryLen = _psetloc_data->bAbbrevLanguage ? 2 : GetPrimaryLen(_psetloc_data->pchLanguage);
495
496 EnumSystemLocalesW(LanguageEnumProc, LCID_INSTALLED);
497
498 // locale value is invalid if the language was not installed
499 // or the language was not available for the country specified
500 if (!(_psetloc_downlevel_data->iLcidState & __LCID_FULL))
501 _psetloc_downlevel_data->iLcidState = 0;
502 }
503
504 /***
505 *BOOL CALLBACK LanguageEnumProc - callback routine for GetLcidFromLanguage
506 *
507 *Purpose:
508 * Determine if LCID given matches the default country for the
509 * language in pchLanguage.
510 *
511 *Entry:
512 * lpLcidString - pointer to string with decimal LCID
513 * pchLanguage - pointer to language name
514 * bAbbrevLanguage - set if language is three-letter abbreviation
515 *
516 *Exit:
517 * lcidLanguage - lcidCountry - LCID matched
518 * FALSE if match occurred to terminate enumeration, else TRUE.
519 *
520 *Exceptions:
521 *
522 *******************************************************************************/
LanguageEnumProc(_In_z_ PWSTR lpLcidString)523 static BOOL CALLBACK LanguageEnumProc (_In_z_ PWSTR lpLcidString)
524 {
525 __crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
526 __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data = __acrt_getptd()->_setloc_downlevel_data;
527
528 LCID lcid = LcidFromHexString(lpLcidString);
529 wchar_t rgcInfo[120];
530
531 // test locale for language specified
532 if (GetLocaleInfoW(lcid, _psetloc_data->bAbbrevLanguage ? LOCALE_SABBREVLANGNAME
533 : LOCALE_SENGLANGUAGE,
534 rgcInfo, _countof(rgcInfo)) == 0)
535 {
536 // set error condition and exit
537 _psetloc_downlevel_data->iLcidState = 0;
538 return TRUE;
539 }
540
541 if (!_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
542 {
543 // language matched - test if locale country is default
544 // or if locale is implied in the language string
545 if (_psetloc_data->bAbbrevLanguage || TestDefaultLanguage(lcid, TRUE, _psetloc_downlevel_data))
546 {
547 // this locale has the default country
548 _psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = lcid;
549 _psetloc_downlevel_data->iLcidState |= __LCID_FULL;
550 }
551 }
552 else if (!_psetloc_data->bAbbrevLanguage && _psetloc_data->iPrimaryLen
553 && !_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
554 {
555 // primary language matched - test if locale country is default
556 if (TestDefaultLanguage(lcid, FALSE, _psetloc_downlevel_data))
557 {
558 // this is the default country
559 _psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = lcid;
560 _psetloc_downlevel_data->iLcidState |= __LCID_FULL;
561 }
562 }
563
564 return (_psetloc_downlevel_data->iLcidState & __LCID_FULL) == 0;
565 }
566
567 /***
568 *void GetLcidFromCountry - get LCIDs from country string
569 *
570 *Purpose:
571 * Match the best LCIDs to the country string given. After global
572 * variables are initialized, the CountryEnumProc routine is
573 * registered as an EnumSystemLocalesA callback to actually perform
574 * the matching as the LCIDs are enumerated.
575 *
576 *Entry:
577 * pchCountry - country string
578 * bAbbrevCountry - country string is a three-letter abbreviation
579 *
580 *Exit:
581 * lcidLanguage - lcidCountry - LCID of country with default
582 * language
583 *
584 *Exceptions:
585 *
586 *******************************************************************************/
GetLcidFromCountry(__crt_qualified_locale_data_downlevel * _psetloc_downlevel_data)587 static void GetLcidFromCountry (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
588 {
589 __crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
590 _psetloc_data->bAbbrevCountry = wcslen(_psetloc_data->pchCountry) == 3;
591
592 EnumSystemLocalesW(CountryEnumProc, LCID_INSTALLED);
593
594 // locale value is invalid if the country was not defined or
595 // no default language was found
596 if (!(_psetloc_downlevel_data->iLcidState & __LCID_FULL))
597 _psetloc_downlevel_data->iLcidState = 0;
598 }
599
600 /***
601 *BOOL CALLBACK CountryEnumProc - callback routine for GetLcidFromCountry
602 *
603 *Purpose:
604 * Determine if LCID given matches the default language for the
605 * country in pchCountry.
606 *
607 *Entry:
608 * lpLcidString - pointer to string with decimal LCID
609 * pchCountry - pointer to country name
610 * bAbbrevCountry - set if country is three-letter abbreviation
611 *
612 *Exit:
613 * lcidLanguage - lcidCountry - LCID matched
614 * FALSE if match occurred to terminate enumeration, else TRUE.
615 *
616 *Exceptions:
617 *
618 *******************************************************************************/
CountryEnumProc(_In_z_ PWSTR lpLcidString)619 static BOOL CALLBACK CountryEnumProc (_In_z_ PWSTR lpLcidString)
620 {
621 __crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
622 __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data = __acrt_getptd()->_setloc_downlevel_data;
623 LCID lcid = LcidFromHexString(lpLcidString);
624 wchar_t rgcInfo[120];
625
626 // test locale for country specified
627 if (GetLocaleInfoW(lcid, _psetloc_data->bAbbrevCountry ? LOCALE_SABBREVCTRYNAME
628 : LOCALE_SENGCOUNTRY,
629 rgcInfo, _countof(rgcInfo)) == 0)
630 {
631 // set error condition and exit
632 _psetloc_downlevel_data->iLcidState = 0;
633 return TRUE;
634 }
635 if (!_wcsicmp(_psetloc_data->pchCountry, rgcInfo))
636 {
637 // language matched - test if locale country is default
638 if (TestDefaultCountry(lcid))
639 {
640 // this locale has the default language
641 _psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = lcid;
642 _psetloc_downlevel_data->iLcidState |= __LCID_FULL;
643 }
644 }
645 return (_psetloc_downlevel_data->iLcidState & __LCID_FULL) == 0;
646 }
647
648 /***
649 *void GetLcidFromDefault - get default LCIDs
650 *
651 *Purpose:
652 * Set both language and country LCIDs to the system default.
653 *
654 *Entry:
655 * None.
656 *
657 *Exit:
658 * lcidLanguage - set to system LCID
659 * lcidCountry - set to system LCID
660 *
661 *Exceptions:
662 *
663 *******************************************************************************/
GetLcidFromDefault(__crt_qualified_locale_data_downlevel * _psetloc_downlevel_data)664 static void GetLcidFromDefault (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
665 {
666 _psetloc_downlevel_data->iLcidState |= (__LCID_FULL | __LCID_LANGUAGE);
667 _psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = GetUserDefaultLCID();
668 }
669
670 /***
671 *int ProcessCodePage - convert codepage string to numeric value
672 *
673 *Purpose:
674 * Process codepage string consisting of a decimal string, or the
675 * special case strings "ACP" and "OCP", for ANSI and OEM codepages,
676 * respectively. Null pointer or string returns the ANSI codepage.
677 *
678 *Entry:
679 * lpCodePageStr - pointer to codepage string
680 *
681 *Exit:
682 * Returns numeric value of codepage.
683 *
684 *Exceptions:
685 *
686 *******************************************************************************/
ProcessCodePage(LPCWSTR lpCodePageStr,__crt_qualified_locale_data_downlevel * _psetloc_downlevel_data)687 static int ProcessCodePage (LPCWSTR lpCodePageStr, __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
688 {
689 int iCodePage;
690
691 if (!lpCodePageStr || !*lpCodePageStr || !wcscmp(lpCodePageStr, L"ACP"))
692 {
693 // get ANSI codepage for the country LCID
694 if (GetLocaleInfoW(_psetloc_downlevel_data->lcidCountry, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
695 (LPWSTR) &iCodePage, sizeof(iCodePage) / sizeof(wchar_t)) == 0)
696 return 0;
697
698 if (iCodePage == 0) // for locales have no assoicated ANSI codepage, e.g. Hindi locale
699 return GetACP();
700 }
701 else if (!wcscmp(lpCodePageStr, L"OCP"))
702 {
703 // get OEM codepage for the country LCID
704 if (GetLocaleInfoW(_psetloc_downlevel_data->lcidCountry, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
705 (LPWSTR) &iCodePage, sizeof(iCodePage) / sizeof(wchar_t)) == 0)
706 return 0;
707 }
708 else
709 {
710 // convert decimal string to numeric value
711 iCodePage = (int)_wtol(lpCodePageStr);
712 }
713
714 return iCodePage;
715 }
716
717 /***
718 *BOOL TestDefaultCountry - determine if default locale for country
719 *
720 *Purpose:
721 * Using a hardcoded list, determine if the locale of the given LCID
722 * has the default sublanguage for the locale primary language. The
723 * list contains the locales NOT having the default sublanguage.
724 *
725 *Entry:
726 * lcid - LCID of locale to test
727 *
728 *Exit:
729 * Returns TRUE if default sublanguage, else FALSE.
730 *
731 *Exceptions:
732 *
733 *******************************************************************************/
TestDefaultCountry(LCID lcid)734 static BOOL TestDefaultCountry (LCID lcid)
735 {
736 LANGID langid = LANGIDFROMLCID(lcid);
737 int i;
738
739 for (i = 0; i < _countof(__rglangidNotDefault); i++)
740 {
741 if (langid == __rglangidNotDefault[i])
742 return FALSE;
743 }
744 return TRUE;
745 }
746
747 /***
748 *BOOL TestDefaultLanguage - determine if default locale for language
749 *
750 *Purpose:
751 * Determines if the given LCID has the default sublanguage.
752 * If bTestPrimary is set, also allow TRUE when string contains an
753 * implicit sublanguage.
754 *
755 *Entry:
756 * LCID - lcid of locale to test
757 * bTestPrimary - set if testing if language is primary
758 *
759 *Exit:
760 * Returns TRUE if sublanguage is default for locale tested.
761 * If bTestPrimary set, TRUE is language has implied sublanguge.
762 *
763 *Exceptions:
764 *
765 *******************************************************************************/
TestDefaultLanguage(LCID lcid,BOOL bTestPrimary,__crt_qualified_locale_data_downlevel * _psetloc_downlevel_data)766 static BOOL TestDefaultLanguage (LCID lcid, BOOL bTestPrimary, __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
767 {
768 UNREFERENCED_PARAMETER(_psetloc_downlevel_data); // CRT_REFACTOR TODO
769
770 DWORD dwLanguage;
771 LCID lcidDefault = MAKELCID(MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(lcid)), SUBLANG_DEFAULT), SORT_DEFAULT);
772 __crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
773
774 if (GetLocaleInfoW(lcidDefault, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER,
775 (LPWSTR) &dwLanguage, sizeof(dwLanguage) / sizeof(wchar_t)) == 0)
776 return FALSE;
777
778 if (lcid != dwLanguage)
779 {
780 // test if string contains an implicit sublanguage by
781 // having a character other than upper/lowercase letters.
782 if (bTestPrimary && GetPrimaryLen(_psetloc_data->pchLanguage) == (int)wcslen(_psetloc_data->pchLanguage))
783 return FALSE;
784 }
785 return TRUE;
786 }
787
788 /***
789 *LCID LcidFromHexString - convert hex string to value for LCID
790 *
791 *Purpose:
792 * LCID values returned in hex ANSI strings - straight conversion
793 *
794 *Entry:
795 * lpHexString - pointer to hex string to convert
796 *
797 *Exit:
798 * Returns LCID computed.
799 *
800 *Exceptions:
801 *
802 *******************************************************************************/
LcidFromHexString(_In_z_ PCWSTR lpHexString)803 static LCID LcidFromHexString (_In_z_ PCWSTR lpHexString)
804 {
805 wchar_t ch;
806 DWORD lcid = 0;
807
808 #pragma warning(disable:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED) // 26018 This is an idiomatic nul termination check that Prefast doesn't understand.
809 while ((ch = *lpHexString++) != '\0')
810 {
811 if (ch >= 'a' && ch <= 'f')
812 ch += static_cast<wchar_t>('9' + 1 - 'a');
813 else if (ch >= 'A' && ch <= 'F')
814 ch += static_cast<wchar_t>('9' + 1 - 'A');
815 lcid = lcid * 0x10 + ch - '0';
816 }
817
818 return (LCID)lcid;
819 }
820
821 /***
822 *int GetPrimaryLen - get length of primary language name
823 *
824 *Purpose:
825 * Determine primary language string length by scanning until
826 * first non-alphabetic character.
827 *
828 *Entry:
829 * pchLanguage - string to scan
830 *
831 *Exit:
832 * Returns length of primary language string.
833 *
834 *Exceptions:
835 *
836 *******************************************************************************/
GetPrimaryLen(wchar_t const * pchLanguage)837 static int GetPrimaryLen (wchar_t const* pchLanguage)
838 {
839 int len = 0;
840 wchar_t ch;
841
842 ch = *pchLanguage++;
843 while ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
844 {
845 len++;
846 ch = *pchLanguage++;
847 }
848
849 return len;
850 }
851
852 } // extern "C"
853