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