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