1 | /* |
2 | * Copyright (c) 1998, 2018, 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 | /* Take an array of string pairs (map of key->value) and a string (key). |
72 | * Examine each pair in the map to see if the first string (key) matches the |
73 | * string. If so, store the second string of the pair (value) in the value and |
74 | * return 1. Otherwise do nothing and return 0. The end of the map is |
75 | * indicated by an empty string at the start of a pair (key of ""). |
76 | */ |
77 | static int |
78 | mapLookup(char* map[], const char* key, char** value) { |
79 | int i; |
80 | for (i = 0; strcmp(map[i], "" ); i += 2){ |
81 | if (!strcmp(key, map[i])){ |
82 | *value = map[i + 1]; |
83 | return 1; |
84 | } |
85 | } |
86 | return 0; |
87 | } |
88 | |
89 | #ifndef P_tmpdir |
90 | #define P_tmpdir "/var/tmp" |
91 | #endif |
92 | |
93 | static int ParseLocale(JNIEnv* env, int cat, char ** std_language, char ** std_script, |
94 | char ** std_country, char ** std_variant, char ** std_encoding) { |
95 | char *temp = NULL; |
96 | char *language = NULL, *country = NULL, *variant = NULL, |
97 | *encoding = NULL; |
98 | char *p, *encoding_variant, *old_temp, *old_ev; |
99 | char *lc; |
100 | |
101 | /* Query the locale set for the category */ |
102 | |
103 | #ifdef MACOSX |
104 | lc = setupMacOSXLocale(cat); // malloc'd memory, need to free |
105 | #else |
106 | lc = setlocale(cat, NULL); |
107 | #endif |
108 | |
109 | #ifndef __linux__ |
110 | if (lc == NULL) { |
111 | return 0; |
112 | } |
113 | |
114 | temp = malloc(strlen(lc) + 1); |
115 | if (temp == NULL) { |
116 | #ifdef MACOSX |
117 | free(lc); // malloced memory |
118 | #endif |
119 | JNU_ThrowOutOfMemoryError(env, NULL); |
120 | return 0; |
121 | } |
122 | |
123 | if (cat == LC_CTYPE) { |
124 | /* |
125 | * Workaround for Solaris bug 4201684: Xlib doesn't like @euro |
126 | * locales. Since we don't depend on the libc @euro behavior, |
127 | * we just remove the qualifier. |
128 | * On Linux, the bug doesn't occur; on the other hand, @euro |
129 | * is needed there because it's a shortcut that also determines |
130 | * the encoding - without it, we wouldn't get ISO-8859-15. |
131 | * Therefore, this code section is Solaris-specific. |
132 | */ |
133 | strcpy(temp, lc); |
134 | p = strstr(temp, "@euro" ); |
135 | if (p != NULL) { |
136 | *p = '\0'; |
137 | setlocale(LC_ALL, temp); |
138 | } |
139 | } |
140 | #else |
141 | if (lc == NULL || !strcmp(lc, "C" ) || !strcmp(lc, "POSIX" )) { |
142 | lc = "en_US" ; |
143 | } |
144 | |
145 | temp = malloc(strlen(lc) + 1); |
146 | if (temp == NULL) { |
147 | JNU_ThrowOutOfMemoryError(env, NULL); |
148 | return 0; |
149 | } |
150 | |
151 | #endif |
152 | |
153 | /* |
154 | * locale string format in Solaris is |
155 | * <language name>_<country name>.<encoding name>@<variant name> |
156 | * <country name>, <encoding name>, and <variant name> are optional. |
157 | */ |
158 | |
159 | strcpy(temp, lc); |
160 | #ifdef MACOSX |
161 | free(lc); // malloced memory |
162 | #endif |
163 | /* Parse the language, country, encoding, and variant from the |
164 | * locale. Any of the elements may be missing, but they must occur |
165 | * in the order language_country.encoding@variant, and must be |
166 | * preceded by their delimiter (except for language). |
167 | * |
168 | * If the locale name (without .encoding@variant, if any) matches |
169 | * any of the names in the locale_aliases list, map it to the |
170 | * corresponding full locale name. Most of the entries in the |
171 | * locale_aliases list are locales that include a language name but |
172 | * no country name, and this facility is used to map each language |
173 | * to a default country if that's possible. It's also used to map |
174 | * the Solaris locale aliases to their proper Java locale IDs. |
175 | */ |
176 | |
177 | encoding_variant = malloc(strlen(temp)+1); |
178 | if (encoding_variant == NULL) { |
179 | free(temp); |
180 | JNU_ThrowOutOfMemoryError(env, NULL); |
181 | return 0; |
182 | } |
183 | |
184 | if ((p = strchr(temp, '.')) != NULL) { |
185 | strcpy(encoding_variant, p); /* Copy the leading '.' */ |
186 | *p = '\0'; |
187 | } else if ((p = strchr(temp, '@')) != NULL) { |
188 | strcpy(encoding_variant, p); /* Copy the leading '@' */ |
189 | *p = '\0'; |
190 | } else { |
191 | *encoding_variant = '\0'; |
192 | } |
193 | |
194 | if (mapLookup(locale_aliases, temp, &p)) { |
195 | old_temp = temp; |
196 | temp = realloc(temp, strlen(p)+1); |
197 | if (temp == NULL) { |
198 | free(old_temp); |
199 | free(encoding_variant); |
200 | JNU_ThrowOutOfMemoryError(env, NULL); |
201 | return 0; |
202 | } |
203 | strcpy(temp, p); |
204 | old_ev = encoding_variant; |
205 | encoding_variant = realloc(encoding_variant, strlen(temp)+1); |
206 | if (encoding_variant == NULL) { |
207 | free(old_ev); |
208 | free(temp); |
209 | JNU_ThrowOutOfMemoryError(env, NULL); |
210 | return 0; |
211 | } |
212 | // check the "encoding_variant" again, if any. |
213 | if ((p = strchr(temp, '.')) != NULL) { |
214 | strcpy(encoding_variant, p); /* Copy the leading '.' */ |
215 | *p = '\0'; |
216 | } else if ((p = strchr(temp, '@')) != NULL) { |
217 | strcpy(encoding_variant, p); /* Copy the leading '@' */ |
218 | *p = '\0'; |
219 | } |
220 | } |
221 | |
222 | language = temp; |
223 | if ((country = strchr(temp, '_')) != NULL) { |
224 | *country++ = '\0'; |
225 | } |
226 | |
227 | p = encoding_variant; |
228 | if ((encoding = strchr(p, '.')) != NULL) { |
229 | p[encoding++ - p] = '\0'; |
230 | p = encoding; |
231 | } |
232 | if ((variant = strchr(p, '@')) != NULL) { |
233 | p[variant++ - p] = '\0'; |
234 | } |
235 | |
236 | /* Normalize the language name */ |
237 | if (std_language != NULL) { |
238 | *std_language = "en" ; |
239 | if (language != NULL && mapLookup(language_names, language, std_language) == 0) { |
240 | *std_language = malloc(strlen(language)+1); |
241 | strcpy(*std_language, language); |
242 | } |
243 | } |
244 | |
245 | /* Normalize the country name */ |
246 | if (std_country != NULL && country != NULL) { |
247 | if (mapLookup(country_names, country, std_country) == 0) { |
248 | *std_country = malloc(strlen(country)+1); |
249 | strcpy(*std_country, country); |
250 | } |
251 | } |
252 | |
253 | /* Normalize the script and variant name. Note that we only use |
254 | * variants listed in the mapping array; others are ignored. |
255 | */ |
256 | if (variant != NULL) { |
257 | if (std_script != NULL) { |
258 | mapLookup(script_names, variant, std_script); |
259 | } |
260 | |
261 | if (std_variant != NULL) { |
262 | mapLookup(variant_names, variant, std_variant); |
263 | } |
264 | } |
265 | |
266 | /* Normalize the encoding name. Note that we IGNORE the string |
267 | * 'encoding' extracted from the locale name above. Instead, we use the |
268 | * more reliable method of calling nl_langinfo(CODESET). This function |
269 | * returns an empty string if no encoding is set for the given locale |
270 | * (e.g., the C or POSIX locales); we use the default ISO 8859-1 |
271 | * converter for such locales. |
272 | */ |
273 | if (std_encoding != NULL) { |
274 | /* OK, not so reliable - nl_langinfo() gives wrong answers on |
275 | * Euro locales, in particular. */ |
276 | if (strcmp(p, "ISO8859-15" ) == 0) |
277 | p = "ISO8859-15" ; |
278 | else |
279 | p = nl_langinfo(CODESET); |
280 | |
281 | /* Convert the bare "646" used on Solaris to a proper IANA name */ |
282 | if (strcmp(p, "646" ) == 0) |
283 | p = "ISO646-US" ; |
284 | |
285 | /* return same result nl_langinfo would return for en_UK, |
286 | * in order to use optimizations. */ |
287 | *std_encoding = (*p != '\0') ? p : "ISO8859-1" ; |
288 | |
289 | #ifdef __linux__ |
290 | /* |
291 | * Remap the encoding string to a different value for japanese |
292 | * locales on linux so that customized converters are used instead |
293 | * of the default converter for "EUC-JP". The customized converters |
294 | * omit support for the JIS0212 encoding which is not supported by |
295 | * the variant of "EUC-JP" encoding used on linux |
296 | */ |
297 | if (strcmp(p, "EUC-JP" ) == 0) { |
298 | *std_encoding = "EUC-JP-LINUX" ; |
299 | } |
300 | #endif |
301 | |
302 | #ifdef _AIX |
303 | if (strcmp(p, "big5" ) == 0) { |
304 | /* On AIX Traditional Chinese Big5 codeset is mapped to IBM-950 */ |
305 | *std_encoding = "IBM-950" ; |
306 | } else if (strcmp(p, "IBM-943" ) == 0) { |
307 | /* |
308 | * On AIX, IBM-943 is mapped to IBM-943C in which symbol 'yen' and |
309 | * 'overline' are replaced with 'backslash' and 'tilde' from ASCII |
310 | * making first 96 code points same as ASCII. |
311 | */ |
312 | *std_encoding = "IBM-943C" ; |
313 | } |
314 | #endif |
315 | |
316 | #ifdef __solaris__ |
317 | if (strcmp(p,"eucJP" ) == 0) { |
318 | /* For Solaris use customized vendor defined character |
319 | * customized EUC-JP converter |
320 | */ |
321 | *std_encoding = "eucJP-open" ; |
322 | } else if (strcmp(p, "Big5" ) == 0 || strcmp(p, "BIG5" ) == 0) { |
323 | /* |
324 | * Remap the encoding string to Big5_Solaris which augments |
325 | * the default converter for Solaris Big5 locales to include |
326 | * seven additional ideographic characters beyond those included |
327 | * in the Java "Big5" converter. |
328 | */ |
329 | *std_encoding = "Big5_Solaris" ; |
330 | } else if (strcmp(p, "Big5-HKSCS" ) == 0) { |
331 | /* |
332 | * Solaris uses HKSCS2001 |
333 | */ |
334 | *std_encoding = "Big5-HKSCS-2001" ; |
335 | } |
336 | #endif |
337 | #ifdef MACOSX |
338 | /* |
339 | * For the case on MacOS X where encoding is set to US-ASCII, but we |
340 | * don't have any encoding hints from LANG/LC_ALL/LC_CTYPE, use UTF-8 |
341 | * instead. |
342 | * |
343 | * The contents of ASCII files will still be read and displayed |
344 | * correctly, but so will files containing UTF-8 characters beyond the |
345 | * standard ASCII range. |
346 | * |
347 | * Specifically, this allows apps launched by double-clicking a .jar |
348 | * file to correctly read UTF-8 files using the default encoding (see |
349 | * 8011194). |
350 | */ |
351 | const char* env_lang = getenv("LANG" ); |
352 | const char* env_lc_all = getenv("LC_ALL" ); |
353 | const char* env_lc_ctype = getenv("LC_CTYPE" ); |
354 | |
355 | if (strcmp(p,"US-ASCII" ) == 0 && |
356 | (env_lang == NULL || strlen(env_lang) == 0) && |
357 | (env_lc_all == NULL || strlen(env_lc_all) == 0) && |
358 | (env_lc_ctype == NULL || strlen(env_lc_ctype) == 0)) { |
359 | *std_encoding = "UTF-8" ; |
360 | } |
361 | #endif |
362 | } |
363 | |
364 | free(temp); |
365 | free(encoding_variant); |
366 | |
367 | return 1; |
368 | } |
369 | |
370 | /* This function gets called very early, before VM_CALLS are setup. |
371 | * Do not use any of the VM_CALLS entries!!! |
372 | */ |
373 | java_props_t * |
374 | GetJavaProperties(JNIEnv *env) |
375 | { |
376 | static java_props_t sprops; |
377 | char *v; /* tmp var */ |
378 | |
379 | if (sprops.user_dir) { |
380 | return &sprops; |
381 | } |
382 | |
383 | /* tmp dir */ |
384 | sprops.tmp_dir = P_tmpdir; |
385 | #ifdef MACOSX |
386 | /* darwin has a per-user temp dir */ |
387 | static char tmp_path[PATH_MAX]; |
388 | int pathSize = confstr(_CS_DARWIN_USER_TEMP_DIR, tmp_path, PATH_MAX); |
389 | if (pathSize > 0 && pathSize <= PATH_MAX) { |
390 | sprops.tmp_dir = tmp_path; |
391 | } |
392 | #endif /* MACOSX */ |
393 | |
394 | /* patches/service packs installed */ |
395 | sprops.patch_level = NULL; // leave it undefined |
396 | |
397 | #ifdef SI_ISALIST |
398 | /* supported instruction sets */ |
399 | { |
400 | char list[258]; |
401 | sysinfo(SI_ISALIST, list, sizeof(list)); |
402 | sprops.cpu_isalist = strdup(list); |
403 | } |
404 | #else |
405 | sprops.cpu_isalist = NULL; |
406 | #endif |
407 | |
408 | /* endianness of platform */ |
409 | { |
410 | unsigned int endianTest = 0xff000000; |
411 | if (((char*)(&endianTest))[0] != 0) |
412 | sprops.cpu_endian = "big" ; |
413 | else |
414 | sprops.cpu_endian = "little" ; |
415 | } |
416 | |
417 | /* os properties */ |
418 | { |
419 | #ifdef MACOSX |
420 | setOSNameAndVersion(&sprops); |
421 | #else |
422 | struct utsname name; |
423 | uname(&name); |
424 | sprops.os_name = strdup(name.sysname); |
425 | #ifdef _AIX |
426 | { |
427 | char *os_version = malloc(strlen(name.version) + |
428 | strlen(name.release) + 2); |
429 | if (os_version != NULL) { |
430 | strcpy(os_version, name.version); |
431 | strcat(os_version, "." ); |
432 | strcat(os_version, name.release); |
433 | } |
434 | sprops.os_version = os_version; |
435 | } |
436 | #else |
437 | sprops.os_version = strdup(name.release); |
438 | #endif /* _AIX */ |
439 | #endif /* MACOSX */ |
440 | |
441 | sprops.os_arch = ARCHPROPNAME; |
442 | } |
443 | |
444 | /* ABI property (optional) */ |
445 | #ifdef JDK_ARCH_ABI_PROP_NAME |
446 | sprops.sun_arch_abi = JDK_ARCH_ABI_PROP_NAME; |
447 | #endif |
448 | |
449 | /* Determine the language, country, variant, and encoding from the host, |
450 | * and store these in the user.language, user.country, user.variant and |
451 | * file.encoding system properties. */ |
452 | setlocale(LC_ALL, "" ); |
453 | if (ParseLocale(env, LC_CTYPE, |
454 | &(sprops.format_language), |
455 | &(sprops.format_script), |
456 | &(sprops.format_country), |
457 | &(sprops.format_variant), |
458 | &(sprops.encoding))) { |
459 | ParseLocale(env, LC_MESSAGES, |
460 | &(sprops.display_language), |
461 | &(sprops.display_script), |
462 | &(sprops.display_country), |
463 | &(sprops.display_variant), |
464 | NULL); |
465 | } else { |
466 | sprops.display_language = "en" ; |
467 | sprops.encoding = "ISO8859-1" ; |
468 | } |
469 | |
470 | /* ParseLocale failed with OOME */ |
471 | JNU_CHECK_EXCEPTION_RETURN(env, NULL); |
472 | |
473 | #ifdef MACOSX |
474 | sprops.sun_jnu_encoding = "UTF-8" ; |
475 | #else |
476 | sprops.sun_jnu_encoding = sprops.encoding; |
477 | #endif |
478 | |
479 | #ifdef _ALLBSD_SOURCE |
480 | #if BYTE_ORDER == _LITTLE_ENDIAN |
481 | sprops.unicode_encoding = "UnicodeLittle" ; |
482 | #else |
483 | sprops.unicode_encoding = "UnicodeBig" ; |
484 | #endif |
485 | #else /* !_ALLBSD_SOURCE */ |
486 | #ifdef __linux__ |
487 | #if __BYTE_ORDER == __LITTLE_ENDIAN |
488 | sprops.unicode_encoding = "UnicodeLittle" ; |
489 | #else |
490 | sprops.unicode_encoding = "UnicodeBig" ; |
491 | #endif |
492 | #else |
493 | sprops.unicode_encoding = "UnicodeBig" ; |
494 | #endif |
495 | #endif /* _ALLBSD_SOURCE */ |
496 | |
497 | /* user properties */ |
498 | { |
499 | struct passwd *pwent = getpwuid(getuid()); |
500 | sprops.user_name = pwent ? strdup(pwent->pw_name) : "?" ; |
501 | #ifdef MACOSX |
502 | setUserHome(&sprops); |
503 | #else |
504 | sprops.user_home = pwent ? strdup(pwent->pw_dir) : NULL; |
505 | #endif |
506 | if (sprops.user_home == NULL) { |
507 | sprops.user_home = "?" ; |
508 | } |
509 | } |
510 | |
511 | /* User TIMEZONE |
512 | * We defer setting up timezone until it's actually necessary. |
513 | * Refer to TimeZone.getDefault(). The system property |
514 | * is able to be set by the command line interface -Duser.timezone. |
515 | */ |
516 | tzset(); /* for compatibility */ |
517 | |
518 | /* Current directory */ |
519 | { |
520 | char buf[MAXPATHLEN]; |
521 | errno = 0; |
522 | if (getcwd(buf, sizeof(buf)) == NULL) |
523 | JNU_ThrowByName(env, "java/lang/Error" , |
524 | "Properties init: Could not determine current working directory." ); |
525 | else |
526 | sprops.user_dir = strdup(buf); |
527 | } |
528 | |
529 | sprops.file_separator = "/" ; |
530 | sprops.path_separator = ":" ; |
531 | sprops.line_separator = "\n" ; |
532 | |
533 | #ifdef MACOSX |
534 | setProxyProperties(&sprops); |
535 | #endif |
536 | |
537 | return &sprops; |
538 | } |
539 | |
540 | jstring |
541 | GetStringPlatform(JNIEnv *env, nchar* cstr) |
542 | { |
543 | return JNU_NewStringPlatform(env, cstr); |
544 | } |
545 | |