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