1 /*
   2  * Copyright (c) 1998, 2020, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /* Access APIs for Windows Vista and above */
  27 #ifndef _WIN32_WINNT
  28 #define _WIN32_WINNT 0x0601
  29 #endif
  30 
  31 #include "jni.h"
  32 #include "jni_util.h"
  33 
  34 #include <windows.h>
  35 #include <shlobj.h>
  36 #include <objidl.h>
  37 #include <locale.h>
  38 #include <sys/types.h>
  39 #include <sys/timeb.h>
  40 #include <tchar.h>
  41 
  42 #include <stdlib.h>
  43 #include <Wincon.h>
  44 
  45 #include "locale_str.h"
  46 #include "java_props.h"
  47 
  48 #ifndef VER_PLATFORM_WIN32_WINDOWS
  49 #define VER_PLATFORM_WIN32_WINDOWS 1
  50 #endif
  51 
  52 #ifndef PROCESSOR_ARCHITECTURE_AMD64
  53 #define PROCESSOR_ARCHITECTURE_AMD64 9
  54 #endif
  55 
  56 typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
  57 static boolean SetupI18nProps(LCID lcid, char** language, char** script, char** country,
  58                char** variant, char** encoding);
  59 
  60 #define PROPSIZE 9      // eight-letter + null terminator
  61 #define SNAMESIZE 86    // max number of chars for LOCALE_SNAME is 85
  62 
  63 static char *
  64 getEncodingInternal(LCID lcid)
  65 {
  66     int codepage;
  67     char * ret = malloc(16);
  68     if (ret == NULL) {
  69         return NULL;
  70     }
  71 
  72     if (GetLocaleInfo(lcid,
  73                       LOCALE_IDEFAULTANSICODEPAGE,
  74                       ret+2, 14) == 0) {
  75         codepage = 1252;
  76         strcpy(ret+2, "1252");
  77     } else {
  78         codepage = atoi(ret+2);
  79     }
  80 
  81     switch (codepage) {
  82     case 0:
  83         strcpy(ret, "UTF-8");
  84         break;
  85     case 874:     /*  9:Thai     */
  86     case 932:     /* 10:Japanese */
  87     case 949:     /* 12:Korean Extended Wansung */
  88     case 950:     /* 13:Chinese (Taiwan, Hongkong, Macau) */
  89     case 1361:    /* 15:Korean Johab */
  90         ret[0] = 'M';
  91         ret[1] = 'S';
  92         break;
  93     case 936:
  94         strcpy(ret, "GBK");
  95         break;
  96     case 54936:
  97         strcpy(ret, "GB18030");
  98         break;
  99     default:
 100         ret[0] = 'C';
 101         ret[1] = 'p';
 102         break;
 103     }
 104 
 105     //Traditional Chinese Windows should use MS950_HKSCS_XP as the
 106     //default encoding, if HKSCS patch has been installed.
 107     // "old" MS950 0xfa41 -> u+e001
 108     // "new" MS950 0xfa41 -> u+92db
 109     if (strcmp(ret, "MS950") == 0) {
 110         TCHAR  mbChar[2] = {(char)0xfa, (char)0x41};
 111         WCHAR  unicodeChar;
 112         MultiByteToWideChar(CP_ACP, 0, mbChar, 2, &unicodeChar, 1);
 113         if (unicodeChar == 0x92db) {
 114             strcpy(ret, "MS950_HKSCS_XP");
 115         }
 116     } else {
 117         //SimpChinese Windows should use GB18030 as the default
 118         //encoding, if gb18030 patch has been installed (on windows
 119         //2000/XP, (1)Codepage 54936 will be available
 120         //(2)simsun18030.ttc will exist under system fonts dir )
 121         if (strcmp(ret, "GBK") == 0 && IsValidCodePage(54936)) {
 122             char systemPath[MAX_PATH + 1];
 123             char* gb18030Font = "\\FONTS\\SimSun18030.ttc";
 124             FILE *f = NULL;
 125             if (GetWindowsDirectory(systemPath, MAX_PATH + 1) != 0 &&
 126                 strlen(systemPath) + strlen(gb18030Font) < MAX_PATH + 1) {
 127                 strcat(systemPath, "\\FONTS\\SimSun18030.ttc");
 128                 if ((f = fopen(systemPath, "r")) != NULL) {
 129                     fclose(f);
 130                     strcpy(ret, "GB18030");
 131                 }
 132             }
 133         }
 134     }
 135 
 136     return ret;
 137 }
 138 
 139 static char* getConsoleEncoding()
 140 {
 141     char* buf = malloc(16);
 142     int cp;
 143     if (buf == NULL) {
 144         return NULL;
 145     }
 146     cp = GetConsoleCP();
 147     if (cp >= 874 && cp <= 950)
 148         sprintf(buf, "ms%d", cp);
 149     else
 150         sprintf(buf, "cp%d", cp);
 151     return buf;
 152 }
 153 
 154 // Exported entries for AWT
 155 DllExport const char *
 156 getEncodingFromLangID(LANGID langID)
 157 {
 158     return getEncodingInternal(MAKELCID(langID, SORT_DEFAULT));
 159 }
 160 
 161 // Returns BCP47 Language Tag
 162 DllExport const char *
 163 getJavaIDFromLangID(LANGID langID)
 164 {
 165     char * elems[5]; // lang, script, ctry, variant, encoding
 166     char * ret;
 167     int index;
 168 
 169     ret = malloc(SNAMESIZE);
 170     if (ret == NULL) {
 171         return NULL;
 172     }
 173 
 174     for (index = 0; index < 5; index++) {
 175         elems[index] = NULL;
 176     }
 177 
 178     if (SetupI18nProps(MAKELCID(langID, SORT_DEFAULT),
 179                    &(elems[0]), &(elems[1]), &(elems[2]), &(elems[3]), &(elems[4]))) {
 180 
 181         // there always is the "language" tag
 182         strcpy(ret, elems[0]);
 183 
 184         // append other elements, if any
 185         for (index = 1; index < 4; index++) {
 186             if ((elems[index])[0] != '\0') {
 187                 strcat(ret, "-");
 188                 strcat(ret, elems[index]);
 189             }
 190         }
 191     } else {
 192         free(ret);
 193         ret = NULL;
 194     }
 195 
 196     for (index = 0; index < 5; index++) {
 197         if (elems[index] != NULL) {
 198             free(elems[index]);
 199         }
 200     }
 201 
 202     return ret;
 203 }
 204 
 205 /*
 206  * Code to figure out the user's home directory using shell32.dll
 207  */
 208 WCHAR*
 209 getHomeFromShell32()
 210 {
 211     /*
 212      * Note that we don't free the memory allocated
 213      * by getHomeFromShell32.
 214      */
 215     static WCHAR *u_path = NULL;
 216     if (u_path == NULL) {
 217         HRESULT hr;
 218 
 219         /*
 220          * SHELL32 DLL is delay load DLL and we can use the trick with
 221          * __try/__except block.
 222          */
 223         __try {
 224             /*
 225              * For Windows Vista and later (or patched MS OS) we need to use
 226              * [SHGetKnownFolderPath] call to avoid MAX_PATH length limitation.
 227              * Shell32.dll (version 6.0.6000 or later)
 228              */
 229             hr = SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DONT_VERIFY, NULL, &u_path);
 230         } __except(EXCEPTION_EXECUTE_HANDLER) {
 231             /* Exception: no [SHGetKnownFolderPath] entry */
 232             hr = E_FAIL;
 233         }
 234 
 235         if (FAILED(hr)) {
 236             WCHAR path[MAX_PATH+1];
 237 
 238             /* fallback solution for WinXP and Windows 2000 */
 239             hr = SHGetFolderPathW(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, path);
 240             if (FAILED(hr)) {
 241                 /* we can't find the shell folder. */
 242                 u_path = NULL;
 243             } else {
 244                 /* Just to be sure about the path length until Windows Vista approach.
 245                  * [S_FALSE] could not be returned due to [CSIDL_FLAG_DONT_VERIFY] flag and UNICODE version.
 246                  */
 247                 path[MAX_PATH] = 0;
 248                 u_path = _wcsdup(path);
 249             }
 250         }
 251     }
 252     return u_path;
 253 }
 254 
 255 static boolean
 256 haveMMX(void)
 257 {
 258     return IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE);
 259 }
 260 
 261 static const char *
 262 cpu_isalist(void)
 263 {
 264     SYSTEM_INFO info;
 265     GetSystemInfo(&info);
 266     switch (info.wProcessorArchitecture) {
 267 #ifdef PROCESSOR_ARCHITECTURE_IA64
 268     case PROCESSOR_ARCHITECTURE_IA64: return "ia64";
 269 #endif
 270 #ifdef PROCESSOR_ARCHITECTURE_AMD64
 271     case PROCESSOR_ARCHITECTURE_AMD64: return "amd64";
 272 #endif
 273     case PROCESSOR_ARCHITECTURE_INTEL:
 274         switch (info.wProcessorLevel) {
 275         case 6: return haveMMX()
 276             ? "pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86"
 277             : "pentium_pro pentium i486 i386 i86";
 278         case 5: return haveMMX()
 279             ? "pentium+mmx pentium i486 i386 i86"
 280             : "pentium i486 i386 i86";
 281         case 4: return "i486 i386 i86";
 282         case 3: return "i386 i86";
 283         }
 284     }
 285     return NULL;
 286 }
 287 
 288 static boolean
 289 SetupI18nProps(LCID lcid, char** language, char** script, char** country,
 290                char** variant, char** encoding) {
 291     /* script */
 292     char tmp[SNAMESIZE];
 293     *script = malloc(PROPSIZE);
 294     if (*script == NULL) {
 295         return FALSE;
 296     }
 297     if (GetLocaleInfo(lcid,
 298                       LOCALE_SNAME, tmp, SNAMESIZE) == 0 ||
 299         sscanf(tmp, "%*[a-z\\-]%1[A-Z]%[a-z]", *script, &((*script)[1])) == 0 ||
 300         strlen(*script) != 4) {
 301         (*script)[0] = '\0';
 302     }
 303 
 304     /* country */
 305     *country = malloc(PROPSIZE);
 306     if (*country == NULL) {
 307         return FALSE;
 308     }
 309     if (GetLocaleInfo(lcid,
 310                       LOCALE_SISO3166CTRYNAME, *country, PROPSIZE) == 0 &&
 311         GetLocaleInfo(lcid,
 312                       LOCALE_SISO3166CTRYNAME2, *country, PROPSIZE) == 0) {
 313         (*country)[0] = '\0';
 314     }
 315 
 316     /* language */
 317     *language = malloc(PROPSIZE);
 318     if (*language == NULL) {
 319         return FALSE;
 320     }
 321     if (GetLocaleInfo(lcid,
 322                       LOCALE_SISO639LANGNAME, *language, PROPSIZE) == 0 &&
 323         GetLocaleInfo(lcid,
 324                       LOCALE_SISO639LANGNAME2, *language, PROPSIZE) == 0) {
 325             /* defaults to en_US */
 326             strcpy(*language, "en");
 327             strcpy(*country, "US");
 328         }
 329 
 330     /* variant */
 331     *variant = malloc(PROPSIZE);
 332     if (*variant == NULL) {
 333         return FALSE;
 334     }
 335     (*variant)[0] = '\0';
 336 
 337     /* handling for Norwegian */
 338     if (strcmp(*language, "nb") == 0) {
 339         strcpy(*language, "no");
 340         strcpy(*country , "NO");
 341     } else if (strcmp(*language, "nn") == 0) {
 342         strcpy(*language, "no");
 343         strcpy(*country , "NO");
 344         strcpy(*variant, "NY");
 345     }
 346 
 347     /* encoding */
 348     *encoding = getEncodingInternal(lcid);
 349     if (*encoding == NULL) {
 350         return FALSE;
 351     }
 352     return TRUE;
 353 }
 354 
 355 // GetVersionEx is deprecated; disable the warning until a replacement is found
 356 #pragma warning(disable : 4996)
 357 java_props_t *
 358 GetJavaProperties(JNIEnv* env)
 359 {
 360     static java_props_t sprops = {0};
 361     int majorVersion;
 362     int minorVersion;
 363     int buildNumber = 0;
 364 
 365     if (sprops.line_separator) {
 366         return &sprops;
 367     }
 368 
 369     /* tmp dir */
 370     {
 371         WCHAR tmpdir[MAX_PATH + 1];
 372         /* we might want to check that this succeed */
 373         GetTempPathW(MAX_PATH + 1, tmpdir);
 374         sprops.tmp_dir = _wcsdup(tmpdir);
 375     }
 376 
 377     /* OS properties */
 378     {
 379         char buf[100];
 380         boolean is_workstation;
 381         boolean is_64bit;
 382         DWORD platformId;
 383         {
 384             OSVERSIONINFOEX ver;
 385             ver.dwOSVersionInfoSize = sizeof(ver);
 386             GetVersionEx((OSVERSIONINFO *) &ver);
 387             majorVersion = ver.dwMajorVersion;
 388             minorVersion = ver.dwMinorVersion;
 389             /* distinguish Windows Server 2016 and 2019 by build number */
 390             buildNumber = ver.dwBuildNumber;
 391             is_workstation = (ver.wProductType == VER_NT_WORKSTATION);
 392             platformId = ver.dwPlatformId;
 393             sprops.patch_level = _strdup(ver.szCSDVersion);
 394         }
 395 
 396         {
 397             SYSTEM_INFO si;
 398             ZeroMemory(&si, sizeof(SYSTEM_INFO));
 399             GetNativeSystemInfo(&si);
 400 
 401             is_64bit = (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64);
 402         }
 403         do {
 404             // Read the major and minor version number from kernel32.dll
 405             VS_FIXEDFILEINFO *file_info;
 406             WCHAR kernel32_path[MAX_PATH];
 407             DWORD version_size;
 408             LPTSTR version_info;
 409             UINT len, ret;
 410 
 411             // Get the full path to \Windows\System32\kernel32.dll and use that for
 412             // determining what version of Windows we're running on.
 413             len = MAX_PATH - (UINT)strlen("\\kernel32.dll") - 1;
 414             ret = GetSystemDirectoryW(kernel32_path, len);
 415             if (ret == 0 || ret > len) {
 416                 break;
 417             }
 418             wcsncat(kernel32_path, L"\\kernel32.dll", MAX_PATH - ret);
 419 
 420             version_size = GetFileVersionInfoSizeW(kernel32_path, NULL);
 421             if (version_size == 0) {
 422                 break;
 423             }
 424 
 425             version_info = (LPTSTR)malloc(version_size);
 426             if (version_info == NULL) {
 427                 break;
 428             }
 429 
 430             if (!GetFileVersionInfoW(kernel32_path, 0, version_size, version_info)) {
 431                 free(version_info);
 432                 break;
 433             }
 434 
 435             if (!VerQueryValueW(version_info, L"\\", (LPVOID*)&file_info, &len)) {
 436                 free(version_info);
 437                 break;
 438             }
 439             majorVersion = HIWORD(file_info->dwProductVersionMS);
 440             minorVersion = LOWORD(file_info->dwProductVersionMS);
 441             buildNumber  = HIWORD(file_info->dwProductVersionLS);
 442             free(version_info);
 443         } while (0);
 444 
 445         /*
 446          * From msdn page on OSVERSIONINFOEX, current as of this
 447          * writing, decoding of dwMajorVersion and dwMinorVersion.
 448          *
 449          *  Operating system            dwMajorVersion  dwMinorVersion
 450          * ==================           ==============  ==============
 451          *
 452          * Windows 95                   4               0
 453          * Windows 98                   4               10
 454          * Windows ME                   4               90
 455          * Windows 3.51                 3               51
 456          * Windows NT 4.0               4               0
 457          * Windows 2000                 5               0
 458          * Windows XP 32 bit            5               1
 459          * Windows Server 2003 family   5               2
 460          * Windows XP 64 bit            5               2
 461          *       where ((&ver.wServicePackMinor) + 2) = 1
 462          *       and  si.wProcessorArchitecture = 9
 463          * Windows Vista family         6               0  (VER_NT_WORKSTATION)
 464          * Windows Server 2008          6               0  (!VER_NT_WORKSTATION)
 465          * Windows 7                    6               1  (VER_NT_WORKSTATION)
 466          * Windows Server 2008 R2       6               1  (!VER_NT_WORKSTATION)
 467          * Windows 8                    6               2  (VER_NT_WORKSTATION)
 468          * Windows Server 2012          6               2  (!VER_NT_WORKSTATION)
 469          * Windows Server 2012 R2       6               3  (!VER_NT_WORKSTATION)
 470          * Windows 10                   10              0  (VER_NT_WORKSTATION)
 471          * Windows Server 2016          10              0  (!VER_NT_WORKSTATION)
 472          * Windows Server 2019          10              0  (!VER_NT_WORKSTATION)
 473          *       where (buildNumber > 17762)
 474          *
 475          * This mapping will presumably be augmented as new Windows
 476          * versions are released.
 477          */
 478         switch (platformId) {
 479         case VER_PLATFORM_WIN32_WINDOWS:
 480            if (majorVersion == 4) {
 481                 switch (minorVersion) {
 482                 case  0: sprops.os_name = "Windows 95";           break;
 483                 case 10: sprops.os_name = "Windows 98";           break;
 484                 case 90: sprops.os_name = "Windows Me";           break;
 485                 default: sprops.os_name = "Windows 9X (unknown)"; break;
 486                 }
 487             } else {
 488                 sprops.os_name = "Windows 9X (unknown)";
 489             }
 490             break;
 491         case VER_PLATFORM_WIN32_NT:
 492             if (majorVersion <= 4) {
 493                 sprops.os_name = "Windows NT";
 494             } else if (majorVersion == 5) {
 495                 switch (minorVersion) {
 496                 case  0: sprops.os_name = "Windows 2000";         break;
 497                 case  1: sprops.os_name = "Windows XP";           break;
 498                 case  2:
 499                    /*
 500                     * From MSDN OSVERSIONINFOEX and SYSTEM_INFO documentation:
 501                     *
 502                     * "Because the version numbers for Windows Server 2003
 503                     * and Windows XP 6u4 bit are identical, you must also test
 504                     * whether the wProductType member is VER_NT_WORKSTATION.
 505                     * and si.wProcessorArchitecture is
 506                     * PROCESSOR_ARCHITECTURE_AMD64 (which is 9)
 507                     * If it is, the operating system is Windows XP 64 bit;
 508                     * otherwise, it is Windows Server 2003."
 509                     */
 510                     if (is_workstation && is_64bit) {
 511                         sprops.os_name = "Windows XP"; /* 64 bit */
 512                     } else {
 513                         sprops.os_name = "Windows 2003";
 514                     }
 515                     break;
 516                 default: sprops.os_name = "Windows NT (unknown)"; break;
 517                 }
 518             } else if (majorVersion == 6) {
 519                 /*
 520                  * See table in MSDN OSVERSIONINFOEX documentation.
 521                  */
 522                 if (is_workstation) {
 523                     switch (minorVersion) {
 524                     case  0: sprops.os_name = "Windows Vista";        break;
 525                     case  1: sprops.os_name = "Windows 7";            break;
 526                     case  2: sprops.os_name = "Windows 8";            break;
 527                     case  3: sprops.os_name = "Windows 8.1";          break;
 528                     default: sprops.os_name = "Windows NT (unknown)";
 529                     }
 530                 } else {
 531                     switch (minorVersion) {
 532                     case  0: sprops.os_name = "Windows Server 2008";    break;
 533                     case  1: sprops.os_name = "Windows Server 2008 R2"; break;
 534                     case  2: sprops.os_name = "Windows Server 2012";    break;
 535                     case  3: sprops.os_name = "Windows Server 2012 R2"; break;
 536                     default: sprops.os_name = "Windows NT (unknown)";
 537                     }
 538                 }
 539             } else if (majorVersion == 10) {
 540                 if (is_workstation) {
 541                     switch (minorVersion) {
 542                     case  0: sprops.os_name = "Windows 10";           break;
 543                     default: sprops.os_name = "Windows NT (unknown)";
 544                     }
 545                 } else {
 546                     switch (minorVersion) {
 547                     case  0:
 548                         /* Windows server 2019 GA 10/2018 build number is 17763 */
 549                         if (buildNumber > 17762) {
 550                             sprops.os_name = "Windows Server 2019";
 551                         } else {
 552                             sprops.os_name = "Windows Server 2016";
 553                         }
 554                         break;
 555                     default: sprops.os_name = "Windows NT (unknown)";
 556                     }
 557                 }
 558             } else {
 559                 sprops.os_name = "Windows NT (unknown)";
 560             }
 561             break;
 562         default:
 563             sprops.os_name = "Windows (unknown)";
 564             break;
 565         }
 566         sprintf(buf, "%d.%d", majorVersion, minorVersion);
 567         sprops.os_version = _strdup(buf);
 568 #if defined(_M_AMD64)
 569         sprops.os_arch = "amd64";
 570 #elif defined(_X86_)
 571         sprops.os_arch = "x86";
 572 #else
 573         sprops.os_arch = "unknown";
 574 #endif
 575     }
 576 
 577     /* Endianness of platform */
 578     {
 579         unsigned int endianTest = 0xff000000;
 580         if (((char*)(&endianTest))[0] != 0) {
 581             sprops.cpu_endian = "big";
 582         } else {
 583             sprops.cpu_endian = "little";
 584         }
 585     }
 586 
 587     /* CPU ISA list */
 588     sprops.cpu_isalist = cpu_isalist();
 589 
 590     /*
 591      * User name
 592      * We try to avoid calling GetUserName as it turns out to
 593      * be surprisingly expensive on NT.  It pulls in an extra
 594      * 100 K of footprint.
 595      */
 596     {
 597         WCHAR *uname = _wgetenv(L"USERNAME");
 598         if (uname != NULL && wcslen(uname) > 0) {
 599             sprops.user_name = _wcsdup(uname);
 600         } else {
 601             DWORD buflen = 0;
 602             if (GetUserNameW(NULL, &buflen) == 0 &&
 603                 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
 604             {
 605                 uname = (WCHAR*)malloc(buflen * sizeof(WCHAR));
 606                 if (uname != NULL && GetUserNameW(uname, &buflen) == 0) {
 607                     free(uname);
 608                     uname = NULL;
 609                 }
 610             } else {
 611                 uname = NULL;
 612             }
 613             sprops.user_name = (uname != NULL) ? uname : L"unknown";
 614         }
 615     }
 616 
 617     /*
 618      * Home directory
 619      *
 620      * The normal result is that for a given user name XXX:
 621      *     On multi-user NT, user.home gets set to c:\winnt\profiles\XXX.
 622      *     On multi-user Win95, user.home gets set to c:\windows\profiles\XXX.
 623      *     On single-user Win95, user.home gets set to c:\windows.
 624      */
 625     {
 626         WCHAR *homep = getHomeFromShell32();
 627         if (homep == NULL) {
 628             homep = L"C:\\";
 629         }
 630         sprops.user_home = homep;
 631     }
 632 
 633     /*
 634      *  user.language
 635      *  user.script, user.country, user.variant (if user's environment specifies them)
 636      *  file.encoding
 637      */
 638     {
 639         /*
 640          * query the system for the current system default locale
 641          * (which is a Windows LCID value),
 642          */
 643         LCID userDefaultLCID = GetUserDefaultLCID();
 644         LCID systemDefaultLCID = GetSystemDefaultLCID();
 645         LANGID userDefaultUILang = GetUserDefaultUILanguage();
 646         LCID userDefaultUILCID = MAKELCID(userDefaultUILang, SORTIDFROMLCID(userDefaultLCID));
 647 
 648         {
 649             char * display_encoding;
 650             HANDLE hStdOutErr;
 651 
 652             // Windows UI Language selection list only cares "language"
 653             // information of the UI Language. For example, the list
 654             // just lists "English" but it actually means "en_US", and
 655             // the user cannot select "en_GB" (if exists) in the list.
 656             // So, this hack is to use the user LCID region information
 657             // for the UI Language, if the "language" portion of those
 658             // two locales are the same.
 659             if (PRIMARYLANGID(LANGIDFROMLCID(userDefaultLCID)) ==
 660                 PRIMARYLANGID(userDefaultUILang)) {
 661                 userDefaultUILCID = userDefaultLCID;
 662             }
 663 
 664             SetupI18nProps(userDefaultLCID,
 665                            &sprops.format_language,
 666                            &sprops.format_script,
 667                            &sprops.format_country,
 668                            &sprops.format_variant,
 669                            &sprops.encoding);
 670             SetupI18nProps(userDefaultUILCID,
 671                            &sprops.display_language,
 672                            &sprops.display_script,
 673                            &sprops.display_country,
 674                            &sprops.display_variant,
 675                            &display_encoding);
 676 
 677             sprops.sun_jnu_encoding = getEncodingInternal(systemDefaultLCID);
 678             if (LANGIDFROMLCID(userDefaultLCID) == 0x0c04 && majorVersion == 6) {
 679                 // MS claims "Vista has built-in support for HKSCS-2004.
 680                 // All of the HKSCS-2004 characters have Unicode 4.1.
 681                 // PUA code point assignments". But what it really means
 682                 // is that the HKSCS-2004 is ONLY supported in Unicode.
 683                 // Test indicates the MS950 in its zh_HK locale is a
 684                 // "regular" MS950 which does not handle HKSCS-2004 at
 685                 // all. Set encoding to MS950_HKSCS.
 686                 sprops.encoding = "MS950_HKSCS";
 687                 sprops.sun_jnu_encoding = "MS950_HKSCS";
 688             }
 689 
 690             hStdOutErr = GetStdHandle(STD_OUTPUT_HANDLE);
 691             if (hStdOutErr != INVALID_HANDLE_VALUE &&
 692                 GetFileType(hStdOutErr) == FILE_TYPE_CHAR) {
 693                 sprops.sun_stdout_encoding = getConsoleEncoding();
 694             }
 695             hStdOutErr = GetStdHandle(STD_ERROR_HANDLE);
 696             if (hStdOutErr != INVALID_HANDLE_VALUE &&
 697                 GetFileType(hStdOutErr) == FILE_TYPE_CHAR) {
 698                 if (sprops.sun_stdout_encoding != NULL)
 699                     sprops.sun_stderr_encoding = sprops.sun_stdout_encoding;
 700                 else
 701                     sprops.sun_stderr_encoding = getConsoleEncoding();
 702             }
 703         }
 704     }
 705 
 706     sprops.unicode_encoding = "UnicodeLittle";
 707 
 708     /* User TIMEZONE
 709      * We defer setting up timezone until it's actually necessary.
 710      * Refer to TimeZone.getDefault(). The system property
 711      * is able to be set by the command line interface -Duser.timezone.
 712      */
 713 
 714     /* Current directory */
 715     {
 716         WCHAR buf[MAX_PATH];
 717         if (GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR), buf) != 0)
 718             sprops.user_dir = _wcsdup(buf);
 719     }
 720 
 721     sprops.file_separator = "\\";
 722     sprops.path_separator = ";";
 723     sprops.line_separator = "\r\n";
 724 
 725     return &sprops;
 726 }
 727 
 728 jstring
 729 GetStringPlatform(JNIEnv *env, nchar* wcstr)
 730 {
 731     return (*env)->NewString(env, wcstr, (jsize)wcslen(wcstr));
 732 }