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