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          * Windows 7                    6               1
 721          *
 722          * This mapping will presumably be augmented as new Windows
 723          * versions are released.
 724          */
 725         switch (ver.dwPlatformId) {
 726         case VER_PLATFORM_WIN32s:
 727             sprops.os_name = "Windows 3.1";
 728             break;
 729         case VER_PLATFORM_WIN32_WINDOWS:
 730            if (ver.dwMajorVersion == 4) {
 731                 switch (ver.dwMinorVersion) {
 732                 case  0: sprops.os_name = "Windows 95";           break;
 733                 case 10: sprops.os_name = "Windows 98";           break;
 734                 case 90: sprops.os_name = "Windows Me";           break;
 735                 default: sprops.os_name = "Windows 9X (unknown)"; break;
 736                 }
 737             } else {
 738                 sprops.os_name = "Windows 9X (unknown)";
 739             }
 740             break;
 741         case VER_PLATFORM_WIN32_NT:
 742             if (ver.dwMajorVersion <= 4) {
 743                 sprops.os_name = "Windows NT";
 744             } else if (ver.dwMajorVersion == 5) {
 745                 switch (ver.dwMinorVersion) {
 746                 case  0: sprops.os_name = "Windows 2000";         break;
 747                 case  1: sprops.os_name = "Windows XP";           break;
 748                 case  2:
 749                    /*
 750                     * From MSDN OSVERSIONINFOEX and SYSTEM_INFO documentation:
 751                     *
 752                     * "Because the version numbers for Windows Server 2003
 753                     * and Windows XP 6u4 bit are identical, you must also test
 754                     * whether the wProductType member is VER_NT_WORKSTATION.
 755                     * and si.wProcessorArchitecture is
 756                     * PROCESSOR_ARCHITECTURE_AMD64 (which is 9)
 757                     * If it is, the operating system is Windows XP 64 bit;
 758                     * otherwise, it is Windows Server 2003."
 759                     */
 760                     if(ver.wProductType == VER_NT_WORKSTATION &&
 761                        si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
 762                         sprops.os_name = "Windows XP"; /* 64 bit */
 763                     } else {
 764                         sprops.os_name = "Windows 2003";
 765                     }
 766                     break;
 767                 default: sprops.os_name = "Windows NT (unknown)"; break;
 768                 }
 769             } else if (ver.dwMajorVersion == 6) {
 770                 /*
 771                  * From MSDN OSVERSIONINFOEX documentation:
 772                  *
 773                  * "Because the version numbers for Windows Server 2008
 774                  * and Windows Vista are identical, you must also test
 775                  * whether the wProductType member is VER_NT_WORKSTATION.
 776                  * If wProductType is VER_NT_WORKSTATION, the operating
 777                  * system is Windows Vista or 7; otherwise, it is Windows
 778                  * Server 2008."
 779                  */
 780                 if (ver.wProductType == VER_NT_WORKSTATION) {
 781                     switch (ver.dwMinorVersion) {
 782                     case  0: sprops.os_name = "Windows Vista";        break;
 783                     case  1: sprops.os_name = "Windows 7";            break;
 784                     default: sprops.os_name = "Windows NT (unknown)";
 785                     }
 786                 } else {
 787                     sprops.os_name = "Windows Server 2008";
 788                 }
 789             } else {
 790                 sprops.os_name = "Windows NT (unknown)";
 791             }
 792             break;
 793         default:
 794             sprops.os_name = "Windows (unknown)";
 795             break;
 796         }
 797         sprintf(buf, "%d.%d", ver.dwMajorVersion, ver.dwMinorVersion);
 798         sprops.os_version = strdup(buf);
 799 #if _M_IA64
 800         sprops.os_arch = "ia64";
 801 #elif _M_AMD64
 802         sprops.os_arch = "amd64";
 803 #elif _X86_
 804         sprops.os_arch = "x86";
 805 #else
 806         sprops.os_arch = "unknown";
 807 #endif
 808 
 809         sprops.patch_level = strdup(ver.szCSDVersion);
 810 
 811         sprops.desktop = "windows";
 812     }
 813 
 814     /* Endianness of platform */
 815     {
 816         unsigned int endianTest = 0xff000000;
 817         if (((char*)(&endianTest))[0] != 0) {
 818             sprops.cpu_endian = "big";
 819         } else {
 820             sprops.cpu_endian = "little";
 821         }
 822     }
 823 
 824     /* CPU ISA list */
 825     sprops.cpu_isalist = cpu_isalist();
 826 
 827     /*
 828      * User name
 829      * We try to avoid calling GetUserName as it turns out to
 830      * be surprisingly expensive on NT.  It pulls in an extra
 831      * 100 K of footprint.
 832      */
 833     {
 834         char *uname = getenv("USERNAME");
 835         if (uname != NULL && strlen(uname) > 0) {
 836             sprops.user_name = strdup(uname);
 837         } else {
 838             char buf[100];
 839             int buflen = sizeof(buf);
 840             sprops.user_name =
 841                 GetUserName(buf, &buflen) ? strdup(buf) : "unknown";
 842         }
 843     }
 844 
 845     /*
 846      * Home directory/
 847      *
 848      * We first look under a standard registry key.  If that fails we
 849      * fall back on using a SHELL32.DLL API.  If that fails we use a
 850      * default value.
 851      *
 852      * Note: To save space we want to avoid loading SHELL32.DLL
 853      * unless really necessary.  However if we do load it, we leave it
 854      * in memory, as it may be needed again later.
 855      *
 856      * The normal result is that for a given user name XXX:
 857      *     On multi-user NT, user.home gets set to c:\winnt\profiles\XXX.
 858      *     On multi-user Win95, user.home gets set to c:\windows\profiles\XXX.
 859      *     On single-user Win95, user.home gets set to c:\windows.
 860      */
 861     {
 862         char *homep = getHomeFromRegistry();
 863         if (homep == NULL) {
 864             homep = getHomeFromShell32();
 865             if (homep == NULL) {
 866                 homep = "C:\\";
 867             }
 868         }
 869         sprops.user_home = homep;
 870     }
 871 
 872     /*
 873      *  user.language
 874      *  user.country, user.variant (if user's environment specifies them)
 875      *  file.encoding
 876      *  file.encoding.pkg
 877      */
 878     {
 879         /*
 880          * query the system for the current system default locale
 881          * (which is a Windows LCID value),
 882          */
 883         LANGID langID = LANGIDFROMLCID(GetUserDefaultLCID());
 884         LANGID sysLangID = LANGIDFROMLCID(GetSystemDefaultLCID());
 885 
 886         {
 887             int index = getLocaleEntryIndex(langID);
 888 
 889             /*
 890              * if we didn't find the LCID that the system returned to us,
 891              * we don't have a Java locale ID that corresponds to it.
 892              * Fall back on en_US.
 893              */
 894             if (index == -1) {
 895                 sprops.language = "en";
 896                 sprops.country = "US";
 897                 sprops.encoding = "Cp1252";
 898             } else {
 899 
 900                 /* otherwise, look up the corresponding Java locale ID from
 901                  * the list of Java locale IDs and set up the system properties
 902                  * accordingly.
 903                  */
 904 
 905                 char* lang;
 906                 char* ctry;
 907                 char* variant;
 908 
 909                 lang = strdup(langIDMap[index].javaID);
 910                 ctry = lang;
 911 
 912                 while (*ctry != '_' && *ctry != 0)
 913                     ++ctry;
 914 
 915                 if (*ctry == '_') {
 916                     *ctry++ = 0;
 917                 }
 918 
 919                 variant = ctry;
 920                 while (*variant != '_' && *variant != 0)
 921                     ++variant;
 922 
 923                 if (*variant == '_') {
 924                     *variant++ = 0;
 925                 }
 926 
 927                 sprops.language = lang;
 928                 sprops.country = ctry;
 929                 sprops.variant = variant;
 930                 sprops.encoding = getEncodingInternal(index);
 931             }
 932             index = getLocaleEntryIndex(sysLangID);
 933             if (index == -1) {
 934                 sprops.sun_jnu_encoding = "Cp1252";
 935             } else {
 936                 sprops.sun_jnu_encoding = getEncodingInternal(index);
 937             }
 938         }
 939     }
 940 
 941     sprops.unicode_encoding = "UnicodeLittle";
 942     /* User TIMEZONE */
 943     {
 944         /*
 945          * We defer setting up timezone until it's actually necessary.
 946          * Refer to TimeZone.getDefault(). However, the system
 947          * property is necessary to be able to be set by the command
 948          * line interface -D. Here temporarily set a null string to
 949          * timezone.
 950          */
 951         sprops.timezone = "";
 952     }
 953 
 954     /* Current directory */
 955     {
 956         char buf[MAX_PATH];
 957         GetCurrentDirectory(sizeof(buf), buf);
 958         sprops.user_dir = strdup(buf);
 959     }
 960 
 961     sprops.file_separator = "\\";
 962     sprops.path_separator = ";";
 963     sprops.line_separator = "\r\n";
 964 
 965     return &sprops;
 966 }