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