home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / getqloc.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  37KB  |  1,068 lines

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