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