/*** *getqloc.c - get qualified locale * * Copyright (c) 1993-2001, Microsoft Corporation. All rights reserved. * *Purpose: * defines __get_qualified_locale - get complete locale information * *Revision History: * 12-11-92 CFW initial version * 01-08-93 CFW cleaned up file * 02-02-93 CFW Added test for NULL input string fields * 02-08-93 CFW Casts to remove warnings. * 02-18-93 CFW Removed debugging support routines, changed copyright. * 02-18-93 CFW Removed debugging support routines, changed copyright. * 03-01-93 CFW Test code page validity, use ANSI comments. * 03-02-93 CFW Add ISO 3166 3-letter country codes, verify country table. * 03-04-93 CFW Call IsValidCodePage to test code page vailidity. * 03-10-93 CFW Protect table testing code. * 03-17-93 CFW Add __ to lang & ctry info tables, move defs to setlocal.h. * 03-23-93 CFW Make internal functions static, add _ to GetQualifiedLocale. * 03-24-93 CFW Change to _get_qualified_locale, support ".codepage". * 04-06-93 SKS Replace _CRTAPI* with __cdecl * 04-08-93 SKS Replace stricmp() with ANSI-conforming _stricmp() * 04-20-93 CFW Enable all strange countries. * 05-20-93 GJF Include windows.h, not individual win*.h files * 05-24-93 CFW Clean up file (brief is evil). * 09-15-93 CFW Use ANSI conformant "__" names. * 09-22-93 CFW Use __crtxxx internal NLS API wrapper. * 11-09-93 CFW Add code page for __crtxxx(). * 11-11-93 CFW Verify ALL code pages. * 02-04-94 CFW Remove unused param, clean up, new languages, * default is ANSI, allow .ACP/.OCP for code page. * 02-07-94 CFW Back to OEM, NT 3.1 doesn't handle ANSI properly. * 02-24-94 CFW Back to ANSI, we'll use our own table. * 04-04-94 CFW Update NT-supported countries/languages. * 04-25-94 CFW Update countries to new ISO 3166 (1993) standard. * 02-02-95 BWT Update _POSIX_ support * 04-07-95 CFW Remove NT 3.1 hacks, reduce string space. * 02-14-97 RDK Complete rewrite to dynamically use the installed * system locales to determine the best match for the * language and/or country specified. * 02-19-97 RDK Do not use iPrimaryLen if zero. * 02-24-97 RDK For Win95, simulate nonfunctional GetLocaleInfoA * calls with hard-coded values. * 07-07-97 GJF Made arrays of data global and selectany so linker * can eliminate them when possible. * 10-02-98 GJF Replaced IsThisWindowsNT with test of _osplatform. * 11-10-99 PML Try untranslated language string first (vs7#61130). * 05-17-00 GB Translating LCID 0814 to Norwegian-Nynorsk as special * case * 09-06-00 PML Use proper geopolitical terminology (vs7#81673). Also * move data tables to .rdata. * *******************************************************************************/ #include #include #include #include #include #include #if defined(_POSIX_) BOOL __cdecl __get_qualified_locale(const LPLC_STRINGS lpInStr, LPLC_ID lpOutId, LPLC_STRINGS lpOutStr) { return FALSE; } #else //if defined(_POSIX_) // 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 // local structure definitions typedef struct tagLOCALETAB { CHAR * szName; CHAR chAbbrev[4]; } LOCALETAB; typedef struct tagRGLOCINFO { LCID lcid; char chILanguage[8]; char * pchSEngLanguage; char chSAbbrevLangName[4]; char * pchSEngCountry; char chSAbbrevCtryName[4]; char chIDefaultCodepage[8]; char chIDefaultAnsiCodepage[8]; } RGLOCINFO; // function prototypes BOOL __cdecl __get_qualified_locale(const LPLC_STRINGS, LPLC_ID, LPLC_STRINGS); static BOOL TranslateName(const LOCALETAB *, int, const char **); static void GetLcidFromLangCountry(void); static BOOL CALLBACK LangCountryEnumProc(LPSTR); static void GetLcidFromLanguage(void); static BOOL CALLBACK LanguageEnumProc(LPSTR); static void GetLcidFromCountry(void); static BOOL CALLBACK CountryEnumProc(LPSTR); static void GetLcidFromDefault(void); static int ProcessCodePage(LPSTR); static BOOL TestDefaultCountry(LCID); static BOOL TestDefaultLanguage(LCID, BOOL); static int __stdcall crtGetLocaleInfoA(LCID, LCTYPE, LPSTR, int); static LCID LcidFromHexString(LPSTR); static int GetPrimaryLen(LPSTR); // non-NLS language string table __declspec(selectany) const LOCALETAB __rg_language[] = { {"american", "ENU"}, {"american english", "ENU"}, {"american-english", "ENU"}, {"australian", "ENA"}, {"belgian", "NLB"}, {"canadian", "ENC"}, {"chh", "ZHH"}, {"chi", "ZHI"}, {"chinese", "CHS"}, {"chinese-hongkong", "ZHH"}, {"chinese-simplified", "CHS"}, {"chinese-singapore", "ZHI"}, {"chinese-traditional", "CHT"}, {"dutch-belgian", "NLB"}, {"english-american", "ENU"}, {"english-aus", "ENA"}, {"english-belize", "ENL"}, {"english-can", "ENC"}, {"english-caribbean", "ENB"}, {"english-ire", "ENI"}, {"english-jamaica", "ENJ"}, {"english-nz", "ENZ"}, {"english-south africa", "ENS"}, {"english-trinidad y tobago", "ENT"}, {"english-uk", "ENG"}, {"english-us", "ENU"}, {"english-usa", "ENU"}, {"french-belgian", "FRB"}, {"french-canadian", "FRC"}, {"french-luxembourg", "FRL"}, {"french-swiss", "FRS"}, {"german-austrian", "DEA"}, {"german-lichtenstein", "DEC"}, {"german-luxembourg", "DEL"}, {"german-swiss", "DES"}, {"irish-english", "ENI"}, {"italian-swiss", "ITS"}, {"norwegian", "NOR"}, {"norwegian-bokmal", "NOR"}, {"norwegian-nynorsk", "NON"}, {"portuguese-brazilian", "PTB"}, {"spanish-argentina", "ESS"}, {"spanish-bolivia", "ESB"}, {"spanish-chile", "ESL"}, {"spanish-colombia", "ESO"}, {"spanish-costa rica", "ESC"}, {"spanish-dominican republic", "ESD"}, {"spanish-ecuador", "ESF"}, {"spanish-el salvador", "ESE"}, {"spanish-guatemala", "ESG"}, {"spanish-honduras", "ESH"}, {"spanish-mexican", "ESM"}, {"spanish-modern", "ESN"}, {"spanish-nicaragua", "ESI"}, {"spanish-panama", "ESA"}, {"spanish-paraguay", "ESZ"}, {"spanish-peru", "ESR"}, {"spanish-puerto rico", "ESU"}, {"spanish-uruguay", "ESY"}, {"spanish-venezuela", "ESV"}, {"swedish-finland", "SVF"}, {"swiss", "DES"}, {"uk", "ENG"}, {"us", "ENU"}, {"usa", "ENU"} }; // non-NLS country/region string table __declspec( selectany ) const LOCALETAB __rg_country[] = { {"america", "USA"}, {"britain", "GBR"}, {"china", "CHN"}, {"czech", "CZE"}, {"england", "GBR"}, {"great britain", "GBR"}, {"holland", "NLD"}, {"hong-kong", "HKG"}, {"new-zealand", "NZL"}, {"nz", "NZL"}, {"pr china", "CHN"}, {"pr-china", "CHN"}, {"puerto-rico", "PRI"}, {"slovak", "SVK"}, {"south africa", "ZAF"}, {"south korea", "KOR"}, {"south-africa", "ZAF"}, {"south-korea", "KOR"}, {"trinidad & tobago", "TTO"}, {"uk", "GBR"}, {"united-kingdom", "GBR"}, {"united-states", "USA"}, {"us", "USA"}, }; // LANGID's of locales of nondefault languages __declspec( selectany ) const LANGID __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) }; // locale information not supported in Win95 __declspec( selectany ) const RGLOCINFO __rgLocInfo[] = { { 0x040a, "040a", "Spanish - Traditional Sort", "ESP", "Spain", "ESP", "850", "1252" }, { 0x040b, "040b", "Finnish", "FIN", "Finland", "FIN", "850", "1252" }, { 0x040c, "040c", "French", "FRA", "France", "FRA", "850", "1252" }, { 0x040f, "040f", "Icelandic", "ISL", "Iceland", "ISL", "850", "1252" }, { 0x041d, "041d", "Swedish", "SVE", "Sweden", "SWE", "850", "1252" }, { 0x042d, "042d", "Basque", "EUQ", "Spain", "ESP", "850", "1252" }, { 0x080a, "080a", "Spanish", "ESM", "Mexico", "MEX", "850", "1252" }, { 0x080c, "080c", "French", "FRB", "Belgium", "BEL", "850", "1252" }, { 0x0c07, "0c07", "German", "DEA", "Austria", "AUT", "850", "1252" }, { 0x0c09, "0c09", "English", "ENA", "Australia", "AUS", "850", "1252" }, { 0x0c0a, "0c0a", "Spanish - Modern Sort", "ESN", "Spain", "ESP", "850", "1252" }, { 0x0c0c, "0c0c", "French", "FRC", "Canada", "CAN", "850", "1252" }, { 0x100a, "100a", "Spanish", "ESG", "Guatemala", "GTM", "850", "1252" }, { 0x100c, "100c", "French", "FRS", "Switzerland", "CHE", "850", "1252" }, { 0x140a, "140a", "Spanish", "ESC", "Costa Rica", "CRI", "850", "1252" }, { 0x140c, "140c", "French", "FRL", "Luxembourg", "LUX", "850", "1252" }, { 0x180a, "180a", "Spanish", "ESA", "Panama", "PAN", "850", "1252" }, { 0x1c09, "1c09", "English", "ENS", "South Africa", "ZAF", "437", "1252" }, { 0x1c0a, "1c0a", "Spanish", "ESD", "Dominican Republic", "DOM", "850", "1252" }, { 0x200a, "200a", "Spanish", "ESV", "Venezuela", "VEN", "850", "1252" }, { 0x240a, "240a", "Spanish", "ESO", "Colombia", "COL", "850", "1252" }, { 0x280a, "280a", "Spanish", "ESR", "Peru", "PER", "850", "1252" }, { 0x2c0a, "2c0a", "Spanish", "ESS", "Argentina", "ARG", "850", "1252" }, { 0x300a, "300a", "Spanish", "ESF", "Ecuador", "ECU", "850", "1252" }, { 0x340a, "340a", "Spanish", "ESL", "Chile", "CHL", "850", "1252" }, { 0x380a, "380a", "Spanish", "ESY", "Uruguay", "URY", "850", "1252" }, { 0x3c0a, "3c0a", "Spanish", "ESZ", "Paraguay", "PRY", "850", "1252" } }; // static variable to point to GetLocaleInfoA for Windows NT and // crtGetLocaleInfoA for Win95 typedef int (__stdcall * PFNGETLOCALEINFOA)(LCID, LCTYPE, LPSTR, int); static PFNGETLOCALEINFOA pfnGetLocaleInfoA = NULL; // static variables used in locale enumeration callback routines static char * pchLanguage; static char * pchCountry; static int iLcidState; static int iPrimaryLen; static BOOL bAbbrevLanguage; static BOOL bAbbrevCountry; static LCID lcidLanguage; static LCID lcidCountry; /*** *BOOL __get_qualified_locale - 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 __get_qualified_locale(const LPLC_STRINGS lpInStr, LPLC_ID lpOutId, LPLC_STRINGS lpOutStr) { int iCodePage; // initialize pointer to call locale info routine based on operating system if (!pfnGetLocaleInfoA) { pfnGetLocaleInfoA = (_osplatform == VER_PLATFORM_WIN32_NT) ? GetLocaleInfoA : crtGetLocaleInfoA; } if (!lpInStr) { // if no input defined, just use default LCID GetLcidFromDefault(); } else { pchLanguage = lpInStr->szLanguage; // convert non-NLS country strings to three-letter abbreviations pchCountry = lpInStr->szCountry; if (pchCountry && *pchCountry) TranslateName(__rg_country, sizeof(__rg_country) / sizeof(LOCALETAB) - 1, &pchCountry); iLcidState = 0; if (pchLanguage && *pchLanguage) { if (pchCountry && *pchCountry) { // both language and country strings defined GetLcidFromLangCountry(); } else { // language string defined, but country string undefined GetLcidFromLanguage(); } if (!iLcidState) { // first attempt failed, try substituting the language name // convert non-NLS language strings to three-letter abbrevs if (TranslateName(__rg_language, sizeof(__rg_language) / sizeof(LOCALETAB) - 1, &pchLanguage)) { if (pchCountry && *pchCountry) { GetLcidFromLangCountry(); } else { GetLcidFromLanguage(); } } } } else { if (pchCountry && *pchCountry) { // country string defined, but language string undefined GetLcidFromCountry(); } else { // both language and country strings undefined GetLcidFromDefault(); } } } // test for error in LCID processing if (!iLcidState) return FALSE; // process codepage value iCodePage = ProcessCodePage(lpInStr->szCodePage); // verify codepage validity if (!iCodePage || !IsValidCodePage((WORD)iCodePage)) return FALSE; // verify locale is installed if (!IsValidLocale(lcidLanguage, LCID_INSTALLED)) return FALSE; // set numeric LCID and codepage results if (lpOutId) { lpOutId->wLanguage = LANGIDFROMLCID(lcidLanguage); lpOutId->wCountry = LANGIDFROMLCID(lcidCountry); lpOutId->wCodePage = (WORD)iCodePage; } // set string language, country, and codepage results if (lpOutStr) { // Norwegian-Nynorsk is special case because Langauge and country pair // for Norwegian-Nynorsk and Norwegian is same ie. Norwegian_Norway if ( lpOutId->wLanguage == 0x0814) strcpy(lpOutStr->szLanguage, "Norwegian-Nynorsk"); else if ((*pfnGetLocaleInfoA)(lcidLanguage, LOCALE_SENGLANGUAGE, lpOutStr->szLanguage, MAX_LANG_LEN) == 0) return FALSE; if ((*pfnGetLocaleInfoA)(lcidCountry, LOCALE_SENGCOUNTRY, lpOutStr->szCountry, MAX_CTRY_LEN) == 0) return FALSE; _itoa((int)iCodePage, (char *)lpOutStr->szCodePage, 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 LOCALETAB 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 LOCALETAB * lpTable, int high, const char ** ppchName) { int i; int cmp = 1; int low = 0; // typical binary search - do until no more to search or match while (low <= high && cmp != 0) { i = (low + high) / 2; cmp = _stricmp(*ppchName, (const char *)(*(lpTable + i)).szName); if (cmp == 0) *ppchName = (*(lpTable + i)).chAbbrev; else if (cmp < 0) high = i - 1; else low = i + 1; } return !cmp; } /*** *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 (void) { // initialize static variables for callback use bAbbrevLanguage = strlen(pchLanguage) == 3; bAbbrevCountry = strlen(pchCountry) == 3; lcidLanguage = 0; iPrimaryLen = bAbbrevLanguage ? 2 : GetPrimaryLen(pchLanguage); EnumSystemLocalesA(LangCountryEnumProc, LCID_INSTALLED); // locale value is invalid if the language was not installed or the language // was not available for the country specified if (!(iLcidState & __LCID_LANGUAGE) || !(iLcidState & __LCID_EXISTS) || !(iLcidState & (__LCID_FULL | __LCID_PRIMARY | __LCID_DEFAULT))) 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 (LPSTR lpLcidString) { LCID lcid = LcidFromHexString(lpLcidString); char rgcInfo[120]; // test locale country against input value if ((*pfnGetLocaleInfoA)(lcid, bAbbrevCountry ? LOCALE_SABBREVCTRYNAME : LOCALE_SENGCOUNTRY, rgcInfo, sizeof(rgcInfo)) == 0) { // set error condition and exit iLcidState = 0; return TRUE; } if (!_stricmp(pchCountry, rgcInfo)) { // country matched - test for language match if ((*pfnGetLocaleInfoA)(lcid, bAbbrevLanguage ? LOCALE_SABBREVLANGNAME : LOCALE_SENGLANGUAGE, rgcInfo, sizeof(rgcInfo)) == 0) { // set error condition and exit iLcidState = 0; return TRUE; } if (!_stricmp(pchLanguage, rgcInfo)) { // language matched also - set state and value iLcidState |= (__LCID_FULL | __LCID_LANGUAGE | __LCID_EXISTS); lcidLanguage = lcidCountry = lcid; } // test if match already for primary langauage else if (!(iLcidState & __LCID_PRIMARY)) { // if not, use iPrimaryLen to partial match language string if (iPrimaryLen && !_strnicmp(pchLanguage, rgcInfo, iPrimaryLen)) { // primary language matched - set state and country LCID iLcidState |= __LCID_PRIMARY; lcidCountry = lcid; // if language is primary only (no subtype), set language LCID if ((int)strlen(pchLanguage) == iPrimaryLen) lcidLanguage = lcid; } // test if default language already defined else if (!(iLcidState & __LCID_DEFAULT)) { // if not, test if locale language is default for country if (TestDefaultCountry(lcid)) { // default language for country - set state, value iLcidState |= __LCID_DEFAULT; lcidCountry = lcid; } } } } // test if input language both exists and default primary language defined if ((iLcidState & (__LCID_LANGUAGE | __LCID_EXISTS)) != (__LCID_LANGUAGE | __LCID_EXISTS)) { // test language match to determine whether it is installed if ((*pfnGetLocaleInfoA)(lcid, bAbbrevLanguage ? LOCALE_SABBREVLANGNAME : LOCALE_SENGLANGUAGE, rgcInfo, sizeof(rgcInfo)) == 0) { // set error condition and exit iLcidState = 0; return TRUE; } if (!_stricmp(pchLanguage, rgcInfo)) { // language matched - set bit for existance iLcidState |= __LCID_EXISTS; if (bAbbrevLanguage) { // abbreviation - set state // also set language LCID if not set already iLcidState |= __LCID_LANGUAGE; if (!lcidLanguage) lcidLanguage = lcid; } // test if language is primary only (no sublanguage) else if (iPrimaryLen && ((int)strlen(pchLanguage) == iPrimaryLen)) { // primary language only - test if default LCID if (TestDefaultLanguage(lcid, TRUE)) { // default primary language - set state // also set LCID if not set already iLcidState |= __LCID_LANGUAGE; if (!lcidLanguage) lcidLanguage = lcid; } } else { // language with sublanguage - set state // also set LCID if not set already iLcidState |= __LCID_LANGUAGE; if (!lcidLanguage) lcidLanguage = lcid; } } else if (!bAbbrevLanguage && iPrimaryLen && !_strnicmp(pchLanguage, rgcInfo, iPrimaryLen)) { // primary language match - test for default language only if (TestDefaultLanguage(lcid, FALSE)) { // default primary language - set state // also set LCID if not set already iLcidState |= __LCID_LANGUAGE; if (!lcidLanguage) lcidLanguage = lcid; } } } // if LOCALE_FULL set, return FALSE to stop enumeration, // else return TRUE to continue return (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 (void) { // initialize static variables for callback use bAbbrevLanguage = strlen(pchLanguage) == 3; iPrimaryLen = bAbbrevLanguage ? 2 : GetPrimaryLen(pchLanguage); EnumSystemLocalesA(LanguageEnumProc, LCID_INSTALLED); // locale value is invalid if the language was not installed // or the language was not available for the country specified if (!(iLcidState & __LCID_FULL)) 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 (LPSTR lpLcidString) { LCID lcid = LcidFromHexString(lpLcidString); char rgcInfo[120]; // test locale for language specified if ((*pfnGetLocaleInfoA)(lcid, bAbbrevLanguage ? LOCALE_SABBREVLANGNAME : LOCALE_SENGLANGUAGE, rgcInfo, sizeof(rgcInfo)) == 0) { // set error condition and exit iLcidState = 0; return TRUE; } if (!_stricmp(pchLanguage, rgcInfo)) { // language matched - test if locale country is default // or if locale is implied in the language string if (bAbbrevLanguage || TestDefaultLanguage(lcid, TRUE)) { // this locale has the default country lcidLanguage = lcidCountry = lcid; iLcidState |= __LCID_FULL; } } else if (!bAbbrevLanguage && iPrimaryLen && !_strnicmp(pchLanguage, rgcInfo, iPrimaryLen)) { // primary language matched - test if locale country is default if (TestDefaultLanguage(lcid, FALSE)) { // this is the default country lcidLanguage = lcidCountry = lcid; iLcidState |= __LCID_FULL; } } return (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 (void) { bAbbrevCountry = strlen(pchCountry) == 3; EnumSystemLocalesA(CountryEnumProc, LCID_INSTALLED); // locale value is invalid if the country was not defined or // no default language was found if (!(iLcidState & __LCID_FULL)) 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 (LPSTR lpLcidString) { LCID lcid = LcidFromHexString(lpLcidString); char rgcInfo[120]; // test locale for country specified if ((*pfnGetLocaleInfoA)(lcid, bAbbrevCountry ? LOCALE_SABBREVCTRYNAME : LOCALE_SENGCOUNTRY, rgcInfo, sizeof(rgcInfo)) == 0) { // set error condition and exit iLcidState = 0; return TRUE; } if (!_stricmp(pchCountry, rgcInfo)) { // language matched - test if locale country is default if (TestDefaultCountry(lcid)) { // this locale has the default language lcidLanguage = lcidCountry = lcid; iLcidState |= __LCID_FULL; } } return (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 (void) { iLcidState |= (__LCID_FULL | __LCID_LANGUAGE); lcidLanguage = 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 (LPSTR lpCodePageStr) { char chCodePage[8]; if (!lpCodePageStr || !*lpCodePageStr || !strcmp(lpCodePageStr, "ACP")) { // get ANSI codepage for the country LCID if ((*pfnGetLocaleInfoA)(lcidCountry, LOCALE_IDEFAULTANSICODEPAGE, chCodePage, sizeof(chCodePage)) == 0) return 0; lpCodePageStr = chCodePage; } else if (!strcmp(lpCodePageStr, "OCP")) { // get OEM codepage for the country LCID if ((*pfnGetLocaleInfoA)(lcidCountry, LOCALE_IDEFAULTCODEPAGE, chCodePage, sizeof(chCodePage)) == 0) return 0; lpCodePageStr = chCodePage; } // convert decimal string to numeric value return (int)atol(lpCodePageStr); } /*** *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 < sizeof(__rglangidNotDefault) / sizeof(LANGID); 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) { char rgcInfo[120]; LCID lcidDefault = MAKELCID(MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(lcid)), SUBLANG_DEFAULT), SORT_DEFAULT); if ((*pfnGetLocaleInfoA)(lcidDefault, LOCALE_ILANGUAGE, rgcInfo, sizeof(rgcInfo)) == 0) return FALSE; if (lcid != LcidFromHexString(rgcInfo)) { // test if string contains an implicit sublanguage by // having a character other than upper/lowercase letters. if (bTestPrimary && GetPrimaryLen(pchLanguage) == (int)strlen(pchLanguage)) return FALSE; } return TRUE; } /*** *int crtGetLocalInfoA - get locale information for Win95 * *Purpose: * For Win95, some calls to GetLocaleInfoA return incorrect results. * Simulate these calls with values looked up in a hard-coded table. * *Entry: * lcid - LCID of locale to get information from * lctype - index of information selection * lpdata - pointer to output string * cchdata - size of output string (including null) * *Exit: * lpdata - return string of locale information * returns TRUE if successful, else FALSE * *Exceptions: * *******************************************************************************/ static int __stdcall crtGetLocaleInfoA (LCID lcid, LCTYPE lctype, LPSTR lpdata, int cchdata) { int i; int low = 0; int high = sizeof(__rgLocInfo) / sizeof(RGLOCINFO) - 1; const char * pchResult = NULL; // typical binary search - do until no more to search while (low <= high) { i = (low + high) / 2; if (lcid == __rgLocInfo[i].lcid) { // LCID matched - test for valid LCTYPE to simulate call switch (lctype) { case LOCALE_ILANGUAGE: pchResult = __rgLocInfo[i].chILanguage; break; case LOCALE_SENGLANGUAGE: pchResult = __rgLocInfo[i].pchSEngLanguage; break; case LOCALE_SABBREVLANGNAME: pchResult = __rgLocInfo[i].chSAbbrevLangName; break; case LOCALE_SENGCOUNTRY: pchResult = __rgLocInfo[i].pchSEngCountry; break; case LOCALE_SABBREVCTRYNAME: pchResult = __rgLocInfo[i].chSAbbrevCtryName; break; case LOCALE_IDEFAULTCODEPAGE: pchResult = __rgLocInfo[i].chIDefaultCodepage; break; case LOCALE_IDEFAULTANSICODEPAGE: pchResult = __rgLocInfo[i].chIDefaultAnsiCodepage; default: break; } if (!pchResult || cchdata < 1) // if LCTYPE did not match, break to use normal routine break; else { // copy data as much as possible to result and null-terminate strncpy(lpdata, pchResult, cchdata - 1); *(lpdata + cchdata - 1) = '\0'; return 1; } } else if (lcid < __rgLocInfo[i].lcid) high = i - 1; else low = i + 1; } // LCID not found or LCTYPE not simulated return GetLocaleInfoA(lcid,lctype, lpdata, cchdata); } /*** *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 (LPSTR lpHexString) { char ch; DWORD lcid = 0; while (ch = *lpHexString++) { if (ch >= 'a' && ch <= 'f') ch += '9' + 1 - 'a'; else if (ch >= 'A' && ch <= 'F') ch += '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 (LPSTR pchLanguage) { int len = 0; char ch; ch = *pchLanguage++; while ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { len++; ch = *pchLanguage++; } return len; } #endif //if defined(_POSIX_)