1/*
2 * Copyright (c) 1999, 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#include <stdlib.h>
27#include <stdio.h>
28#include <strings.h>
29#include <time.h>
30#include <limits.h>
31#include <errno.h>
32#include <stddef.h>
33#include <sys/stat.h>
34#include <sys/types.h>
35#include <string.h>
36#include <dirent.h>
37#include <unistd.h>
38#if defined(__solaris__)
39#include <libscf.h>
40#endif
41
42#include "jvm.h"
43#include "TimeZone_md.h"
44
45#define SKIP_SPACE(p) while (*p == ' ' || *p == '\t') p++;
46
47#define RESTARTABLE(_cmd, _result) do { \
48 do { \
49 _result = _cmd; \
50 } while((_result == -1) && (errno == EINTR)); \
51} while(0)
52
53#if !defined(__solaris__) || defined(__sparcv9) || defined(amd64)
54#define fileopen fopen
55#define filegets fgets
56#define fileclose fclose
57#endif
58
59#if defined(_ALLBSD_SOURCE)
60#define stat64 stat
61#define lstat64 lstat
62#define fstat64 fstat
63#endif
64
65#if defined(__linux__) || defined(_ALLBSD_SOURCE)
66static const char *ETC_TIMEZONE_FILE = "/etc/timezone";
67static const char *ZONEINFO_DIR = "/usr/share/zoneinfo";
68static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime";
69#else
70static const char *SYS_INIT_FILE = "/etc/default/init";
71static const char *ZONEINFO_DIR = "/usr/share/lib/zoneinfo";
72static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime";
73#endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */
74
75#if defined(_AIX)
76static const char *ETC_ENVIRONMENT_FILE = "/etc/environment";
77#endif
78
79#if defined(__linux__) || defined(MACOSX) || defined(__solaris__)
80
81/*
82 * Returns a pointer to the zone ID portion of the given zoneinfo file
83 * name, or NULL if the given string doesn't contain "zoneinfo/".
84 */
85static char *
86getZoneName(char *str)
87{
88 static const char *zidir = "zoneinfo/";
89
90 char *pos = strstr((const char *)str, zidir);
91 if (pos == NULL) {
92 return NULL;
93 }
94 return pos + strlen(zidir);
95}
96
97/*
98 * Returns a path name created from the given 'dir' and 'name' under
99 * UNIX. This function allocates memory for the pathname calling
100 * malloc(). NULL is returned if malloc() fails.
101 */
102static char *
103getPathName(const char *dir, const char *name) {
104 char *path;
105
106 path = (char *) malloc(strlen(dir) + strlen(name) + 2);
107 if (path == NULL) {
108 return NULL;
109 }
110 return strcat(strcat(strcpy(path, dir), "/"), name);
111}
112
113/*
114 * Scans the specified directory and its subdirectories to find a
115 * zoneinfo file which has the same content as /etc/localtime on Linux
116 * or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'.
117 * If file is symbolic link, then the contents it points to are in buf.
118 * Returns a zone ID if found, otherwise, NULL is returned.
119 */
120static char *
121findZoneinfoFile(char *buf, size_t size, const char *dir)
122{
123 DIR *dirp = NULL;
124 struct stat64 statbuf;
125 struct dirent *dp = NULL;
126 char *pathname = NULL;
127 int fd = -1;
128 char *dbuf = NULL;
129 char *tz = NULL;
130 int res;
131
132 dirp = opendir(dir);
133 if (dirp == NULL) {
134 return NULL;
135 }
136
137 while ((dp = readdir(dirp)) != NULL) {
138 /*
139 * Skip '.' and '..' (and possibly other .* files)
140 */
141 if (dp->d_name[0] == '.') {
142 continue;
143 }
144
145 /*
146 * Skip "ROC", "posixrules", and "localtime".
147 */
148 if ((strcmp(dp->d_name, "ROC") == 0)
149 || (strcmp(dp->d_name, "posixrules") == 0)
150#if defined(__solaris__)
151 /*
152 * Skip the "src" and "tab" directories on Solaris.
153 */
154 || (strcmp(dp->d_name, "src") == 0)
155 || (strcmp(dp->d_name, "tab") == 0)
156#endif
157 || (strcmp(dp->d_name, "localtime") == 0)) {
158 continue;
159 }
160
161 pathname = getPathName(dir, dp->d_name);
162 if (pathname == NULL) {
163 break;
164 }
165 RESTARTABLE(stat64(pathname, &statbuf), res);
166 if (res == -1) {
167 break;
168 }
169
170 if (S_ISDIR(statbuf.st_mode)) {
171 tz = findZoneinfoFile(buf, size, pathname);
172 if (tz != NULL) {
173 break;
174 }
175 } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) {
176 dbuf = (char *) malloc(size);
177 if (dbuf == NULL) {
178 break;
179 }
180 RESTARTABLE(open(pathname, O_RDONLY), fd);
181 if (fd == -1) {
182 break;
183 }
184 RESTARTABLE(read(fd, dbuf, size), res);
185 if (res != (ssize_t) size) {
186 break;
187 }
188 if (memcmp(buf, dbuf, size) == 0) {
189 tz = getZoneName(pathname);
190 if (tz != NULL) {
191 tz = strdup(tz);
192 }
193 break;
194 }
195 free((void *) dbuf);
196 dbuf = NULL;
197 (void) close(fd);
198 fd = -1;
199 }
200 free((void *) pathname);
201 pathname = NULL;
202 }
203
204 if (dirp != NULL) {
205 (void) closedir(dirp);
206 }
207 if (pathname != NULL) {
208 free((void *) pathname);
209 }
210 if (fd != -1) {
211 (void) close(fd);
212 }
213 if (dbuf != NULL) {
214 free((void *) dbuf);
215 }
216 return tz;
217}
218
219#if defined(__linux__) || defined(MACOSX)
220
221/*
222 * Performs Linux specific mapping and returns a zone ID
223 * if found. Otherwise, NULL is returned.
224 */
225static char *
226getPlatformTimeZoneID()
227{
228 struct stat64 statbuf;
229 char *tz = NULL;
230 FILE *fp;
231 int fd;
232 char *buf;
233 size_t size;
234 int res;
235
236#if defined(__linux__)
237 /*
238 * Try reading the /etc/timezone file for Debian distros. There's
239 * no spec of the file format available. This parsing assumes that
240 * there's one line of an Olson tzid followed by a '\n', no
241 * leading or trailing spaces, no comments.
242 */
243 if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) {
244 char line[256];
245
246 if (fgets(line, sizeof(line), fp) != NULL) {
247 char *p = strchr(line, '\n');
248 if (p != NULL) {
249 *p = '\0';
250 }
251 if (strlen(line) > 0) {
252 tz = strdup(line);
253 }
254 }
255 (void) fclose(fp);
256 if (tz != NULL) {
257 return tz;
258 }
259 }
260#endif /* defined(__linux__) */
261
262 /*
263 * Next, try /etc/localtime to find the zone ID.
264 */
265 RESTARTABLE(lstat64(DEFAULT_ZONEINFO_FILE, &statbuf), res);
266 if (res == -1) {
267 return NULL;
268 }
269
270 /*
271 * If it's a symlink, get the link name and its zone ID part. (The
272 * older versions of timeconfig created a symlink as described in
273 * the Red Hat man page. It was changed in 1999 to create a copy
274 * of a zoneinfo file. It's no longer possible to get the zone ID
275 * from /etc/localtime.)
276 */
277 if (S_ISLNK(statbuf.st_mode)) {
278 char linkbuf[PATH_MAX+1];
279 int len;
280
281 if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) {
282 jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n",
283 DEFAULT_ZONEINFO_FILE);
284 return NULL;
285 }
286 linkbuf[len] = '\0';
287 tz = getZoneName(linkbuf);
288 if (tz != NULL) {
289 tz = strdup(tz);
290 return tz;
291 }
292 }
293
294 /*
295 * If it's a regular file, we need to find out the same zoneinfo file
296 * that has been copied as /etc/localtime.
297 * If initial symbolic link resolution failed, we should treat target
298 * file as a regular file.
299 */
300 RESTARTABLE(open(DEFAULT_ZONEINFO_FILE, O_RDONLY), fd);
301 if (fd == -1) {
302 return NULL;
303 }
304
305 RESTARTABLE(fstat64(fd, &statbuf), res);
306 if (res == -1) {
307 (void) close(fd);
308 return NULL;
309 }
310 size = (size_t) statbuf.st_size;
311 buf = (char *) malloc(size);
312 if (buf == NULL) {
313 (void) close(fd);
314 return NULL;
315 }
316
317 RESTARTABLE(read(fd, buf, size), res);
318 if (res != (ssize_t) size) {
319 (void) close(fd);
320 free((void *) buf);
321 return NULL;
322 }
323 (void) close(fd);
324
325 tz = findZoneinfoFile(buf, size, ZONEINFO_DIR);
326 free((void *) buf);
327 return tz;
328}
329
330#elif defined(__solaris__)
331
332#if !defined(__sparcv9) && !defined(amd64)
333
334/*
335 * Those file* functions mimic the UNIX stream io functions. This is
336 * because of the limitation of the number of open files on Solaris
337 * (32-bit mode only) due to the System V ABI.
338 */
339
340#define BUFFER_SIZE 4096
341
342static struct iobuffer {
343 int magic; /* -1 to distinguish from the real FILE */
344 int fd; /* file descriptor */
345 char *buffer; /* pointer to buffer */
346 char *ptr; /* current read pointer */
347 char *endptr; /* end pointer */
348};
349
350static int
351fileclose(FILE *stream)
352{
353 struct iobuffer *iop = (struct iobuffer *) stream;
354
355 if (iop->magic != -1) {
356 return fclose(stream);
357 }
358
359 if (iop == NULL) {
360 return 0;
361 }
362 close(iop->fd);
363 free((void *)iop->buffer);
364 free((void *)iop);
365 return 0;
366}
367
368static FILE *
369fileopen(const char *fname, const char *fmode)
370{
371 FILE *fp;
372 int fd;
373 struct iobuffer *iop;
374
375 if ((fp = fopen(fname, fmode)) != NULL) {
376 return fp;
377 }
378
379 /*
380 * It assumes read open.
381 */
382 RESTARTABLE(open(fname, O_RDONLY), fd);
383 if (fd == -1) {
384 return NULL;
385 }
386
387 /*
388 * Allocate struct iobuffer and its buffer
389 */
390 iop = malloc(sizeof(struct iobuffer));
391 if (iop == NULL) {
392 (void) close(fd);
393 errno = ENOMEM;
394 return NULL;
395 }
396 iop->magic = -1;
397 iop->fd = fd;
398 iop->buffer = malloc(BUFFER_SIZE);
399 if (iop->buffer == NULL) {
400 (void) close(fd);
401 free((void *) iop);
402 errno = ENOMEM;
403 return NULL;
404 }
405 iop->ptr = iop->buffer;
406 iop->endptr = iop->buffer;
407 return (FILE *)iop;
408}
409
410/*
411 * This implementation assumes that n is large enough and the line
412 * separator is '\n'.
413 */
414static char *
415filegets(char *s, int n, FILE *stream)
416{
417 struct iobuffer *iop = (struct iobuffer *) stream;
418 char *p;
419
420 if (iop->magic != -1) {
421 return fgets(s, n, stream);
422 }
423
424 p = s;
425 for (;;) {
426 char c;
427
428 if (iop->ptr == iop->endptr) {
429 ssize_t len;
430
431 RESTARTABLE(read(iop->fd, (void *)iop->buffer, BUFFER_SIZE), len);
432 if (len == -1) {
433 return NULL;
434 }
435 if (len == 0) {
436 *p = 0;
437 if (s == p) {
438 return NULL;
439 }
440 return s;
441 }
442 iop->ptr = iop->buffer;
443 iop->endptr = iop->buffer + len;
444 }
445 c = *iop->ptr++;
446 *p++ = c;
447 if ((p - s) == (n - 1)) {
448 *p = 0;
449 return s;
450 }
451 if (c == '\n') {
452 *p = 0;
453 return s;
454 }
455 }
456 /*NOTREACHED*/
457}
458#endif /* !defined(__sparcv9) && !defined(amd64) */
459
460/*
461 * Performs Solaris dependent mapping. Returns a zone ID if
462 * found. Otherwise, NULL is returned. Solaris libc looks up
463 * "/etc/default/init" to get the default TZ value if TZ is not defined
464 * as an environment variable.
465 */
466static char *
467getPlatformTimeZoneID()
468{
469 char *tz = NULL;
470 FILE *fp;
471
472 /*
473 * Try the TZ entry in /etc/default/init.
474 */
475 if ((fp = fileopen(SYS_INIT_FILE, "r")) != NULL) {
476 char line[256];
477 char quote = '\0';
478
479 while (filegets(line, sizeof(line), fp) != NULL) {
480 char *p = line;
481 char *s;
482 char c;
483
484 /* quick check for comment lines */
485 if (*p == '#') {
486 continue;
487 }
488 if (strncmp(p, "TZ=", 3) == 0) {
489 p += 3;
490 SKIP_SPACE(p);
491 c = *p;
492 if (c == '"' || c == '\'') {
493 quote = c;
494 p++;
495 }
496
497 /*
498 * PSARC/2001/383: quoted string support
499 */
500 for (s = p; (c = *s) != '\0' && c != '\n'; s++) {
501 /* No '\\' is supported here. */
502 if (c == quote) {
503 quote = '\0';
504 break;
505 }
506 if (c == ' ' && quote == '\0') {
507 break;
508 }
509 }
510 if (quote != '\0') {
511 jio_fprintf(stderr, "ZoneInfo: unterminated time zone name in /etc/TIMEZONE\n");
512 }
513 *s = '\0';
514 tz = strdup(p);
515 break;
516 }
517 }
518 (void) fileclose(fp);
519 }
520 return tz;
521}
522
523#define TIMEZONE_FMRI "svc:/system/timezone:default"
524#define TIMEZONE_PG "timezone"
525#define LOCALTIME_PROP "localtime"
526
527static void
528cleanupScf(scf_handle_t *h,
529 scf_snapshot_t *snap,
530 scf_instance_t *inst,
531 scf_propertygroup_t *pg,
532 scf_property_t *prop,
533 scf_value_t *val,
534 char *buf) {
535 if (buf != NULL) {
536 free(buf);
537 }
538 if (snap != NULL) {
539 scf_snapshot_destroy(snap);
540 }
541 if (val != NULL) {
542 scf_value_destroy(val);
543 }
544 if (prop != NULL) {
545 scf_property_destroy(prop);
546 }
547 if (pg != NULL) {
548 scf_pg_destroy(pg);
549 }
550 if (inst != NULL) {
551 scf_instance_destroy(inst);
552 }
553 if (h != NULL) {
554 scf_handle_destroy(h);
555 }
556}
557
558/*
559 * Returns a zone ID of Solaris when the TZ value is "localtime".
560 * First, it tries scf. If scf fails, it looks for the same file as
561 * /usr/share/lib/zoneinfo/localtime under /usr/share/lib/zoneinfo/.
562 */
563static char *
564getSolarisDefaultZoneID() {
565 char *tz = NULL;
566 struct stat64 statbuf;
567 size_t size;
568 char *buf;
569 int fd;
570 int res;
571 /* scf specific variables */
572 scf_handle_t *h = NULL;
573 scf_snapshot_t *snap = NULL;
574 scf_instance_t *inst = NULL;
575 scf_propertygroup_t *pg = NULL;
576 scf_property_t *prop = NULL;
577 scf_value_t *val = NULL;
578
579 if ((h = scf_handle_create(SCF_VERSION)) != NULL
580 && scf_handle_bind(h) == 0
581 && (inst = scf_instance_create(h)) != NULL
582 && (snap = scf_snapshot_create(h)) != NULL
583 && (pg = scf_pg_create(h)) != NULL
584 && (prop = scf_property_create(h)) != NULL
585 && (val = scf_value_create(h)) != NULL
586 && scf_handle_decode_fmri(h, TIMEZONE_FMRI, NULL, NULL, inst,
587 NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) == 0
588 && scf_instance_get_snapshot(inst, "running", snap) == 0
589 && scf_instance_get_pg_composed(inst, snap, TIMEZONE_PG, pg) == 0
590 && scf_pg_get_property(pg, LOCALTIME_PROP, prop) == 0
591 && scf_property_get_value(prop, val) == 0) {
592 ssize_t len;
593
594 /* Gets the length of the zone ID string */
595 len = scf_value_get_astring(val, NULL, 0);
596 if (len != -1) {
597 tz = malloc(++len); /* +1 for a null byte */
598 if (tz != NULL && scf_value_get_astring(val, tz, len) != -1) {
599 cleanupScf(h, snap, inst, pg, prop, val, NULL);
600 return tz;
601 }
602 }
603 }
604 cleanupScf(h, snap, inst, pg, prop, val, tz);
605
606 RESTARTABLE(stat64(DEFAULT_ZONEINFO_FILE, &statbuf), res);
607 if (res == -1) {
608 return NULL;
609 }
610 size = (size_t) statbuf.st_size;
611 buf = malloc(size);
612 if (buf == NULL) {
613 return NULL;
614 }
615 RESTARTABLE(open(DEFAULT_ZONEINFO_FILE, O_RDONLY), fd);
616 if (fd == -1) {
617 free((void *) buf);
618 return NULL;
619 }
620
621 RESTARTABLE(read(fd, buf, size), res);
622 if (res != (ssize_t) size) {
623 (void) close(fd);
624 free((void *) buf);
625 return NULL;
626 }
627 (void) close(fd);
628 tz = findZoneinfoFile(buf, size, ZONEINFO_DIR);
629 free((void *) buf);
630 return tz;
631}
632
633#endif /* defined(__solaris__) */
634
635#elif defined(_AIX)
636
637static char *
638getPlatformTimeZoneID()
639{
640 FILE *fp;
641 char *tz = NULL;
642 char *tz_key = "TZ=";
643 char line[256];
644 size_t tz_key_len = strlen(tz_key);
645
646 if ((fp = fopen(ETC_ENVIRONMENT_FILE, "r")) != NULL) {
647 while (fgets(line, sizeof(line), fp) != NULL) {
648 char *p = strchr(line, '\n');
649 if (p != NULL) {
650 *p = '\0';
651 }
652 if (0 == strncmp(line, tz_key, tz_key_len)) {
653 tz = strdup(line + tz_key_len);
654 break;
655 }
656 }
657 (void) fclose(fp);
658 }
659
660 return tz;
661}
662
663static char *
664mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) {
665 FILE *tzmapf;
666 char mapfilename[PATH_MAX + 1];
667 char line[256];
668 int linecount = 0;
669 char *tz_buf = NULL;
670 char *temp_tz = NULL;
671 char *javatz = NULL;
672 size_t tz_len = 0;
673
674 /* On AIX, the TZ environment variable may end with a comma
675 * followed by modifier fields. These are ignored here. */
676 temp_tz = strchr(tz, ',');
677 tz_len = (temp_tz == NULL) ? strlen(tz) : temp_tz - tz;
678 tz_buf = (char *)malloc(tz_len + 1);
679 memcpy(tz_buf, tz, tz_len);
680 tz_buf[tz_len] = 0;
681
682 /* Open tzmappings file, with buffer overrun check */
683 if ((strlen(java_home_dir) + 15) > PATH_MAX) {
684 jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir);
685 goto tzerr;
686 }
687 strcpy(mapfilename, java_home_dir);
688 strcat(mapfilename, "/lib/tzmappings");
689 if ((tzmapf = fopen(mapfilename, "r")) == NULL) {
690 jio_fprintf(stderr, "can't open %s\n", mapfilename);
691 goto tzerr;
692 }
693
694 while (fgets(line, sizeof(line), tzmapf) != NULL) {
695 char *p = line;
696 char *sol = line;
697 char *java;
698 int result;
699
700 linecount++;
701 /*
702 * Skip comments and blank lines
703 */
704 if (*p == '#' || *p == '\n') {
705 continue;
706 }
707
708 /*
709 * Get the first field, platform zone ID
710 */
711 while (*p != '\0' && *p != '\t') {
712 p++;
713 }
714 if (*p == '\0') {
715 /* mapping table is broken! */
716 jio_fprintf(stderr, "tzmappings: Illegal format at near line %d.\n", linecount);
717 break;
718 }
719
720 *p++ = '\0';
721 if ((result = strncmp(tz_buf, sol, tz_len)) == 0) {
722 /*
723 * If this is the current platform zone ID,
724 * take the Java time zone ID (2nd field).
725 */
726 java = p;
727 while (*p != '\0' && *p != '\n') {
728 p++;
729 }
730
731 if (*p == '\0') {
732 /* mapping table is broken! */
733 jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", linecount);
734 break;
735 }
736
737 *p = '\0';
738 javatz = strdup(java);
739 break;
740 } else if (result < 0) {
741 break;
742 }
743 }
744 (void) fclose(tzmapf);
745
746tzerr:
747 if (tz_buf != NULL ) {
748 free((void *) tz_buf);
749 }
750
751 if (javatz == NULL) {
752 return getGMTOffsetID();
753 }
754
755 return javatz;
756}
757
758#endif /* defined(_AIX) */
759
760/*
761 * findJavaTZ_md() maps platform time zone ID to Java time zone ID
762 * using <java_home>/lib/tzmappings. If the TZ value is not found, it
763 * trys some libc implementation dependent mappings. If it still
764 * can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm
765 * form.
766 */
767/*ARGSUSED1*/
768char *
769findJavaTZ_md(const char *java_home_dir)
770{
771 char *tz;
772 char *javatz = NULL;
773 char *freetz = NULL;
774
775 tz = getenv("TZ");
776
777 if (tz == NULL || *tz == '\0') {
778 tz = getPlatformTimeZoneID();
779 freetz = tz;
780 }
781
782 if (tz != NULL) {
783 /* Ignore preceding ':' */
784 if (*tz == ':') {
785 tz++;
786 }
787#if defined(__linux__)
788 /* Ignore "posix/" prefix on Linux. */
789 if (strncmp(tz, "posix/", 6) == 0) {
790 tz += 6;
791 }
792#endif
793
794#if defined(_AIX)
795 /* On AIX do the platform to Java mapping. */
796 javatz = mapPlatformToJavaTimezone(java_home_dir, tz);
797 if (freetz != NULL) {
798 free((void *) freetz);
799 }
800#else
801#if defined(__solaris__)
802 /* Solaris might use localtime, so handle it here. */
803 if (strcmp(tz, "localtime") == 0) {
804 javatz = getSolarisDefaultZoneID();
805 if (freetz != NULL) {
806 free((void *) freetz);
807 }
808 } else
809#endif
810 if (freetz == NULL) {
811 /* strdup if we are still working on getenv result. */
812 javatz = strdup(tz);
813 } else if (freetz != tz) {
814 /* strdup and free the old buffer, if we moved the pointer. */
815 javatz = strdup(tz);
816 free((void *) freetz);
817 } else {
818 /* we are good if we already work on a freshly allocated buffer. */
819 javatz = tz;
820 }
821#endif
822 }
823
824 return javatz;
825}
826
827/**
828 * Returns a GMT-offset-based zone ID. (e.g., "GMT-08:00")
829 */
830
831#if defined(MACOSX)
832
833char *
834getGMTOffsetID()
835{
836 time_t offset;
837 char sign, buf[32];
838 struct tm local_tm;
839 time_t clock;
840
841 clock = time(NULL);
842 if (localtime_r(&clock, &local_tm) == NULL) {
843 return strdup("GMT");
844 }
845 offset = (time_t)local_tm.tm_gmtoff;
846 if (offset == 0) {
847 return strdup("GMT");
848 }
849 if (offset > 0) {
850 sign = '+';
851 } else {
852 offset = -offset;
853 sign = '-';
854 }
855 sprintf(buf, (const char *)"GMT%c%02d:%02d",
856 sign, (int)(offset/3600), (int)((offset%3600)/60));
857 return strdup(buf);
858}
859
860#else
861
862char *
863getGMTOffsetID()
864{
865 time_t offset;
866 char sign, buf[32];
867#if defined(__solaris__)
868 struct tm localtm;
869 time_t currenttime;
870
871 currenttime = time(NULL);
872 if (localtime_r(&currenttime, &localtm) == NULL) {
873 return strdup("GMT");
874 }
875
876 offset = localtm.tm_isdst ? altzone : timezone;
877#else
878 offset = timezone;
879#endif
880
881 if (offset == 0) {
882 return strdup("GMT");
883 }
884
885 /* Note that the time offset direction is opposite. */
886 if (offset > 0) {
887 sign = '-';
888 } else {
889 offset = -offset;
890 sign = '+';
891 }
892 sprintf(buf, (const char *)"GMT%c%02d:%02d",
893 sign, (int)(offset/3600), (int)((offset%3600)/60));
894 return strdup(buf);
895}
896#endif /* MACOSX */
897