zdump.c revision 228342
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/contrib/tzcode/zic/zdump.c 228342 2011-12-08 02:40:46Z eadler $"; 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 progname=argv[0]; 264 INITIALIZE(cutlotime); 265 INITIALIZE(cuthitime); 266#if HAVE_GETTEXT 267 (void) setlocale(LC_MESSAGES, ""); 268#ifdef TZ_DOMAINDIR 269 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); 270#endif /* TEXTDOMAINDIR */ 271 (void) textdomain(TZ_DOMAIN); 272#endif /* HAVE_GETTEXT */ 273 for (i = 1; i < argc; ++i) 274 if (strcmp(argv[i], "--version") == 0) { 275 errx(EXIT_SUCCESS, "%s", elsieid); 276 } else if (strcmp(argv[i], "--help") == 0) { 277 usage(stdout, EXIT_SUCCESS); 278 } 279 vflag = 0; 280 cutarg = NULL; 281 while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') 282 if (c == 'v') 283 vflag = 1; 284 else cutarg = optarg; 285 if ((c != -1) || 286 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { 287 usage(stderr, EXIT_FAILURE); 288 } 289 if (vflag) { 290 if (cutarg != NULL) { 291 long lo; 292 long hi; 293 char dummy; 294 295 if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) { 296 cuthiyear = hi; 297 } else if (sscanf(cutarg, "%ld,%ld%c", 298 &lo, &hi, &dummy) == 2) { 299 cutloyear = lo; 300 cuthiyear = hi; 301 } else { 302(void) fprintf(stderr, _("%s: wild -c argument %s\n"), 303 progname, cutarg); 304 exit(EXIT_FAILURE); 305 } 306 } 307 setabsolutes(); 308 cutlotime = yeartot(cutloyear); 309 cuthitime = yeartot(cuthiyear); 310 } 311 (void) time(&now); 312 longest = 0; 313 for (i = optind; i < argc; ++i) 314 if (strlen(argv[i]) > longest) 315 longest = strlen(argv[i]); 316 { 317 register int from; 318 register int to; 319 320 for (i = 0; environ[i] != NULL; ++i) 321 continue; 322 fakeenv = (char **) malloc((size_t) ((i + 2) * 323 sizeof *fakeenv)); 324 if (fakeenv == NULL || 325 (fakeenv[0] = (char *) malloc((size_t) (longest + 326 4))) == NULL) 327 errx(EXIT_FAILURE, 328 _("malloc() failed")); 329 to = 0; 330 (void) strcpy(fakeenv[to++], "TZ="); 331 for (from = 0; environ[from] != NULL; ++from) 332 if (strncmp(environ[from], "TZ=", 3) != 0) 333 fakeenv[to++] = environ[from]; 334 fakeenv[to] = NULL; 335 environ = fakeenv; 336 } 337 for (i = optind; i < argc; ++i) { 338 static char buf[MAX_STRING_LENGTH]; 339 340 (void) strcpy(&fakeenv[0][3], argv[i]); 341 if (!vflag) { 342 show(argv[i], now, FALSE); 343 continue; 344 } 345 warned = FALSE; 346 t = absolute_min_time; 347 show(argv[i], t, TRUE); 348 t += SECSPERHOUR * HOURSPERDAY; 349 show(argv[i], t, TRUE); 350 if (t < cutlotime) 351 t = cutlotime; 352 tmp = my_localtime(&t); 353 if (tmp != NULL) { 354 tm = *tmp; 355 (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); 356 } 357 for ( ; ; ) { 358 if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12) 359 break; 360 newt = t + SECSPERHOUR * 12; 361 newtmp = localtime(&newt); 362 if (newtmp != NULL) 363 newtm = *newtmp; 364 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : 365 (delta(&newtm, &tm) != (newt - t) || 366 newtm.tm_isdst != tm.tm_isdst || 367 strcmp(abbr(&newtm), buf) != 0)) { 368 newt = hunt(argv[i], t, newt); 369 newtmp = localtime(&newt); 370 if (newtmp != NULL) { 371 newtm = *newtmp; 372 (void) strncpy(buf, 373 abbr(&newtm), 374 (sizeof buf) - 1); 375 } 376 } 377 t = newt; 378 tm = newtm; 379 tmp = newtmp; 380 } 381 t = absolute_max_time; 382 t -= SECSPERHOUR * HOURSPERDAY; 383 show(argv[i], t, TRUE); 384 t += SECSPERHOUR * HOURSPERDAY; 385 show(argv[i], t, TRUE); 386 } 387 if (fflush(stdout) || ferror(stdout)) 388 errx(EXIT_FAILURE, _("error writing standard output")); 389 exit(EXIT_SUCCESS); 390 /* If exit fails to exit... */ 391 return(EXIT_FAILURE); 392} 393 394static void 395setabsolutes(void) 396{ 397 if (0.5 == (time_t) 0.5) { 398 /* 399 ** time_t is floating. 400 */ 401 if (sizeof (time_t) == sizeof (float)) { 402 absolute_min_time = (time_t) -FLT_MAX; 403 absolute_max_time = (time_t) FLT_MAX; 404 } else if (sizeof (time_t) == sizeof (double)) { 405 absolute_min_time = (time_t) -DBL_MAX; 406 absolute_max_time = (time_t) DBL_MAX; 407 } else { 408 (void) fprintf(stderr, 409_("%s: use of -v on system with floating time_t other than float or double\n"), 410 progname); 411 exit(EXIT_FAILURE); 412 } 413 } else if (0 > (time_t) -1) { 414 /* 415 ** time_t is signed. Assume overflow wraps around. 416 */ 417 time_t t = 0; 418 time_t t1 = 1; 419 420 while (t < t1) { 421 t = t1; 422 t1 = 2 * t1 + 1; 423 } 424 425 absolute_max_time = t; 426 t = -t; 427 absolute_min_time = t - 1; 428 if (t < absolute_min_time) 429 absolute_min_time = t; 430 } else { 431 /* 432 ** time_t is unsigned. 433 */ 434 absolute_min_time = 0; 435 absolute_max_time = absolute_min_time - 1; 436 } 437} 438 439static time_t 440yeartot(y) 441const long y; 442{ 443 register long myy; 444 register long seconds; 445 register time_t t; 446 447 myy = EPOCH_YEAR; 448 t = 0; 449 while (myy != y) { 450 if (myy < y) { 451 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 452 ++myy; 453 if (t > absolute_max_time - seconds) { 454 t = absolute_max_time; 455 break; 456 } 457 t += seconds; 458 } else { 459 --myy; 460 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 461 if (t < absolute_min_time + seconds) { 462 t = absolute_min_time; 463 break; 464 } 465 t -= seconds; 466 } 467 } 468 return t; 469} 470 471static void 472usage(FILE *stream, int status) 473{ 474 fprintf(stream, 475_("usage: %s [--version] [-v] [--help] [-c [loyear,]hiyear] zonename ...\n\ 476\n\ 477Report bugs to tz@elsie.nci.nih.gov.\n"), progname); 478 exit(status); 479} 480 481static time_t 482hunt(char *name, time_t lot, time_t hit) 483{ 484 time_t t; 485 long diff; 486 struct tm lotm; 487 register struct tm * lotmp; 488 struct tm tm; 489 register struct tm * tmp; 490 char loab[MAX_STRING_LENGTH]; 491 492 lotmp = my_localtime(&lot); 493 if (lotmp != NULL) { 494 lotm = *lotmp; 495 (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); 496 } 497 for ( ; ; ) { 498 diff = (long) (hit - lot); 499 if (diff < 2) 500 break; 501 t = lot; 502 t += diff / 2; 503 if (t <= lot) 504 ++t; 505 else if (t >= hit) 506 --t; 507 tmp = my_localtime(&t); 508 if (tmp != NULL) 509 tm = *tmp; 510 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : 511 (delta(&tm, &lotm) == (t - lot) && 512 tm.tm_isdst == lotm.tm_isdst && 513 strcmp(abbr(&tm), loab) == 0)) { 514 lot = t; 515 lotm = tm; 516 lotmp = tmp; 517 } else hit = t; 518 } 519 show(name, lot, TRUE); 520 show(name, hit, TRUE); 521 return hit; 522} 523 524/* 525** Thanks to Paul Eggert for logic used in delta. 526*/ 527 528static long 529delta(newp, oldp) 530struct tm * newp; 531struct tm * oldp; 532{ 533 register long result; 534 register int tmy; 535 536 if (newp->tm_year < oldp->tm_year) 537 return -delta(oldp, newp); 538 result = 0; 539 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 540 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); 541 result += newp->tm_yday - oldp->tm_yday; 542 result *= HOURSPERDAY; 543 result += newp->tm_hour - oldp->tm_hour; 544 result *= MINSPERHOUR; 545 result += newp->tm_min - oldp->tm_min; 546 result *= SECSPERMIN; 547 result += newp->tm_sec - oldp->tm_sec; 548 return result; 549} 550 551static void 552show(char *zone, time_t t, int v) 553{ 554 register struct tm * tmp; 555 556 (void) printf("%-*s ", (int) longest, zone); 557 if (v) { 558 tmp = gmtime(&t); 559 if (tmp == NULL) { 560 (void) printf(tformat(), t); 561 } else { 562 dumptime(tmp); 563 (void) printf(" UTC"); 564 } 565 (void) printf(" = "); 566 } 567 tmp = my_localtime(&t); 568 dumptime(tmp); 569 if (tmp != NULL) { 570 if (*abbr(tmp) != '\0') 571 (void) printf(" %s", abbr(tmp)); 572 if (v) { 573 (void) printf(" isdst=%d", tmp->tm_isdst); 574#ifdef TM_GMTOFF 575 (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); 576#endif /* defined TM_GMTOFF */ 577 } 578 } 579 (void) printf("\n"); 580 if (tmp != NULL && *abbr(tmp) != '\0') 581 abbrok(abbr(tmp), zone); 582} 583 584static char * 585abbr(tmp) 586struct tm * tmp; 587{ 588 register char * result; 589 static char nada; 590 591 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) 592 return &nada; 593 result = tzname[tmp->tm_isdst]; 594 return (result == NULL) ? &nada : result; 595} 596 597/* 598** The code below can fail on certain theoretical systems; 599** it works on all known real-world systems as of 2004-12-30. 600*/ 601 602static const char * 603tformat(void) 604{ 605 if (0.5 == (time_t) 0.5) { /* floating */ 606 if (sizeof (time_t) > sizeof (double)) 607 return "%Lg"; 608 return "%g"; 609 } 610 if (0 > (time_t) -1) { /* signed */ 611 if (sizeof (time_t) > sizeof (long)) 612 return "%lld"; 613 if (sizeof (time_t) > sizeof (int)) 614 return "%ld"; 615 return "%d"; 616 } 617 if (sizeof (time_t) > sizeof (unsigned long)) 618 return "%llu"; 619 if (sizeof (time_t) > sizeof (unsigned int)) 620 return "%lu"; 621 return "%u"; 622} 623 624static void 625dumptime(timeptr) 626register const struct tm * timeptr; 627{ 628 static const char wday_name[][3] = { 629 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 630 }; 631 static const char mon_name[][3] = { 632 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 633 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 634 }; 635 register const char * wn; 636 register const char * mn; 637 register int lead; 638 register int trail; 639 640 if (timeptr == NULL) { 641 (void) printf("NULL"); 642 return; 643 } 644 /* 645 ** The packaged versions of localtime and gmtime never put out-of-range 646 ** values in tm_wday or tm_mon, but since this code might be compiled 647 ** with other (perhaps experimental) versions, paranoia is in order. 648 */ 649 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= 650 (int) (sizeof wday_name / sizeof wday_name[0])) 651 wn = "???"; 652 else wn = wday_name[timeptr->tm_wday]; 653 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= 654 (int) (sizeof mon_name / sizeof mon_name[0])) 655 mn = "???"; 656 else mn = mon_name[timeptr->tm_mon]; 657 (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", 658 wn, mn, 659 timeptr->tm_mday, timeptr->tm_hour, 660 timeptr->tm_min, timeptr->tm_sec); 661#define DIVISOR 10 662 trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; 663 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + 664 trail / DIVISOR; 665 trail %= DIVISOR; 666 if (trail < 0 && lead > 0) { 667 trail += DIVISOR; 668 --lead; 669 } else if (lead < 0 && trail > 0) { 670 trail -= DIVISOR; 671 ++lead; 672 } 673 if (lead == 0) 674 (void) printf("%d", trail); 675 else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); 676} 677