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