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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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