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