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