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
mapLookup(char * map[],const char * key,char ** value)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
setPathEnvironment(char * envstring)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 
ParseLocale(int cat,char ** std_language,char ** std_script,char ** std_country,char ** std_variant,char ** std_encoding)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  */
getEmbeddedToolkit()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 *
GetJavaProperties(JNIEnv * env)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
GetStringPlatform(JNIEnv * env,nchar * cstr)577 GetStringPlatform(JNIEnv *env, nchar* cstr)
578 {
579     return JNU_NewStringPlatform(env, cstr);
580 }
581