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