Path: blob/master/src/java.base/windows/native/libjava/java_props_md.c
41119 views
/*1* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/* Access APIs for Windows Vista and above */26#ifndef _WIN32_WINNT27#define _WIN32_WINNT 0x060128#endif2930#include "jni.h"31#include "jni_util.h"3233#include <windows.h>34#include <shlobj.h>35#include <objidl.h>36#include <locale.h>37#include <sys/types.h>38#include <sys/timeb.h>39#include <tchar.h>4041#include <stdlib.h>42#include <Wincon.h>4344#include "locale_str.h"45#include "java_props.h"4647#ifndef VER_PLATFORM_WIN32_WINDOWS48#define VER_PLATFORM_WIN32_WINDOWS 149#endif5051#ifndef PROCESSOR_ARCHITECTURE_AMD6452#define PROCESSOR_ARCHITECTURE_AMD64 953#endif5455typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);56static boolean SetupI18nProps(LCID lcid, char** language, char** script, char** country,57char** variant, char** encoding);5859#define PROPSIZE 9 // eight-letter + null terminator60#define SNAMESIZE 86 // max number of chars for LOCALE_SNAME is 856162static char *63getEncodingInternal(LCID lcid)64{65int codepage;66char * ret = malloc(16);67if (ret == NULL) {68return NULL;69}7071if (GetLocaleInfo(lcid,72LOCALE_IDEFAULTANSICODEPAGE,73ret+2, 14) == 0) {74codepage = 1252;75strcpy(ret+2, "1252");76} else {77codepage = atoi(ret+2);78}7980switch (codepage) {81case 0:82case 65001:83strcpy(ret, "UTF-8");84break;85case 874: /* 9:Thai */86case 932: /* 10:Japanese */87case 949: /* 12:Korean Extended Wansung */88case 950: /* 13:Chinese (Taiwan, Hongkong, Macau) */89case 1361: /* 15:Korean Johab */90ret[0] = 'M';91ret[1] = 'S';92break;93case 936:94strcpy(ret, "GBK");95break;96case 54936:97strcpy(ret, "GB18030");98break;99default:100ret[0] = 'C';101ret[1] = 'p';102break;103}104105//Traditional Chinese Windows should use MS950_HKSCS_XP as the106//default encoding, if HKSCS patch has been installed.107// "old" MS950 0xfa41 -> u+e001108// "new" MS950 0xfa41 -> u+92db109if (strcmp(ret, "MS950") == 0) {110TCHAR mbChar[2] = {(char)0xfa, (char)0x41};111WCHAR unicodeChar;112MultiByteToWideChar(CP_ACP, 0, mbChar, 2, &unicodeChar, 1);113if (unicodeChar == 0x92db) {114strcpy(ret, "MS950_HKSCS_XP");115}116} else {117//SimpChinese Windows should use GB18030 as the default118//encoding, if gb18030 patch has been installed (on windows119//2000/XP, (1)Codepage 54936 will be available120//(2)simsun18030.ttc will exist under system fonts dir )121if (strcmp(ret, "GBK") == 0 && IsValidCodePage(54936)) {122char systemPath[MAX_PATH + 1];123char* gb18030Font = "\\FONTS\\SimSun18030.ttc";124FILE *f = NULL;125if (GetWindowsDirectory(systemPath, MAX_PATH + 1) != 0 &&126strlen(systemPath) + strlen(gb18030Font) < MAX_PATH + 1) {127strcat(systemPath, "\\FONTS\\SimSun18030.ttc");128if ((f = fopen(systemPath, "r")) != NULL) {129fclose(f);130strcpy(ret, "GB18030");131}132}133}134}135136return ret;137}138139static char* getConsoleEncoding()140{141char* buf = malloc(16);142int cp;143if (buf == NULL) {144return NULL;145}146cp = GetConsoleCP();147if (cp >= 874 && cp <= 950)148sprintf(buf, "ms%d", cp);149else if (cp == 65001)150sprintf(buf, "UTF-8");151else152sprintf(buf, "cp%d", cp);153return buf;154}155156// Exported entries for AWT157DllExport const char *158getEncodingFromLangID(LANGID langID)159{160return getEncodingInternal(MAKELCID(langID, SORT_DEFAULT));161}162163// Returns BCP47 Language Tag164DllExport const char *165getJavaIDFromLangID(LANGID langID)166{167char * elems[5]; // lang, script, ctry, variant, encoding168char * ret;169int index;170171ret = malloc(SNAMESIZE);172if (ret == NULL) {173return NULL;174}175176for (index = 0; index < 5; index++) {177elems[index] = NULL;178}179180if (SetupI18nProps(MAKELCID(langID, SORT_DEFAULT),181&(elems[0]), &(elems[1]), &(elems[2]), &(elems[3]), &(elems[4]))) {182183// there always is the "language" tag184strcpy(ret, elems[0]);185186// append other elements, if any187for (index = 1; index < 4; index++) {188if ((elems[index])[0] != '\0') {189strcat(ret, "-");190strcat(ret, elems[index]);191}192}193} else {194free(ret);195ret = NULL;196}197198for (index = 0; index < 5; index++) {199if (elems[index] != NULL) {200free(elems[index]);201}202}203204return ret;205}206207/*208* Code to figure out the user's home directory using shell32.dll209*/210WCHAR*211getHomeFromShell32()212{213/*214* Note that we don't free the memory allocated215* by getHomeFromShell32.216*/217static WCHAR *u_path = NULL;218if (u_path == NULL) {219HRESULT hr;220221/*222* SHELL32 DLL is delay load DLL and we can use the trick with223* __try/__except block.224*/225__try {226/*227* For Windows Vista and later (or patched MS OS) we need to use228* [SHGetKnownFolderPath] call to avoid MAX_PATH length limitation.229* Shell32.dll (version 6.0.6000 or later)230*/231hr = SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DONT_VERIFY, NULL, &u_path);232} __except(EXCEPTION_EXECUTE_HANDLER) {233/* Exception: no [SHGetKnownFolderPath] entry */234hr = E_FAIL;235}236237if (FAILED(hr)) {238WCHAR path[MAX_PATH+1];239240/* fallback solution for WinXP and Windows 2000 */241hr = SHGetFolderPathW(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, path);242if (FAILED(hr)) {243/* we can't find the shell folder. */244u_path = NULL;245} else {246/* Just to be sure about the path length until Windows Vista approach.247* [S_FALSE] could not be returned due to [CSIDL_FLAG_DONT_VERIFY] flag and UNICODE version.248*/249path[MAX_PATH] = 0;250u_path = _wcsdup(path);251}252}253}254return u_path;255}256257static boolean258haveMMX(void)259{260return IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE);261}262263static const char *264cpu_isalist(void)265{266SYSTEM_INFO info;267GetSystemInfo(&info);268switch (info.wProcessorArchitecture) {269#ifdef PROCESSOR_ARCHITECTURE_IA64270case PROCESSOR_ARCHITECTURE_IA64: return "ia64";271#endif272#ifdef PROCESSOR_ARCHITECTURE_AMD64273case PROCESSOR_ARCHITECTURE_AMD64: return "amd64";274#endif275case PROCESSOR_ARCHITECTURE_INTEL:276switch (info.wProcessorLevel) {277case 6: return haveMMX()278? "pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86"279: "pentium_pro pentium i486 i386 i86";280case 5: return haveMMX()281? "pentium+mmx pentium i486 i386 i86"282: "pentium i486 i386 i86";283case 4: return "i486 i386 i86";284case 3: return "i386 i86";285}286}287return NULL;288}289290static boolean291SetupI18nProps(LCID lcid, char** language, char** script, char** country,292char** variant, char** encoding) {293/* script */294char tmp[SNAMESIZE];295*script = malloc(PROPSIZE);296if (*script == NULL) {297return FALSE;298}299if (GetLocaleInfo(lcid,300LOCALE_SNAME, tmp, SNAMESIZE) == 0 ||301sscanf(tmp, "%*[a-z\\-]%1[A-Z]%[a-z]", *script, &((*script)[1])) == 0 ||302strlen(*script) != 4) {303(*script)[0] = '\0';304}305306/* country */307*country = malloc(PROPSIZE);308if (*country == NULL) {309return FALSE;310}311if (GetLocaleInfo(lcid,312LOCALE_SISO3166CTRYNAME, *country, PROPSIZE) == 0 &&313GetLocaleInfo(lcid,314LOCALE_SISO3166CTRYNAME2, *country, PROPSIZE) == 0) {315(*country)[0] = '\0';316}317318/* language */319*language = malloc(PROPSIZE);320if (*language == NULL) {321return FALSE;322}323if (GetLocaleInfo(lcid,324LOCALE_SISO639LANGNAME, *language, PROPSIZE) == 0 &&325GetLocaleInfo(lcid,326LOCALE_SISO639LANGNAME2, *language, PROPSIZE) == 0) {327/* defaults to en_US */328strcpy(*language, "en");329strcpy(*country, "US");330}331332/* variant */333*variant = malloc(PROPSIZE);334if (*variant == NULL) {335return FALSE;336}337(*variant)[0] = '\0';338339/* handling for Norwegian */340if (strcmp(*language, "nb") == 0) {341strcpy(*language, "no");342strcpy(*country , "NO");343} else if (strcmp(*language, "nn") == 0) {344strcpy(*language, "no");345strcpy(*country , "NO");346strcpy(*variant, "NY");347}348349/* encoding */350*encoding = getEncodingInternal(lcid);351if (*encoding == NULL) {352return FALSE;353}354return TRUE;355}356357// GetVersionEx is deprecated; disable the warning until a replacement is found358#pragma warning(disable : 4996)359java_props_t *360GetJavaProperties(JNIEnv* env)361{362static java_props_t sprops = {0};363int majorVersion;364int minorVersion;365int buildNumber = 0;366367if (sprops.line_separator) {368return &sprops;369}370371/* tmp dir */372{373WCHAR tmpdir[MAX_PATH + 1];374/* we might want to check that this succeed */375GetTempPathW(MAX_PATH + 1, tmpdir);376sprops.tmp_dir = _wcsdup(tmpdir);377}378379/* OS properties */380{381char buf[100];382boolean is_workstation;383boolean is_64bit;384DWORD platformId;385{386OSVERSIONINFOEX ver;387ver.dwOSVersionInfoSize = sizeof(ver);388GetVersionEx((OSVERSIONINFO *) &ver);389majorVersion = ver.dwMajorVersion;390minorVersion = ver.dwMinorVersion;391/* distinguish Windows Server 2016 and 2019 by build number */392buildNumber = ver.dwBuildNumber;393is_workstation = (ver.wProductType == VER_NT_WORKSTATION);394platformId = ver.dwPlatformId;395sprops.patch_level = _strdup(ver.szCSDVersion);396}397398{399SYSTEM_INFO si;400ZeroMemory(&si, sizeof(SYSTEM_INFO));401GetNativeSystemInfo(&si);402403is_64bit = (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64);404}405do {406// Read the major and minor version number from kernel32.dll407VS_FIXEDFILEINFO *file_info;408WCHAR kernel32_path[MAX_PATH];409DWORD version_size;410LPTSTR version_info;411UINT len, ret;412413// Get the full path to \Windows\System32\kernel32.dll and use that for414// determining what version of Windows we're running on.415len = MAX_PATH - (UINT)strlen("\\kernel32.dll") - 1;416ret = GetSystemDirectoryW(kernel32_path, len);417if (ret == 0 || ret > len) {418break;419}420wcsncat(kernel32_path, L"\\kernel32.dll", MAX_PATH - ret);421422version_size = GetFileVersionInfoSizeW(kernel32_path, NULL);423if (version_size == 0) {424break;425}426427version_info = (LPTSTR)malloc(version_size);428if (version_info == NULL) {429break;430}431432if (!GetFileVersionInfoW(kernel32_path, 0, version_size, version_info)) {433free(version_info);434break;435}436437if (!VerQueryValueW(version_info, L"\\", (LPVOID*)&file_info, &len)) {438free(version_info);439break;440}441majorVersion = HIWORD(file_info->dwProductVersionMS);442minorVersion = LOWORD(file_info->dwProductVersionMS);443buildNumber = HIWORD(file_info->dwProductVersionLS);444free(version_info);445} while (0);446447/*448* From msdn page on OSVERSIONINFOEX, current as of this449* writing, decoding of dwMajorVersion and dwMinorVersion.450*451* Operating system dwMajorVersion dwMinorVersion452* ================== ============== ==============453*454* Windows 95 4 0455* Windows 98 4 10456* Windows ME 4 90457* Windows 3.51 3 51458* Windows NT 4.0 4 0459* Windows 2000 5 0460* Windows XP 32 bit 5 1461* Windows Server 2003 family 5 2462* Windows XP 64 bit 5 2463* where ((&ver.wServicePackMinor) + 2) = 1464* and si.wProcessorArchitecture = 9465* Windows Vista family 6 0 (VER_NT_WORKSTATION)466* Windows Server 2008 6 0 (!VER_NT_WORKSTATION)467* Windows 7 6 1 (VER_NT_WORKSTATION)468* Windows Server 2008 R2 6 1 (!VER_NT_WORKSTATION)469* Windows 8 6 2 (VER_NT_WORKSTATION)470* Windows Server 2012 6 2 (!VER_NT_WORKSTATION)471* Windows Server 2012 R2 6 3 (!VER_NT_WORKSTATION)472* Windows 10 10 0 (VER_NT_WORKSTATION)473* Windows Server 2016 10 0 (!VER_NT_WORKSTATION)474* Windows Server 2019 10 0 (!VER_NT_WORKSTATION)475* where (buildNumber > 17762)476*477* This mapping will presumably be augmented as new Windows478* versions are released.479*/480switch (platformId) {481case VER_PLATFORM_WIN32_WINDOWS:482if (majorVersion == 4) {483switch (minorVersion) {484case 0: sprops.os_name = "Windows 95"; break;485case 10: sprops.os_name = "Windows 98"; break;486case 90: sprops.os_name = "Windows Me"; break;487default: sprops.os_name = "Windows 9X (unknown)"; break;488}489} else {490sprops.os_name = "Windows 9X (unknown)";491}492break;493case VER_PLATFORM_WIN32_NT:494if (majorVersion <= 4) {495sprops.os_name = "Windows NT";496} else if (majorVersion == 5) {497switch (minorVersion) {498case 0: sprops.os_name = "Windows 2000"; break;499case 1: sprops.os_name = "Windows XP"; break;500case 2:501/*502* From MSDN OSVERSIONINFOEX and SYSTEM_INFO documentation:503*504* "Because the version numbers for Windows Server 2003505* and Windows XP 6u4 bit are identical, you must also test506* whether the wProductType member is VER_NT_WORKSTATION.507* and si.wProcessorArchitecture is508* PROCESSOR_ARCHITECTURE_AMD64 (which is 9)509* If it is, the operating system is Windows XP 64 bit;510* otherwise, it is Windows Server 2003."511*/512if (is_workstation && is_64bit) {513sprops.os_name = "Windows XP"; /* 64 bit */514} else {515sprops.os_name = "Windows 2003";516}517break;518default: sprops.os_name = "Windows NT (unknown)"; break;519}520} else if (majorVersion == 6) {521/*522* See table in MSDN OSVERSIONINFOEX documentation.523*/524if (is_workstation) {525switch (minorVersion) {526case 0: sprops.os_name = "Windows Vista"; break;527case 1: sprops.os_name = "Windows 7"; break;528case 2: sprops.os_name = "Windows 8"; break;529case 3: sprops.os_name = "Windows 8.1"; break;530default: sprops.os_name = "Windows NT (unknown)";531}532} else {533switch (minorVersion) {534case 0: sprops.os_name = "Windows Server 2008"; break;535case 1: sprops.os_name = "Windows Server 2008 R2"; break;536case 2: sprops.os_name = "Windows Server 2012"; break;537case 3: sprops.os_name = "Windows Server 2012 R2"; break;538default: sprops.os_name = "Windows NT (unknown)";539}540}541} else if (majorVersion == 10) {542if (is_workstation) {543switch (minorVersion) {544case 0: sprops.os_name = "Windows 10"; break;545default: sprops.os_name = "Windows NT (unknown)";546}547} else {548switch (minorVersion) {549case 0:550/* Windows server 2019 GA 10/2018 build number is 17763 */551if (buildNumber > 17762) {552sprops.os_name = "Windows Server 2019";553} else {554sprops.os_name = "Windows Server 2016";555}556break;557default: sprops.os_name = "Windows NT (unknown)";558}559}560} else {561sprops.os_name = "Windows NT (unknown)";562}563break;564default:565sprops.os_name = "Windows (unknown)";566break;567}568sprintf(buf, "%d.%d", majorVersion, minorVersion);569sprops.os_version = _strdup(buf);570#if defined(_M_AMD64)571sprops.os_arch = "amd64";572#elif defined(_X86_)573sprops.os_arch = "x86";574#elif defined(_M_ARM64)575sprops.os_arch = "aarch64";576#else577sprops.os_arch = "unknown";578#endif579}580581/* Endianness of platform */582{583unsigned int endianTest = 0xff000000;584if (((char*)(&endianTest))[0] != 0) {585sprops.cpu_endian = "big";586} else {587sprops.cpu_endian = "little";588}589}590591/* CPU ISA list */592sprops.cpu_isalist = cpu_isalist();593594/*595* User name596* We try to avoid calling GetUserName as it turns out to597* be surprisingly expensive on NT. It pulls in an extra598* 100 K of footprint.599*/600{601WCHAR *uname = _wgetenv(L"USERNAME");602if (uname != NULL && wcslen(uname) > 0) {603sprops.user_name = _wcsdup(uname);604} else {605DWORD buflen = 0;606if (GetUserNameW(NULL, &buflen) == 0 &&607GetLastError() == ERROR_INSUFFICIENT_BUFFER)608{609uname = (WCHAR*)malloc(buflen * sizeof(WCHAR));610if (uname != NULL && GetUserNameW(uname, &buflen) == 0) {611free(uname);612uname = NULL;613}614} else {615uname = NULL;616}617sprops.user_name = (uname != NULL) ? uname : L"unknown";618}619}620621/*622* Home directory623*624* The normal result is that for a given user name XXX:625* On multi-user NT, user.home gets set to c:\winnt\profiles\XXX.626* On multi-user Win95, user.home gets set to c:\windows\profiles\XXX.627* On single-user Win95, user.home gets set to c:\windows.628*/629{630WCHAR *homep = getHomeFromShell32();631if (homep == NULL) {632homep = L"C:\\";633}634sprops.user_home = homep;635}636637/*638* user.language639* user.script, user.country, user.variant (if user's environment specifies them)640* file.encoding641*/642{643/*644* query the system for the current system default locale645* (which is a Windows LCID value),646*/647LCID userDefaultLCID = GetUserDefaultLCID();648LCID systemDefaultLCID = GetSystemDefaultLCID();649LANGID userDefaultUILang = GetUserDefaultUILanguage();650LCID userDefaultUILCID = MAKELCID(userDefaultUILang, SORTIDFROMLCID(userDefaultLCID));651652{653char * display_encoding;654HANDLE hStdOutErr;655656// Windows UI Language selection list only cares "language"657// information of the UI Language. For example, the list658// just lists "English" but it actually means "en_US", and659// the user cannot select "en_GB" (if exists) in the list.660// So, this hack is to use the user LCID region information661// for the UI Language, if the "language" portion of those662// two locales are the same.663if (PRIMARYLANGID(LANGIDFROMLCID(userDefaultLCID)) ==664PRIMARYLANGID(userDefaultUILang)) {665userDefaultUILCID = userDefaultLCID;666}667668SetupI18nProps(userDefaultLCID,669&sprops.format_language,670&sprops.format_script,671&sprops.format_country,672&sprops.format_variant,673&sprops.encoding);674SetupI18nProps(userDefaultUILCID,675&sprops.display_language,676&sprops.display_script,677&sprops.display_country,678&sprops.display_variant,679&display_encoding);680681sprops.sun_jnu_encoding = getEncodingInternal(systemDefaultLCID);682if (LANGIDFROMLCID(userDefaultLCID) == 0x0c04 && majorVersion == 6) {683// MS claims "Vista has built-in support for HKSCS-2004.684// All of the HKSCS-2004 characters have Unicode 4.1.685// PUA code point assignments". But what it really means686// is that the HKSCS-2004 is ONLY supported in Unicode.687// Test indicates the MS950 in its zh_HK locale is a688// "regular" MS950 which does not handle HKSCS-2004 at689// all. Set encoding to MS950_HKSCS.690sprops.encoding = "MS950_HKSCS";691sprops.sun_jnu_encoding = "MS950_HKSCS";692}693694hStdOutErr = GetStdHandle(STD_OUTPUT_HANDLE);695if (hStdOutErr != INVALID_HANDLE_VALUE &&696GetFileType(hStdOutErr) == FILE_TYPE_CHAR) {697sprops.sun_stdout_encoding = getConsoleEncoding();698}699hStdOutErr = GetStdHandle(STD_ERROR_HANDLE);700if (hStdOutErr != INVALID_HANDLE_VALUE &&701GetFileType(hStdOutErr) == FILE_TYPE_CHAR) {702if (sprops.sun_stdout_encoding != NULL)703sprops.sun_stderr_encoding = sprops.sun_stdout_encoding;704else705sprops.sun_stderr_encoding = getConsoleEncoding();706}707}708}709710sprops.unicode_encoding = "UnicodeLittle";711712/* User TIMEZONE713* We defer setting up timezone until it's actually necessary.714* Refer to TimeZone.getDefault(). The system property715* is able to be set by the command line interface -Duser.timezone.716*/717718/* Current directory */719{720WCHAR buf[MAX_PATH];721if (GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR), buf) != 0)722sprops.user_dir = _wcsdup(buf);723}724725sprops.file_separator = "\\";726sprops.path_separator = ";";727sprops.line_separator = "\r\n";728729return &sprops;730}731732jstring733GetStringPlatform(JNIEnv *env, nchar* wcstr)734{735return (*env)->NewString(env, wcstr, (jsize)wcslen(wcstr));736}737738739