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