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