1 /*
   2  * Copyright 1998-2008 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 #include <windows.h>
  27 #include <shlobj.h>
  28 #include <objidl.h>
  29 #include <locale.h>
  30 #include <sys/types.h>
  31 #include <sys/timeb.h>
  32 #include <tchar.h>
  33 
  34 #include "locale_str.h"
  35 #include "java_props.h"
  36 
  37 #ifndef VER_PLATFORM_WIN32_WINDOWS
  38 #define VER_PLATFORM_WIN32_WINDOWS 1
  39 #endif
  40 
  41 #ifndef PROCESSOR_ARCHITECTURE_AMD64
  42 #define PROCESSOR_ARCHITECTURE_AMD64 9
  43 #endif
  44 
  45 typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
  46 
  47 #define SHELL_KEY "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
  48 
  49 /* Encodings for Windows language groups. According to
  50    www.microsoft.com/globaldev/faqs/locales.asp,
  51    some locales do not have codepages, and are
  52    supported in Windows 2000/XP solely through Unicode.
  53    In this case, we use utf-8 encoding */
  54 
  55 static char *encoding_names[] = {
  56     "Cp1250",    /*  0:Latin 2  */
  57     "Cp1251",    /*  1:Cyrillic */
  58     "Cp1252",    /*  2:Latin 1  */
  59     "Cp1253",    /*  3:Greek    */
  60     "Cp1254",    /*  4:Latin 5  */
  61     "Cp1255",    /*  5:Hebrew   */
  62     "Cp1256",    /*  6:Arabic   */
  63     "Cp1257",    /*  7:Baltic   */
  64     "Cp1258",    /*  8:Viet Nam */
  65     "MS874",     /*  9:Thai     */
  66     "MS932",     /* 10:Japanese */
  67     "GBK",       /* 11:PRC GBK  */
  68     "MS949",     /* 12:Korean Extended Wansung */
  69     "MS950",     /* 13:Chinese (Taiwan, Hongkong, Macau) */
  70     "utf-8",     /* 14:Unicode  */
  71     "MS1361",    /* 15:Korean Johab */
  72 };
  73 
  74 /*
  75  * List mapping from LanguageID to Java locale IDs.
  76  * The entries in this list should not be construed to suggest we actually have
  77  * full locale-data and other support for all of these locales; these are
  78  * merely all of the Windows locales for which we could construct an accurate
  79  * locale ID.  The data is based on the web page "Windows XP/Server 2003 -
  80  * List of Locale IDs, Input Locale, and Language Collection"
  81  * (http://www.microsoft.com/globaldev/reference/winxp/xp-lcid.mspx)
  82  *
  83  * Some of the language IDs below are not yet used by Windows, but were
  84  * defined by Microsoft for other products, such as Office XP. They may
  85  * become Windows language IDs in the future.
  86  *
  87  */
  88 typedef struct LANGIDtoLocale {
  89     WORD    langID;
  90     WORD    encoding;
  91     char*   javaID;
  92 } LANGIDtoLocale;
  93 
  94 static LANGIDtoLocale langIDMap[] = {
  95     /* fallback locales to use when the country code doesn't match anything we have */
  96     0x01,    6, "ar",
  97     0x02,    1, "bg",
  98     0x03,    2, "ca",
  99     0x04,   11, "zh",
 100     0x05,    0, "cs",
 101     0x06,    2, "da",
 102     0x07,    2, "de",
 103     0x08,    3, "el",
 104     0x09,    2, "en",
 105     0x0a,    2, "es",
 106     0x0b,    2, "fi",
 107     0x0c,    2, "fr",
 108     0x0d,    5, "iw",
 109     0x0e,    0, "hu",
 110     0x0f,    2, "is",
 111     0x10,    2, "it",
 112     0x11,   10, "ja",
 113     0x12,   12, "ko",
 114     0x13,    2, "nl",
 115     0x14,    2, "no",
 116     0x15,    0, "pl",
 117     0x16,    2, "pt",
 118     0x17,    2, "rm",
 119     0x18,    0, "ro",
 120     0x19,    1, "ru",
 121     0x1a,    0, "sr",
 122     0x1b,    0, "sk",
 123     0x1c,    0, "sq",
 124     0x1d,    2, "sv",
 125     0x1e,    9, "th",
 126     0x1f,    4, "tr",
 127     0x20,    2, "ur",
 128     0x21,    2, "in",
 129     0x22,    1, "uk",
 130     0x23,    1, "be",
 131     0x24,    0, "sl",
 132     0x25,    7, "et",
 133     0x26,    7, "lv",
 134     0x27,    7, "lt",
 135     0x28,    1, "tg",
 136     0x29,    6, "fa",
 137     0x2a,    8, "vi",
 138     0x2b,   14, "hy",
 139     0x2c,    4, "az",
 140     0x2d,    2, "eu",
 141 /*  0x2e,    2, "??",  no ISO-639 abbreviation for Sorbian */
 142     0x2f,    1, "mk",
 143     0x31,    2, "ts",
 144     0x32,    2, "tn",
 145     0x34,    2, "xh",
 146     0x35,    2, "zu",
 147     0x36,    2, "af",
 148     0x37,   14, "ka",
 149     0x38,    2, "fo",
 150     0x39,   14, "hi",
 151     0x3a,   14, "mt",
 152     0x3b,    2, "se",
 153     0x3c,    2, "gd",
 154     0x3d,    2, "yi",
 155     0x3e,    2, "ms",
 156     0x3f,    1, "kk",
 157     0x40,    1, "ky",
 158     0x41,    2, "sw",
 159     0x42,    0, "tk",
 160     0x43,    1, "uz",
 161     0x44,    1, "tt",
 162     0x45,   14, "bn",
 163     0x46,   14, "pa",
 164     0x47,   14, "gu",
 165     0x48,   14, "or",
 166     0x49,   14, "ta",
 167     0x4a,   14, "te",
 168     0x4b,   14, "kn",
 169     0x4c,   14, "ml",
 170     0x4d,   14, "as",
 171     0x4e,   14, "mr",
 172     0x4f,   14, "sa",
 173     0x50,    1, "mn",
 174     0x51,   14, "bo",
 175     0x52,    1, "cy",
 176     0x53,   14, "km",
 177     0x54,   14, "lo",
 178     0x56,    2, "gl",
 179     0x5b,   14, "si",
 180     0x5d,   14, "iu",
 181     0x5e,   14, "am",
 182 /*  0x5f,    2, "??",  no ISO-639 abbreviation for Tamazight */
 183     0x68,    2, "ha",
 184     0x6a,    2, "yo",
 185     0x6b,    2, "qu",
 186     0x6d,    1, "ba",
 187     0x6f,    2, "kl",
 188     0x70,    2, "ig",
 189 /*  0x78,   14, "??",  no ISO-639 abbreviation for Yi */
 190     0x7e,    2, "br",
 191     0x80,    6, "ug",
 192     0x81,   14, "mi",
 193     0x82,    2, "oc",
 194     0x83,    2, "co",
 195 /*  0x84,    2, "??",  no ISO-639 abbreviation for Alsatian */
 196 /*  0x85,    1, "??",  no ISO-639 abbreviation for Yakut */
 197 /*  0x86,    2, "??",  no ISO-639 abbreviation for K'iche */
 198     0x87,    2, "rw",
 199     0x88,    2, "wo",
 200 /*  0x8c,    6, "??",  no ISO-639 abbreviation for Dari */
 201     /* mappings for real Windows LCID values */
 202     0x0401,  6, "ar_SA",
 203     0x0402,  1, "bg_BG",
 204     0x0403,  2, "ca_ES",
 205     0x0404, 13, "zh_TW",
 206     0x0405,  0, "cs_CZ",
 207     0x0406,  2, "da_DK",
 208     0x0407,  2, "de_DE",
 209     0x0408,  3, "el_GR",
 210     0x0409,  2, "en_US",
 211     0x040a,  2, "es_ES",  /* (traditional sort) */
 212     0x040b,  2, "fi_FI",
 213     0x040c,  2, "fr_FR",
 214     0x040d,  5, "iw_IL",
 215     0x040e,  0, "hu_HU",
 216     0x040f,  2, "is_IS",
 217     0x0410,  2, "it_IT",
 218     0x0411, 10, "ja_JP",
 219     0x0412, 12, "ko_KR",
 220     0x0413,  2, "nl_NL",
 221     0x0414,  2, "no_NO",
 222     0x0415,  0, "pl_PL",
 223     0x0416,  2, "pt_BR",
 224     0x0417,  2, "rm_CH",
 225     0x0418,  0, "ro_RO",
 226     0x0419,  1, "ru_RU",
 227     0x041a,  0, "hr_HR",
 228     0x041b,  0, "sk_SK",
 229     0x041c,  0, "sq_AL",
 230     0x041d,  2, "sv_SE",
 231     0x041e,  9, "th_TH",
 232     0x041f,  4, "tr_TR",
 233     0x0420,  6, "ur_PK",
 234     0x0421,  2, "in_ID",
 235     0x0422,  1, "uk_UA",
 236     0x0423,  1, "be_BY",
 237     0x0424,  0, "sl_SI",
 238     0x0425,  7, "et_EE",
 239     0x0426,  7, "lv_LV",
 240     0x0427,  7, "lt_LT",
 241     0x0428,  1, "tg_TJ",
 242     0x0429,  6, "fa_IR",
 243     0x042a,  8, "vi_VN",
 244     0x042b, 14, "hy_AM",  /* Armenian  */
 245     0x042c,  4, "az_AZ",  /* Azeri_Latin */
 246     0x042d,  2, "eu_ES",
 247 /*  0x042e,  2, "??",      no ISO-639 abbreviation for Upper Sorbian */
 248     0x042f,  1, "mk_MK",
 249 /*  0x0430,  2, "??",      no ISO-639 abbreviation for Sutu */
 250     0x0431,  2, "ts",     /* (country?) */
 251     0x0432,  2, "tn_ZA",
 252 /*  0x0433,  2, "??",      no ISO-639 abbreviation for Venda */
 253     0x0434,  2, "xh_ZA",
 254     0x0435,  2, "zu_ZA",
 255     0x0436,  2, "af_ZA",
 256     0x0437, 14, "ka_GE",  /* Georgian   */
 257     0x0438,  2, "fo_FO",
 258     0x0439, 14, "hi_IN",
 259     0x043a, 14, "mt_MT",
 260     0x043b,  2, "se_NO",  /* Sami, Northern - Norway */
 261     0x043c,  2, "gd_GB",
 262     0x043d,  2, "yi",     /* (country?) */
 263     0x043e,  2, "ms_MY",
 264     0x043f,  1, "kk_KZ",  /* Kazakh */
 265     0x0440,  1, "ky_KG",  /* Kyrgyz     */
 266     0x0441,  2, "sw_KE",
 267     0x0442,  0, "tk_TM",
 268     0x0443,  4, "uz_UZ",  /* Uzbek_Latin */
 269     0x0444,  1, "tt_RU",  /* Tatar */
 270     0x0445, 14, "bn_IN",  /* Bengali   */
 271     0x0446, 14, "pa_IN",  /* Punjabi   */
 272     0x0447, 14, "gu_IN",  /* Gujarati  */
 273     0x0448, 14, "or_IN",  /* Oriya     */
 274     0x0449, 14, "ta_IN",  /* Tamil     */
 275     0x044a, 14, "te_IN",  /* Telugu    */
 276     0x044b, 14, "kn_IN",  /* Kannada   */
 277     0x044c, 14, "ml_IN",  /* Malayalam */
 278     0x044d, 14, "as_IN",  /* Assamese  */
 279     0x044e, 14, "mr_IN",  /* Marathi   */
 280     0x044f, 14, "sa_IN",  /* Sanskrit  */
 281     0x0450,  1, "mn_MN",  /* Mongolian */
 282     0x0451, 14, "bo_CN",  /* Tibetan   */
 283     0x0452,  2, "cy_GB",  /* Welsh     */
 284     0x0453, 14, "km_KH",  /* Khmer     */
 285     0x0454, 14, "lo_LA",  /* Lao       */
 286     0x0456,  2, "gl_ES",  /* Galician  */
 287 /*  0x0457, 14, "??_IN",  /* Konkani, no ISO-639 abbreviation*/
 288 /*  0x045a, 14, "??_SY",  /* Syriac, no ISO-639 abbreviation*/
 289     0x045b, 14, "si_LK",  /* Sinhala   */
 290     0x045d, 14, "iu_CA",  /* Inuktitut */
 291     0x045e, 14, "am_ET",  /* Amharic   */
 292     0x0461, 14, "ne_NP",  /* Nepali */
 293     0x0462,  2, "fy_NL",  /* Frisian */
 294     0x0463,  6, "ps_AF",  /* Pushto */
 295 /*  0x0464,  2, "??_PH",  /* Filipino, no ISO-639 abbreviation*/
 296     0x0465, 14, "dv_MV",  /* Divehi    */
 297     0x0468,  2, "ha_NG",  /* Hausa     */
 298     0x046a,  2, "yo_NG",  /* Yoruba    */
 299     0x046b,  2, "qu_BO",  /* Quechua - Bolivia */
 300 /*  0x046c,  2, "??_ZA",  /* Northern Sotho, no ISO-639 abbreviation */
 301     0x046d,  1, "ba_RU",  /* Bashkir   */
 302     0x046e,  2, "lb_LU",  /* Luxembourgish */
 303     0x046f,  2, "kl_GL",  /* Greenlandic */
 304     0x0470,  2, "ig_NG",  /* Igbo      */
 305 /*  0x0478, 14, "??_CN",  /* Yi (PRC), no ISO-639 abbreviation */
 306 /*  0x047a,  2, "??_CL",  /* Mapudungun (Araucanian), no ISO-639 abbreviation */
 307 /*  0x047c,  2, "??_CA",  /* Mohawk, no ISO-639 abbreviation */
 308     0x047e,  2, "br_FR",  /* Breton    */
 309     0x0480,  6, "ug_CN",  /* Uighur    */
 310     0x0481, 14, "mi_NZ",  /* Maori - New Zealand */
 311     0x0482,  2, "oc_FR",  /* Occitan   */
 312     0x0483,  2, "co_FR",  /* Corsican  */
 313 /*  0x0484,  2, "??_FR",  /* Alsatian, no ISO-639 abbreviation */
 314 /*  0x0485,  1, "??_RU",  /* Yakut, no ISO-639 abbreviation */
 315 /*  0x0486,  2, "??_GT",  /* K'iche, no ISO-639 abbreviation */
 316     0x0487,  2, "rw_RW",  /* Kinyarwanda */
 317     0x0488,  2, "wo_SN",  /* Wolof */
 318 /*  0x048c,  6, "??_AF",  /* Dari, no ISO-639 abbreviation */
 319     0x0801,  6, "ar_IQ",
 320     0x0804, 11, "zh_CN",
 321     0x0807,  2, "de_CH",
 322     0x0809,  2, "en_GB",
 323     0x080a,  2, "es_MX",
 324     0x080c,  2, "fr_BE",
 325     0x0810,  2, "it_CH",
 326     0x0812, 15, "ko_KR",  /* Korean(Johab)*/
 327     0x0813,  2, "nl_BE",
 328     0x0814,  2, "no_NO_NY",
 329     0x0816,  2, "pt_PT",
 330     0x0818,  0, "ro_MD",
 331     0x0819,  1, "ru_MD",
 332     0x081a,  0, "sr_CS",
 333     0x081d,  2, "sv_FI",
 334     0x082c,  1, "az_AZ",  /* Azeri_Cyrillic */
 335 /*  0x082e,  2, "??",      no ISO-639 abbreviation for Lower Sorbian */
 336     0x083b,  2, "se_SE",  /* Sami, Northern - Sweden */
 337     0x083c,  2, "ga_IE",
 338     0x083e,  2, "ms_BN",
 339     0x0843,  1, "uz_UZ",  /* Uzbek_Cyrillic */
 340     0x0845, 14, "bn_BD",  /* Bengali   */
 341     0x0850, 14, "mn_CN",  /* Traditional Mongolian */
 342     0x085d,  2, "iu_CA",  /* Inuktitut */
 343 /*  0x085f,  2, "??_DZ",      no ISO-639 abbreviation for Tamazight */
 344     0x086b,  2, "qu_EC",  /* Quechua - Ecuador */
 345     0x0c01,  6, "ar_EG",
 346     0x0c04, 13, "zh_HK",
 347     0x0c07,  2, "de_AT",
 348     0x0c09,  2, "en_AU",
 349     0x0c0a,  2, "es_ES",  /* (modern sort) */
 350     0x0c0c,  2, "fr_CA",
 351     0x0c1a,  1, "sr_CS",
 352     0x0c3b,  2, "se_FI",  /* Sami, Northern - Finland */
 353     0x0c6b,  2, "qu_PE",  /* Quechua - Peru */
 354     0x1001,  6, "ar_LY",
 355     0x1004, 11, "zh_SG",
 356     0x1007,  2, "de_LU",
 357     0x1009,  2, "en_CA",
 358     0x100a,  2, "es_GT",
 359     0x100c,  2, "fr_CH",
 360     0x101a,  0, "hr_BA",
 361 /*  0x103b,  2, "??_NO",  /* Sami, Lule - Norway */
 362     0x1401,  6, "ar_DZ",
 363     0x1404, 13, "zh_MO",
 364     0x1407,  2, "de_LI",
 365     0x1409,  2, "en_NZ",
 366     0x140a,  2, "es_CR",
 367     0x140c,  2, "fr_LU",
 368     0x141a,  0, "bs_BA",
 369 /*  0x143b,  2, "??_SE",  /* Sami, Lule - Sweden */
 370     0x1801,  6, "ar_MA",
 371     0x1809,  2, "en_IE",
 372     0x180a,  2, "es_PA",
 373     0x180c,  2, "fr_MC",
 374     0x181a,  0, "sr_BA",
 375 /*  0x183b,  2, "??_NO",  /* Sami, Southern - Norway */
 376     0x1c01,  6, "ar_TN",
 377     0x1c09,  2, "en_ZA",
 378     0x1c0a,  2, "es_DO",
 379     0x1c1a,  1, "sr_BA",
 380 /*  0x1c3b,  2, "??_SE",  /* Sami, Southern - Sweden */
 381     0x2001,  6, "ar_OM",
 382     0x2009,  2, "en_JM",
 383     0x200a,  2, "es_VE",
 384     0x201a,  0, "bs_BA",  /* Bosnian (Cyrillic) */
 385 /*  0x203b,  2, "??_FI",  /* Sami, Skolt - Finland */
 386     0x2401,  6, "ar_YE",
 387     0x2409,  2, "en",     /* ("Caribbean", which could be any of many countries) */
 388     0x240a,  2, "es_CO",
 389 /*  0x243b,  2, "??_FI",  /* Sami, Inari - Finland */
 390     0x2801,  6, "ar_SY",
 391     0x2809,  2, "en_BZ",
 392     0x280a,  2, "es_PE",
 393     0x2c01,  6, "ar_JO",
 394     0x2c09,  2, "en_TT",
 395     0x2c0a,  2, "es_AR",
 396     0x3001,  6, "ar_LB",
 397     0x3009,  2, "en_ZW",
 398     0x300a,  2, "es_EC",
 399     0x3401,  6, "ar_KW",
 400     0x3409,  2, "en_PH",
 401     0x340a,  2, "es_CL",
 402     0x3801,  6, "ar_AE",
 403     0x380a,  2, "es_UY",
 404     0x3c01,  6, "ar_BH",
 405     0x3c0a,  2, "es_PY",
 406     0x4001,  6, "ar_QA",
 407     0x4009,  2, "en_IN",
 408     0x400a,  2, "es_BO",
 409     0x4409,  2, "en_MY",
 410     0x440a,  2, "es_SV",
 411     0x4809,  2, "en_SG",
 412     0x480a,  2, "es_HN",
 413     0x4c0a,  2, "es_NI",
 414     0x500a,  2, "es_PR",
 415     0x540a,  2, "es_US"
 416 };
 417 
 418 /*
 419  * binary-search our list of LANGID values.  If we don't find the
 420  * one we're looking for, mask out the country code and try again
 421  * with just the primary language ID
 422  */
 423 static int
 424 getLocaleEntryIndex(LANGID langID)
 425 {
 426     int index = -1;
 427     int tries = 0;
 428     do {
 429         int lo, hi, mid;
 430         lo = 0;
 431         hi = sizeof(langIDMap) / sizeof(LANGIDtoLocale);
 432         while (index == -1 && lo < hi) {
 433             mid = (lo + hi) / 2;
 434             if (langIDMap[mid].langID == langID) {
 435                 index = mid;
 436             } else if (langIDMap[mid].langID > langID) {
 437                 hi = mid;
 438             } else {
 439                 lo = mid + 1;
 440             }
 441         }
 442         langID = PRIMARYLANGID(langID);
 443         ++tries;
 444     } while (index == -1 && tries < 2);
 445 
 446     return index;
 447 }
 448 
 449 static char *
 450 getEncodingInternal(int index)
 451 {
 452     char * ret = encoding_names[langIDMap[index].encoding];
 453 
 454     //Traditional Chinese Windows should use MS950_HKSCS as the
 455     //default encoding, if HKSCS patch has been installed.
 456     // "old" MS950 0xfa41 -> u+e001
 457     // "new" MS950 0xfa41 -> u+92db
 458     if (strcmp(ret, "MS950") == 0) {
 459         TCHAR  mbChar[2] = {(char)0xfa, (char)0x41};
 460         WCHAR  unicodeChar;
 461         MultiByteToWideChar(CP_ACP, 0, mbChar, 2, &unicodeChar, 1);
 462         if (unicodeChar == 0x92db) {
 463             ret = "MS950_HKSCS";
 464         }
 465     } else {
 466         //SimpChinese Windows should use GB18030 as the default
 467         //encoding, if gb18030 patch has been installed (on windows
 468         //2000/XP, (1)Codepage 54936 will be available
 469         //(2)simsun18030.ttc will exist under system fonts dir )
 470         if (strcmp(ret, "GBK") == 0 && IsValidCodePage(54936)) {
 471             char systemPath[MAX_PATH + 1];
 472             char* gb18030Font = "\\FONTS\\SimSun18030.ttc";
 473             FILE *f = NULL;
 474             if (GetWindowsDirectory(systemPath, MAX_PATH + 1) != 0 &&
 475                 strlen(systemPath) + strlen(gb18030Font) < MAX_PATH + 1) {
 476                 strcat(systemPath, "\\FONTS\\SimSun18030.ttc");
 477                 if ((f = fopen(systemPath, "r")) != NULL) {
 478                     fclose(f);
 479                     ret = "GB18030";
 480                 }
 481             }
 482         }
 483     }
 484 
 485     return ret;
 486 }
 487 
 488 // Exported entries for AWT
 489 DllExport const char *
 490 getEncodingFromLangID(LANGID langID)
 491 {
 492     int index = getLocaleEntryIndex(langID);
 493 
 494     if (index != (-1)) {
 495         return getEncodingInternal(index);
 496     } else {
 497         return "Cp1252";
 498     }
 499 }
 500 
 501 DllExport const char *
 502 getJavaIDFromLangID(LANGID langID)
 503 {
 504     int index = getLocaleEntryIndex(langID);
 505 
 506     if (index != (-1)) {
 507         return langIDMap[index].javaID;
 508     } else {
 509         return NULL;
 510     }
 511 }
 512 
 513 /*
 514  * Code to figure out the user's home directory using the registry
 515 */
 516 static char *
 517 getHomeFromRegistry()
 518 {
 519     HKEY key;
 520     int rc;
 521     DWORD type;
 522     char *p;
 523     char path[MAX_PATH+1];
 524     int size = MAX_PATH+1;
 525 
 526     rc = RegOpenKeyEx(HKEY_CURRENT_USER, SHELL_KEY, 0, KEY_READ, &key);
 527     if (rc != ERROR_SUCCESS) {
 528         // Shell folder doesn't exist??!!
 529         return NULL;
 530     }
 531 
 532     path[0] = 0;
 533     rc = RegQueryValueEx(key, "Desktop", 0, &type, path, &size);
 534     if (rc != ERROR_SUCCESS || type != REG_SZ) {
 535         return NULL;
 536     }
 537     RegCloseKey(key);
 538     /* Get the parent of Desktop directory */
 539     p = strrchr(path, '\\');
 540     if (p == NULL) {
 541         return NULL;
 542     }
 543     *p = '\0';
 544     return strdup(path);
 545 }
 546 
 547 /*
 548  * Code to figure out the user's home directory using shell32.dll
 549  */
 550 typedef HRESULT (WINAPI *GetSpecialFolderType)(HWND, int, LPITEMIDLIST *);
 551 typedef BOOL (WINAPI *GetPathFromIDListType)(LPCITEMIDLIST, LPSTR);
 552 
 553 char *
 554 getHomeFromShell32()
 555 {
 556     HMODULE lib = LoadLibrary("SHELL32.DLL");
 557     GetSpecialFolderType do_get_folder;
 558     GetPathFromIDListType do_get_path;
 559     HRESULT rc;
 560     LPITEMIDLIST item_list = 0;
 561     char *p;
 562     char path[MAX_PATH+1];
 563     int size = MAX_PATH+1;
 564 
 565     if (lib == 0) {
 566         // We can't load the library !!??
 567         return NULL;
 568     }
 569 
 570     do_get_folder = (GetSpecialFolderType)GetProcAddress(lib, "SHGetSpecialFolderLocation");
 571     do_get_path = (GetPathFromIDListType)GetProcAddress(lib, "SHGetPathFromIDListA");
 572 
 573     if (do_get_folder == 0 || do_get_path == 0) {
 574         // the library doesn't hold the right functions !!??
 575         return NULL;
 576     }
 577 
 578     rc = (*do_get_folder)(NULL, CSIDL_DESKTOPDIRECTORY, &item_list);
 579     if (!SUCCEEDED(rc)) {
 580         // we can't find the shell folder.
 581         return NULL;
 582     }
 583 
 584     path[0] = 0;
 585     (*do_get_path)(item_list, path);
 586 
 587     /* Get the parent of Desktop directory */
 588     p = strrchr(path, '\\');
 589     if (p) {
 590         *p = 0;
 591     }
 592 
 593     /*
 594      * We've been successful.  Note that we don't free the memory allocated
 595      * by ShGetSpecialFolderLocation.  We only ever come through here once,
 596      * and only if the registry lookup failed, so it's just not worth it.
 597      *
 598      * We also don't unload the SHELL32 DLL.  We've paid the hit for loading
 599      * it and we may need it again later.
 600      */
 601 
 602     return strdup(path);
 603 }
 604 
 605 static boolean
 606 haveMMX(void)
 607 {
 608     boolean mmx = 0;
 609     HMODULE lib = LoadLibrary("KERNEL32");
 610     if (lib != NULL) {
 611         BOOL (WINAPI *isProcessorFeaturePresent)(DWORD) =
 612             (BOOL (WINAPI *)(DWORD))
 613             GetProcAddress(lib, "IsProcessorFeaturePresent");
 614         if (isProcessorFeaturePresent != NULL)
 615             mmx = isProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE);
 616         FreeLibrary(lib);
 617     }
 618     return mmx;
 619 }
 620 
 621 static const char *
 622 cpu_isalist(void)
 623 {
 624     SYSTEM_INFO info;
 625     GetSystemInfo(&info);
 626     switch (info.wProcessorArchitecture) {
 627 #ifdef PROCESSOR_ARCHITECTURE_IA64
 628     case PROCESSOR_ARCHITECTURE_IA64: return "ia64";
 629 #endif
 630 #ifdef PROCESSOR_ARCHITECTURE_AMD64
 631     case PROCESSOR_ARCHITECTURE_AMD64: return "amd64";
 632 #endif
 633     case PROCESSOR_ARCHITECTURE_INTEL:
 634         switch (info.wProcessorLevel) {
 635         case 6: return haveMMX()
 636             ? "pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86"
 637             : "pentium_pro pentium i486 i386 i86";
 638         case 5: return haveMMX()
 639             ? "pentium+mmx pentium i486 i386 i86"
 640             : "pentium i486 i386 i86";
 641         case 4: return "i486 i386 i86";
 642         case 3: return "i386 i86";
 643         }
 644     }
 645     return NULL;
 646 }
 647 
 648 java_props_t *
 649 GetJavaProperties(JNIEnv* env)
 650 {
 651     static java_props_t sprops = {0};
 652 
 653     if (sprops.user_dir) {
 654         return &sprops;
 655     }
 656 
 657     /* AWT properties */
 658     sprops.awt_toolkit = "sun.awt.windows.WToolkit";
 659 
 660     /* tmp dir */
 661     {
 662         char tmpdir[MAX_PATH + 1];
 663         /* we might want to check that this succeed */
 664         GetTempPath(MAX_PATH + 1, tmpdir);
 665         sprops.tmp_dir = strdup(tmpdir);
 666     }
 667 
 668     /* Printing properties */
 669     sprops.printerJob = "sun.awt.windows.WPrinterJob";
 670 
 671     /* Java2D properties */
 672     sprops.graphics_env = "sun.awt.Win32GraphicsEnvironment";
 673 
 674     {    /* This is used only for debugging of font problems. */
 675         char *path = getenv("JAVA2D_FONTPATH");
 676         sprops.font_dir = (path != 0) ? strdup(path) : NULL;
 677     }
 678 
 679     /* OS properties */
 680     {
 681         char buf[100];
 682         OSVERSIONINFOEX ver;
 683         SYSTEM_INFO si;
 684         PGNSI pGNSI;
 685 
 686         ver.dwOSVersionInfoSize = sizeof(ver);
 687         GetVersionEx((OSVERSIONINFO *) &ver);
 688 
 689         ZeroMemory(&si, sizeof(SYSTEM_INFO));
 690         // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
 691         pGNSI = (PGNSI) GetProcAddress(
 692                 GetModuleHandle(TEXT("kernel32.dll")),
 693                 "GetNativeSystemInfo");
 694         if(NULL != pGNSI)
 695             pGNSI(&si);
 696         else
 697             GetSystemInfo(&si);
 698 
 699         /*
 700          * From msdn page on OSVERSIONINFOEX, current as of this
 701          * writing, decoding of dwMajorVersion and dwMinorVersion.
 702          *
 703          *  Operating system            dwMajorVersion  dwMinorVersion
 704          * ==================           ==============  ==============
 705          *
 706          * Windows 95                   4               0
 707          * Windows 98                   4               10
 708          * Windows ME                   4               90
 709          * Windows 3.51                 3               51
 710          * Windows NT 4.0               4               0
 711          * Windows 2000                 5               0
 712          * Windows XP 32 bit            5               1
 713          * Windows Server 2003 family   5               2
 714          * Windows XP 64 bit            5               2
 715          *       where ((&ver.wServicePackMinor) + 2) = 1
 716          *       and  si.wProcessorArchitecture = 9
 717          * Windows Vista family         6               0
 718          * Windows 2008                 6               0
 719          *       where ((&ver.wServicePackMinor) + 2) = 1
 720          *
 721          * This mapping will presumably be augmented as new Windows
 722          * versions are released.
 723          */
 724         switch (ver.dwPlatformId) {
 725         case VER_PLATFORM_WIN32s:
 726             sprops.os_name = "Windows 3.1";
 727             break;
 728         case VER_PLATFORM_WIN32_WINDOWS:
 729            if (ver.dwMajorVersion == 4) {
 730                 switch (ver.dwMinorVersion) {
 731                 case  0: sprops.os_name = "Windows 95";           break;
 732                 case 10: sprops.os_name = "Windows 98";           break;
 733                 case 90: sprops.os_name = "Windows Me";           break;
 734                 default: sprops.os_name = "Windows 9X (unknown)"; break;
 735                 }
 736             } else {
 737                 sprops.os_name = "Windows 9X (unknown)";
 738             }
 739             break;
 740         case VER_PLATFORM_WIN32_NT:
 741             if (ver.dwMajorVersion <= 4) {
 742                 sprops.os_name = "Windows NT";
 743             } else if (ver.dwMajorVersion == 5) {
 744                 switch (ver.dwMinorVersion) {
 745                 case  0: sprops.os_name = "Windows 2000";         break;
 746                 case  1: sprops.os_name = "Windows XP";           break;
 747                 case  2:
 748                    /*
 749                     * From MSDN OSVERSIONINFOEX and SYSTEM_INFO documentation:
 750                     *
 751                     * "Because the version numbers for Windows Server 2003
 752                     * and Windows XP 6u4 bit are identical, you must also test
 753                     * whether the wProductType member is VER_NT_WORKSTATION.
 754                     * and si.wProcessorArchitecture is
 755                     * PROCESSOR_ARCHITECTURE_AMD64 (which is 9)
 756                     * If it is, the operating system is Windows XP 64 bit;
 757                     * otherwise, it is Windows Server 2003."
 758                     */
 759                     if(ver.wProductType == VER_NT_WORKSTATION &&
 760                        si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
 761                         sprops.os_name = "Windows XP"; /* 64 bit */
 762                     } else {
 763                         sprops.os_name = "Windows 2003";
 764                     }
 765                     break;
 766                 default: sprops.os_name = "Windows NT (unknown)"; break;
 767                 }
 768             } else if (ver.dwMajorVersion == 6) {
 769                 /*
 770                  * From MSDN OSVERSIONINFOEX documentation:
 771                  *
 772                  * "Because the version numbers for Windows Server 2008
 773                  * and Windows Vista are identical, you must also test
 774                  * whether the wProductType member is VER_NT_WORKSTATION.
 775                  * If wProductType is VER_NT_WORKSTATION, the operating
 776                  * system is Windows Vista; otherwise, it is Windows
 777                  * Server 2008."
 778                  */
 779                 if (ver.wProductType == VER_NT_WORKSTATION)
 780                     sprops.os_name = "Windows Vista";
 781                 else
 782                     sprops.os_name = "Windows Server 2008";
 783             } else {
 784                 sprops.os_name = "Windows NT (unknown)";
 785             }
 786             break;
 787         default:
 788             sprops.os_name = "Windows (unknown)";
 789             break;
 790         }
 791         sprintf(buf, "%d.%d", ver.dwMajorVersion, ver.dwMinorVersion);
 792         sprops.os_version = strdup(buf);
 793 #if _M_IA64
 794         sprops.os_arch = "ia64";
 795 #elif _M_AMD64
 796         sprops.os_arch = "amd64";
 797 #elif _X86_
 798         sprops.os_arch = "x86";
 799 #else
 800         sprops.os_arch = "unknown";
 801 #endif
 802 
 803         sprops.patch_level = strdup(ver.szCSDVersion);
 804 
 805         sprops.desktop = "windows";
 806     }
 807 
 808     /* Endianness of platform */
 809     {
 810         unsigned int endianTest = 0xff000000;
 811         if (((char*)(&endianTest))[0] != 0) {
 812             sprops.cpu_endian = "big";
 813         } else {
 814             sprops.cpu_endian = "little";
 815         }
 816     }
 817 
 818     /* CPU ISA list */
 819     sprops.cpu_isalist = cpu_isalist();
 820 
 821     /*
 822      * User name
 823      * We try to avoid calling GetUserName as it turns out to
 824      * be surprisingly expensive on NT.  It pulls in an extra
 825      * 100 K of footprint.
 826      */
 827     {
 828         char *uname = getenv("USERNAME");
 829         if (uname != NULL && strlen(uname) > 0) {
 830             sprops.user_name = strdup(uname);
 831         } else {
 832             char buf[100];
 833             int buflen = sizeof(buf);
 834             sprops.user_name =
 835                 GetUserName(buf, &buflen) ? strdup(buf) : "unknown";
 836         }
 837     }
 838 
 839     /*
 840      * Home directory/
 841      *
 842      * We first look under a standard registry key.  If that fails we
 843      * fall back on using a SHELL32.DLL API.  If that fails we use a
 844      * default value.
 845      *
 846      * Note: To save space we want to avoid loading SHELL32.DLL
 847      * unless really necessary.  However if we do load it, we leave it
 848      * in memory, as it may be needed again later.
 849      *
 850      * The normal result is that for a given user name XXX:
 851      *     On multi-user NT, user.home gets set to c:\winnt\profiles\XXX.
 852      *     On multi-user Win95, user.home gets set to c:\windows\profiles\XXX.
 853      *     On single-user Win95, user.home gets set to c:\windows.
 854      */
 855     {
 856         char *homep = getHomeFromRegistry();
 857         if (homep == NULL) {
 858             homep = getHomeFromShell32();
 859             if (homep == NULL) {
 860                 homep = "C:\\";
 861             }
 862         }
 863         sprops.user_home = homep;
 864     }
 865 
 866     /*
 867      *  user.language
 868      *  user.country, user.variant (if user's environment specifies them)
 869      *  file.encoding
 870      *  file.encoding.pkg
 871      */
 872     {
 873         /*
 874          * query the system for the current system default locale
 875          * (which is a Windows LCID value),
 876          */
 877         LANGID langID = LANGIDFROMLCID(GetUserDefaultLCID());
 878         LANGID sysLangID = LANGIDFROMLCID(GetSystemDefaultLCID());
 879 
 880         {
 881             int index = getLocaleEntryIndex(langID);
 882 
 883             /*
 884              * if we didn't find the LCID that the system returned to us,
 885              * we don't have a Java locale ID that corresponds to it.
 886              * Fall back on en_US.
 887              */
 888             if (index == -1) {
 889                 sprops.language = "en";
 890                 sprops.country = "US";
 891                 sprops.encoding = "Cp1252";
 892             } else {
 893 
 894                 /* otherwise, look up the corresponding Java locale ID from
 895                  * the list of Java locale IDs and set up the system properties
 896                  * accordingly.
 897                  */
 898 
 899                 char* lang;
 900                 char* ctry;
 901                 char* variant;
 902 
 903                 lang = strdup(langIDMap[index].javaID);
 904                 ctry = lang;
 905 
 906                 while (*ctry != '_' && *ctry != 0)
 907                     ++ctry;
 908 
 909                 if (*ctry == '_') {
 910                     *ctry++ = 0;
 911                 }
 912 
 913                 variant = ctry;
 914                 while (*variant != '_' && *variant != 0)
 915                     ++variant;
 916 
 917                 if (*variant == '_') {
 918                     *variant++ = 0;
 919                 }
 920 
 921                 sprops.language = lang;
 922                 sprops.country = ctry;
 923                 sprops.variant = variant;
 924                 sprops.encoding = getEncodingInternal(index);
 925             }
 926             index = getLocaleEntryIndex(sysLangID);
 927             if (index == -1) {
 928                 sprops.sun_jnu_encoding = "Cp1252";
 929             } else {
 930                 sprops.sun_jnu_encoding = getEncodingInternal(index);
 931             }
 932         }
 933     }
 934 
 935     sprops.unicode_encoding = "UnicodeLittle";
 936     /* User TIMEZONE */
 937     {
 938         /*
 939          * We defer setting up timezone until it's actually necessary.
 940          * Refer to TimeZone.getDefault(). However, the system
 941          * property is necessary to be able to be set by the command
 942          * line interface -D. Here temporarily set a null string to
 943          * timezone.
 944          */
 945         sprops.timezone = "";
 946     }
 947 
 948     /* Current directory */
 949     {
 950         char buf[MAX_PATH];
 951         GetCurrentDirectory(sizeof(buf), buf);
 952         sprops.user_dir = strdup(buf);
 953     }
 954 
 955     sprops.file_separator = "\\";
 956     sprops.path_separator = ";";
 957     sprops.line_separator = "\r\n";
 958 
 959     return &sprops;
 960 }