Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1123 lines
39 KiB

  1. /***
  2. *getqloc.c - get qualified locale
  3. *
  4. * Copyright (c) 1993-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. * defines __get_qualified_locale - get complete locale information
  8. *
  9. *Revision History:
  10. * 12-11-92 CFW initial version
  11. * 01-08-93 CFW cleaned up file
  12. * 02-02-93 CFW Added test for NULL input string fields
  13. * 02-08-93 CFW Casts to remove warnings.
  14. * 02-18-93 CFW Removed debugging support routines, changed copyright.
  15. * 02-18-93 CFW Removed debugging support routines, changed copyright.
  16. * 03-01-93 CFW Test code page validity, use ANSI comments.
  17. * 03-02-93 CFW Add ISO 3166 3-letter country codes, verify country table.
  18. * 03-04-93 CFW Call IsValidCodePage to test code page vailidity.
  19. * 03-10-93 CFW Protect table testing code.
  20. * 03-17-93 CFW Add __ to lang & ctry info tables, move defs to setlocal.h.
  21. * 03-23-93 CFW Make internal functions static, add _ to GetQualifiedLocale.
  22. * 03-24-93 CFW Change to _get_qualified_locale, support ".codepage".
  23. * 04-06-93 SKS Replace _CRTAPI* with __cdecl
  24. * 04-08-93 SKS Replace stricmp() with ANSI-conforming _stricmp()
  25. * 04-20-93 CFW Enable all strange countries.
  26. * 05-20-93 GJF Include windows.h, not individual win*.h files
  27. * 05-24-93 CFW Clean up file (brief is evil).
  28. * 09-15-93 CFW Use ANSI conformant "__" names.
  29. * 09-22-93 CFW Use __crtxxx internal NLS API wrapper.
  30. * 11-09-93 CFW Add code page for __crtxxx().
  31. * 11-11-93 CFW Verify ALL code pages.
  32. * 02-04-94 CFW Remove unused param, clean up, new languages,
  33. * default is ANSI, allow .ACP/.OCP for code page.
  34. * 02-07-94 CFW Back to OEM, NT 3.1 doesn't handle ANSI properly.
  35. * 02-24-94 CFW Back to ANSI, we'll use our own table.
  36. * 04-04-94 CFW Update NT-supported countries/languages.
  37. * 04-25-94 CFW Update countries to new ISO 3166 (1993) standard.
  38. * 02-02-95 BWT Update _POSIX_ support
  39. * 04-07-95 CFW Remove NT 3.1 hacks, reduce string space.
  40. * 02-14-97 RDK Complete rewrite to dynamically use the installed
  41. * system locales to determine the best match for the
  42. * language and/or country specified.
  43. * 02-19-97 RDK Do not use iPrimaryLen if zero.
  44. * 02-24-97 RDK For Win95, simulate nonfunctional GetLocaleInfoA
  45. * calls with hard-coded values.
  46. * 07-07-97 GJF Made arrays of data global and selectany so linker
  47. * can eliminate them when possible.
  48. * 10-02-98 GJF Replaced IsThisWindowsNT with test of _osplatform.
  49. * 11-10-99 PML Try untranslated language string first (vs7#61130).
  50. * 05-17-00 GB Translating LCID 0814 to Norwegian-Nynorsk as special
  51. * case
  52. * 09-06-00 PML Use proper geopolitical terminology (vs7#81673). Also
  53. * move data tables to .rdata.
  54. * 02-20-02 BWT prefast fixes - Check lpInStr before indirecting (for iCodePage)
  55. *
  56. *******************************************************************************/
  57. #include <cruntime.h>
  58. #include <stdlib.h>
  59. #include <string.h>
  60. #include <windows.h>
  61. #include <setlocal.h>
  62. #include <awint.h>
  63. #if defined(_POSIX_)
  64. BOOL __cdecl __get_qualified_locale(const LPLC_STRINGS lpInStr, LPLC_ID lpOutId,
  65. LPLC_STRINGS lpOutStr)
  66. {
  67. return FALSE;
  68. }
  69. #else //if defined(_POSIX_)
  70. // local defines
  71. #define __LCID_DEFAULT 0x1 // default language locale for country
  72. #define __LCID_PRIMARY 0x2 // primary language locale for country
  73. #define __LCID_FULL 0x4 // fully matched language locale for country
  74. #define __LCID_LANGUAGE 0x100 // language default seen
  75. #define __LCID_EXISTS 0x200 // language is installed
  76. // local structure definitions
  77. typedef struct tagLOCALETAB
  78. {
  79. CHAR * szName;
  80. CHAR chAbbrev[4];
  81. } LOCALETAB;
  82. typedef struct tagRGLOCINFO
  83. {
  84. LCID lcid;
  85. char chILanguage[8];
  86. char * pchSEngLanguage;
  87. char chSAbbrevLangName[4];
  88. char * pchSEngCountry;
  89. char chSAbbrevCtryName[4];
  90. char chIDefaultCodepage[8];
  91. char chIDefaultAnsiCodepage[8];
  92. } RGLOCINFO;
  93. // function prototypes
  94. BOOL __cdecl __get_qualified_locale(const LPLC_STRINGS, LPLC_ID, LPLC_STRINGS);
  95. static BOOL TranslateName(const LOCALETAB *, int, const char **);
  96. static void GetLcidFromLangCountry(void);
  97. static BOOL CALLBACK LangCountryEnumProc(LPSTR);
  98. static void GetLcidFromLanguage(void);
  99. static BOOL CALLBACK LanguageEnumProc(LPSTR);
  100. static void GetLcidFromCountry(void);
  101. static BOOL CALLBACK CountryEnumProc(LPSTR);
  102. static void GetLcidFromDefault(void);
  103. static int ProcessCodePage(LPSTR);
  104. static BOOL TestDefaultCountry(LCID);
  105. static BOOL TestDefaultLanguage(LCID, BOOL);
  106. static int __stdcall crtGetLocaleInfoA(LCID, LCTYPE, LPSTR, int);
  107. static LCID LcidFromHexString(LPSTR);
  108. static int GetPrimaryLen(LPSTR);
  109. // non-NLS language string table
  110. __declspec(selectany) const LOCALETAB __rg_language[] =
  111. {
  112. {"american", "ENU"},
  113. {"american english", "ENU"},
  114. {"american-english", "ENU"},
  115. {"australian", "ENA"},
  116. {"belgian", "NLB"},
  117. {"canadian", "ENC"},
  118. {"chh", "ZHH"},
  119. {"chi", "ZHI"},
  120. {"chinese", "CHS"},
  121. {"chinese-hongkong", "ZHH"},
  122. {"chinese-simplified", "CHS"},
  123. {"chinese-singapore", "ZHI"},
  124. {"chinese-traditional", "CHT"},
  125. {"dutch-belgian", "NLB"},
  126. {"english-american", "ENU"},
  127. {"english-aus", "ENA"},
  128. {"english-belize", "ENL"},
  129. {"english-can", "ENC"},
  130. {"english-caribbean", "ENB"},
  131. {"english-ire", "ENI"},
  132. {"english-jamaica", "ENJ"},
  133. {"english-nz", "ENZ"},
  134. {"english-south africa", "ENS"},
  135. {"english-trinidad y tobago", "ENT"},
  136. {"english-uk", "ENG"},
  137. {"english-us", "ENU"},
  138. {"english-usa", "ENU"},
  139. {"french-belgian", "FRB"},
  140. {"french-canadian", "FRC"},
  141. {"french-luxembourg", "FRL"},
  142. {"french-swiss", "FRS"},
  143. {"german-austrian", "DEA"},
  144. {"german-lichtenstein", "DEC"},
  145. {"german-luxembourg", "DEL"},
  146. {"german-swiss", "DES"},
  147. {"irish-english", "ENI"},
  148. {"italian-swiss", "ITS"},
  149. {"norwegian", "NOR"},
  150. {"norwegian-bokmal", "NOR"},
  151. {"norwegian-nynorsk", "NON"},
  152. {"portuguese-brazilian", "PTB"},
  153. {"spanish-argentina", "ESS"},
  154. {"spanish-bolivia", "ESB"},
  155. {"spanish-chile", "ESL"},
  156. {"spanish-colombia", "ESO"},
  157. {"spanish-costa rica", "ESC"},
  158. {"spanish-dominican republic", "ESD"},
  159. {"spanish-ecuador", "ESF"},
  160. {"spanish-el salvador", "ESE"},
  161. {"spanish-guatemala", "ESG"},
  162. {"spanish-honduras", "ESH"},
  163. {"spanish-mexican", "ESM"},
  164. {"spanish-modern", "ESN"},
  165. {"spanish-nicaragua", "ESI"},
  166. {"spanish-panama", "ESA"},
  167. {"spanish-paraguay", "ESZ"},
  168. {"spanish-peru", "ESR"},
  169. {"spanish-puerto rico", "ESU"},
  170. {"spanish-uruguay", "ESY"},
  171. {"spanish-venezuela", "ESV"},
  172. {"swedish-finland", "SVF"},
  173. {"swiss", "DES"},
  174. {"uk", "ENG"},
  175. {"us", "ENU"},
  176. {"usa", "ENU"}
  177. };
  178. // non-NLS country/region string table
  179. __declspec( selectany ) const LOCALETAB __rg_country[] =
  180. {
  181. {"america", "USA"},
  182. {"britain", "GBR"},
  183. {"china", "CHN"},
  184. {"czech", "CZE"},
  185. {"england", "GBR"},
  186. {"great britain", "GBR"},
  187. {"holland", "NLD"},
  188. {"hong-kong", "HKG"},
  189. {"new-zealand", "NZL"},
  190. {"nz", "NZL"},
  191. {"pr china", "CHN"},
  192. {"pr-china", "CHN"},
  193. {"puerto-rico", "PRI"},
  194. {"slovak", "SVK"},
  195. {"south africa", "ZAF"},
  196. {"south korea", "KOR"},
  197. {"south-africa", "ZAF"},
  198. {"south-korea", "KOR"},
  199. {"trinidad & tobago", "TTO"},
  200. {"uk", "GBR"},
  201. {"united-kingdom", "GBR"},
  202. {"united-states", "USA"},
  203. {"us", "USA"},
  204. };
  205. // LANGID's of locales of nondefault languages
  206. __declspec( selectany ) const LANGID __rglangidNotDefault[] =
  207. {
  208. MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_CANADIAN),
  209. MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC),
  210. MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_LUXEMBOURG),
  211. MAKELANGID(LANG_AFRIKAANS, SUBLANG_DEFAULT),
  212. MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN),
  213. MAKELANGID(LANG_BASQUE, SUBLANG_DEFAULT),
  214. MAKELANGID(LANG_CATALAN, SUBLANG_DEFAULT),
  215. MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_SWISS),
  216. MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN_SWISS),
  217. MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH_FINLAND)
  218. };
  219. // locale information not supported in Win95
  220. __declspec( selectany ) const RGLOCINFO __rgLocInfo[] =
  221. {
  222. { 0x040a, "040a", "Spanish - Traditional Sort", "ESP", "Spain",
  223. "ESP", "850", "1252" },
  224. { 0x040b, "040b", "Finnish", "FIN", "Finland", "FIN", "850", "1252" },
  225. { 0x040c, "040c", "French", "FRA", "France", "FRA", "850", "1252" },
  226. { 0x040f, "040f", "Icelandic", "ISL", "Iceland", "ISL", "850", "1252" },
  227. { 0x041d, "041d", "Swedish", "SVE", "Sweden", "SWE", "850", "1252" },
  228. { 0x042d, "042d", "Basque", "EUQ", "Spain", "ESP", "850", "1252" },
  229. { 0x080a, "080a", "Spanish", "ESM", "Mexico", "MEX", "850", "1252" },
  230. { 0x080c, "080c", "French", "FRB", "Belgium", "BEL", "850", "1252" },
  231. { 0x0c07, "0c07", "German", "DEA", "Austria", "AUT", "850", "1252" },
  232. { 0x0c09, "0c09", "English", "ENA", "Australia", "AUS", "850", "1252" },
  233. { 0x0c0a, "0c0a", "Spanish - Modern Sort", "ESN", "Spain",
  234. "ESP", "850", "1252" },
  235. { 0x0c0c, "0c0c", "French", "FRC", "Canada", "CAN", "850", "1252" },
  236. { 0x100a, "100a", "Spanish", "ESG", "Guatemala", "GTM", "850", "1252" },
  237. { 0x100c, "100c", "French", "FRS", "Switzerland", "CHE", "850", "1252" },
  238. { 0x140a, "140a", "Spanish", "ESC", "Costa Rica", "CRI", "850", "1252" },
  239. { 0x140c, "140c", "French", "FRL", "Luxembourg", "LUX", "850", "1252" },
  240. { 0x180a, "180a", "Spanish", "ESA", "Panama", "PAN", "850", "1252" },
  241. { 0x1c09, "1c09", "English", "ENS", "South Africa", "ZAF", "437", "1252" },
  242. { 0x1c0a, "1c0a", "Spanish", "ESD", "Dominican Republic",
  243. "DOM", "850", "1252" },
  244. { 0x200a, "200a", "Spanish", "ESV", "Venezuela", "VEN", "850", "1252" },
  245. { 0x240a, "240a", "Spanish", "ESO", "Colombia", "COL", "850", "1252" },
  246. { 0x280a, "280a", "Spanish", "ESR", "Peru", "PER", "850", "1252" },
  247. { 0x2c0a, "2c0a", "Spanish", "ESS", "Argentina", "ARG", "850", "1252" },
  248. { 0x300a, "300a", "Spanish", "ESF", "Ecuador", "ECU", "850", "1252" },
  249. { 0x340a, "340a", "Spanish", "ESL", "Chile", "CHL", "850", "1252" },
  250. { 0x380a, "380a", "Spanish", "ESY", "Uruguay", "URY", "850", "1252" },
  251. { 0x3c0a, "3c0a", "Spanish", "ESZ", "Paraguay", "PRY", "850", "1252" }
  252. };
  253. // static variable to point to GetLocaleInfoA for Windows NT and
  254. // crtGetLocaleInfoA for Win95
  255. typedef int (__stdcall * PFNGETLOCALEINFOA)(LCID, LCTYPE, LPSTR, int);
  256. static PFNGETLOCALEINFOA pfnGetLocaleInfoA = NULL;
  257. // static variables used in locale enumeration callback routines
  258. static char * pchLanguage;
  259. static char * pchCountry;
  260. static int iLcidState;
  261. static int iPrimaryLen;
  262. static BOOL bAbbrevLanguage;
  263. static BOOL bAbbrevCountry;
  264. static LCID lcidLanguage;
  265. static LCID lcidCountry;
  266. /***
  267. *BOOL __get_qualified_locale - return fully qualified locale
  268. *
  269. *Purpose:
  270. * get default locale, qualify partially complete locales
  271. *
  272. *Entry:
  273. * lpInStr - input strings to be qualified
  274. * lpOutId - pointer to numeric LCIDs and codepage output
  275. * lpOutStr - pointer to string LCIDs and codepage output
  276. *
  277. *Exit:
  278. * TRUE if success, qualified locale is valid
  279. * FALSE if failure
  280. *
  281. *Exceptions:
  282. *
  283. *******************************************************************************/
  284. BOOL __cdecl __get_qualified_locale(const LPLC_STRINGS lpInStr, LPLC_ID lpOutId,
  285. LPLC_STRINGS lpOutStr)
  286. {
  287. int iCodePage;
  288. // initialize pointer to call locale info routine based on operating system
  289. if (!pfnGetLocaleInfoA)
  290. {
  291. pfnGetLocaleInfoA = (_osplatform == VER_PLATFORM_WIN32_NT) ?
  292. GetLocaleInfoA : crtGetLocaleInfoA;
  293. }
  294. if (!lpInStr)
  295. {
  296. // if no input defined, just use default LCID
  297. GetLcidFromDefault();
  298. }
  299. else
  300. {
  301. pchLanguage = lpInStr->szLanguage;
  302. // convert non-NLS country strings to three-letter abbreviations
  303. pchCountry = lpInStr->szCountry;
  304. if (pchCountry && *pchCountry)
  305. TranslateName(__rg_country,
  306. sizeof(__rg_country) / sizeof(LOCALETAB) - 1,
  307. &pchCountry);
  308. iLcidState = 0;
  309. if (pchLanguage && *pchLanguage)
  310. {
  311. if (pchCountry && *pchCountry)
  312. {
  313. // both language and country strings defined
  314. GetLcidFromLangCountry();
  315. }
  316. else
  317. {
  318. // language string defined, but country string undefined
  319. GetLcidFromLanguage();
  320. }
  321. if (!iLcidState) {
  322. // first attempt failed, try substituting the language name
  323. // convert non-NLS language strings to three-letter abbrevs
  324. if (TranslateName(__rg_language,
  325. sizeof(__rg_language) / sizeof(LOCALETAB) - 1,
  326. &pchLanguage))
  327. {
  328. if (pchCountry && *pchCountry)
  329. {
  330. GetLcidFromLangCountry();
  331. }
  332. else
  333. {
  334. GetLcidFromLanguage();
  335. }
  336. }
  337. }
  338. }
  339. else
  340. {
  341. if (pchCountry && *pchCountry)
  342. {
  343. // country string defined, but language string undefined
  344. GetLcidFromCountry();
  345. }
  346. else
  347. {
  348. // both language and country strings undefined
  349. GetLcidFromDefault();
  350. }
  351. }
  352. }
  353. // test for error in LCID processing
  354. if (!iLcidState)
  355. return FALSE;
  356. // process codepage value
  357. iCodePage = ProcessCodePage(lpInStr ? lpInStr->szCodePage: NULL);
  358. // verify codepage validity
  359. if (!iCodePage || !IsValidCodePage((WORD)iCodePage))
  360. return FALSE;
  361. // verify locale is installed
  362. if (!IsValidLocale(lcidLanguage, LCID_INSTALLED))
  363. return FALSE;
  364. // set numeric LCID and codepage results
  365. if (lpOutId)
  366. {
  367. lpOutId->wLanguage = LANGIDFROMLCID(lcidLanguage);
  368. lpOutId->wCountry = LANGIDFROMLCID(lcidCountry);
  369. lpOutId->wCodePage = (WORD)iCodePage;
  370. }
  371. // set string language, country, and codepage results
  372. if (lpOutStr)
  373. {
  374. // Norwegian-Nynorsk is special case because Langauge and country pair
  375. // for Norwegian-Nynorsk and Norwegian is same ie. Norwegian_Norway
  376. if ( lpOutId->wLanguage == 0x0814)
  377. strcpy(lpOutStr->szLanguage, "Norwegian-Nynorsk");
  378. else if ((*pfnGetLocaleInfoA)(lcidLanguage, LOCALE_SENGLANGUAGE,
  379. lpOutStr->szLanguage, MAX_LANG_LEN) == 0)
  380. return FALSE;
  381. if ((*pfnGetLocaleInfoA)(lcidCountry, LOCALE_SENGCOUNTRY,
  382. lpOutStr->szCountry, MAX_CTRY_LEN) == 0)
  383. return FALSE;
  384. _itoa((int)iCodePage, (char *)lpOutStr->szCodePage, 10);
  385. }
  386. return TRUE;
  387. }
  388. /***
  389. *BOOL TranslateName - convert known non-NLS string to NLS equivalent
  390. *
  391. *Purpose:
  392. * Provide compatibility with existing code for non-NLS strings
  393. *
  394. *Entry:
  395. * lpTable - pointer to LOCALETAB used for translation
  396. * high - maximum index of table (size - 1)
  397. * ppchName - pointer to pointer of string to translate
  398. *
  399. *Exit:
  400. * ppchName - pointer to pointer of string possibly translated
  401. * TRUE if string translated, FALSE if unchanged
  402. *
  403. *Exceptions:
  404. *
  405. *******************************************************************************/
  406. static BOOL TranslateName (
  407. const LOCALETAB * lpTable,
  408. int high,
  409. const char ** ppchName)
  410. {
  411. int i;
  412. int cmp = 1;
  413. int low = 0;
  414. // typical binary search - do until no more to search or match
  415. while (low <= high && cmp != 0)
  416. {
  417. i = (low + high) / 2;
  418. cmp = _stricmp(*ppchName, (const char *)(*(lpTable + i)).szName);
  419. if (cmp == 0)
  420. *ppchName = (*(lpTable + i)).chAbbrev;
  421. else if (cmp < 0)
  422. high = i - 1;
  423. else
  424. low = i + 1;
  425. }
  426. return !cmp;
  427. }
  428. /***
  429. *void GetLcidFromLangCountry - get LCIDs from language and country strings
  430. *
  431. *Purpose:
  432. * Match the best LCIDs to the language and country string given.
  433. * After global variables are initialized, the LangCountryEnumProc
  434. * routine is registered as an EnumSystemLocalesA callback to actually
  435. * perform the matching as the LCIDs are enumerated.
  436. *
  437. *Entry:
  438. * pchLanguage - language string
  439. * bAbbrevLanguage - language string is a three-letter abbreviation
  440. * pchCountry - country string
  441. * bAbbrevCountry - country string ia a three-letter abbreviation
  442. * iPrimaryLen - length of language string with primary name
  443. *
  444. *Exit:
  445. * lcidLanguage - LCID of language string
  446. * lcidCountry - LCID of country string
  447. *
  448. *Exceptions:
  449. *
  450. *******************************************************************************/
  451. static void GetLcidFromLangCountry (void)
  452. {
  453. // initialize static variables for callback use
  454. bAbbrevLanguage = strlen(pchLanguage) == 3;
  455. bAbbrevCountry = strlen(pchCountry) == 3;
  456. lcidLanguage = 0;
  457. iPrimaryLen = bAbbrevLanguage ? 2 : GetPrimaryLen(pchLanguage);
  458. EnumSystemLocalesA(LangCountryEnumProc, LCID_INSTALLED);
  459. // locale value is invalid if the language was not installed or the language
  460. // was not available for the country specified
  461. if (!(iLcidState & __LCID_LANGUAGE) || !(iLcidState & __LCID_EXISTS) ||
  462. !(iLcidState & (__LCID_FULL | __LCID_PRIMARY | __LCID_DEFAULT)))
  463. iLcidState = 0;
  464. }
  465. /***
  466. *BOOL CALLBACK LangCountryEnumProc - callback routine for GetLcidFromLangCountry
  467. *
  468. *Purpose:
  469. * Determine if LCID given matches the language in pchLanguage
  470. * and country in pchCountry.
  471. *
  472. *Entry:
  473. * lpLcidString - pointer to string with decimal LCID
  474. * pchCountry - pointer to country name
  475. * bAbbrevCountry - set if country is three-letter abbreviation
  476. *
  477. *Exit:
  478. * iLcidState - status of match
  479. * __LCID_FULL - both language and country match (best match)
  480. * __LCID_PRIMARY - primary language and country match (better)
  481. * __LCID_DEFAULT - default language and country match (good)
  482. * __LCID_LANGUAGE - default primary language exists
  483. * __LCID_EXISTS - full match of language string exists
  484. * (Overall match occurs for the best of FULL/PRIMARY/DEFAULT
  485. * and LANGUAGE/EXISTS both set.)
  486. * lcidLanguage - LCID matched
  487. * lcidCountry - LCID matched
  488. * FALSE if match occurred to terminate enumeration, else TRUE.
  489. *
  490. *Exceptions:
  491. *
  492. *******************************************************************************/
  493. static BOOL CALLBACK LangCountryEnumProc (LPSTR lpLcidString)
  494. {
  495. LCID lcid = LcidFromHexString(lpLcidString);
  496. char rgcInfo[120];
  497. // test locale country against input value
  498. if ((*pfnGetLocaleInfoA)(lcid, bAbbrevCountry ? LOCALE_SABBREVCTRYNAME
  499. : LOCALE_SENGCOUNTRY,
  500. rgcInfo, sizeof(rgcInfo)) == 0)
  501. {
  502. // set error condition and exit
  503. iLcidState = 0;
  504. return TRUE;
  505. }
  506. if (!_stricmp(pchCountry, rgcInfo))
  507. {
  508. // country matched - test for language match
  509. if ((*pfnGetLocaleInfoA)(lcid, bAbbrevLanguage ? LOCALE_SABBREVLANGNAME
  510. : LOCALE_SENGLANGUAGE,
  511. rgcInfo, sizeof(rgcInfo)) == 0)
  512. {
  513. // set error condition and exit
  514. iLcidState = 0;
  515. return TRUE;
  516. }
  517. if (!_stricmp(pchLanguage, rgcInfo))
  518. {
  519. // language matched also - set state and value
  520. iLcidState |= (__LCID_FULL | __LCID_LANGUAGE | __LCID_EXISTS);
  521. lcidLanguage = lcidCountry = lcid;
  522. }
  523. // test if match already for primary langauage
  524. else if (!(iLcidState & __LCID_PRIMARY))
  525. {
  526. // if not, use iPrimaryLen to partial match language string
  527. if (iPrimaryLen && !_strnicmp(pchLanguage, rgcInfo, iPrimaryLen))
  528. {
  529. // primary language matched - set state and country LCID
  530. iLcidState |= __LCID_PRIMARY;
  531. lcidCountry = lcid;
  532. // if language is primary only (no subtype), set language LCID
  533. if ((int)strlen(pchLanguage) == iPrimaryLen)
  534. lcidLanguage = lcid;
  535. }
  536. // test if default language already defined
  537. else if (!(iLcidState & __LCID_DEFAULT))
  538. {
  539. // if not, test if locale language is default for country
  540. if (TestDefaultCountry(lcid))
  541. {
  542. // default language for country - set state, value
  543. iLcidState |= __LCID_DEFAULT;
  544. lcidCountry = lcid;
  545. }
  546. }
  547. }
  548. }
  549. // test if input language both exists and default primary language defined
  550. if ((iLcidState & (__LCID_LANGUAGE | __LCID_EXISTS)) !=
  551. (__LCID_LANGUAGE | __LCID_EXISTS))
  552. {
  553. // test language match to determine whether it is installed
  554. if ((*pfnGetLocaleInfoA)(lcid, bAbbrevLanguage ? LOCALE_SABBREVLANGNAME
  555. : LOCALE_SENGLANGUAGE,
  556. rgcInfo, sizeof(rgcInfo)) == 0)
  557. {
  558. // set error condition and exit
  559. iLcidState = 0;
  560. return TRUE;
  561. }
  562. if (!_stricmp(pchLanguage, rgcInfo))
  563. {
  564. // language matched - set bit for existance
  565. iLcidState |= __LCID_EXISTS;
  566. if (bAbbrevLanguage)
  567. {
  568. // abbreviation - set state
  569. // also set language LCID if not set already
  570. iLcidState |= __LCID_LANGUAGE;
  571. if (!lcidLanguage)
  572. lcidLanguage = lcid;
  573. }
  574. // test if language is primary only (no sublanguage)
  575. else if (iPrimaryLen && ((int)strlen(pchLanguage) == iPrimaryLen))
  576. {
  577. // primary language only - test if default LCID
  578. if (TestDefaultLanguage(lcid, TRUE))
  579. {
  580. // default primary language - set state
  581. // also set LCID if not set already
  582. iLcidState |= __LCID_LANGUAGE;
  583. if (!lcidLanguage)
  584. lcidLanguage = lcid;
  585. }
  586. }
  587. else
  588. {
  589. // language with sublanguage - set state
  590. // also set LCID if not set already
  591. iLcidState |= __LCID_LANGUAGE;
  592. if (!lcidLanguage)
  593. lcidLanguage = lcid;
  594. }
  595. }
  596. else if (!bAbbrevLanguage && iPrimaryLen
  597. && !_strnicmp(pchLanguage, rgcInfo, iPrimaryLen))
  598. {
  599. // primary language match - test for default language only
  600. if (TestDefaultLanguage(lcid, FALSE))
  601. {
  602. // default primary language - set state
  603. // also set LCID if not set already
  604. iLcidState |= __LCID_LANGUAGE;
  605. if (!lcidLanguage)
  606. lcidLanguage = lcid;
  607. }
  608. }
  609. }
  610. // if LOCALE_FULL set, return FALSE to stop enumeration,
  611. // else return TRUE to continue
  612. return (iLcidState & __LCID_FULL) == 0;
  613. }
  614. /***
  615. *void GetLcidFromLanguage - get LCIDs from language string
  616. *
  617. *Purpose:
  618. * Match the best LCIDs to the language string given. After global
  619. * variables are initialized, the LanguageEnumProc routine is
  620. * registered as an EnumSystemLocalesA callback to actually perform
  621. * the matching as the LCIDs are enumerated.
  622. *
  623. *Entry:
  624. * pchLanguage - language string
  625. * bAbbrevLanguage - language string is a three-letter abbreviation
  626. * iPrimaryLen - length of language string with primary name
  627. *
  628. *Exit:
  629. * lcidLanguage - lcidCountry - LCID of language with default
  630. * country
  631. *
  632. *Exceptions:
  633. *
  634. *******************************************************************************/
  635. static void GetLcidFromLanguage (void)
  636. {
  637. // initialize static variables for callback use
  638. bAbbrevLanguage = strlen(pchLanguage) == 3;
  639. iPrimaryLen = bAbbrevLanguage ? 2 : GetPrimaryLen(pchLanguage);
  640. EnumSystemLocalesA(LanguageEnumProc, LCID_INSTALLED);
  641. // locale value is invalid if the language was not installed
  642. // or the language was not available for the country specified
  643. if (!(iLcidState & __LCID_FULL))
  644. iLcidState = 0;
  645. }
  646. /***
  647. *BOOL CALLBACK LanguageEnumProc - callback routine for GetLcidFromLanguage
  648. *
  649. *Purpose:
  650. * Determine if LCID given matches the default country for the
  651. * language in pchLanguage.
  652. *
  653. *Entry:
  654. * lpLcidString - pointer to string with decimal LCID
  655. * pchLanguage - pointer to language name
  656. * bAbbrevLanguage - set if language is three-letter abbreviation
  657. *
  658. *Exit:
  659. * lcidLanguage - lcidCountry - LCID matched
  660. * FALSE if match occurred to terminate enumeration, else TRUE.
  661. *
  662. *Exceptions:
  663. *
  664. *******************************************************************************/
  665. static BOOL CALLBACK LanguageEnumProc (LPSTR lpLcidString)
  666. {
  667. LCID lcid = LcidFromHexString(lpLcidString);
  668. char rgcInfo[120];
  669. // test locale for language specified
  670. if ((*pfnGetLocaleInfoA)(lcid, bAbbrevLanguage ? LOCALE_SABBREVLANGNAME
  671. : LOCALE_SENGLANGUAGE,
  672. rgcInfo, sizeof(rgcInfo)) == 0)
  673. {
  674. // set error condition and exit
  675. iLcidState = 0;
  676. return TRUE;
  677. }
  678. if (!_stricmp(pchLanguage, rgcInfo))
  679. {
  680. // language matched - test if locale country is default
  681. // or if locale is implied in the language string
  682. if (bAbbrevLanguage || TestDefaultLanguage(lcid, TRUE))
  683. {
  684. // this locale has the default country
  685. lcidLanguage = lcidCountry = lcid;
  686. iLcidState |= __LCID_FULL;
  687. }
  688. }
  689. else if (!bAbbrevLanguage && iPrimaryLen
  690. && !_strnicmp(pchLanguage, rgcInfo, iPrimaryLen))
  691. {
  692. // primary language matched - test if locale country is default
  693. if (TestDefaultLanguage(lcid, FALSE))
  694. {
  695. // this is the default country
  696. lcidLanguage = lcidCountry = lcid;
  697. iLcidState |= __LCID_FULL;
  698. }
  699. }
  700. return (iLcidState & __LCID_FULL) == 0;
  701. }
  702. /***
  703. *void GetLcidFromCountry - get LCIDs from country string
  704. *
  705. *Purpose:
  706. * Match the best LCIDs to the country string given. After global
  707. * variables are initialized, the CountryEnumProc routine is
  708. * registered as an EnumSystemLocalesA callback to actually perform
  709. * the matching as the LCIDs are enumerated.
  710. *
  711. *Entry:
  712. * pchCountry - country string
  713. * bAbbrevCountry - country string is a three-letter abbreviation
  714. *
  715. *Exit:
  716. * lcidLanguage - lcidCountry - LCID of country with default
  717. * language
  718. *
  719. *Exceptions:
  720. *
  721. *******************************************************************************/
  722. static void GetLcidFromCountry (void)
  723. {
  724. bAbbrevCountry = strlen(pchCountry) == 3;
  725. EnumSystemLocalesA(CountryEnumProc, LCID_INSTALLED);
  726. // locale value is invalid if the country was not defined or
  727. // no default language was found
  728. if (!(iLcidState & __LCID_FULL))
  729. iLcidState = 0;
  730. }
  731. /***
  732. *BOOL CALLBACK CountryEnumProc - callback routine for GetLcidFromCountry
  733. *
  734. *Purpose:
  735. * Determine if LCID given matches the default language for the
  736. * country in pchCountry.
  737. *
  738. *Entry:
  739. * lpLcidString - pointer to string with decimal LCID
  740. * pchCountry - pointer to country name
  741. * bAbbrevCountry - set if country is three-letter abbreviation
  742. *
  743. *Exit:
  744. * lcidLanguage - lcidCountry - LCID matched
  745. * FALSE if match occurred to terminate enumeration, else TRUE.
  746. *
  747. *Exceptions:
  748. *
  749. *******************************************************************************/
  750. static BOOL CALLBACK CountryEnumProc (LPSTR lpLcidString)
  751. {
  752. LCID lcid = LcidFromHexString(lpLcidString);
  753. char rgcInfo[120];
  754. // test locale for country specified
  755. if ((*pfnGetLocaleInfoA)(lcid, bAbbrevCountry ? LOCALE_SABBREVCTRYNAME
  756. : LOCALE_SENGCOUNTRY,
  757. rgcInfo, sizeof(rgcInfo)) == 0)
  758. {
  759. // set error condition and exit
  760. iLcidState = 0;
  761. return TRUE;
  762. }
  763. if (!_stricmp(pchCountry, rgcInfo))
  764. {
  765. // language matched - test if locale country is default
  766. if (TestDefaultCountry(lcid))
  767. {
  768. // this locale has the default language
  769. lcidLanguage = lcidCountry = lcid;
  770. iLcidState |= __LCID_FULL;
  771. }
  772. }
  773. return (iLcidState & __LCID_FULL) == 0;
  774. }
  775. /***
  776. *void GetLcidFromDefault - get default LCIDs
  777. *
  778. *Purpose:
  779. * Set both language and country LCIDs to the system default.
  780. *
  781. *Entry:
  782. * None.
  783. *
  784. *Exit:
  785. * lcidLanguage - set to system LCID
  786. * lcidCountry - set to system LCID
  787. *
  788. *Exceptions:
  789. *
  790. *******************************************************************************/
  791. static void GetLcidFromDefault (void)
  792. {
  793. iLcidState |= (__LCID_FULL | __LCID_LANGUAGE);
  794. lcidLanguage = lcidCountry = GetUserDefaultLCID();
  795. }
  796. /***
  797. *int ProcessCodePage - convert codepage string to numeric value
  798. *
  799. *Purpose:
  800. * Process codepage string consisting of a decimal string, or the
  801. * special case strings "ACP" and "OCP", for ANSI and OEM codepages,
  802. * respectively. Null pointer or string returns the ANSI codepage.
  803. *
  804. *Entry:
  805. * lpCodePageStr - pointer to codepage string
  806. *
  807. *Exit:
  808. * Returns numeric value of codepage.
  809. *
  810. *Exceptions:
  811. *
  812. *******************************************************************************/
  813. static int ProcessCodePage (LPSTR lpCodePageStr)
  814. {
  815. char chCodePage[8];
  816. if (!lpCodePageStr || !*lpCodePageStr || !strcmp(lpCodePageStr, "ACP"))
  817. {
  818. // get ANSI codepage for the country LCID
  819. if ((*pfnGetLocaleInfoA)(lcidCountry, LOCALE_IDEFAULTANSICODEPAGE,
  820. chCodePage, sizeof(chCodePage)) == 0)
  821. return 0;
  822. lpCodePageStr = chCodePage;
  823. }
  824. else if (!strcmp(lpCodePageStr, "OCP"))
  825. {
  826. // get OEM codepage for the country LCID
  827. if ((*pfnGetLocaleInfoA)(lcidCountry, LOCALE_IDEFAULTCODEPAGE,
  828. chCodePage, sizeof(chCodePage)) == 0)
  829. return 0;
  830. lpCodePageStr = chCodePage;
  831. }
  832. // convert decimal string to numeric value
  833. return (int)atol(lpCodePageStr);
  834. }
  835. /***
  836. *BOOL TestDefaultCountry - determine if default locale for country
  837. *
  838. *Purpose:
  839. * Using a hardcoded list, determine if the locale of the given LCID
  840. * has the default sublanguage for the locale primary language. The
  841. * list contains the locales NOT having the default sublanguage.
  842. *
  843. *Entry:
  844. * lcid - LCID of locale to test
  845. *
  846. *Exit:
  847. * Returns TRUE if default sublanguage, else FALSE.
  848. *
  849. *Exceptions:
  850. *
  851. *******************************************************************************/
  852. static BOOL TestDefaultCountry (LCID lcid)
  853. {
  854. LANGID langid = LANGIDFROMLCID(lcid);
  855. int i;
  856. for (i = 0; i < sizeof(__rglangidNotDefault) / sizeof(LANGID); i++)
  857. {
  858. if (langid == __rglangidNotDefault[i])
  859. return FALSE;
  860. }
  861. return TRUE;
  862. }
  863. /***
  864. *BOOL TestDefaultLanguage - determine if default locale for language
  865. *
  866. *Purpose:
  867. * Determines if the given LCID has the default sublanguage.
  868. * If bTestPrimary is set, also allow TRUE when string contains an
  869. * implicit sublanguage.
  870. *
  871. *Entry:
  872. * LCID - lcid of locale to test
  873. * bTestPrimary - set if testing if language is primary
  874. *
  875. *Exit:
  876. * Returns TRUE if sublanguage is default for locale tested.
  877. * If bTestPrimary set, TRUE is language has implied sublanguge.
  878. *
  879. *Exceptions:
  880. *
  881. *******************************************************************************/
  882. static BOOL TestDefaultLanguage (LCID lcid, BOOL bTestPrimary)
  883. {
  884. char rgcInfo[120];
  885. LCID lcidDefault = MAKELCID(MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(lcid)),
  886. SUBLANG_DEFAULT), SORT_DEFAULT);
  887. if ((*pfnGetLocaleInfoA)(lcidDefault, LOCALE_ILANGUAGE, rgcInfo,
  888. sizeof(rgcInfo)) == 0)
  889. return FALSE;
  890. if (lcid != LcidFromHexString(rgcInfo))
  891. {
  892. // test if string contains an implicit sublanguage by
  893. // having a character other than upper/lowercase letters.
  894. if (bTestPrimary && GetPrimaryLen(pchLanguage) == (int)strlen(pchLanguage))
  895. return FALSE;
  896. }
  897. return TRUE;
  898. }
  899. /***
  900. *int crtGetLocalInfoA - get locale information for Win95
  901. *
  902. *Purpose:
  903. * For Win95, some calls to GetLocaleInfoA return incorrect results.
  904. * Simulate these calls with values looked up in a hard-coded table.
  905. *
  906. *Entry:
  907. * lcid - LCID of locale to get information from
  908. * lctype - index of information selection
  909. * lpdata - pointer to output string
  910. * cchdata - size of output string (including null)
  911. *
  912. *Exit:
  913. * lpdata - return string of locale information
  914. * returns TRUE if successful, else FALSE
  915. *
  916. *Exceptions:
  917. *
  918. *******************************************************************************/
  919. static int __stdcall crtGetLocaleInfoA (LCID lcid, LCTYPE lctype, LPSTR lpdata,
  920. int cchdata)
  921. {
  922. int i;
  923. int low = 0;
  924. int high = sizeof(__rgLocInfo) / sizeof(RGLOCINFO) - 1;
  925. const char * pchResult = NULL;
  926. // typical binary search - do until no more to search
  927. while (low <= high)
  928. {
  929. i = (low + high) / 2;
  930. if (lcid == __rgLocInfo[i].lcid)
  931. {
  932. // LCID matched - test for valid LCTYPE to simulate call
  933. switch (lctype)
  934. {
  935. case LOCALE_ILANGUAGE:
  936. pchResult = __rgLocInfo[i].chILanguage;
  937. break;
  938. case LOCALE_SENGLANGUAGE:
  939. pchResult = __rgLocInfo[i].pchSEngLanguage;
  940. break;
  941. case LOCALE_SABBREVLANGNAME:
  942. pchResult = __rgLocInfo[i].chSAbbrevLangName;
  943. break;
  944. case LOCALE_SENGCOUNTRY:
  945. pchResult = __rgLocInfo[i].pchSEngCountry;
  946. break;
  947. case LOCALE_SABBREVCTRYNAME:
  948. pchResult = __rgLocInfo[i].chSAbbrevCtryName;
  949. break;
  950. case LOCALE_IDEFAULTCODEPAGE:
  951. pchResult = __rgLocInfo[i].chIDefaultCodepage;
  952. break;
  953. case LOCALE_IDEFAULTANSICODEPAGE:
  954. pchResult = __rgLocInfo[i].chIDefaultAnsiCodepage;
  955. default:
  956. break;
  957. }
  958. if (!pchResult || cchdata < 1)
  959. // if LCTYPE did not match, break to use normal routine
  960. break;
  961. else
  962. {
  963. // copy data as much as possible to result and null-terminate
  964. strncpy(lpdata, pchResult, cchdata - 1);
  965. *(lpdata + cchdata - 1) = '\0';
  966. return 1;
  967. }
  968. }
  969. else if (lcid < __rgLocInfo[i].lcid)
  970. high = i - 1;
  971. else
  972. low = i + 1;
  973. }
  974. // LCID not found or LCTYPE not simulated
  975. return GetLocaleInfoA(lcid,lctype, lpdata, cchdata);
  976. }
  977. /***
  978. *LCID LcidFromHexString - convert hex string to value for LCID
  979. *
  980. *Purpose:
  981. * LCID values returned in hex ANSI strings - straight conversion
  982. *
  983. *Entry:
  984. * lpHexString - pointer to hex string to convert
  985. *
  986. *Exit:
  987. * Returns LCID computed.
  988. *
  989. *Exceptions:
  990. *
  991. *******************************************************************************/
  992. static LCID LcidFromHexString (LPSTR lpHexString)
  993. {
  994. char ch;
  995. DWORD lcid = 0;
  996. while (ch = *lpHexString++)
  997. {
  998. if (ch >= 'a' && ch <= 'f')
  999. ch += '9' + 1 - 'a';
  1000. else if (ch >= 'A' && ch <= 'F')
  1001. ch += '9' + 1 - 'A';
  1002. lcid = lcid * 0x10 + ch - '0';
  1003. }
  1004. return (LCID)lcid;
  1005. }
  1006. /***
  1007. *int GetPrimaryLen - get length of primary language name
  1008. *
  1009. *Purpose:
  1010. * Determine primary language string length by scanning until
  1011. * first non-alphabetic character.
  1012. *
  1013. *Entry:
  1014. * pchLanguage - string to scan
  1015. *
  1016. *Exit:
  1017. * Returns length of primary language string.
  1018. *
  1019. *Exceptions:
  1020. *
  1021. *******************************************************************************/
  1022. static int GetPrimaryLen (LPSTR pchLanguage)
  1023. {
  1024. int len = 0;
  1025. char ch;
  1026. ch = *pchLanguage++;
  1027. while ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
  1028. {
  1029. len++;
  1030. ch = *pchLanguage++;
  1031. }
  1032. return len;
  1033. }
  1034. #endif //if defined(_POSIX_)