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