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 WCHAR* 200 getHomeFromShell32() 201 { 202 HRESULT rc; 203 LPITEMIDLIST item_list = 0; 204 WCHAR *p; 205 WCHAR path[MAX_PATH+1]; 206 int size = MAX_PATH+1; 207 208 rc = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOPDIRECTORY, &item_list); 209 if (!SUCCEEDED(rc)) { 210 // we can't find the shell folder. 211 return NULL; 212 } 213 214 path[0] = 0; 215 SHGetPathFromIDListW(item_list, (LPWSTR)path); 216 217 /* Get the parent of Desktop directory */ 218 p = wcsrchr(path, L'\\'); 219 if (p) { 220 *p = 0; 221 } 222 223 /* 224 * We've been successful. Note that we don't free the memory allocated 225 * by ShGetSpecialFolderLocation. We only ever come through here once, 226 * and only if the registry lookup failed, so it's just not worth it. 227 * 228 * We also don't unload the SHELL32 DLL. We've paid the hit for loading 229 * it and we may need it again later. 230 */ 231 return _wcsdup(path); 232 } 233 234 static boolean 235 haveMMX(void) 236 { 237 return IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE); 238 } 239 240 static const char * 241 cpu_isalist(void) 242 { 243 SYSTEM_INFO info; 244 GetSystemInfo(&info); 245 switch (info.wProcessorArchitecture) { 246 #ifdef PROCESSOR_ARCHITECTURE_IA64 247 case PROCESSOR_ARCHITECTURE_IA64: return "ia64"; 248 #endif 249 #ifdef PROCESSOR_ARCHITECTURE_AMD64 250 case PROCESSOR_ARCHITECTURE_AMD64: return "amd64"; 251 #endif 252 case PROCESSOR_ARCHITECTURE_INTEL: 253 switch (info.wProcessorLevel) { 254 case 6: return haveMMX() 255 ? "pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86" 256 : "pentium_pro pentium i486 i386 i86"; 257 case 5: return haveMMX() 258 ? "pentium+mmx pentium i486 i386 i86" 259 : "pentium i486 i386 i86"; 260 case 4: return "i486 i386 i86"; 261 case 3: return "i386 i86"; 262 } 263 } 264 return NULL; 265 } 266 267 static void 268 SetupI18nProps(LCID lcid, char** language, char** script, char** country, 269 char** variant, char** encoding) { 270 /* script */ 271 char tmp[SNAMESIZE]; 272 *script = malloc(PROPSIZE); 273 if (GetLocaleInfo(lcid, 274 LOCALE_SNAME, tmp, SNAMESIZE) == 0 || 275 sscanf(tmp, "%*[a-z\\-]%1[A-Z]%[a-z]", *script, &((*script)[1])) == 0 || 276 strlen(*script) != 4) { 277 (*script)[0] = '\0'; 278 } 279 280 /* country */ 281 *country = malloc(PROPSIZE); 282 if (GetLocaleInfo(lcid, 283 LOCALE_SISO3166CTRYNAME, *country, PROPSIZE) == 0 && 284 GetLocaleInfo(lcid, 285 LOCALE_SISO3166CTRYNAME2, *country, PROPSIZE) == 0) { 286 (*country)[0] = '\0'; 287 } 288 289 /* language */ 290 *language = malloc(PROPSIZE); 291 if (GetLocaleInfo(lcid, 292 LOCALE_SISO639LANGNAME, *language, PROPSIZE) == 0 && 293 GetLocaleInfo(lcid, 294 LOCALE_SISO639LANGNAME2, *language, PROPSIZE) == 0) { 295 /* defaults to en_US */ 296 strcpy(*language, "en"); 297 strcpy(*country, "US"); 298 } 299 300 /* variant */ 301 *variant = malloc(PROPSIZE); 302 (*variant)[0] = '\0'; 303 304 /* handling for Norwegian */ 305 if (strcmp(*language, "nb") == 0) { 306 strcpy(*language, "no"); 307 strcpy(*country , "NO"); 308 } else if (strcmp(*language, "nn") == 0) { 309 strcpy(*language, "no"); 310 strcpy(*country , "NO"); 311 strcpy(*variant, "NY"); 312 } 313 314 /* encoding */ 315 *encoding = getEncodingInternal(lcid); 316 } 317 318 java_props_t * 319 GetJavaProperties(JNIEnv* env) 320 { 321 static java_props_t sprops = {0}; 322 323 OSVERSIONINFOEX ver; 324 325 if (sprops.user_dir) { 326 return &sprops; 327 } 328 329 /* AWT properties */ 330 sprops.awt_toolkit = "sun.awt.windows.WToolkit"; 331 332 /* tmp dir */ 333 { 334 WCHAR tmpdir[MAX_PATH + 1]; 335 /* we might want to check that this succeed */ 336 GetTempPathW(MAX_PATH + 1, tmpdir); 337 sprops.tmp_dir = _wcsdup(tmpdir); 338 } 339 340 /* Printing properties */ 341 sprops.printerJob = "sun.awt.windows.WPrinterJob"; 342 343 /* Java2D properties */ 344 sprops.graphics_env = "sun.awt.Win32GraphicsEnvironment"; 345 346 { /* This is used only for debugging of font problems. */ 347 WCHAR *path = _wgetenv(L"JAVA2D_FONTPATH"); 348 sprops.font_dir = (path != NULL) ? _wcsdup(path) : NULL; 349 } 350 351 /* OS properties */ 352 { 353 char buf[100]; 354 SYSTEM_INFO si; 355 PGNSI pGNSI; 356 357 ver.dwOSVersionInfoSize = sizeof(ver); 358 GetVersionEx((OSVERSIONINFO *) &ver); 359 360 ZeroMemory(&si, sizeof(SYSTEM_INFO)); 361 // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. 362 pGNSI = (PGNSI) GetProcAddress( 363 GetModuleHandle(TEXT("kernel32.dll")), 364 "GetNativeSystemInfo"); 365 if(NULL != pGNSI) 366 pGNSI(&si); 367 else 368 GetSystemInfo(&si); 369 370 /* 371 * From msdn page on OSVERSIONINFOEX, current as of this 372 * writing, decoding of dwMajorVersion and dwMinorVersion. 373 * 374 * Operating system dwMajorVersion dwMinorVersion 375 * ================== ============== ============== 376 * 377 * Windows 95 4 0 378 * Windows 98 4 10 379 * Windows ME 4 90 380 * Windows 3.51 3 51 381 * Windows NT 4.0 4 0 382 * Windows 2000 5 0 383 * Windows XP 32 bit 5 1 384 * Windows Server 2003 family 5 2 385 * Windows XP 64 bit 5 2 386 * where ((&ver.wServicePackMinor) + 2) = 1 387 * and si.wProcessorArchitecture = 9 388 * Windows Vista family 6 0 (VER_NT_WORKSTATION) 389 * Windows Server 2008 6 0 (!VER_NT_WORKSTATION) 390 * Windows 7 6 1 (VER_NT_WORKSTATION) 391 * Windows Server 2008 R2 6 1 (!VER_NT_WORKSTATION) 392 * Windows 8 6 2 (VER_NT_WORKSTATION) 393 * 394 * This mapping will presumably be augmented as new Windows 395 * versions are released. 396 */ 397 switch (ver.dwPlatformId) { 398 case VER_PLATFORM_WIN32s: 399 sprops.os_name = "Windows 3.1"; 400 break; 401 case VER_PLATFORM_WIN32_WINDOWS: 402 if (ver.dwMajorVersion == 4) { 403 switch (ver.dwMinorVersion) { 404 case 0: sprops.os_name = "Windows 95"; break; 405 case 10: sprops.os_name = "Windows 98"; break; 406 case 90: sprops.os_name = "Windows Me"; break; 407 default: sprops.os_name = "Windows 9X (unknown)"; break; 408 } 409 } else { 410 sprops.os_name = "Windows 9X (unknown)"; 411 } 412 break; 413 case VER_PLATFORM_WIN32_NT: 414 if (ver.dwMajorVersion <= 4) { 415 sprops.os_name = "Windows NT"; 416 } else if (ver.dwMajorVersion == 5) { 417 switch (ver.dwMinorVersion) { 418 case 0: sprops.os_name = "Windows 2000"; break; 419 case 1: sprops.os_name = "Windows XP"; break; 420 case 2: 421 /* 422 * From MSDN OSVERSIONINFOEX and SYSTEM_INFO documentation: 423 * 424 * "Because the version numbers for Windows Server 2003 425 * and Windows XP 6u4 bit are identical, you must also test 426 * whether the wProductType member is VER_NT_WORKSTATION. 427 * and si.wProcessorArchitecture is 428 * PROCESSOR_ARCHITECTURE_AMD64 (which is 9) 429 * If it is, the operating system is Windows XP 64 bit; 430 * otherwise, it is Windows Server 2003." 431 */ 432 if(ver.wProductType == VER_NT_WORKSTATION && 433 si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { 434 sprops.os_name = "Windows XP"; /* 64 bit */ 435 } else { 436 sprops.os_name = "Windows 2003"; 437 } 438 break; 439 default: sprops.os_name = "Windows NT (unknown)"; break; 440 } 441 } else if (ver.dwMajorVersion == 6) { 442 /* 443 * See table in MSDN OSVERSIONINFOEX documentation. 444 */ 445 if (ver.wProductType == VER_NT_WORKSTATION) { 446 switch (ver.dwMinorVersion) { 447 case 0: sprops.os_name = "Windows Vista"; break; 448 case 1: sprops.os_name = "Windows 7"; break; 449 case 2: sprops.os_name = "Windows 8"; break; 450 default: sprops.os_name = "Windows NT (unknown)"; 451 } 452 } else { 453 switch (ver.dwMinorVersion) { 454 case 0: sprops.os_name = "Windows Server 2008"; break; 455 case 1: sprops.os_name = "Windows Server 2008 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 * We first look under a standard registry key. If that fails we 528 * fall back on using a SHELL32.DLL API. If that fails we use a 529 * default value. 530 * 531 * Note: To save space we want to avoid loading SHELL32.DLL 532 * unless really necessary. However if we do load it, we leave it 533 * in memory, as it may be needed again later. 534 * 535 * The normal result is that for a given user name XXX: 536 * On multi-user NT, user.home gets set to c:\winnt\profiles\XXX. 537 * On multi-user Win95, user.home gets set to c:\windows\profiles\XXX. 538 * On single-user Win95, user.home gets set to c:\windows. 539 */ 540 { 541 WCHAR *homep = getHomeFromRegistry(); 542 if (homep == NULL) { 543 homep = getHomeFromShell32(); 544 if (homep == NULL) 545 homep = L"C:\\"; 546 } 547 sprops.user_home = _wcsdup(homep); 548 } 549 550 /* 551 * user.language 552 * user.script, user.country, user.variant (if user's environment specifies them) 553 * file.encoding 554 * file.encoding.pkg 555 */ 556 { 557 /* 558 * query the system for the current system default locale 559 * (which is a Windows LCID value), 560 */ 561 LCID userDefaultLCID = GetUserDefaultLCID(); 562 LCID systemDefaultLCID = GetSystemDefaultLCID(); 563 LCID userDefaultUILang = GetUserDefaultUILanguage(); 564 565 { 566 char * display_encoding; 567 568 // Windows UI Language selection list only cares "language" 569 // information of the UI Language. For example, the list 570 // just lists "English" but it actually means "en_US", and 571 // the user cannot select "en_GB" (if exists) in the list. 572 // So, this hack is to use the user LCID region information 573 // for the UI Language, if the "language" portion of those 574 // two locales are the same. 575 if (PRIMARYLANGID(LANGIDFROMLCID(userDefaultLCID)) == 576 PRIMARYLANGID(LANGIDFROMLCID(userDefaultUILang))) { 577 userDefaultUILang = userDefaultLCID; 578 } 579 580 SetupI18nProps(userDefaultUILang, 581 &sprops.language, 582 &sprops.script, 583 &sprops.country, 584 &sprops.variant, 585 &display_encoding); 586 SetupI18nProps(userDefaultLCID, 587 &sprops.format_language, 588 &sprops.format_script, 589 &sprops.format_country, 590 &sprops.format_variant, 591 &sprops.encoding); 592 SetupI18nProps(userDefaultUILang, 593 &sprops.display_language, 594 &sprops.display_script, 595 &sprops.display_country, 596 &sprops.display_variant, 597 &display_encoding); 598 599 sprops.sun_jnu_encoding = getEncodingInternal(systemDefaultLCID); 600 if (LANGIDFROMLCID(userDefaultLCID) == 0x0c04 && ver.dwMajorVersion == 6) { 601 // MS claims "Vista has built-in support for HKSCS-2004. 602 // All of the HKSCS-2004 characters have Unicode 4.1. 603 // PUA code point assignments". But what it really means 604 // is that the HKSCS-2004 is ONLY supported in Unicode. 605 // Test indicates the MS950 in its zh_HK locale is a 606 // "regular" MS950 which does not handle HKSCS-2004 at 607 // all. Set encoding to MS950_HKSCS. 608 sprops.encoding = "MS950_HKSCS"; 609 sprops.sun_jnu_encoding = "MS950_HKSCS"; 610 } 611 } 612 } 613 614 sprops.unicode_encoding = "UnicodeLittle"; 615 /* User TIMEZONE */ 616 { 617 /* 618 * We defer setting up timezone until it's actually necessary. 619 * Refer to TimeZone.getDefault(). However, the system 620 * property is necessary to be able to be set by the command 621 * line interface -D. Here temporarily set a null string to 622 * timezone. 623 */ 624 sprops.timezone = ""; 625 } 626 627 /* Current directory */ 628 { 629 WCHAR buf[MAX_PATH]; 630 if (GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR), buf) != 0) 631 sprops.user_dir = _wcsdup(buf); 632 } 633 634 sprops.file_separator = "\\"; 635 sprops.path_separator = ";"; 636 sprops.line_separator = "\r\n"; 637 638 return &sprops; 639 } 640 641 jstring 642 GetStringPlatform(JNIEnv *env, nchar* wcstr) 643 { 644 return (*env)->NewString(env, wcstr, wcslen(wcstr)); 645 }