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