1static char elsieid[] = "@(#)zdump.c 8.3"; 2 3/* 4** This code has been made independent of the rest of the time 5** conversion package to increase confidence in the verification it provides. 6** You can use this code to help in verifying other implementations. 7*/ 8 9#include "stdio.h" /* for stdout, stderr, perror */ 10#include "string.h" /* for strcpy */ 11#include "sys/types.h" /* for time_t */ 12#include "time.h" /* for struct tm */ 13#include "stdlib.h" /* for exit, malloc, atoi */ 14#include "float.h" /* for FLT_MAX and DBL_MAX */ 15#include "ctype.h" /* for isalpha et al. */ 16#ifndef isascii 17#define isascii(x) 1 18#endif /* !defined isascii */ 19 20#ifndef ZDUMP_LO_YEAR 21#define ZDUMP_LO_YEAR (-500) 22#endif /* !defined ZDUMP_LO_YEAR */ 23 24#ifndef ZDUMP_HI_YEAR 25#define ZDUMP_HI_YEAR 2500 26#endif /* !defined ZDUMP_HI_YEAR */ 27 28#ifndef MAX_STRING_LENGTH 29#define MAX_STRING_LENGTH 1024 30#endif /* !defined MAX_STRING_LENGTH */ 31 32#ifndef TRUE 33#define TRUE 1 34#endif /* !defined TRUE */ 35 36#ifndef FALSE 37#define FALSE 0 38#endif /* !defined FALSE */ 39 40#ifndef EXIT_SUCCESS 41#define EXIT_SUCCESS 0 42#endif /* !defined EXIT_SUCCESS */ 43 44#ifndef EXIT_FAILURE 45#define EXIT_FAILURE 1 46#endif /* !defined EXIT_FAILURE */ 47 48#ifndef SECSPERMIN 49#define SECSPERMIN 60 50#endif /* !defined SECSPERMIN */ 51 52#ifndef MINSPERHOUR 53#define MINSPERHOUR 60 54#endif /* !defined MINSPERHOUR */ 55 56#ifndef SECSPERHOUR 57#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 58#endif /* !defined SECSPERHOUR */ 59 60#ifndef HOURSPERDAY 61#define HOURSPERDAY 24 62#endif /* !defined HOURSPERDAY */ 63 64#ifndef EPOCH_YEAR 65#define EPOCH_YEAR 1970 66#endif /* !defined EPOCH_YEAR */ 67 68#ifndef TM_YEAR_BASE 69#define TM_YEAR_BASE 1900 70#endif /* !defined TM_YEAR_BASE */ 71 72#ifndef DAYSPERNYEAR 73#define DAYSPERNYEAR 365 74#endif /* !defined DAYSPERNYEAR */ 75 76#ifndef isleap 77#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 78#endif /* !defined isleap */ 79 80#ifndef isleap_sum 81/* 82** See tzfile.h for details on isleap_sum. 83*/ 84#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) 85#endif /* !defined isleap_sum */ 86 87#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) 88#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) 89#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) 90 91#if HAVE_GETTEXT 92#include "locale.h" /* for setlocale */ 93#include "libintl.h" 94#endif /* HAVE_GETTEXT */ 95 96#ifndef GNUC_or_lint 97#ifdef lint 98#define GNUC_or_lint 99#else /* !defined lint */ 100#ifdef __GNUC__ 101#define GNUC_or_lint 102#endif /* defined __GNUC__ */ 103#endif /* !defined lint */ 104#endif /* !defined GNUC_or_lint */ 105 106#ifndef INITIALIZE 107#ifdef GNUC_or_lint 108#define INITIALIZE(x) ((x) = 0) 109#else /* !defined GNUC_or_lint */ 110#define INITIALIZE(x) 111#endif /* !defined GNUC_or_lint */ 112#endif /* !defined INITIALIZE */ 113 114/* 115** For the benefit of GNU folk... 116** `_(MSGID)' uses the current locale's message library string for MSGID. 117** The default is to use gettext if available, and use MSGID otherwise. 118*/ 119 120#ifndef _ 121#if HAVE_GETTEXT 122#define _(msgid) gettext(msgid) 123#else /* !HAVE_GETTEXT */ 124#define _(msgid) msgid 125#endif /* !HAVE_GETTEXT */ 126#endif /* !defined _ */ 127 128#ifndef TZ_DOMAIN 129#define TZ_DOMAIN "tz" 130#endif /* !defined TZ_DOMAIN */ 131 132#ifndef P 133#define P(x) x 134#endif /* !defined P */ 135 136extern char ** environ; 137extern int getopt P((int argc, char * const argv[], 138 const char * options)); 139extern char * optarg; 140extern int optind; 141extern char * tzname[2]; 142 143static time_t absolute_min_time; 144static time_t absolute_max_time; 145static size_t longest; 146static char * progname; 147static int warned; 148 149static char * abbr P((struct tm * tmp)); 150static void abbrok P((const char * abbrp, const char * zone)); 151static long delta P((struct tm * newp, struct tm * oldp)); 152static void dumptime P((const struct tm * tmp)); 153static time_t hunt P((char * name, time_t lot, time_t hit)); 154static void setabsolutes P((void)); 155static void show P((char * zone, time_t t, int v)); 156static const char * tformat P((void)); 157static time_t yeartot P((long y)); 158 159#ifndef TYPECHECK 160#define my_localtime localtime 161#else /* !defined TYPECHECK */ 162static struct tm * 163my_localtime(tp) 164time_t * tp; 165{ 166 register struct tm * tmp; 167 168 tmp = localtime(tp); 169 if (tp != NULL && tmp != NULL) { 170 struct tm tm; 171 register time_t t; 172 173 tm = *tmp; 174 t = mktime(&tm); 175 if (t - *tp >= 1 || *tp - t >= 1) { 176 (void) fflush(stdout); 177 (void) fprintf(stderr, "\n%s: ", progname); 178 (void) fprintf(stderr, tformat(), *tp); 179 (void) fprintf(stderr, " ->"); 180 (void) fprintf(stderr, " year=%d", tmp->tm_year); 181 (void) fprintf(stderr, " mon=%d", tmp->tm_mon); 182 (void) fprintf(stderr, " mday=%d", tmp->tm_mday); 183 (void) fprintf(stderr, " hour=%d", tmp->tm_hour); 184 (void) fprintf(stderr, " min=%d", tmp->tm_min); 185 (void) fprintf(stderr, " sec=%d", tmp->tm_sec); 186 (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst); 187 (void) fprintf(stderr, " -> "); 188 (void) fprintf(stderr, tformat(), t); 189 (void) fprintf(stderr, "\n"); 190 } 191 } 192 return tmp; 193} 194#endif /* !defined TYPECHECK */ 195 196static void 197abbrok(abbrp, zone) 198const char * const abbrp; 199const char * const zone; 200{ 201 register const char * cp; 202 register char * wp; 203 204 if (warned) 205 return; 206 cp = abbrp; 207 wp = NULL; 208 while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp)) 209 ++cp; 210 if (cp - abbrp == 0) 211 wp = _("lacks alphabetic at start"); 212 else if (cp - abbrp < 3) 213 wp = _("has fewer than 3 alphabetics"); 214 else if (cp - abbrp > 6) 215 wp = _("has more than 6 alphabetics"); 216 if (wp == NULL && (*cp == '+' || *cp == '-')) { 217 ++cp; 218 if (isascii((unsigned char) *cp) && 219 isdigit((unsigned char) *cp)) 220 if (*cp++ == '1' && *cp >= '0' && *cp <= '4') 221 ++cp; 222 if (*cp != '\0') 223 wp = _("differs from POSIX standard"); 224 } 225 if (wp == NULL) 226 return; 227 (void) fflush(stdout); 228 (void) fprintf(stderr, 229 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), 230 progname, zone, abbrp, wp); 231 warned = TRUE; 232} 233 234int 235main(argc, argv) 236int argc; 237char * argv[]; 238{ 239 register int i; 240 register int c; 241 register int vflag; 242 register char * cutarg; 243 register long cutloyear = ZDUMP_LO_YEAR; 244 register long cuthiyear = ZDUMP_HI_YEAR; 245 register time_t cutlotime; 246 register time_t cuthitime; 247 register char ** fakeenv; 248 time_t now; 249 time_t t; 250 time_t newt; 251 struct tm tm; 252 struct tm newtm; 253 register struct tm * tmp; 254 register struct tm * newtmp; 255 256 INITIALIZE(cutlotime); 257 INITIALIZE(cuthitime); 258#if HAVE_GETTEXT 259 (void) setlocale(LC_ALL, ""); 260#ifdef TZ_DOMAINDIR 261 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); 262#endif /* defined TEXTDOMAINDIR */ 263 (void) textdomain(TZ_DOMAIN); 264#endif /* HAVE_GETTEXT */ 265 progname = argv[0]; 266 for (i = 1; i < argc; ++i) 267 if (strcmp(argv[i], "--version") == 0) { 268 (void) printf("%s\n", elsieid); 269 exit(EXIT_SUCCESS); 270 } 271 vflag = 0; 272 cutarg = NULL; 273 while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') 274 if (c == 'v') 275 vflag = 1; 276 else cutarg = optarg; 277 if ((c != EOF && c != -1) || 278 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { 279 (void) fprintf(stderr, 280_("%s: usage is %s [ --version ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n"), 281 progname, progname); 282 exit(EXIT_FAILURE); 283 } 284 if (vflag) { 285 if (cutarg != NULL) { 286 long lo; 287 long hi; 288 char dummy; 289 290 if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) { 291 cuthiyear = hi; 292 } else if (sscanf(cutarg, "%ld,%ld%c", 293 &lo, &hi, &dummy) == 2) { 294 cutloyear = lo; 295 cuthiyear = hi; 296 } else { 297(void) fprintf(stderr, _("%s: wild -c argument %s\n"), 298 progname, cutarg); 299 exit(EXIT_FAILURE); 300 } 301 } 302 setabsolutes(); 303 cutlotime = yeartot(cutloyear); 304 cuthitime = yeartot(cuthiyear); 305 } 306 (void) time(&now); 307 longest = 0; 308 for (i = optind; i < argc; ++i) 309 if (strlen(argv[i]) > longest) 310 longest = strlen(argv[i]); 311 { 312 register int from; 313 register int to; 314 315 for (i = 0; environ[i] != NULL; ++i) 316 continue; 317 fakeenv = (char **) malloc((size_t) ((i + 2) * 318 sizeof *fakeenv)); 319 if (fakeenv == NULL || 320 (fakeenv[0] = (char *) malloc(longest + 4)) == NULL) { 321 (void) perror(progname); 322 exit(EXIT_FAILURE); 323 } 324 to = 0; 325 (void) strcpy(fakeenv[to++], "TZ="); 326 for (from = 0; environ[from] != NULL; ++from) 327 if (strncmp(environ[from], "TZ=", 3) != 0) 328 fakeenv[to++] = environ[from]; 329 fakeenv[to] = NULL; 330 environ = fakeenv; 331 } 332 for (i = optind; i < argc; ++i) { 333 static char buf[MAX_STRING_LENGTH]; 334 335 (void) strcpy(&fakeenv[0][3], argv[i]); 336 if (!vflag) { 337 show(argv[i], now, FALSE); 338 continue; 339 } 340 warned = FALSE; 341 t = absolute_min_time; 342 show(argv[i], t, TRUE); 343 t += SECSPERHOUR * HOURSPERDAY; 344 show(argv[i], t, TRUE); 345 if (t < cutlotime) 346 t = cutlotime; 347 tmp = my_localtime(&t); 348 if (tmp != NULL) { 349 tm = *tmp; 350 (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); 351 } 352 for ( ; ; ) { 353 if (t >= cuthitime) 354 break; 355 newt = t + SECSPERHOUR * 12; 356 if (newt >= cuthitime) 357 break; 358 if (newt <= t) 359 break; 360 newtmp = localtime(&newt); 361 if (newtmp != NULL) 362 newtm = *newtmp; 363 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : 364 (delta(&newtm, &tm) != (newt - t) || 365 newtm.tm_isdst != tm.tm_isdst || 366 strcmp(abbr(&newtm), buf) != 0)) { 367 newt = hunt(argv[i], t, newt); 368 newtmp = localtime(&newt); 369 if (newtmp != NULL) { 370 newtm = *newtmp; 371 (void) strncpy(buf, 372 abbr(&newtm), 373 (sizeof buf) - 1); 374 } 375 } 376 t = newt; 377 tm = newtm; 378 tmp = newtmp; 379 } 380 t = absolute_max_time; 381 t -= SECSPERHOUR * HOURSPERDAY; 382 show(argv[i], t, TRUE); 383 t += SECSPERHOUR * HOURSPERDAY; 384 show(argv[i], t, TRUE); 385 } 386 if (fflush(stdout) || ferror(stdout)) { 387 (void) fprintf(stderr, "%s: ", progname); 388 (void) perror(_("Error writing to standard output")); 389 exit(EXIT_FAILURE); 390 } 391 exit(EXIT_SUCCESS); 392 /* If exit fails to exit... */ 393 return EXIT_FAILURE; 394} 395 396static void 397setabsolutes() 398{ 399 if (0.5 == (time_t) 0.5) { 400 /* 401 ** time_t is floating. 402 */ 403 if (sizeof (time_t) == sizeof (float)) { 404 absolute_min_time = (time_t) -FLT_MAX; 405 absolute_max_time = (time_t) FLT_MAX; 406 } else if (sizeof (time_t) == sizeof (double)) { 407 absolute_min_time = (time_t) -DBL_MAX; 408 absolute_max_time = (time_t) DBL_MAX; 409 } else { 410 (void) fprintf(stderr, 411_("%s: use of -v on system with floating time_t other than float or double\n"), 412 progname); 413 exit(EXIT_FAILURE); 414 } 415 } else if (0 > (time_t) -1) { 416 /* 417 ** time_t is signed. Assume overflow wraps around. 418 */ 419 time_t t = 0; 420 time_t t1 = 1; 421 422 while (t < t1) { 423 t = t1; 424 t1 = 2 * t1 + 1; 425 } 426 427 absolute_max_time = t; 428 t = -t; 429 absolute_min_time = t - 1; 430 if (t < absolute_min_time) 431 absolute_min_time = t; 432 } else { 433 /* 434 ** time_t is unsigned. 435 */ 436 absolute_min_time = 0; 437 absolute_max_time = absolute_min_time - 1; 438 } 439} 440 441static time_t 442yeartot(y) 443const long y; 444{ 445 register long myy; 446 register long seconds; 447 register time_t t; 448 449 myy = EPOCH_YEAR; 450 t = 0; 451 while (myy != y) { 452 if (myy < y) { 453 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 454 ++myy; 455 if (t > absolute_max_time - seconds) { 456 t = absolute_max_time; 457 break; 458 } 459 t += seconds; 460 } else { 461 --myy; 462 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 463 if (t < absolute_min_time + seconds) { 464 t = absolute_min_time; 465 break; 466 } 467 t -= seconds; 468 } 469 } 470 return t; 471} 472 473static time_t 474hunt(char *name, time_t lot, time_t hit) 475{ 476 time_t t; 477 long diff; 478 struct tm lotm; 479 register struct tm * lotmp; 480 struct tm tm; 481 register struct tm * tmp; 482 char loab[MAX_STRING_LENGTH]; 483 484 lotmp = my_localtime(&lot); 485 if (lotmp != NULL) { 486 lotm = *lotmp; 487 (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); 488 } 489 for ( ; ; ) { 490 diff = (long) (hit - lot); 491 if (diff < 2) 492 break; 493 t = lot; 494 t += diff / 2; 495 if (t <= lot) 496 ++t; 497 else if (t >= hit) 498 --t; 499 tmp = my_localtime(&t); 500 if (tmp != NULL) 501 tm = *tmp; 502 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : 503 (delta(&tm, &lotm) == (t - lot) && 504 tm.tm_isdst == lotm.tm_isdst && 505 strcmp(abbr(&tm), loab) == 0)) { 506 lot = t; 507 lotm = tm; 508 lotmp = tmp; 509 } else hit = t; 510 } 511 show(name, lot, TRUE); 512 show(name, hit, TRUE); 513 return hit; 514} 515 516/* 517** Thanks to Paul Eggert for logic used in delta. 518*/ 519 520static long 521delta(newp, oldp) 522struct tm * newp; 523struct tm * oldp; 524{ 525 register long result; 526 register int tmy; 527 528 if (newp->tm_year < oldp->tm_year) 529 return -delta(oldp, newp); 530 result = 0; 531 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 532 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); 533 result += newp->tm_yday - oldp->tm_yday; 534 result *= HOURSPERDAY; 535 result += newp->tm_hour - oldp->tm_hour; 536 result *= MINSPERHOUR; 537 result += newp->tm_min - oldp->tm_min; 538 result *= SECSPERMIN; 539 result += newp->tm_sec - oldp->tm_sec; 540 return result; 541} 542 543static void 544show(char *zone, time_t t, int v) 545{ 546 register struct tm * tmp; 547 548 (void) printf("%-*s ", (int) longest, zone); 549 if (v) { 550 tmp = gmtime(&t); 551 if (tmp == NULL) { 552 (void) printf(tformat(), t); 553 } else { 554 dumptime(tmp); 555 (void) printf(" UTC"); 556 } 557 (void) printf(" = "); 558 } 559 tmp = my_localtime(&t); 560 dumptime(tmp); 561 if (tmp != NULL) { 562 if (*abbr(tmp) != '\0') 563 (void) printf(" %s", abbr(tmp)); 564 if (v) { 565 (void) printf(" isdst=%d", tmp->tm_isdst); 566#ifdef TM_GMTOFF 567 (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); 568#endif /* defined TM_GMTOFF */ 569 } 570 } 571 (void) printf("\n"); 572 if (tmp != NULL && *abbr(tmp) != '\0') 573 abbrok(abbr(tmp), zone); 574} 575 576static char * 577abbr(tmp) 578struct tm * tmp; 579{ 580 register char * result; 581 static char nada; 582 583 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) 584 return &nada; 585 result = tzname[tmp->tm_isdst]; 586 return (result == NULL) ? &nada : result; 587} 588 589/* 590** The code below can fail on certain theoretical systems; 591** it works on all known real-world systems as of 2004-12-30. 592*/ 593 594static const char * 595tformat() 596{ 597 if (0.5 == (time_t) 0.5) { /* floating */ 598 if (sizeof (time_t) > sizeof (double)) 599 return "%Lg"; 600 return "%g"; 601 } 602 if (0 > (time_t) -1) { /* signed */ 603 if (sizeof (time_t) > sizeof (long)) 604 return "%lld"; 605 if (sizeof (time_t) > sizeof (int)) 606 return "%ld"; 607 return "%d"; 608 } 609 if (sizeof (time_t) > sizeof (unsigned long)) 610 return "%llu"; 611 if (sizeof (time_t) > sizeof (unsigned int)) 612 return "%lu"; 613 return "%u"; 614} 615 616static void 617dumptime(timeptr) 618register const struct tm * timeptr; 619{ 620 static const char wday_name[][3] = { 621 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 622 }; 623 static const char mon_name[][3] = { 624 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 625 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 626 }; 627 register const char * wn; 628 register const char * mn; 629 register int lead; 630 register int trail; 631 632 if (timeptr == NULL) { 633 (void) printf("NULL"); 634 return; 635 } 636 /* 637 ** The packaged versions of localtime and gmtime never put out-of-range 638 ** values in tm_wday or tm_mon, but since this code might be compiled 639 ** with other (perhaps experimental) versions, paranoia is in order. 640 */ 641 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= 642 (int) (sizeof wday_name / sizeof wday_name[0])) 643 wn = "???"; 644 else wn = wday_name[timeptr->tm_wday]; 645 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= 646 (int) (sizeof mon_name / sizeof mon_name[0])) 647 mn = "???"; 648 else mn = mon_name[timeptr->tm_mon]; 649 (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", 650 wn, mn, 651 timeptr->tm_mday, timeptr->tm_hour, 652 timeptr->tm_min, timeptr->tm_sec); 653#define DIVISOR 10 654 trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; 655 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + 656 trail / DIVISOR; 657 trail %= DIVISOR; 658 if (trail < 0 && lead > 0) { 659 trail += DIVISOR; 660 --lead; 661 } else if (lead < 0 && trail > 0) { 662 trail -= DIVISOR; 663 ++lead; 664 } 665 if (lead == 0) 666 (void) printf("%d", trail); 667 else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); 668} 669