Path: blob/master/src/java.base/windows/native/libjava/java_props_md.c
67769 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 = 0;66char * ret = malloc(16);67if (ret == NULL) {68return NULL;69}7071if (lcid == 0) { // for sun.jnu.encoding72codepage = GetACP();73_itoa_s(codepage, ret + 2, 14, 10);74} else if (GetLocaleInfo(lcid,75LOCALE_IDEFAULTANSICODEPAGE,76ret + 2, 14) != 0) {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+ 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 11 10 0 (VER_NT_WORKSTATION)474* where (buildNumber >= 22000)475* Windows Server 2016 10 0 (!VER_NT_WORKSTATION)476* Windows Server 2019 10 0 (!VER_NT_WORKSTATION)477* where (buildNumber > 17762)478* Windows Server 2022 10 0 (!VER_NT_WORKSTATION)479* where (buildNumber > 20347)480*481* This mapping will presumably be augmented as new Windows482* versions are released.483*/484switch (platformId) {485case VER_PLATFORM_WIN32_WINDOWS:486if (majorVersion == 4) {487switch (minorVersion) {488case 0: sprops.os_name = "Windows 95"; break;489case 10: sprops.os_name = "Windows 98"; break;490case 90: sprops.os_name = "Windows Me"; break;491default: sprops.os_name = "Windows 9X (unknown)"; break;492}493} else {494sprops.os_name = "Windows 9X (unknown)";495}496break;497case VER_PLATFORM_WIN32_NT:498if (majorVersion <= 4) {499sprops.os_name = "Windows NT";500} else if (majorVersion == 5) {501switch (minorVersion) {502case 0: sprops.os_name = "Windows 2000"; break;503case 1: sprops.os_name = "Windows XP"; break;504case 2:505/*506* From MSDN OSVERSIONINFOEX and SYSTEM_INFO documentation:507*508* "Because the version numbers for Windows Server 2003509* and Windows XP 6u4 bit are identical, you must also test510* whether the wProductType member is VER_NT_WORKSTATION.511* and si.wProcessorArchitecture is512* PROCESSOR_ARCHITECTURE_AMD64 (which is 9)513* If it is, the operating system is Windows XP 64 bit;514* otherwise, it is Windows Server 2003."515*/516if (is_workstation && is_64bit) {517sprops.os_name = "Windows XP"; /* 64 bit */518} else {519sprops.os_name = "Windows 2003";520}521break;522default: sprops.os_name = "Windows NT (unknown)"; break;523}524} else if (majorVersion == 6) {525/*526* See table in MSDN OSVERSIONINFOEX documentation.527*/528if (is_workstation) {529switch (minorVersion) {530case 0: sprops.os_name = "Windows Vista"; break;531case 1: sprops.os_name = "Windows 7"; break;532case 2: sprops.os_name = "Windows 8"; break;533case 3: sprops.os_name = "Windows 8.1"; break;534default: sprops.os_name = "Windows NT (unknown)";535}536} else {537switch (minorVersion) {538case 0: sprops.os_name = "Windows Server 2008"; break;539case 1: sprops.os_name = "Windows Server 2008 R2"; break;540case 2: sprops.os_name = "Windows Server 2012"; break;541case 3: sprops.os_name = "Windows Server 2012 R2"; break;542default: sprops.os_name = "Windows NT (unknown)";543}544}545} else if (majorVersion == 10) {546if (is_workstation) {547switch (minorVersion) {548case 0:549/* Windows 11 21H2 (original release) build number is 22000 */550if (buildNumber >= 22000) {551sprops.os_name = "Windows 11";552} else {553sprops.os_name = "Windows 10";554}555break;556default: sprops.os_name = "Windows NT (unknown)";557}558} else {559switch (minorVersion) {560case 0:561/* Windows server 2019 GA 10/2018 build number is 17763 */562/* Windows server 2022 build number is 20348 */563if (buildNumber > 20347) {564sprops.os_name = "Windows Server 2022";565} else if (buildNumber > 17676) {566sprops.os_name = "Windows Server 2019";567} else {568sprops.os_name = "Windows Server 2016";569}570break;571default: sprops.os_name = "Windows NT (unknown)";572}573}574} else {575sprops.os_name = "Windows NT (unknown)";576}577break;578default:579sprops.os_name = "Windows (unknown)";580break;581}582sprintf(buf, "%d.%d", majorVersion, minorVersion);583sprops.os_version = _strdup(buf);584#if defined(_M_AMD64)585sprops.os_arch = "amd64";586#elif defined(_X86_)587sprops.os_arch = "x86";588#elif defined(_M_ARM64)589sprops.os_arch = "aarch64";590#else591sprops.os_arch = "unknown";592#endif593}594595/* Endianness of platform */596{597unsigned int endianTest = 0xff000000;598if (((char*)(&endianTest))[0] != 0) {599sprops.cpu_endian = "big";600} else {601sprops.cpu_endian = "little";602}603}604605/* CPU ISA list */606sprops.cpu_isalist = cpu_isalist();607608/*609* User name610* We try to avoid calling GetUserName as it turns out to611* be surprisingly expensive on NT. It pulls in an extra612* 100 K of footprint.613*/614{615WCHAR *uname = _wgetenv(L"USERNAME");616if (uname != NULL && wcslen(uname) > 0) {617sprops.user_name = _wcsdup(uname);618} else {619DWORD buflen = 0;620if (GetUserNameW(NULL, &buflen) == 0 &&621GetLastError() == ERROR_INSUFFICIENT_BUFFER)622{623uname = (WCHAR*)malloc(buflen * sizeof(WCHAR));624if (uname != NULL && GetUserNameW(uname, &buflen) == 0) {625free(uname);626uname = NULL;627}628} else {629uname = NULL;630}631sprops.user_name = (uname != NULL) ? uname : L"unknown";632}633}634635/*636* Home directory637*638* The normal result is that for a given user name XXX:639* On multi-user NT, user.home gets set to c:\winnt\profiles\XXX.640* On multi-user Win95, user.home gets set to c:\windows\profiles\XXX.641* On single-user Win95, user.home gets set to c:\windows.642*/643{644WCHAR *homep = getHomeFromShell32();645if (homep == NULL) {646homep = L"C:\\";647}648sprops.user_home = homep;649}650651/*652* user.language653* user.script, user.country, user.variant (if user's environment specifies them)654* file.encoding655*/656{657/*658* query the system for the current system default locale659* (which is a Windows LCID value),660*/661LCID userDefaultLCID = GetUserDefaultLCID();662LANGID userDefaultUILang = GetUserDefaultUILanguage();663LCID userDefaultUILCID = MAKELCID(userDefaultUILang, SORTIDFROMLCID(userDefaultLCID));664665{666char * display_encoding;667HANDLE hStdOutErr;668669// Windows UI Language selection list only cares "language"670// information of the UI Language. For example, the list671// just lists "English" but it actually means "en_US", and672// the user cannot select "en_GB" (if exists) in the list.673// So, this hack is to use the user LCID region information674// for the UI Language, if the "language" portion of those675// two locales are the same.676if (PRIMARYLANGID(LANGIDFROMLCID(userDefaultLCID)) ==677PRIMARYLANGID(userDefaultUILang)) {678userDefaultUILCID = userDefaultLCID;679}680681SetupI18nProps(userDefaultLCID,682&sprops.format_language,683&sprops.format_script,684&sprops.format_country,685&sprops.format_variant,686&sprops.encoding);687SetupI18nProps(userDefaultUILCID,688&sprops.display_language,689&sprops.display_script,690&sprops.display_country,691&sprops.display_variant,692&display_encoding);693694sprops.sun_jnu_encoding = getEncodingInternal(0);695if (sprops.sun_jnu_encoding == NULL) {696sprops.sun_jnu_encoding = "UTF-8";697}698if (LANGIDFROMLCID(userDefaultLCID) == 0x0c04 && majorVersion == 6) {699// MS claims "Vista has built-in support for HKSCS-2004.700// All of the HKSCS-2004 characters have Unicode 4.1.701// PUA code point assignments". But what it really means702// is that the HKSCS-2004 is ONLY supported in Unicode.703// Test indicates the MS950 in its zh_HK locale is a704// "regular" MS950 which does not handle HKSCS-2004 at705// all. Set encoding to MS950_HKSCS.706sprops.encoding = "MS950_HKSCS";707sprops.sun_jnu_encoding = "MS950_HKSCS";708}709710hStdOutErr = GetStdHandle(STD_OUTPUT_HANDLE);711if (hStdOutErr != INVALID_HANDLE_VALUE &&712GetFileType(hStdOutErr) == FILE_TYPE_CHAR) {713sprops.sun_stdout_encoding = getConsoleEncoding();714}715hStdOutErr = GetStdHandle(STD_ERROR_HANDLE);716if (hStdOutErr != INVALID_HANDLE_VALUE &&717GetFileType(hStdOutErr) == FILE_TYPE_CHAR) {718if (sprops.sun_stdout_encoding != NULL)719sprops.sun_stderr_encoding = sprops.sun_stdout_encoding;720else721sprops.sun_stderr_encoding = getConsoleEncoding();722}723}724}725726sprops.unicode_encoding = "UnicodeLittle";727728/* User TIMEZONE729* We defer setting up timezone until it's actually necessary.730* Refer to TimeZone.getDefault(). The system property731* is able to be set by the command line interface -Duser.timezone.732*/733734/* Current directory */735{736WCHAR buf[MAX_PATH];737if (GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR), buf) != 0)738sprops.user_dir = _wcsdup(buf);739}740741sprops.file_separator = "\\";742sprops.path_separator = ";";743sprops.line_separator = "\r\n";744745return &sprops;746}747748jstring749GetStringPlatform(JNIEnv *env, nchar* wcstr)750{751return (*env)->NewString(env, wcstr, (jsize)wcslen(wcstr));752}753754755