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 #if defined(__linux__) || defined(_ALLBSD_SOURCE) 27 #include <stdio.h> 28 #include <ctype.h> 29 #endif 30 #include <pwd.h> 31 #include <locale.h> 32 #ifndef ARCHPROPNAME 33 #error "The macro ARCHPROPNAME has not been defined" 34 #endif 35 #include <sys/utsname.h> /* For os_name and os_version */ 36 #include <langinfo.h> /* For nl_langinfo */ 37 #include <stdlib.h> 38 #include <string.h> 39 #include <sys/types.h> 40 #include <unistd.h> 41 #include <sys/param.h> 42 #include <time.h> 43 #include <errno.h> 44 45 #ifdef MACOSX 46 #include "java_props_macosx.h" 47 #endif 48 49 #if defined(_ALLBSD_SOURCE) 50 #if !defined(P_tmpdir) 51 #include <paths.h> 52 #define P_tmpdir _PATH_VARTMP 53 #endif 54 #endif 55 56 #include "locale_str.h" 57 #include "java_props.h" 58 59 #if !defined(_ALLBSD_SOURCE) 60 #ifdef __linux__ 61 #ifndef CODESET 62 #define CODESET _NL_CTYPE_CODESET_NAME 63 #endif 64 #else 65 #ifdef ALT_CODESET_KEY 66 #define CODESET ALT_CODESET_KEY 67 #endif 68 #endif 69 #endif /* !_ALLBSD_SOURCE */ 70 71 #ifdef JAVASE_EMBEDDED 72 #include <dlfcn.h> 73 #include <sys/stat.h> 74 #endif 75 76 /* Take an array of string pairs (map of key->value) and a string (key). 77 * Examine each pair in the map to see if the first string (key) matches the 78 * string. If so, store the second string of the pair (value) in the value and 79 * return 1. Otherwise do nothing and return 0. The end of the map is 80 * indicated by an empty string at the start of a pair (key of ""). 81 */ 82 static int 83 mapLookup(char* map[], const char* key, char** value) { 84 int i; 85 for (i = 0; strcmp(map[i], ""); i += 2){ 86 if (!strcmp(key, map[i])){ 87 *value = map[i + 1]; 88 return 1; 89 } 90 } 91 return 0; 92 } 93 94 /* This function sets an environment variable using envstring. 95 * The format of envstring is "name=value". 96 * If the name has already existed, it will append value to the name. 97 */ 98 static void 99 setPathEnvironment(char *envstring) 100 { 101 char name[20], *value, *current; 102 103 value = strchr(envstring, '='); /* locate name and value separator */ 104 105 if (! value) 106 return; /* not a valid environment setting */ 107 108 /* copy first part as environment name */ 109 strncpy(name, envstring, value - envstring); 110 name[value-envstring] = '\0'; 111 112 value++; /* set value point to value of the envstring */ 113 114 current = getenv(name); 115 if (current) { 116 if (! strstr(current, value)) { 117 /* value is not found in current environment, append it */ 118 char *temp = malloc(strlen(envstring) + strlen(current) + 2); 119 strcpy(temp, name); 120 strcat(temp, "="); 121 strcat(temp, current); 122 strcat(temp, ":"); 123 strcat(temp, value); 124 putenv(temp); 125 } 126 /* else the value has already been set, do nothing */ 127 } 128 else { 129 /* environment variable is not found */ 130 putenv(envstring); 131 } 132 } 133 134 #ifndef P_tmpdir 135 #define P_tmpdir "/var/tmp" 136 #endif 137 138 static int ParseLocale(JNIEnv* env, int cat, char ** std_language, char ** std_script, 139 char ** std_country, char ** std_variant, char ** std_encoding) { 140 char *temp = NULL; 141 char *language = NULL, *country = NULL, *variant = NULL, 142 *encoding = NULL; 143 char *p, *encoding_variant, *old_temp, *old_ev; 144 char *lc; 145 146 /* Query the locale set for the category */ 147 148 #ifdef MACOSX 149 lc = setupMacOSXLocale(cat); // malloc'd memory, need to free 150 #else 151 lc = setlocale(cat, NULL); 152 #endif 153 154 #ifndef __linux__ 155 if (lc == NULL) { 156 return 0; 157 } 158 159 temp = malloc(strlen(lc) + 1); 160 if (temp == NULL) { 161 JNU_ThrowOutOfMemoryError(env, NULL); 162 return 0; 163 } 164 165 if (cat == LC_CTYPE) { 166 /* 167 * Workaround for Solaris bug 4201684: Xlib doesn't like @euro 168 * locales. Since we don't depend on the libc @euro behavior, 169 * we just remove the qualifier. 170 * On Linux, the bug doesn't occur; on the other hand, @euro 171 * is needed there because it's a shortcut that also determines 172 * the encoding - without it, we wouldn't get ISO-8859-15. 173 * Therefore, this code section is Solaris-specific. 174 */ 175 lc = strdup(lc); /* keep a copy, setlocale trashes original. */ 176 strcpy(temp, lc); 177 p = strstr(temp, "@euro"); 178 if (p != NULL) { 179 *p = '\0'; 180 setlocale(LC_ALL, temp); 181 } 182 } 183 #else 184 if (lc == NULL || !strcmp(lc, "C") || !strcmp(lc, "POSIX")) { 185 lc = "en_US"; 186 } 187 188 temp = malloc(strlen(lc) + 1); 189 if (temp == NULL) { 190 JNU_ThrowOutOfMemoryError(env, NULL); 191 return 0; 192 } 193 194 #endif 195 196 /* 197 * locale string format in Solaris is 198 * <language name>_<country name>.<encoding name>@<variant name> 199 * <country name>, <encoding name>, and <variant name> are optional. 200 */ 201 202 strcpy(temp, lc); 203 #ifdef MACOSX 204 free(lc); // malloced memory 205 #endif 206 /* Parse the language, country, encoding, and variant from the 207 * locale. Any of the elements may be missing, but they must occur 208 * in the order language_country.encoding@variant, and must be 209 * preceded by their delimiter (except for language). 210 * 211 * If the locale name (without .encoding@variant, if any) matches 212 * any of the names in the locale_aliases list, map it to the 213 * corresponding full locale name. Most of the entries in the 214 * locale_aliases list are locales that include a language name but 215 * no country name, and this facility is used to map each language 216 * to a default country if that's possible. It's also used to map 217 * the Solaris locale aliases to their proper Java locale IDs. 218 */ 219 220 encoding_variant = malloc(strlen(temp)+1); 221 if (encoding_variant == NULL) { 222 free(temp); 223 JNU_ThrowOutOfMemoryError(env, NULL); 224 return 0; 225 } 226 227 if ((p = strchr(temp, '.')) != NULL) { 228 strcpy(encoding_variant, p); /* Copy the leading '.' */ 229 *p = '\0'; 230 } else if ((p = strchr(temp, '@')) != NULL) { 231 strcpy(encoding_variant, p); /* Copy the leading '@' */ 232 *p = '\0'; 233 } else { 234 *encoding_variant = '\0'; 235 } 236 237 if (mapLookup(locale_aliases, temp, &p)) { 238 old_temp = temp; 239 temp = realloc(temp, strlen(p)+1); 240 if (temp == NULL) { 241 free(old_temp); 242 free(encoding_variant); 243 JNU_ThrowOutOfMemoryError(env, NULL); 244 return 0; 245 } 246 strcpy(temp, p); 247 old_ev = encoding_variant; 248 encoding_variant = realloc(encoding_variant, strlen(temp)+1); 249 if (encoding_variant == NULL) { 250 free(old_ev); 251 free(temp); 252 JNU_ThrowOutOfMemoryError(env, NULL); 253 return 0; 254 } 255 // check the "encoding_variant" again, if any. 256 if ((p = strchr(temp, '.')) != NULL) { 257 strcpy(encoding_variant, p); /* Copy the leading '.' */ 258 *p = '\0'; 259 } else if ((p = strchr(temp, '@')) != NULL) { 260 strcpy(encoding_variant, p); /* Copy the leading '@' */ 261 *p = '\0'; 262 } 263 } 264 265 language = temp; 266 if ((country = strchr(temp, '_')) != NULL) { 267 *country++ = '\0'; 268 } 269 270 p = encoding_variant; 271 if ((encoding = strchr(p, '.')) != NULL) { 272 p[encoding++ - p] = '\0'; 273 p = encoding; 274 } 275 if ((variant = strchr(p, '@')) != NULL) { 276 p[variant++ - p] = '\0'; 277 } 278 279 /* Normalize the language name */ 280 if (std_language != NULL) { 281 *std_language = "en"; 282 if (language != NULL && mapLookup(language_names, language, std_language) == 0) { 283 *std_language = malloc(strlen(language)+1); 284 strcpy(*std_language, language); 285 } 286 } 287 288 /* Normalize the country name */ 289 if (std_country != NULL && country != NULL) { 290 if (mapLookup(country_names, country, std_country) == 0) { 291 *std_country = malloc(strlen(country)+1); 292 strcpy(*std_country, country); 293 } 294 } 295 296 /* Normalize the script and variant name. Note that we only use 297 * variants listed in the mapping array; others are ignored. 298 */ 299 if (variant != NULL) { 300 if (std_script != NULL) { 301 mapLookup(script_names, variant, std_script); 302 } 303 304 if (std_variant != NULL) { 305 mapLookup(variant_names, variant, std_variant); 306 } 307 } 308 309 /* Normalize the encoding name. Note that we IGNORE the string 310 * 'encoding' extracted from the locale name above. Instead, we use the 311 * more reliable method of calling nl_langinfo(CODESET). This function 312 * returns an empty string if no encoding is set for the given locale 313 * (e.g., the C or POSIX locales); we use the default ISO 8859-1 314 * converter for such locales. 315 */ 316 if (std_encoding != NULL) { 317 /* OK, not so reliable - nl_langinfo() gives wrong answers on 318 * Euro locales, in particular. */ 319 if (strcmp(p, "ISO8859-15") == 0) 320 p = "ISO8859-15"; 321 else 322 p = nl_langinfo(CODESET); 323 324 /* Convert the bare "646" used on Solaris to a proper IANA name */ 325 if (strcmp(p, "646") == 0) 326 p = "ISO646-US"; 327 328 /* return same result nl_langinfo would return for en_UK, 329 * in order to use optimizations. */ 330 *std_encoding = (*p != '\0') ? p : "ISO8859-1"; 331 332 #ifdef __linux__ 333 /* 334 * Remap the encoding string to a different value for japanese 335 * locales on linux so that customized converters are used instead 336 * of the default converter for "EUC-JP". The customized converters 337 * omit support for the JIS0212 encoding which is not supported by 338 * the variant of "EUC-JP" encoding used on linux 339 */ 340 if (strcmp(p, "EUC-JP") == 0) { 341 *std_encoding = "EUC-JP-LINUX"; 342 } 343 #else 344 if (strcmp(p,"eucJP") == 0) { 345 /* For Solaris use customized vendor defined character 346 * customized EUC-JP converter 347 */ 348 *std_encoding = "eucJP-open"; 349 } else if (strcmp(p, "Big5") == 0 || strcmp(p, "BIG5") == 0) { 350 /* 351 * Remap the encoding string to Big5_Solaris which augments 352 * the default converter for Solaris Big5 locales to include 353 * seven additional ideographic characters beyond those included 354 * in the Java "Big5" converter. 355 */ 356 *std_encoding = "Big5_Solaris"; 357 } else if (strcmp(p, "Big5-HKSCS") == 0) { 358 /* 359 * Solaris uses HKSCS2001 360 */ 361 *std_encoding = "Big5-HKSCS-2001"; 362 } 363 #endif 364 #ifdef MACOSX 365 /* 366 * For the case on MacOS X where encoding is set to US-ASCII, but we 367 * don't have any encoding hints from LANG/LC_ALL/LC_CTYPE, use UTF-8 368 * instead. 369 * 370 * The contents of ASCII files will still be read and displayed 371 * correctly, but so will files containing UTF-8 characters beyond the 372 * standard ASCII range. 373 * 374 * Specifically, this allows apps launched by double-clicking a .jar 375 * file to correctly read UTF-8 files using the default encoding (see 376 * 8011194). 377 */ 378 if (strcmp(p,"US-ASCII") == 0 && getenv("LANG") == NULL && 379 getenv("LC_ALL") == NULL && getenv("LC_CTYPE") == NULL) { 380 *std_encoding = "UTF-8"; 381 } 382 #endif 383 } 384 385 free(temp); 386 free(encoding_variant); 387 388 return 1; 389 } 390 391 #ifdef JAVASE_EMBEDDED 392 /* Determine the default embedded toolkit based on whether libawt_xawt 393 * exists in the JRE. This can still be overridden by -Dawt.toolkit=XXX 394 */ 395 static char* getEmbeddedToolkit() { 396 Dl_info dlinfo; 397 char buf[MAXPATHLEN]; 398 int32_t len; 399 char *p; 400 struct stat statbuf; 401 402 /* Get address of this library and the directory containing it. */ 403 dladdr((void *)getEmbeddedToolkit, &dlinfo); 404 realpath((char *)dlinfo.dli_fname, buf); 405 len = strlen(buf); 406 p = strrchr(buf, '/'); 407 /* Default AWT Toolkit on Linux and Solaris is XAWT (libawt_xawt.so). */ 408 strncpy(p, "/libawt_xawt.so", MAXPATHLEN-len-1); 409 /* Check if it exists */ 410 if (stat(buf, &statbuf) == -1 && errno == ENOENT) { 411 /* No - this is a reduced-headless-jre so use special HToolkit */ 412 return "sun.awt.HToolkit"; 413 } 414 else { 415 /* Yes - this is a headful JRE so fallback to SE defaults */ 416 return NULL; 417 } 418 } 419 #endif 420 421 /* This function gets called very early, before VM_CALLS are setup. 422 * Do not use any of the VM_CALLS entries!!! 423 */ 424 java_props_t * 425 GetJavaProperties(JNIEnv *env) 426 { 427 static java_props_t sprops; 428 char *v; /* tmp var */ 429 430 if (sprops.user_dir) { 431 return &sprops; 432 } 433 434 /* tmp dir */ 435 sprops.tmp_dir = P_tmpdir; 436 #ifdef MACOSX 437 /* darwin has a per-user temp dir */ 438 static char tmp_path[PATH_MAX]; 439 int pathSize = confstr(_CS_DARWIN_USER_TEMP_DIR, tmp_path, PATH_MAX); 440 if (pathSize > 0 && pathSize <= PATH_MAX) { 441 sprops.tmp_dir = tmp_path; 442 } 443 #endif /* MACOSX */ 444 445 /* Printing properties */ 446 #ifdef MACOSX 447 sprops.printerJob = "sun.lwawt.macosx.CPrinterJob"; 448 #else 449 sprops.printerJob = "sun.print.PSPrinterJob"; 450 #endif 451 452 /* patches/service packs installed */ 453 sprops.patch_level = "unknown"; 454 455 /* Java 2D properties */ 456 #ifdef MACOSX 457 PreferredToolkit prefToolkit = getPreferredToolkit(); 458 switch (prefToolkit) { 459 case CToolkit: 460 case HToolkit: 461 sprops.graphics_env = "sun.awt.CGraphicsEnvironment"; 462 break; 463 case XToolkit: 464 #endif 465 sprops.graphics_env = "sun.awt.X11GraphicsEnvironment"; 466 #ifdef MACOSX 467 break; 468 } 469 #endif 470 /* AWT properties */ 471 #ifdef JAVASE_EMBEDDED 472 sprops.awt_toolkit = getEmbeddedToolkit(); 473 if (sprops.awt_toolkit == NULL) // default as below 474 #endif 475 #ifdef MACOSX 476 switch (prefToolkit) { 477 case CToolkit: 478 sprops.awt_toolkit = "sun.lwawt.macosx.LWCToolkit"; 479 break; 480 case XToolkit: 481 #endif 482 sprops.awt_toolkit = "sun.awt.X11.XToolkit"; 483 #ifdef MACOSX 484 break; 485 default: 486 sprops.awt_toolkit = "sun.awt.HToolkit"; 487 break; 488 } 489 #endif 490 491 /* This is used only for debugging of font problems. */ 492 v = getenv("JAVA2D_FONTPATH"); 493 sprops.font_dir = v ? v : NULL; 494 495 #ifdef SI_ISALIST 496 /* supported instruction sets */ 497 { 498 char list[258]; 499 sysinfo(SI_ISALIST, list, sizeof(list)); 500 sprops.cpu_isalist = strdup(list); 501 } 502 #else 503 sprops.cpu_isalist = NULL; 504 #endif 505 506 /* endianness of platform */ 507 { 508 unsigned int endianTest = 0xff000000; 509 if (((char*)(&endianTest))[0] != 0) 510 sprops.cpu_endian = "big"; 511 else 512 sprops.cpu_endian = "little"; 513 } 514 515 /* os properties */ 516 { 517 #ifdef MACOSX 518 setOSNameAndVersion(&sprops); 519 #else 520 struct utsname name; 521 uname(&name); 522 sprops.os_name = strdup(name.sysname); 523 sprops.os_version = strdup(name.release); 524 #endif 525 526 sprops.os_arch = ARCHPROPNAME; 527 528 if (getenv("GNOME_DESKTOP_SESSION_ID") != NULL) { 529 sprops.desktop = "gnome"; 530 } 531 else { 532 sprops.desktop = NULL; 533 } 534 } 535 536 /* ABI property (optional) */ 537 #ifdef JDK_ARCH_ABI_PROP_NAME 538 sprops.sun_arch_abi = JDK_ARCH_ABI_PROP_NAME; 539 #endif 540 541 /* Determine the language, country, variant, and encoding from the host, 542 * and store these in the user.language, user.country, user.variant and 543 * file.encoding system properties. */ 544 setlocale(LC_ALL, ""); 545 if (ParseLocale(env, LC_CTYPE, 546 &(sprops.format_language), 547 &(sprops.format_script), 548 &(sprops.format_country), 549 &(sprops.format_variant), 550 &(sprops.encoding))) { 551 ParseLocale(env, LC_MESSAGES, 552 &(sprops.language), 553 &(sprops.script), 554 &(sprops.country), 555 &(sprops.variant), 556 NULL); 557 } else { 558 sprops.language = "en"; 559 sprops.encoding = "ISO8859-1"; 560 } 561 sprops.display_language = sprops.language; 562 sprops.display_script = sprops.script; 563 sprops.display_country = sprops.country; 564 sprops.display_variant = sprops.variant; 565 566 #ifdef MACOSX 567 sprops.sun_jnu_encoding = "UTF-8"; 568 #else 569 sprops.sun_jnu_encoding = sprops.encoding; 570 #endif 571 572 #ifdef _ALLBSD_SOURCE 573 #if BYTE_ORDER == _LITTLE_ENDIAN 574 sprops.unicode_encoding = "UnicodeLittle"; 575 #else 576 sprops.unicode_encoding = "UnicodeBig"; 577 #endif 578 #else /* !_ALLBSD_SOURCE */ 579 #ifdef __linux__ 580 #if __BYTE_ORDER == __LITTLE_ENDIAN 581 sprops.unicode_encoding = "UnicodeLittle"; 582 #else 583 sprops.unicode_encoding = "UnicodeBig"; 584 #endif 585 #else 586 sprops.unicode_encoding = "UnicodeBig"; 587 #endif 588 #endif /* _ALLBSD_SOURCE */ 589 590 /* user properties */ 591 { 592 struct passwd *pwent = getpwuid(getuid()); 593 sprops.user_name = pwent ? strdup(pwent->pw_name) : "?"; 594 #ifdef MACOSX 595 setUserHome(&sprops); 596 #else 597 sprops.user_home = pwent ? strdup(pwent->pw_dir) : NULL; 598 #endif 599 if (sprops.user_home == NULL) { 600 sprops.user_home = "?"; 601 } 602 } 603 604 /* User TIMEZONE */ 605 { 606 /* 607 * We defer setting up timezone until it's actually necessary. 608 * Refer to TimeZone.getDefault(). However, the system 609 * property is necessary to be able to be set by the command 610 * line interface -D. Here temporarily set a null string to 611 * timezone. 612 */ 613 tzset(); /* for compatibility */ 614 sprops.timezone = ""; 615 } 616 617 /* Current directory */ 618 { 619 char buf[MAXPATHLEN]; 620 errno = 0; 621 if (getcwd(buf, sizeof(buf)) == NULL) 622 JNU_ThrowByName(env, "java/lang/Error", 623 "Properties init: Could not determine current working directory."); 624 else 625 sprops.user_dir = strdup(buf); 626 } 627 628 sprops.file_separator = "/"; 629 sprops.path_separator = ":"; 630 sprops.line_separator = "\n"; 631 632 #if !defined(_ALLBSD_SOURCE) 633 /* Append CDE message and resource search path to NLSPATH and 634 * XFILESEARCHPATH, in order to pick localized message for 635 * FileSelectionDialog window (Bug 4173641). 636 */ 637 setPathEnvironment("NLSPATH=/usr/dt/lib/nls/msg/%L/%N.cat"); 638 setPathEnvironment("XFILESEARCHPATH=/usr/dt/app-defaults/%L/Dt"); 639 #endif 640 641 642 #ifdef MACOSX 643 setProxyProperties(&sprops); 644 #endif 645 646 return &sprops; 647 } 648 649 jstring 650 GetStringPlatform(JNIEnv *env, nchar* cstr) 651 { 652 return JNU_NewStringPlatform(env, cstr); 653 }