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: stable/11/contrib/tzcode/zic/zdump.c 307358 2016-10-15 12:37:57Z bapt $"; 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) && 216 (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+')) 217 ++cp; 218 if (cp - abbrp < 3) 219 wp = _("has fewer than 3 characters"); 220 else if (cp - abbrp > 6) 221 wp = _("has more than 6 characters"); 222 else if (*cp) 223 wp = "has characters other than ASCII alphanumerics, '-' or '+'"; 224 else 225 return; 226 (void) fflush(stdout); 227 (void) fprintf(stderr, 228 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), 229 progname, zone, abbrp, wp); 230 warned = TRUE; 231} 232 233int 234main(argc, argv) 235int argc; 236char * argv[]; 237{ 238 register int i; 239 register int c; 240 register int vflag; 241 register char * cutarg; 242 register long cutloyear = ZDUMP_LO_YEAR; 243 register long cuthiyear = ZDUMP_HI_YEAR; 244 register time_t cutlotime; 245 register time_t cuthitime; 246 register char ** fakeenv; 247 time_t now; 248 time_t t; 249 time_t newt; 250 struct tm tm; 251 struct tm newtm; 252 register struct tm * tmp; 253 register struct tm * newtmp; 254 255 progname=argv[0]; 256 INITIALIZE(cutlotime); 257 INITIALIZE(cuthitime); 258#if HAVE_GETTEXT 259 (void) setlocale(LC_MESSAGES, ""); 260#ifdef TZ_DOMAINDIR 261 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); 262#endif /* TEXTDOMAINDIR */ 263 (void) textdomain(TZ_DOMAIN); 264#endif /* HAVE_GETTEXT */ 265 for (i = 1; i < argc; ++i) 266 if (strcmp(argv[i], "--version") == 0) { 267 errx(EXIT_SUCCESS, "%s", elsieid); 268 } else if (strcmp(argv[i], "--help") == 0) { 269 usage(stdout, 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 != -1) || 278 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { 279 usage(stderr, EXIT_FAILURE); 280 } 281 if (vflag) { 282 if (cutarg != NULL) { 283 long lo; 284 long hi; 285 char dummy; 286 287 if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) { 288 cuthiyear = hi; 289 } else if (sscanf(cutarg, "%ld,%ld%c", 290 &lo, &hi, &dummy) == 2) { 291 cutloyear = lo; 292 cuthiyear = hi; 293 } else { 294(void) fprintf(stderr, _("%s: wild -c argument %s\n"), 295 progname, cutarg); 296 exit(EXIT_FAILURE); 297 } 298 } 299 setabsolutes(); 300 cutlotime = yeartot(cutloyear); 301 cuthitime = yeartot(cuthiyear); 302 } 303 (void) time(&now); 304 longest = 0; 305 for (i = optind; i < argc; ++i) 306 if (strlen(argv[i]) > longest) 307 longest = strlen(argv[i]); 308 { 309 register int from; 310 register int to; 311 312 for (i = 0; environ[i] != NULL; ++i) 313 continue; 314 fakeenv = (char **) malloc((size_t) ((i + 2) * 315 sizeof *fakeenv)); 316 if (fakeenv == NULL || 317 (fakeenv[0] = (char *) malloc((size_t) (longest + 318 4))) == NULL) 319 errx(EXIT_FAILURE, 320 _("malloc() failed")); 321 to = 0; 322 (void) strcpy(fakeenv[to++], "TZ="); 323 for (from = 0; environ[from] != NULL; ++from) 324 if (strncmp(environ[from], "TZ=", 3) != 0) 325 fakeenv[to++] = environ[from]; 326 fakeenv[to] = NULL; 327 environ = fakeenv; 328 } 329 for (i = optind; i < argc; ++i) { 330 static char buf[MAX_STRING_LENGTH]; 331 332 (void) strcpy(&fakeenv[0][3], argv[i]); 333 if (!vflag) { 334 show(argv[i], now, FALSE); 335 continue; 336 } 337 warned = FALSE; 338 t = absolute_min_time; 339 show(argv[i], t, TRUE); 340 t += SECSPERHOUR * HOURSPERDAY; 341 show(argv[i], t, TRUE); 342 if (t < cutlotime) 343 t = cutlotime; 344 tmp = my_localtime(&t); 345 if (tmp != NULL) { 346 tm = *tmp; 347 (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); 348 } 349 for ( ; ; ) { 350 if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12) 351 break; 352 newt = t + SECSPERHOUR * 12; 353 newtmp = localtime(&newt); 354 if (newtmp != NULL) 355 newtm = *newtmp; 356 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : 357 (delta(&newtm, &tm) != (newt - t) || 358 newtm.tm_isdst != tm.tm_isdst || 359 strcmp(abbr(&newtm), buf) != 0)) { 360 newt = hunt(argv[i], t, newt); 361 newtmp = localtime(&newt); 362 if (newtmp != NULL) { 363 newtm = *newtmp; 364 (void) strncpy(buf, 365 abbr(&newtm), 366 (sizeof buf) - 1); 367 } 368 } 369 t = newt; 370 tm = newtm; 371 tmp = newtmp; 372 } 373 t = absolute_max_time; 374 t -= SECSPERHOUR * HOURSPERDAY; 375 show(argv[i], t, TRUE); 376 t += SECSPERHOUR * HOURSPERDAY; 377 show(argv[i], t, TRUE); 378 } 379 if (fflush(stdout) || ferror(stdout)) 380 errx(EXIT_FAILURE, _("error writing standard output")); 381 exit(EXIT_SUCCESS); 382 /* If exit fails to exit... */ 383 return(EXIT_FAILURE); 384} 385 386static void 387setabsolutes(void) 388{ 389 if (0.5 == (time_t) 0.5) { 390 /* 391 ** time_t is floating. 392 */ 393 if (sizeof (time_t) == sizeof (float)) { 394 absolute_min_time = (time_t) -FLT_MAX; 395 absolute_max_time = (time_t) FLT_MAX; 396 } else if (sizeof (time_t) == sizeof (double)) { 397 absolute_min_time = (time_t) -DBL_MAX; 398 absolute_max_time = (time_t) DBL_MAX; 399 } else { 400 (void) fprintf(stderr, 401_("%s: use of -v on system with floating time_t other than float or double\n"), 402 progname); 403 exit(EXIT_FAILURE); 404 } 405 } else if (0 > (time_t) -1) { 406 /* 407 ** time_t is signed. Assume overflow wraps around. 408 */ 409 time_t t = 0; 410 time_t t1 = 1; 411 412 while (t < t1) { 413 t = t1; 414 t1 = 2 * t1 + 1; 415 } 416 417 absolute_max_time = t; 418 t = -t; 419 absolute_min_time = t - 1; 420 if (t < absolute_min_time) 421 absolute_min_time = t; 422 } else { 423 /* 424 ** time_t is unsigned. 425 */ 426 absolute_min_time = 0; 427 absolute_max_time = absolute_min_time - 1; 428 } 429} 430 431static time_t 432yeartot(y) 433const long y; 434{ 435 register long myy; 436 register long seconds; 437 register time_t t; 438 439 myy = EPOCH_YEAR; 440 t = 0; 441 while (myy != y) { 442 if (myy < y) { 443 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 444 ++myy; 445 if (t > absolute_max_time - seconds) { 446 t = absolute_max_time; 447 break; 448 } 449 t += seconds; 450 } else { 451 --myy; 452 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 453 if (t < absolute_min_time + seconds) { 454 t = absolute_min_time; 455 break; 456 } 457 t -= seconds; 458 } 459 } 460 return t; 461} 462 463static void 464usage(FILE *stream, int status) 465{ 466 fprintf(stream, 467_("usage: %s [--version] [-v] [--help] [-c [loyear,]hiyear] zonename ...\n\ 468\n\ 469Report bugs to tz@elsie.nci.nih.gov.\n"), progname); 470 exit(status); 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(void) 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