mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 18:52:57 +00:00

Imported from https://www.nuget.org/packages/Microsoft.Windows.SDK.CRTSource/10.0.22621.3 License: MIT
852 lines
30 KiB
C++
852 lines
30 KiB
C++
/***
|
|
*getqloc_downlevel.c - get qualified locale for downlevel OS
|
|
*
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
* defines __acrt_get_qualified_locale_downlevel - get complete locale information
|
|
*
|
|
*******************************************************************************/
|
|
#include <corecrt_internal.h>
|
|
#include <locale.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
extern "C" {
|
|
|
|
// local defines
|
|
#define __LCID_DEFAULT 0x1 // default language locale for country
|
|
#define __LCID_PRIMARY 0x2 // primary language locale for country
|
|
#define __LCID_FULL 0x4 // fully matched language locale for country
|
|
#define __LCID_LANGUAGE 0x100 // language default seen
|
|
#define __LCID_EXISTS 0x200 // language is installed
|
|
|
|
typedef struct tagRGLOCINFO
|
|
{
|
|
LCID lcid;
|
|
wchar_t chILanguage[8];
|
|
wchar_t * pchSEngLanguage;
|
|
wchar_t chSAbbrevLangName[4];
|
|
wchar_t * pchSEngCountry;
|
|
wchar_t chSAbbrevCtryName[4];
|
|
wchar_t chIDefaultCodepage[8];
|
|
wchar_t chIDefaultAnsiCodepage[8];
|
|
} RGLOCINFO;
|
|
|
|
static bool TranslateName(const __crt_locale_string_table *, int, const wchar_t **);
|
|
|
|
static void GetLcidFromLangCountry (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
|
|
static BOOL CALLBACK LangCountryEnumProc(_In_z_ PWSTR);
|
|
|
|
static void GetLcidFromLanguage (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
|
|
static BOOL CALLBACK LanguageEnumProc(_In_z_ PWSTR);
|
|
|
|
static void GetLcidFromCountry (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
|
|
static BOOL CALLBACK CountryEnumProc(_In_z_ PWSTR);
|
|
|
|
static void GetLcidFromDefault (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
|
|
|
|
static int ProcessCodePage (LPCWSTR lpCodePageStr, __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
|
|
static BOOL TestDefaultCountry(LCID);
|
|
static BOOL TestDefaultLanguage (LCID lcid, BOOL bTestPrimary, __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data);
|
|
|
|
static LCID LcidFromHexString(_In_z_ PCWSTR);
|
|
static int GetPrimaryLen(wchar_t const*);
|
|
|
|
// LANGID's of locales of nondefault languages
|
|
extern __declspec(selectany) LANGID const __rglangidNotDefault[] =
|
|
{
|
|
MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_CANADIAN),
|
|
MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC),
|
|
MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_LUXEMBOURG),
|
|
MAKELANGID(LANG_AFRIKAANS, SUBLANG_DEFAULT),
|
|
MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN),
|
|
MAKELANGID(LANG_BASQUE, SUBLANG_DEFAULT),
|
|
MAKELANGID(LANG_CATALAN, SUBLANG_DEFAULT),
|
|
MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_SWISS),
|
|
MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN_SWISS),
|
|
MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH_FINLAND)
|
|
};
|
|
|
|
/***
|
|
*BOOL __acrt_get_qualified_locale_downlevel - return fully qualified locale
|
|
*
|
|
*Purpose:
|
|
* get default locale, qualify partially complete locales
|
|
*
|
|
*Entry:
|
|
* lpInStr - input strings to be qualified
|
|
* lpOutId - pointer to numeric LCIDs and codepage output
|
|
* lpOutStr - pointer to string LCIDs and codepage output
|
|
*
|
|
*Exit:
|
|
* TRUE if success, qualified locale is valid
|
|
* FALSE if failure
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
BOOL __cdecl __acrt_get_qualified_locale_downlevel(const __crt_locale_strings* lpInStr, UINT* lpOutCodePage, __crt_locale_strings* lpOutStr)
|
|
{
|
|
int iCodePage;
|
|
|
|
// Get setloc data from per thread data struct
|
|
__crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
|
|
|
|
// Set downlevel setloc data in per thread data struct for use by LCID downlevel APIs
|
|
__crt_qualified_locale_data_downlevel downlevel_setloc;
|
|
__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data;
|
|
|
|
memset(&downlevel_setloc, '\0', sizeof(__crt_qualified_locale_data_downlevel));
|
|
_psetloc_downlevel_data = __acrt_getptd()->_setloc_downlevel_data = &downlevel_setloc;
|
|
|
|
// initialize pointer to call locale info routine based on operating system
|
|
|
|
_psetloc_data->pchLanguage = lpInStr->szLanguage;
|
|
|
|
// convert non-NLS country strings to three-letter abbreviations
|
|
_psetloc_data->pchCountry = lpInStr->szCountry;
|
|
if (_psetloc_data->pchCountry && *_psetloc_data->pchCountry)
|
|
TranslateName(__acrt_rg_country,
|
|
static_cast<int>(__acrt_rg_country_count - 1),
|
|
&_psetloc_data->pchCountry);
|
|
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
|
|
if (_psetloc_data->pchLanguage && *_psetloc_data->pchLanguage)
|
|
{
|
|
if (_psetloc_data->pchCountry && *_psetloc_data->pchCountry)
|
|
{
|
|
// both language and country strings defined
|
|
GetLcidFromLangCountry(_psetloc_downlevel_data);
|
|
}
|
|
else
|
|
{
|
|
// language string defined, but country string undefined
|
|
GetLcidFromLanguage(_psetloc_downlevel_data);
|
|
}
|
|
|
|
if (!_psetloc_downlevel_data->iLcidState) {
|
|
// first attempt failed, try substituting the language name
|
|
// convert non-NLS language strings to three-letter abbrevs
|
|
if (TranslateName(__acrt_rg_language,
|
|
static_cast<int>(__acrt_rg_language_count - 1),
|
|
&_psetloc_data->pchLanguage))
|
|
{
|
|
if (_psetloc_data->pchCountry && *_psetloc_data->pchCountry)
|
|
{
|
|
GetLcidFromLangCountry(_psetloc_downlevel_data);
|
|
}
|
|
else
|
|
{
|
|
GetLcidFromLanguage(_psetloc_downlevel_data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_psetloc_data->pchCountry && *_psetloc_data->pchCountry)
|
|
{
|
|
// country string defined, but language string undefined
|
|
GetLcidFromCountry(_psetloc_downlevel_data);
|
|
}
|
|
else
|
|
{
|
|
// both language and country strings undefined
|
|
GetLcidFromDefault(_psetloc_downlevel_data);
|
|
}
|
|
}
|
|
|
|
// test for error in LCID processing
|
|
if (!_psetloc_downlevel_data->iLcidState)
|
|
return FALSE;
|
|
|
|
// process codepage value
|
|
iCodePage = ProcessCodePage(lpInStr ? lpInStr->szCodePage: nullptr, _psetloc_downlevel_data);
|
|
|
|
// verify codepage validity
|
|
if (!iCodePage || !IsValidCodePage((WORD)iCodePage))
|
|
return FALSE;
|
|
|
|
// verify locale is installed
|
|
if (!IsValidLocale(_psetloc_downlevel_data->lcidLanguage, LCID_INSTALLED))
|
|
return FALSE;
|
|
|
|
// set codepage
|
|
if (lpOutCodePage)
|
|
{
|
|
*lpOutCodePage = (UINT)iCodePage;
|
|
}
|
|
|
|
// store locale name in cache
|
|
__acrt_LCIDToLocaleName(
|
|
_psetloc_downlevel_data->lcidLanguage,
|
|
_psetloc_data->_cacheLocaleName,
|
|
(int)_countof(_psetloc_data->_cacheLocaleName),
|
|
0);
|
|
|
|
// set locale name and codepage results
|
|
if (lpOutStr)
|
|
{
|
|
__acrt_LCIDToLocaleName(
|
|
_psetloc_downlevel_data->lcidLanguage,
|
|
lpOutStr->szLocaleName,
|
|
(int)_countof(lpOutStr->szLocaleName),
|
|
0);
|
|
|
|
if (GetLocaleInfoW(_psetloc_downlevel_data->lcidLanguage, LOCALE_SENGLANGUAGE,
|
|
lpOutStr->szLanguage, MAX_LANG_LEN) == 0)
|
|
return FALSE;
|
|
|
|
if (GetLocaleInfoW(_psetloc_downlevel_data->lcidCountry, LOCALE_SENGCOUNTRY,
|
|
lpOutStr->szCountry, MAX_CTRY_LEN) == 0)
|
|
return FALSE;
|
|
|
|
_itow_s((int)iCodePage, (wchar_t *)lpOutStr->szCodePage, MAX_CP_LEN, 10);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***
|
|
*BOOL TranslateName - convert known non-NLS string to NLS equivalent
|
|
*
|
|
*Purpose:
|
|
* Provide compatibility with existing code for non-NLS strings
|
|
*
|
|
*Entry:
|
|
* lpTable - pointer to __crt_locale_string_table used for translation
|
|
* high - maximum index of table (size - 1)
|
|
* ppchName - pointer to pointer of string to translate
|
|
*
|
|
*Exit:
|
|
* ppchName - pointer to pointer of string possibly translated
|
|
* TRUE if string translated, FALSE if unchanged
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static bool TranslateName (
|
|
const __crt_locale_string_table* lpTable,
|
|
int high,
|
|
const wchar_t ** ppchName)
|
|
{
|
|
int low = 0;
|
|
|
|
// typical binary search - do until no more to search or match
|
|
while (low <= high)
|
|
{
|
|
int const i = (low + high) / 2;
|
|
int const cmp = _wcsicmp(*ppchName, lpTable[i].szName);
|
|
|
|
if (cmp == 0)
|
|
{
|
|
*ppchName = lpTable[i].chAbbrev;
|
|
return true;
|
|
}
|
|
else if (cmp < 0)
|
|
high = i - 1;
|
|
else
|
|
low = i + 1;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/***
|
|
*void GetLcidFromLangCountry - get LCIDs from language and country strings
|
|
*
|
|
*Purpose:
|
|
* Match the best LCIDs to the language and country string given.
|
|
* After global variables are initialized, the LangCountryEnumProc
|
|
* routine is registered as an EnumSystemLocalesA callback to actually
|
|
* perform the matching as the LCIDs are enumerated.
|
|
*
|
|
*Entry:
|
|
* pchLanguage - language string
|
|
* bAbbrevLanguage - language string is a three-letter abbreviation
|
|
* pchCountry - country string
|
|
* bAbbrevCountry - country string ia a three-letter abbreviation
|
|
* iPrimaryLen - length of language string with primary name
|
|
*
|
|
*Exit:
|
|
* lcidLanguage - LCID of language string
|
|
* lcidCountry - LCID of country string
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static void GetLcidFromLangCountry (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
|
|
{
|
|
__crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
|
|
|
|
// initialize static variables for callback use
|
|
_psetloc_data->bAbbrevLanguage = wcslen(_psetloc_data->pchLanguage) == 3;
|
|
_psetloc_data->bAbbrevCountry = wcslen(_psetloc_data->pchCountry) == 3;
|
|
_psetloc_downlevel_data->lcidLanguage = 0;
|
|
_psetloc_data->iPrimaryLen = _psetloc_data->bAbbrevLanguage ?
|
|
2 : GetPrimaryLen(_psetloc_data->pchLanguage);
|
|
|
|
EnumSystemLocalesW(LangCountryEnumProc, LCID_INSTALLED);
|
|
|
|
// locale value is invalid if the language was not installed or the language
|
|
// was not available for the country specified
|
|
if (!(_psetloc_downlevel_data->iLcidState & __LCID_LANGUAGE) ||
|
|
!(_psetloc_downlevel_data->iLcidState & __LCID_EXISTS) ||
|
|
!(_psetloc_downlevel_data->iLcidState & (__LCID_FULL |
|
|
__LCID_PRIMARY |
|
|
__LCID_DEFAULT)))
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
}
|
|
|
|
/***
|
|
*BOOL CALLBACK LangCountryEnumProc - callback routine for GetLcidFromLangCountry
|
|
*
|
|
*Purpose:
|
|
* Determine if LCID given matches the language in pchLanguage
|
|
* and country in pchCountry.
|
|
*
|
|
*Entry:
|
|
* lpLcidString - pointer to string with decimal LCID
|
|
* pchCountry - pointer to country name
|
|
* bAbbrevCountry - set if country is three-letter abbreviation
|
|
*
|
|
*Exit:
|
|
* iLcidState - status of match
|
|
* __LCID_FULL - both language and country match (best match)
|
|
* __LCID_PRIMARY - primary language and country match (better)
|
|
* __LCID_DEFAULT - default language and country match (good)
|
|
* __LCID_LANGUAGE - default primary language exists
|
|
* __LCID_EXISTS - full match of language string exists
|
|
* (Overall match occurs for the best of FULL/PRIMARY/DEFAULT
|
|
* and LANGUAGE/EXISTS both set.)
|
|
* lcidLanguage - LCID matched
|
|
* lcidCountry - LCID matched
|
|
* FALSE if match occurred to terminate enumeration, else TRUE.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static BOOL CALLBACK LangCountryEnumProc (_In_z_ PWSTR lpLcidString)
|
|
{
|
|
__crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
|
|
__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data = __acrt_getptd()->_setloc_downlevel_data;
|
|
LCID lcid = LcidFromHexString(lpLcidString);
|
|
wchar_t rgcInfo[120];
|
|
|
|
// test locale country against input value
|
|
if (GetLocaleInfoW(lcid,
|
|
_psetloc_data->bAbbrevCountry ?
|
|
LOCALE_SABBREVCTRYNAME : LOCALE_SENGCOUNTRY,
|
|
rgcInfo, _countof(rgcInfo)) == 0)
|
|
{
|
|
// set error condition and exit
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
return TRUE;
|
|
}
|
|
if (!_wcsicmp(_psetloc_data->pchCountry, rgcInfo))
|
|
{
|
|
// country matched - test for language match
|
|
if (GetLocaleInfoW(lcid,
|
|
_psetloc_data->bAbbrevLanguage ?
|
|
LOCALE_SABBREVLANGNAME : LOCALE_SENGLANGUAGE,
|
|
rgcInfo, _countof(rgcInfo)) == 0)
|
|
{
|
|
// set error condition and exit
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
return TRUE;
|
|
}
|
|
if (!_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
|
|
{
|
|
// language matched also - set state and value
|
|
_psetloc_downlevel_data->iLcidState |= (__LCID_FULL |
|
|
__LCID_LANGUAGE |
|
|
__LCID_EXISTS);
|
|
_psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = lcid;
|
|
}
|
|
|
|
// test if match already for primary langauage
|
|
else if (!(_psetloc_downlevel_data->iLcidState & __LCID_PRIMARY))
|
|
{
|
|
// if not, use _psetloc_data->iPrimaryLen to partial match language string
|
|
if (_psetloc_data->iPrimaryLen && !_wcsnicmp(_psetloc_data->pchLanguage, rgcInfo, _psetloc_data->iPrimaryLen))
|
|
{
|
|
// primary language matched - set state and country LCID
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_PRIMARY;
|
|
_psetloc_downlevel_data->lcidCountry = lcid;
|
|
|
|
// if language is primary only (no subtype), set language LCID
|
|
if ((int)wcslen(_psetloc_data->pchLanguage) == _psetloc_data->iPrimaryLen)
|
|
_psetloc_downlevel_data->lcidLanguage = lcid;
|
|
}
|
|
|
|
// test if default language already defined
|
|
else if (!(_psetloc_downlevel_data->iLcidState & __LCID_DEFAULT))
|
|
{
|
|
// if not, test if locale language is default for country
|
|
if (TestDefaultCountry(lcid))
|
|
{
|
|
// default language for country - set state, value
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_DEFAULT;
|
|
_psetloc_downlevel_data->lcidCountry = lcid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// test if input language both exists and default primary language defined
|
|
if ((_psetloc_downlevel_data->iLcidState & (__LCID_LANGUAGE | __LCID_EXISTS)) !=
|
|
(__LCID_LANGUAGE | __LCID_EXISTS))
|
|
{
|
|
// test language match to determine whether it is installed
|
|
if (GetLocaleInfoW(lcid, _psetloc_data->bAbbrevLanguage ? LOCALE_SABBREVLANGNAME
|
|
: LOCALE_SENGLANGUAGE,
|
|
rgcInfo, _countof(rgcInfo)) == 0)
|
|
{
|
|
// set error condition and exit
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
|
|
{
|
|
// language matched - set bit for existance
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_EXISTS;
|
|
|
|
if (_psetloc_data->bAbbrevLanguage)
|
|
{
|
|
// abbreviation - set state
|
|
// also set language LCID if not set already
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_LANGUAGE;
|
|
if (!_psetloc_downlevel_data->lcidLanguage)
|
|
_psetloc_downlevel_data->lcidLanguage = lcid;
|
|
}
|
|
|
|
// test if language is primary only (no sublanguage)
|
|
else if (_psetloc_data->iPrimaryLen && ((int)wcslen(_psetloc_data->pchLanguage) == _psetloc_data->iPrimaryLen))
|
|
{
|
|
// primary language only - test if default LCID
|
|
if (TestDefaultLanguage(lcid, TRUE, _psetloc_downlevel_data))
|
|
{
|
|
// default primary language - set state
|
|
// also set LCID if not set already
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_LANGUAGE;
|
|
if (!_psetloc_downlevel_data->lcidLanguage)
|
|
_psetloc_downlevel_data->lcidLanguage = lcid;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// language with sublanguage - set state
|
|
// also set LCID if not set already
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_LANGUAGE;
|
|
if (!_psetloc_downlevel_data->lcidLanguage)
|
|
_psetloc_downlevel_data->lcidLanguage = lcid;
|
|
}
|
|
}
|
|
else if (!_psetloc_data->bAbbrevLanguage && _psetloc_data->iPrimaryLen
|
|
&& !_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
|
|
{
|
|
// primary language match - test for default language only
|
|
if (TestDefaultLanguage(lcid, FALSE, _psetloc_downlevel_data))
|
|
{
|
|
// default primary language - set state
|
|
// also set LCID if not set already
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_LANGUAGE;
|
|
if (!_psetloc_downlevel_data->lcidLanguage)
|
|
_psetloc_downlevel_data->lcidLanguage = lcid;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if LOCALE_FULL set, return FALSE to stop enumeration,
|
|
// else return TRUE to continue
|
|
return (_psetloc_downlevel_data->iLcidState & __LCID_FULL) == 0;
|
|
}
|
|
|
|
/***
|
|
*void GetLcidFromLanguage - get LCIDs from language string
|
|
*
|
|
*Purpose:
|
|
* Match the best LCIDs to the language string given. After global
|
|
* variables are initialized, the LanguageEnumProc routine is
|
|
* registered as an EnumSystemLocalesA callback to actually perform
|
|
* the matching as the LCIDs are enumerated.
|
|
*
|
|
*Entry:
|
|
* pchLanguage - language string
|
|
* bAbbrevLanguage - language string is a three-letter abbreviation
|
|
* iPrimaryLen - length of language string with primary name
|
|
*
|
|
*Exit:
|
|
* lcidLanguage - lcidCountry - LCID of language with default
|
|
* country
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static void GetLcidFromLanguage (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
|
|
{
|
|
__crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
|
|
|
|
// initialize static variables for callback use
|
|
_psetloc_data->bAbbrevLanguage = wcslen(_psetloc_data->pchLanguage) == 3;
|
|
_psetloc_data->iPrimaryLen = _psetloc_data->bAbbrevLanguage ? 2 : GetPrimaryLen(_psetloc_data->pchLanguage);
|
|
|
|
EnumSystemLocalesW(LanguageEnumProc, LCID_INSTALLED);
|
|
|
|
// locale value is invalid if the language was not installed
|
|
// or the language was not available for the country specified
|
|
if (!(_psetloc_downlevel_data->iLcidState & __LCID_FULL))
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
}
|
|
|
|
/***
|
|
*BOOL CALLBACK LanguageEnumProc - callback routine for GetLcidFromLanguage
|
|
*
|
|
*Purpose:
|
|
* Determine if LCID given matches the default country for the
|
|
* language in pchLanguage.
|
|
*
|
|
*Entry:
|
|
* lpLcidString - pointer to string with decimal LCID
|
|
* pchLanguage - pointer to language name
|
|
* bAbbrevLanguage - set if language is three-letter abbreviation
|
|
*
|
|
*Exit:
|
|
* lcidLanguage - lcidCountry - LCID matched
|
|
* FALSE if match occurred to terminate enumeration, else TRUE.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static BOOL CALLBACK LanguageEnumProc (_In_z_ PWSTR lpLcidString)
|
|
{
|
|
__crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
|
|
__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data = __acrt_getptd()->_setloc_downlevel_data;
|
|
|
|
LCID lcid = LcidFromHexString(lpLcidString);
|
|
wchar_t rgcInfo[120];
|
|
|
|
// test locale for language specified
|
|
if (GetLocaleInfoW(lcid, _psetloc_data->bAbbrevLanguage ? LOCALE_SABBREVLANGNAME
|
|
: LOCALE_SENGLANGUAGE,
|
|
rgcInfo, _countof(rgcInfo)) == 0)
|
|
{
|
|
// set error condition and exit
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
|
|
{
|
|
// language matched - test if locale country is default
|
|
// or if locale is implied in the language string
|
|
if (_psetloc_data->bAbbrevLanguage || TestDefaultLanguage(lcid, TRUE, _psetloc_downlevel_data))
|
|
{
|
|
// this locale has the default country
|
|
_psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = lcid;
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_FULL;
|
|
}
|
|
}
|
|
else if (!_psetloc_data->bAbbrevLanguage && _psetloc_data->iPrimaryLen
|
|
&& !_wcsicmp(_psetloc_data->pchLanguage, rgcInfo))
|
|
{
|
|
// primary language matched - test if locale country is default
|
|
if (TestDefaultLanguage(lcid, FALSE, _psetloc_downlevel_data))
|
|
{
|
|
// this is the default country
|
|
_psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = lcid;
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_FULL;
|
|
}
|
|
}
|
|
|
|
return (_psetloc_downlevel_data->iLcidState & __LCID_FULL) == 0;
|
|
}
|
|
|
|
/***
|
|
*void GetLcidFromCountry - get LCIDs from country string
|
|
*
|
|
*Purpose:
|
|
* Match the best LCIDs to the country string given. After global
|
|
* variables are initialized, the CountryEnumProc routine is
|
|
* registered as an EnumSystemLocalesA callback to actually perform
|
|
* the matching as the LCIDs are enumerated.
|
|
*
|
|
*Entry:
|
|
* pchCountry - country string
|
|
* bAbbrevCountry - country string is a three-letter abbreviation
|
|
*
|
|
*Exit:
|
|
* lcidLanguage - lcidCountry - LCID of country with default
|
|
* language
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static void GetLcidFromCountry (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
|
|
{
|
|
__crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
|
|
_psetloc_data->bAbbrevCountry = wcslen(_psetloc_data->pchCountry) == 3;
|
|
|
|
EnumSystemLocalesW(CountryEnumProc, LCID_INSTALLED);
|
|
|
|
// locale value is invalid if the country was not defined or
|
|
// no default language was found
|
|
if (!(_psetloc_downlevel_data->iLcidState & __LCID_FULL))
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
}
|
|
|
|
/***
|
|
*BOOL CALLBACK CountryEnumProc - callback routine for GetLcidFromCountry
|
|
*
|
|
*Purpose:
|
|
* Determine if LCID given matches the default language for the
|
|
* country in pchCountry.
|
|
*
|
|
*Entry:
|
|
* lpLcidString - pointer to string with decimal LCID
|
|
* pchCountry - pointer to country name
|
|
* bAbbrevCountry - set if country is three-letter abbreviation
|
|
*
|
|
*Exit:
|
|
* lcidLanguage - lcidCountry - LCID matched
|
|
* FALSE if match occurred to terminate enumeration, else TRUE.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static BOOL CALLBACK CountryEnumProc (_In_z_ PWSTR lpLcidString)
|
|
{
|
|
__crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
|
|
__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data = __acrt_getptd()->_setloc_downlevel_data;
|
|
LCID lcid = LcidFromHexString(lpLcidString);
|
|
wchar_t rgcInfo[120];
|
|
|
|
// test locale for country specified
|
|
if (GetLocaleInfoW(lcid, _psetloc_data->bAbbrevCountry ? LOCALE_SABBREVCTRYNAME
|
|
: LOCALE_SENGCOUNTRY,
|
|
rgcInfo, _countof(rgcInfo)) == 0)
|
|
{
|
|
// set error condition and exit
|
|
_psetloc_downlevel_data->iLcidState = 0;
|
|
return TRUE;
|
|
}
|
|
if (!_wcsicmp(_psetloc_data->pchCountry, rgcInfo))
|
|
{
|
|
// language matched - test if locale country is default
|
|
if (TestDefaultCountry(lcid))
|
|
{
|
|
// this locale has the default language
|
|
_psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = lcid;
|
|
_psetloc_downlevel_data->iLcidState |= __LCID_FULL;
|
|
}
|
|
}
|
|
return (_psetloc_downlevel_data->iLcidState & __LCID_FULL) == 0;
|
|
}
|
|
|
|
/***
|
|
*void GetLcidFromDefault - get default LCIDs
|
|
*
|
|
*Purpose:
|
|
* Set both language and country LCIDs to the system default.
|
|
*
|
|
*Entry:
|
|
* None.
|
|
*
|
|
*Exit:
|
|
* lcidLanguage - set to system LCID
|
|
* lcidCountry - set to system LCID
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static void GetLcidFromDefault (__crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
|
|
{
|
|
_psetloc_downlevel_data->iLcidState |= (__LCID_FULL | __LCID_LANGUAGE);
|
|
_psetloc_downlevel_data->lcidLanguage = _psetloc_downlevel_data->lcidCountry = GetUserDefaultLCID();
|
|
}
|
|
|
|
/***
|
|
*int ProcessCodePage - convert codepage string to numeric value
|
|
*
|
|
*Purpose:
|
|
* Process codepage string consisting of a decimal string, or the
|
|
* special case strings "ACP" and "OCP", for ANSI and OEM codepages,
|
|
* respectively. Null pointer or string returns the ANSI codepage.
|
|
*
|
|
*Entry:
|
|
* lpCodePageStr - pointer to codepage string
|
|
*
|
|
*Exit:
|
|
* Returns numeric value of codepage.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static int ProcessCodePage (LPCWSTR lpCodePageStr, __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
|
|
{
|
|
int iCodePage;
|
|
|
|
if (!lpCodePageStr || !*lpCodePageStr || !wcscmp(lpCodePageStr, L"ACP"))
|
|
{
|
|
// get ANSI codepage for the country LCID
|
|
if (GetLocaleInfoW(_psetloc_downlevel_data->lcidCountry, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
|
|
(LPWSTR) &iCodePage, sizeof(iCodePage) / sizeof(wchar_t)) == 0)
|
|
return 0;
|
|
|
|
if (iCodePage == 0) // for locales have no assoicated ANSI codepage, e.g. Hindi locale
|
|
return GetACP();
|
|
}
|
|
else if (!wcscmp(lpCodePageStr, L"OCP"))
|
|
{
|
|
// get OEM codepage for the country LCID
|
|
if (GetLocaleInfoW(_psetloc_downlevel_data->lcidCountry, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
|
|
(LPWSTR) &iCodePage, sizeof(iCodePage) / sizeof(wchar_t)) == 0)
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// convert decimal string to numeric value
|
|
iCodePage = (int)_wtol(lpCodePageStr);
|
|
}
|
|
|
|
return iCodePage;
|
|
}
|
|
|
|
/***
|
|
*BOOL TestDefaultCountry - determine if default locale for country
|
|
*
|
|
*Purpose:
|
|
* Using a hardcoded list, determine if the locale of the given LCID
|
|
* has the default sublanguage for the locale primary language. The
|
|
* list contains the locales NOT having the default sublanguage.
|
|
*
|
|
*Entry:
|
|
* lcid - LCID of locale to test
|
|
*
|
|
*Exit:
|
|
* Returns TRUE if default sublanguage, else FALSE.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static BOOL TestDefaultCountry (LCID lcid)
|
|
{
|
|
LANGID langid = LANGIDFROMLCID(lcid);
|
|
int i;
|
|
|
|
for (i = 0; i < _countof(__rglangidNotDefault); i++)
|
|
{
|
|
if (langid == __rglangidNotDefault[i])
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/***
|
|
*BOOL TestDefaultLanguage - determine if default locale for language
|
|
*
|
|
*Purpose:
|
|
* Determines if the given LCID has the default sublanguage.
|
|
* If bTestPrimary is set, also allow TRUE when string contains an
|
|
* implicit sublanguage.
|
|
*
|
|
*Entry:
|
|
* LCID - lcid of locale to test
|
|
* bTestPrimary - set if testing if language is primary
|
|
*
|
|
*Exit:
|
|
* Returns TRUE if sublanguage is default for locale tested.
|
|
* If bTestPrimary set, TRUE is language has implied sublanguge.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static BOOL TestDefaultLanguage (LCID lcid, BOOL bTestPrimary, __crt_qualified_locale_data_downlevel* _psetloc_downlevel_data)
|
|
{
|
|
UNREFERENCED_PARAMETER(_psetloc_downlevel_data); // CRT_REFACTOR TODO
|
|
|
|
DWORD dwLanguage;
|
|
LCID lcidDefault = MAKELCID(MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(lcid)), SUBLANG_DEFAULT), SORT_DEFAULT);
|
|
__crt_qualified_locale_data* _psetloc_data = &__acrt_getptd()->_setloc_data;
|
|
|
|
if (GetLocaleInfoW(lcidDefault, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER,
|
|
(LPWSTR) &dwLanguage, sizeof(dwLanguage) / sizeof(wchar_t)) == 0)
|
|
return FALSE;
|
|
|
|
if (lcid != dwLanguage)
|
|
{
|
|
// test if string contains an implicit sublanguage by
|
|
// having a character other than upper/lowercase letters.
|
|
if (bTestPrimary && GetPrimaryLen(_psetloc_data->pchLanguage) == (int)wcslen(_psetloc_data->pchLanguage))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/***
|
|
*LCID LcidFromHexString - convert hex string to value for LCID
|
|
*
|
|
*Purpose:
|
|
* LCID values returned in hex ANSI strings - straight conversion
|
|
*
|
|
*Entry:
|
|
* lpHexString - pointer to hex string to convert
|
|
*
|
|
*Exit:
|
|
* Returns LCID computed.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static LCID LcidFromHexString (_In_z_ PCWSTR lpHexString)
|
|
{
|
|
wchar_t ch;
|
|
DWORD lcid = 0;
|
|
|
|
#pragma warning(disable:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED) // 26018 This is an idiomatic nul termination check that Prefast doesn't understand.
|
|
while ((ch = *lpHexString++) != '\0')
|
|
{
|
|
if (ch >= 'a' && ch <= 'f')
|
|
ch += static_cast<wchar_t>('9' + 1 - 'a');
|
|
else if (ch >= 'A' && ch <= 'F')
|
|
ch += static_cast<wchar_t>('9' + 1 - 'A');
|
|
lcid = lcid * 0x10 + ch - '0';
|
|
}
|
|
|
|
return (LCID)lcid;
|
|
}
|
|
|
|
/***
|
|
*int GetPrimaryLen - get length of primary language name
|
|
*
|
|
*Purpose:
|
|
* Determine primary language string length by scanning until
|
|
* first non-alphabetic character.
|
|
*
|
|
*Entry:
|
|
* pchLanguage - string to scan
|
|
*
|
|
*Exit:
|
|
* Returns length of primary language string.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static int GetPrimaryLen (wchar_t const* pchLanguage)
|
|
{
|
|
int len = 0;
|
|
wchar_t ch;
|
|
|
|
ch = *pchLanguage++;
|
|
while ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
|
|
{
|
|
len++;
|
|
ch = *pchLanguage++;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
} // extern "C"
|