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(¤ttime, &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 */