| /* |
| * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <strings.h> |
| #include <time.h> |
| #include <limits.h> |
| #include <errno.h> |
| #include <stddef.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <string.h> |
| #include <dirent.h> |
| #include <unistd.h> |
| #if defined(__solaris__) |
| #include <libscf.h> |
| #endif |
| |
| #include "jvm.h" |
| #include "TimeZone_md.h" |
| |
| #define SKIP_SPACE(p) while (*p == ' ' || *p == '\t') p++; |
| |
| #define RESTARTABLE(_cmd, _result) do { \ |
| do { \ |
| _result = _cmd; \ |
| } while((_result == -1) && (errno == EINTR)); \ |
| } while(0) |
| |
| #if defined(_ALLBSD_SOURCE) |
| #define dirent64 dirent |
| #define readdir64_r readdir_r |
| #endif |
| |
| #if !defined(__solaris__) || defined(__sparcv9) || defined(amd64) |
| #define fileopen fopen |
| #define filegets fgets |
| #define fileclose fclose |
| #endif |
| |
| #if defined(__linux__) || defined(_ALLBSD_SOURCE) |
| static const char *ETC_TIMEZONE_FILE = "/etc/timezone"; |
| static const char *ZONEINFO_DIR = "/usr/share/zoneinfo"; |
| static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime"; |
| #else |
| static const char *SYS_INIT_FILE = "/etc/default/init"; |
| static const char *ZONEINFO_DIR = "/usr/share/lib/zoneinfo"; |
| static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime"; |
| #endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */ |
| |
| #if defined(_AIX) |
| static const char *ETC_ENVIRONMENT_FILE = "/etc/environment"; |
| #endif |
| |
| #if defined(__linux__) || defined(MACOSX) || defined(__solaris__) |
| |
| /* |
| * Returns a pointer to the zone ID portion of the given zoneinfo file |
| * name, or NULL if the given string doesn't contain "zoneinfo/". |
| */ |
| static char * |
| getZoneName(char *str) |
| { |
| static const char *zidir = "zoneinfo/"; |
| |
| char *pos = strstr((const char *)str, zidir); |
| if (pos == NULL) { |
| return NULL; |
| } |
| return pos + strlen(zidir); |
| } |
| |
| /* |
| * Returns a path name created from the given 'dir' and 'name' under |
| * UNIX. This function allocates memory for the pathname calling |
| * malloc(). NULL is returned if malloc() fails. |
| */ |
| static char * |
| getPathName(const char *dir, const char *name) { |
| char *path; |
| |
| path = (char *) malloc(strlen(dir) + strlen(name) + 2); |
| if (path == NULL) { |
| return NULL; |
| } |
| return strcat(strcat(strcpy(path, dir), "/"), name); |
| } |
| |
| /* |
| * Scans the specified directory and its subdirectories to find a |
| * zoneinfo file which has the same content as /etc/localtime on Linux |
| * or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'. |
| * If file is symbolic link, then the contents it points to are in buf. |
| * Returns a zone ID if found, otherwise, NULL is returned. |
| */ |
| static char * |
| findZoneinfoFile(char *buf, size_t size, const char *dir) |
| { |
| DIR *dirp = NULL; |
| struct stat statbuf; |
| struct dirent64 *dp = NULL; |
| struct dirent64 *entry = NULL; |
| char *pathname = NULL; |
| int fd = -1; |
| char *dbuf = NULL; |
| char *tz = NULL; |
| int res; |
| long name_max = 0; |
| |
| dirp = opendir(dir); |
| if (dirp == NULL) { |
| return NULL; |
| } |
| |
| name_max = pathconf(dir, _PC_NAME_MAX); |
| // If pathconf did not work, fall back to a mimimum buffer size. |
| if (name_max < 1024) { |
| name_max = 1024; |
| } |
| |
| entry = (struct dirent64 *)malloc(offsetof(struct dirent64, d_name) + name_max + 1); |
| if (entry == NULL) { |
| (void) closedir(dirp); |
| return NULL; |
| } |
| |
| while (readdir64_r(dirp, entry, &dp) == 0 && dp != NULL) { |
| /* |
| * Skip '.' and '..' (and possibly other .* files) |
| */ |
| if (dp->d_name[0] == '.') { |
| continue; |
| } |
| |
| /* |
| * Skip "ROC", "posixrules", and "localtime". |
| */ |
| if ((strcmp(dp->d_name, "ROC") == 0) |
| || (strcmp(dp->d_name, "posixrules") == 0) |
| #if defined(__solaris__) |
| /* |
| * Skip the "src" and "tab" directories on Solaris. |
| */ |
| || (strcmp(dp->d_name, "src") == 0) |
| || (strcmp(dp->d_name, "tab") == 0) |
| #endif |
| || (strcmp(dp->d_name, "localtime") == 0)) { |
| continue; |
| } |
| |
| pathname = getPathName(dir, dp->d_name); |
| if (pathname == NULL) { |
| break; |
| } |
| RESTARTABLE(stat(pathname, &statbuf), res); |
| if (res == -1) { |
| break; |
| } |
| |
| if (S_ISDIR(statbuf.st_mode)) { |
| tz = findZoneinfoFile(buf, size, pathname); |
| if (tz != NULL) { |
| break; |
| } |
| } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) { |
| dbuf = (char *) malloc(size); |
| if (dbuf == NULL) { |
| break; |
| } |
| RESTARTABLE(open(pathname, O_RDONLY), fd); |
| if (fd == -1) { |
| break; |
| } |
| RESTARTABLE(read(fd, dbuf, size), res); |
| if (res != (ssize_t) size) { |
| break; |
| } |
| if (memcmp(buf, dbuf, size) == 0) { |
| tz = getZoneName(pathname); |
| if (tz != NULL) { |
| tz = strdup(tz); |
| } |
| break; |
| } |
| free((void *) dbuf); |
| dbuf = NULL; |
| (void) close(fd); |
| fd = -1; |
| } |
| free((void *) pathname); |
| pathname = NULL; |
| } |
| |
| if (entry != NULL) { |
| free((void *) entry); |
| } |
| if (dirp != NULL) { |
| (void) closedir(dirp); |
| } |
| if (pathname != NULL) { |
| free((void *) pathname); |
| } |
| if (fd != -1) { |
| (void) close(fd); |
| } |
| if (dbuf != NULL) { |
| free((void *) dbuf); |
| } |
| return tz; |
| } |
| |
| #if defined(__linux__) || defined(MACOSX) |
| |
| /* |
| * Performs Linux specific mapping and returns a zone ID |
| * if found. Otherwise, NULL is returned. |
| */ |
| static char * |
| getPlatformTimeZoneID() |
| { |
| struct stat statbuf; |
| char *tz = NULL; |
| FILE *fp; |
| int fd; |
| char *buf; |
| size_t size; |
| int res; |
| |
| #if defined(__linux__) |
| /* |
| * Try reading the /etc/timezone file for Debian distros. There's |
| * no spec of the file format available. This parsing assumes that |
| * there's one line of an Olson tzid followed by a '\n', no |
| * leading or trailing spaces, no comments. |
| */ |
| if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) { |
| char line[256]; |
| |
| if (fgets(line, sizeof(line), fp) != NULL) { |
| char *p = strchr(line, '\n'); |
| if (p != NULL) { |
| *p = '\0'; |
| } |
| if (strlen(line) > 0) { |
| tz = strdup(line); |
| } |
| } |
| (void) fclose(fp); |
| if (tz != NULL) { |
| return tz; |
| } |
| } |
| #endif /* defined(__linux__) */ |
| |
| /* |
| * Next, try /etc/localtime to find the zone ID. |
| */ |
| RESTARTABLE(lstat(DEFAULT_ZONEINFO_FILE, &statbuf), res); |
| if (res == -1) { |
| return NULL; |
| } |
| |
| /* |
| * If it's a symlink, get the link name and its zone ID part. (The |
| * older versions of timeconfig created a symlink as described in |
| * the Red Hat man page. It was changed in 1999 to create a copy |
| * of a zoneinfo file. It's no longer possible to get the zone ID |
| * from /etc/localtime.) |
| */ |
| if (S_ISLNK(statbuf.st_mode)) { |
| char linkbuf[PATH_MAX+1]; |
| int len; |
| |
| if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) { |
| jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n", |
| DEFAULT_ZONEINFO_FILE); |
| return NULL; |
| } |
| linkbuf[len] = '\0'; |
| tz = getZoneName(linkbuf); |
| if (tz != NULL) { |
| tz = strdup(tz); |
| return tz; |
| } |
| } |
| |
| /* |
| * If it's a regular file, we need to find out the same zoneinfo file |
| * that has been copied as /etc/localtime. |
| * If initial symbolic link resolution failed, we should treat target |
| * file as a regular file. |
| */ |
| RESTARTABLE(open(DEFAULT_ZONEINFO_FILE, O_RDONLY), fd); |
| if (fd == -1) { |
| return NULL; |
| } |
| |
| RESTARTABLE(fstat(fd, &statbuf), res); |
| if (res == -1) { |
| (void) close(fd); |
| return NULL; |
| } |
| size = (size_t) statbuf.st_size; |
| buf = (char *) malloc(size); |
| if (buf == NULL) { |
| (void) close(fd); |
| return NULL; |
| } |
| |
| RESTARTABLE(read(fd, buf, size), res); |
| if (res != (ssize_t) size) { |
| (void) close(fd); |
| free((void *) buf); |
| return NULL; |
| } |
| (void) close(fd); |
| |
| tz = findZoneinfoFile(buf, size, ZONEINFO_DIR); |
| free((void *) buf); |
| return tz; |
| } |
| |
| #elif defined(__solaris__) |
| |
| #if !defined(__sparcv9) && !defined(amd64) |
| |
| /* |
| * Those file* functions mimic the UNIX stream io functions. This is |
| * because of the limitation of the number of open files on Solaris |
| * (32-bit mode only) due to the System V ABI. |
| */ |
| |
| #define BUFFER_SIZE 4096 |
| |
| static struct iobuffer { |
| int magic; /* -1 to distinguish from the real FILE */ |
| int fd; /* file descriptor */ |
| char *buffer; /* pointer to buffer */ |
| char *ptr; /* current read pointer */ |
| char *endptr; /* end pointer */ |
| }; |
| |
| static int |
| fileclose(FILE *stream) |
| { |
| struct iobuffer *iop = (struct iobuffer *) stream; |
| |
| if (iop->magic != -1) { |
| return fclose(stream); |
| } |
| |
| if (iop == NULL) { |
| return 0; |
| } |
| close(iop->fd); |
| free((void *)iop->buffer); |
| free((void *)iop); |
| return 0; |
| } |
| |
| static FILE * |
| fileopen(const char *fname, const char *fmode) |
| { |
| FILE *fp; |
| int fd; |
| struct iobuffer *iop; |
| |
| if ((fp = fopen(fname, fmode)) != NULL) { |
| return fp; |
| } |
| |
| /* |
| * It assumes read open. |
| */ |
| RESTARTABLE(open(fname, O_RDONLY), fd); |
| if (fd == -1) { |
| return NULL; |
| } |
| |
| /* |
| * Allocate struct iobuffer and its buffer |
| */ |
| iop = malloc(sizeof(struct iobuffer)); |
| if (iop == NULL) { |
| (void) close(fd); |
| errno = ENOMEM; |
| return NULL; |
| } |
| iop->magic = -1; |
| iop->fd = fd; |
| iop->buffer = malloc(BUFFER_SIZE); |
| if (iop->buffer == NULL) { |
| (void) close(fd); |
| free((void *) iop); |
| errno = ENOMEM; |
| return NULL; |
| } |
| iop->ptr = iop->buffer; |
| iop->endptr = iop->buffer; |
| return (FILE *)iop; |
| } |
| |
| /* |
| * This implementation assumes that n is large enough and the line |
| * separator is '\n'. |
| */ |
| static char * |
| filegets(char *s, int n, FILE *stream) |
| { |
| struct iobuffer *iop = (struct iobuffer *) stream; |
| char *p; |
| |
| if (iop->magic != -1) { |
| return fgets(s, n, stream); |
| } |
| |
| p = s; |
| for (;;) { |
| char c; |
| |
| if (iop->ptr == iop->endptr) { |
| ssize_t len; |
| |
| RESTARTABLE(read(iop->fd, (void *)iop->buffer, BUFFER_SIZE), len); |
| if (len == -1) { |
| return NULL; |
| } |
| if (len == 0) { |
| *p = 0; |
| if (s == p) { |
| return NULL; |
| } |
| return s; |
| } |
| iop->ptr = iop->buffer; |
| iop->endptr = iop->buffer + len; |
| } |
| c = *iop->ptr++; |
| *p++ = c; |
| if ((p - s) == (n - 1)) { |
| *p = 0; |
| return s; |
| } |
| if (c == '\n') { |
| *p = 0; |
| return s; |
| } |
| } |
| /*NOTREACHED*/ |
| } |
| #endif /* !defined(__sparcv9) && !defined(amd64) */ |
| |
| /* |
| * Performs Solaris dependent mapping. Returns a zone ID if |
| * found. Otherwise, NULL is returned. Solaris libc looks up |
| * "/etc/default/init" to get the default TZ value if TZ is not defined |
| * as an environment variable. |
| */ |
| static char * |
| getPlatformTimeZoneID() |
| { |
| char *tz = NULL; |
| FILE *fp; |
| |
| /* |
| * Try the TZ entry in /etc/default/init. |
| */ |
| if ((fp = fileopen(SYS_INIT_FILE, "r")) != NULL) { |
| char line[256]; |
| char quote = '\0'; |
| |
| while (filegets(line, sizeof(line), fp) != NULL) { |
| char *p = line; |
| char *s; |
| char c; |
| |
| /* quick check for comment lines */ |
| if (*p == '#') { |
| continue; |
| } |
| if (strncmp(p, "TZ=", 3) == 0) { |
| p += 3; |
| SKIP_SPACE(p); |
| c = *p; |
| if (c == '"' || c == '\'') { |
| quote = c; |
| p++; |
| } |
| |
| /* |
| * PSARC/2001/383: quoted string support |
| */ |
| for (s = p; (c = *s) != '\0' && c != '\n'; s++) { |
| /* No '\\' is supported here. */ |
| if (c == quote) { |
| quote = '\0'; |
| break; |
| } |
| if (c == ' ' && quote == '\0') { |
| break; |
| } |
| } |
| if (quote != '\0') { |
| jio_fprintf(stderr, "ZoneInfo: unterminated time zone name in /etc/TIMEZONE\n"); |
| } |
| *s = '\0'; |
| tz = strdup(p); |
| break; |
| } |
| } |
| (void) fileclose(fp); |
| } |
| return tz; |
| } |
| |
| #define TIMEZONE_FMRI "svc:/system/timezone:default" |
| #define TIMEZONE_PG "timezone" |
| #define LOCALTIME_PROP "localtime" |
| |
| static void |
| cleanupScf(scf_handle_t *h, |
| scf_snapshot_t *snap, |
| scf_instance_t *inst, |
| scf_propertygroup_t *pg, |
| scf_property_t *prop, |
| scf_value_t *val, |
| char *buf) { |
| if (buf != NULL) { |
| free(buf); |
| } |
| if (snap != NULL) { |
| scf_snapshot_destroy(snap); |
| } |
| if (val != NULL) { |
| scf_value_destroy(val); |
| } |
| if (prop != NULL) { |
| scf_property_destroy(prop); |
| } |
| if (pg != NULL) { |
| scf_pg_destroy(pg); |
| } |
| if (inst != NULL) { |
| scf_instance_destroy(inst); |
| } |
| if (h != NULL) { |
| scf_handle_destroy(h); |
| } |
| } |
| |
| /* |
| * Returns a zone ID of Solaris when the TZ value is "localtime". |
| * First, it tries scf. If scf fails, it looks for the same file as |
| * /usr/share/lib/zoneinfo/localtime under /usr/share/lib/zoneinfo/. |
| */ |
| static char * |
| getSolarisDefaultZoneID() { |
| char *tz = NULL; |
| struct stat statbuf; |
| size_t size; |
| char *buf; |
| int fd; |
| int res; |
| /* scf specific variables */ |
| scf_handle_t *h = NULL; |
| scf_snapshot_t *snap = NULL; |
| scf_instance_t *inst = NULL; |
| scf_propertygroup_t *pg = NULL; |
| scf_property_t *prop = NULL; |
| scf_value_t *val = NULL; |
| |
| if ((h = scf_handle_create(SCF_VERSION)) != NULL |
| && scf_handle_bind(h) == 0 |
| && (inst = scf_instance_create(h)) != NULL |
| && (snap = scf_snapshot_create(h)) != NULL |
| && (pg = scf_pg_create(h)) != NULL |
| && (prop = scf_property_create(h)) != NULL |
| && (val = scf_value_create(h)) != NULL |
| && scf_handle_decode_fmri(h, TIMEZONE_FMRI, NULL, NULL, inst, |
| NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) == 0 |
| && scf_instance_get_snapshot(inst, "running", snap) == 0 |
| && scf_instance_get_pg_composed(inst, snap, TIMEZONE_PG, pg) == 0 |
| && scf_pg_get_property(pg, LOCALTIME_PROP, prop) == 0 |
| && scf_property_get_value(prop, val) == 0) { |
| ssize_t len; |
| |
| /* Gets the length of the zone ID string */ |
| len = scf_value_get_astring(val, NULL, 0); |
| if (len != -1) { |
| tz = malloc(++len); /* +1 for a null byte */ |
| if (tz != NULL && scf_value_get_astring(val, tz, len) != -1) { |
| cleanupScf(h, snap, inst, pg, prop, val, NULL); |
| return tz; |
| } |
| } |
| } |
| cleanupScf(h, snap, inst, pg, prop, val, tz); |
| |
| RESTARTABLE(stat(DEFAULT_ZONEINFO_FILE, &statbuf), res); |
| if (res == -1) { |
| return NULL; |
| } |
| size = (size_t) statbuf.st_size; |
| buf = malloc(size); |
| if (buf == NULL) { |
| return NULL; |
| } |
| RESTARTABLE(open(DEFAULT_ZONEINFO_FILE, O_RDONLY), fd); |
| if (fd == -1) { |
| free((void *) buf); |
| return NULL; |
| } |
| |
| RESTARTABLE(read(fd, buf, size), res); |
| if (res != (ssize_t) size) { |
| (void) close(fd); |
| free((void *) buf); |
| return NULL; |
| } |
| (void) close(fd); |
| tz = findZoneinfoFile(buf, size, ZONEINFO_DIR); |
| free((void *) buf); |
| return tz; |
| } |
| |
| #endif /* defined(__solaris__) */ |
| |
| #elif defined(_AIX) |
| |
| static char * |
| getPlatformTimeZoneID() |
| { |
| FILE *fp; |
| char *tz = NULL; |
| char *tz_key = "TZ="; |
| char line[256]; |
| size_t tz_key_len = strlen(tz_key); |
| |
| if ((fp = fopen(ETC_ENVIRONMENT_FILE, "r")) != NULL) { |
| while (fgets(line, sizeof(line), fp) != NULL) { |
| char *p = strchr(line, '\n'); |
| if (p != NULL) { |
| *p = '\0'; |
| } |
| if (0 == strncmp(line, tz_key, tz_key_len)) { |
| tz = strdup(line + tz_key_len); |
| break; |
| } |
| } |
| (void) fclose(fp); |
| } |
| |
| return tz; |
| } |
| |
| static char * |
| mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) { |
| FILE *tzmapf; |
| char mapfilename[PATH_MAX + 1]; |
| char line[256]; |
| int linecount = 0; |
| char *tz_buf = NULL; |
| char *temp_tz = NULL; |
| char *javatz = NULL; |
| size_t tz_len = 0; |
| |
| /* On AIX, the TZ environment variable may end with a comma |
| * followed by modifier fields. These are ignored here. */ |
| temp_tz = strchr(tz, ','); |
| tz_len = (temp_tz == NULL) ? strlen(tz) : temp_tz - tz; |
| tz_buf = (char *)malloc(tz_len + 1); |
| memcpy(tz_buf, tz, tz_len); |
| tz_buf[tz_len] = 0; |
| |
| /* Open tzmappings file, with buffer overrun check */ |
| if ((strlen(java_home_dir) + 15) > PATH_MAX) { |
| jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir); |
| goto tzerr; |
| } |
| strcpy(mapfilename, java_home_dir); |
| strcat(mapfilename, "/lib/tzmappings"); |
| if ((tzmapf = fopen(mapfilename, "r")) == NULL) { |
| jio_fprintf(stderr, "can't open %s\n", mapfilename); |
| goto tzerr; |
| } |
| |
| while (fgets(line, sizeof(line), tzmapf) != NULL) { |
| char *p = line; |
| char *sol = line; |
| char *java; |
| int result; |
| |
| linecount++; |
| /* |
| * Skip comments and blank lines |
| */ |
| if (*p == '#' || *p == '\n') { |
| continue; |
| } |
| |
| /* |
| * Get the first field, platform zone ID |
| */ |
| while (*p != '\0' && *p != '\t') { |
| p++; |
| } |
| if (*p == '\0') { |
| /* mapping table is broken! */ |
| jio_fprintf(stderr, "tzmappings: Illegal format at near line %d.\n", linecount); |
| break; |
| } |
| |
| *p++ = '\0'; |
| if ((result = strncmp(tz_buf, sol, tz_len)) == 0) { |
| /* |
| * If this is the current platform zone ID, |
| * take the Java time zone ID (2nd field). |
| */ |
| java = p; |
| while (*p != '\0' && *p != '\n') { |
| p++; |
| } |
| |
| if (*p == '\0') { |
| /* mapping table is broken! */ |
| jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", linecount); |
| break; |
| } |
| |
| *p = '\0'; |
| javatz = strdup(java); |
| break; |
| } else if (result < 0) { |
| break; |
| } |
| } |
| (void) fclose(tzmapf); |
| |
| tzerr: |
| if (tz_buf != NULL ) { |
| free((void *) tz_buf); |
| } |
| |
| if (javatz == NULL) { |
| return getGMTOffsetID(); |
| } |
| |
| return javatz; |
| } |
| |
| #endif /* defined(_AIX) */ |
| |
| /* |
| * findJavaTZ_md() maps platform time zone ID to Java time zone ID |
| * using <java_home>/lib/tzmappings. If the TZ value is not found, it |
| * trys some libc implementation dependent mappings. If it still |
| * can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm |
| * form. |
| */ |
| /*ARGSUSED1*/ |
| char * |
| findJavaTZ_md(const char *java_home_dir) |
| { |
| char *tz; |
| char *javatz = NULL; |
| char *freetz = NULL; |
| |
| tz = getenv("TZ"); |
| |
| if (tz == NULL || *tz == '\0') { |
| tz = getPlatformTimeZoneID(); |
| freetz = tz; |
| } |
| |
| if (tz != NULL) { |
| /* Ignore preceding ':' */ |
| if (*tz == ':') { |
| tz++; |
| } |
| #if defined(__linux__) |
| /* Ignore "posix/" prefix on Linux. */ |
| if (strncmp(tz, "posix/", 6) == 0) { |
| tz += 6; |
| } |
| #endif |
| |
| #if defined(_AIX) |
| /* On AIX do the platform to Java mapping. */ |
| javatz = mapPlatformToJavaTimezone(java_home_dir, tz); |
| if (freetz != NULL) { |
| free((void *) freetz); |
| } |
| #else |
| #if defined(__solaris__) |
| /* Solaris might use localtime, so handle it here. */ |
| if (strcmp(tz, "localtime") == 0) { |
| javatz = getSolarisDefaultZoneID(); |
| if (freetz != NULL) { |
| free((void *) freetz); |
| } |
| } else |
| #endif |
| if (freetz == NULL) { |
| /* strdup if we are still working on getenv result. */ |
| javatz = strdup(tz); |
| } else if (freetz != tz) { |
| /* strdup and free the old buffer, if we moved the pointer. */ |
| javatz = strdup(tz); |
| free((void *) freetz); |
| } else { |
| /* we are good if we already work on a freshly allocated buffer. */ |
| javatz = tz; |
| } |
| #endif |
| } |
| |
| return javatz; |
| } |
| |
| /** |
| * Returns a GMT-offset-based zone ID. (e.g., "GMT-08:00") |
| */ |
| |
| #if defined(MACOSX) |
| |
| char * |
| getGMTOffsetID() |
| { |
| time_t offset; |
| char sign, buf[32]; |
| struct tm local_tm; |
| time_t clock; |
| |
| clock = time(NULL); |
| if (localtime_r(&clock, &local_tm) == NULL) { |
| return strdup("GMT"); |
| } |
| offset = (time_t)local_tm.tm_gmtoff; |
| if (offset == 0) { |
| return strdup("GMT"); |
| } |
| if (offset > 0) { |
| sign = '+'; |
| } else { |
| offset = -offset; |
| sign = '-'; |
| } |
| sprintf(buf, (const char *)"GMT%c%02d:%02d", |
| sign, (int)(offset/3600), (int)((offset%3600)/60)); |
| return strdup(buf); |
| } |
| |
| #else |
| |
| char * |
| getGMTOffsetID() |
| { |
| time_t offset; |
| char sign, buf[32]; |
| #if defined(__solaris__) |
| struct tm localtm; |
| time_t currenttime; |
| |
| currenttime = time(NULL); |
| if (localtime_r(¤ttime, &localtm) == NULL) { |
| return strdup("GMT"); |
| } |
| |
| offset = localtm.tm_isdst ? altzone : timezone; |
| #else |
| offset = timezone; |
| #endif |
| |
| if (offset == 0) { |
| return strdup("GMT"); |
| } |
| |
| /* Note that the time offset direction is opposite. */ |
| if (offset > 0) { |
| sign = '-'; |
| } else { |
| offset = -offset; |
| sign = '+'; |
| } |
| sprintf(buf, (const char *)"GMT%c%02d:%02d", |
| sign, (int)(offset/3600), (int)((offset%3600)/60)); |
| return strdup(buf); |
| } |
| #endif /* MACOSX */ |