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 while (isalpha(*cp) || isdigit(*cp) || *cp == '-' || *cp == '+') 189 ++cp; 190 if (cp - abbrp < 3) 191 wp = _("has fewer than 3 characters"); 192 else if (cp - abbrp > 6) 193 wp = _("has more than 6 characters"); 194 else if (*cp) 195 wp = _("has characters other than ASCII alphanumerics, '-' or '+'"); 196 else 197 return; 198 fflush(stdout); 199 warnx(_("warning: zone \"%s\" abbreviation \"%s\" %s\n"), 200 zone, abbrp, wp); 201 warned = TRUE; 202 } 203 204 int 205 main(int argc, char *argv[]) 206 { 207 int i; 208 int vflag; 209 int Vflag; 210 char *cutarg; 211 char *cuttimes; 212 time_t cutlotime; 213 time_t cuthitime; 214 char **fakeenv; 215 time_t now; 216 time_t t; 217 time_t newt; 218 struct tm tm; 219 struct tm newtm; 220 struct tm *tmp; 221 struct tm *newtmp; 222 223 cutlotime = absolute_min_time; 224 cuthitime = absolute_max_time; 225 vflag = Vflag = 0; 226 cutarg = cuttimes = NULL; 227 for (;;) 228 switch (getopt(argc, argv, "c:t:vV")) { 229 case 'c': cutarg = optarg; break; 230 case 't': cuttimes = optarg; break; 231 case 'v': vflag = 1; break; 232 case 'V': Vflag = 1; break; 233 case -1: 234 if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) 235 goto arg_processing_done; 236 /* Fall through. */ 237 default: 238 usage(); 239 } 240 arg_processing_done:; 241 242 if (vflag | Vflag) { 243 intmax_t lo; 244 intmax_t hi; 245 char *loend, *hiend; 246 intmax_t cutloyear = ZDUMP_LO_YEAR; 247 intmax_t cuthiyear = ZDUMP_HI_YEAR; 248 if (cutarg != NULL) { 249 lo = strtoimax(cutarg, &loend, 10); 250 if (cutarg != loend && !*loend) { 251 hi = lo; 252 cuthiyear = hi; 253 } else if (cutarg != loend && *loend == ',' 254 && (hi = strtoimax(loend + 1, &hiend, 10), 255 loend + 1 != hiend && !*hiend)) { 256 cutloyear = lo; 257 cuthiyear = hi; 258 } else { 259 errx(EXIT_FAILURE, 260 _("wild -c argument %s\n"), 261 cutarg); 262 } 263 } 264 if (cutarg != NULL || cuttimes == NULL) { 265 cutlotime = yeartot(cutloyear); 266 cuthitime = yeartot(cuthiyear); 267 } 268 if (cuttimes != NULL) { 269 lo = strtoimax(cuttimes, &loend, 10); 270 if (cuttimes != loend && !*loend) { 271 hi = lo; 272 if (hi < cuthitime) { 273 if (hi < absolute_min_time) 274 hi = absolute_min_time; 275 cuthitime = hi; 276 } 277 } else if (cuttimes != loend && *loend == ',' 278 && (hi = strtoimax(loend + 1, &hiend, 10), 279 loend + 1 != hiend && !*hiend)) { 280 if (cutlotime < lo) { 281 if (absolute_max_time < lo) 282 lo = absolute_max_time; 283 cutlotime = lo; 284 } 285 if (hi < cuthitime) { 286 if (hi < absolute_min_time) 287 hi = absolute_min_time; 288 cuthitime = hi; 289 } 290 } else { 291 errx(EXIT_FAILURE, 292 _("wild -t argument %s\n"), 293 cuttimes); 294 } 295 } 296 } 297 time(&now); 298 longest = 0; 299 for (i = optind; i < argc; ++i) 300 if (strlen(argv[i]) > longest) 301 longest = strlen(argv[i]); 302 { 303 int from; 304 int to; 305 306 for (i = 0; environ[i] != NULL; ++i) 307 continue; 308 fakeenv = malloc((i + 2) * sizeof *fakeenv); 309 if (fakeenv == NULL 310 || (fakeenv[0] = malloc(longest + 4)) == NULL) { 311 errx(EXIT_FAILURE, 312 _("malloc() failed")); 313 } 314 to = 0; 315 strcpy(fakeenv[to++], "TZ="); 316 for (from = 0; environ[from] != NULL; ++from) 317 if (strncmp(environ[from], "TZ=", 3) != 0) 318 fakeenv[to++] = environ[from]; 319 fakeenv[to] = NULL; 320 environ = fakeenv; 321 } 322 for (i = optind; i < argc; ++i) { 323 static char buf[MAX_STRING_LENGTH]; 324 325 strcpy(&fakeenv[0][3], argv[i]); 326 if (! (vflag | Vflag)) { 327 show(argv[i], now, FALSE); 328 continue; 329 } 330 warned = FALSE; 331 t = absolute_min_time; 332 if (!Vflag) { 333 show(argv[i], t, TRUE); 334 t += SECSPERDAY; 335 show(argv[i], t, TRUE); 336 } 337 if (t < cutlotime) 338 t = cutlotime; 339 tmp = my_localtime(&t); 340 if (tmp != NULL) { 341 tm = *tmp; 342 strncpy(buf, abbr(&tm), (sizeof buf) - 1); 343 } 344 for ( ; ; ) { 345 newt = (t < absolute_max_time - SECSPERDAY / 2 346 ? t + SECSPERDAY / 2 347 : absolute_max_time); 348 if (cuthitime <= newt) 349 break; 350 newtmp = localtime(&newt); 351 if (newtmp != NULL) 352 newtm = *newtmp; 353 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : 354 (delta(&newtm, &tm) != (newt - t) || 355 newtm.tm_isdst != tm.tm_isdst || 356 strcmp(abbr(&newtm), buf) != 0)) { 357 newt = hunt(argv[i], t, newt); 358 newtmp = localtime(&newt); 359 if (newtmp != NULL) { 360 newtm = *newtmp; 361 strncpy(buf, 362 abbr(&newtm), 363 (sizeof buf) - 1); 364 } 365 } 366 t = newt; 367 tm = newtm; 368 tmp = newtmp; 369 } 370 if (!Vflag) { 371 t = absolute_max_time; 372 t -= SECSPERDAY; 373 show(argv[i], t, TRUE); 374 t += SECSPERDAY; 375 show(argv[i], t, TRUE); 376 } 377 } 378 if (fflush(stdout) || ferror(stdout)) 379 errx(EXIT_FAILURE, _("error writing standard output")); 380 exit(EXIT_SUCCESS); 381 /* If exit fails to exit... */ 382 return EXIT_FAILURE; 383 } 384 385 static void 386 usage(void) 387 { 388 fprintf(stderr, _("usage: zdump [-vV] [-c [loyear,]hiyear] [-t [lotime,]hitime] zonename ...\n")); 389 exit(EXIT_FAILURE); 390 } 391 392 static time_t 393 yeartot(const intmax_t y) 394 { 395 intmax_t myy, seconds, years; 396 time_t t; 397 398 myy = EPOCH_YEAR; 399 t = 0; 400 while (myy < y) { 401 if (SECSPER400YEARS_FITS && 400 <= y - myy) { 402 intmax_t diff400 = (y - myy) / 400; 403 if (INTMAX_MAX / SECSPER400YEARS < diff400) 404 return absolute_max_time; 405 seconds = diff400 * SECSPER400YEARS; 406 years = diff400 * 400; 407 } else { 408 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 409 years = 1; 410 } 411 myy += years; 412 if (t > absolute_max_time - seconds) 413 return absolute_max_time; 414 t += seconds; 415 } 416 while (y < myy) { 417 if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) { 418 intmax_t diff400 = (myy - y) / 400; 419 if (INTMAX_MAX / SECSPER400YEARS < diff400) 420 return absolute_min_time; 421 seconds = diff400 * SECSPER400YEARS; 422 years = diff400 * 400; 423 } else { 424 seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR; 425 years = 1; 426 } 427 myy -= years; 428 if (t < absolute_min_time + seconds) 429 return absolute_min_time; 430 t -= seconds; 431 } 432 return t; 433 } 434 435 static time_t 436 hunt(char *name, time_t lot, time_t hit) 437 { 438 time_t t; 439 struct tm lotm; 440 struct tm * lotmp; 441 struct tm tm; 442 struct tm * tmp; 443 char loab[MAX_STRING_LENGTH]; 444 445 lotmp = my_localtime(&lot); 446 if (lotmp != NULL) { 447 lotm = *lotmp; 448 strncpy(loab, abbr(&lotm), (sizeof loab) - 1); 449 } 450 for ( ; ; ) { 451 time_t diff = hit - lot; 452 if (diff < 2) 453 break; 454 t = lot; 455 t += diff / 2; 456 if (t <= lot) 457 ++t; 458 else if (t >= hit) 459 --t; 460 tmp = my_localtime(&t); 461 if (tmp != NULL) 462 tm = *tmp; 463 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : 464 (delta(&tm, &lotm) == (t - lot) && 465 tm.tm_isdst == lotm.tm_isdst && 466 strcmp(abbr(&tm), loab) == 0)) { 467 lot = t; 468 lotm = tm; 469 lotmp = tmp; 470 } else hit = t; 471 } 472 show(name, lot, TRUE); 473 show(name, hit, TRUE); 474 return hit; 475 } 476 477 /* 478 ** Thanks to Paul Eggert for logic used in delta. 479 */ 480 481 static intmax_t 482 delta(struct tm *newp, struct tm *oldp) 483 { 484 intmax_t result; 485 int tmy; 486 487 if (newp->tm_year < oldp->tm_year) 488 return -delta(oldp, newp); 489 result = 0; 490 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 491 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); 492 result += newp->tm_yday - oldp->tm_yday; 493 result *= HOURSPERDAY; 494 result += newp->tm_hour - oldp->tm_hour; 495 result *= MINSPERHOUR; 496 result += newp->tm_min - oldp->tm_min; 497 result *= SECSPERMIN; 498 result += newp->tm_sec - oldp->tm_sec; 499 return result; 500 } 501 502 static void 503 show(char *zone, time_t t, int v) 504 { 505 struct tm * tmp; 506 507 printf("%-*s ", (int) longest, zone); 508 if (v) { 509 tmp = gmtime(&t); 510 if (tmp == NULL) { 511 printf(tformat(), t); 512 } else { 513 dumptime(tmp); 514 printf(" UT"); 515 } 516 printf(" = "); 517 } 518 tmp = my_localtime(&t); 519 dumptime(tmp); 520 if (tmp != NULL) { 521 if (*abbr(tmp) != '\0') 522 printf(" %s", abbr(tmp)); 523 if (v) { 524 printf(" isdst=%d", tmp->tm_isdst); 525 printf(" gmtoff=%ld", tmp->TM_GMTOFF); 526 } 527 } 528 printf("\n"); 529 if (tmp != NULL && *abbr(tmp) != '\0') 530 abbrok(abbr(tmp), zone); 531 } 532 533 static char * 534 abbr(struct tm *tmp) 535 { 536 char * result; 537 static char nada; 538 539 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) 540 return &nada; 541 result = tzname[tmp->tm_isdst]; 542 return (result == NULL) ? &nada : result; 543 } 544 545 /* 546 ** The code below can fail on certain theoretical systems; 547 ** it works on all known real-world systems as of 2004-12-30. 548 */ 549 550 static const char * 551 tformat(void) 552 { 553 if (0 > (time_t) -1) { /* signed */ 554 if (sizeof (time_t) == sizeof (intmax_t)) 555 return "%"PRIdMAX; 556 if (sizeof (time_t) > sizeof (long)) 557 return "%lld"; 558 if (sizeof (time_t) > sizeof (int)) 559 return "%ld"; 560 return "%d"; 561 } 562 if (sizeof (time_t) == sizeof (uintmax_t)) 563 return "%"PRIuMAX; 564 if (sizeof (time_t) > sizeof (unsigned long)) 565 return "%llu"; 566 if (sizeof (time_t) > sizeof (unsigned int)) 567 return "%lu"; 568 return "%u"; 569 } 570 571 static void 572 dumptime(const struct tm *timeptr) 573 { 574 static const char wday_name[][3] = { 575 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 576 }; 577 static const char mon_name[][3] = { 578 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 579 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 580 }; 581 const char * wn; 582 const char * mn; 583 int lead; 584 int trail; 585 586 if (timeptr == NULL) { 587 printf("NULL"); 588 return; 589 } 590 /* 591 ** The packaged versions of localtime and gmtime never put out-of-range 592 ** values in tm_wday or tm_mon, but since this code might be compiled 593 ** with other (perhaps experimental) versions, paranoia is in order. 594 */ 595 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= 596 (int) (sizeof wday_name / sizeof wday_name[0])) 597 wn = "???"; 598 else wn = wday_name[timeptr->tm_wday]; 599 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= 600 (int) (sizeof mon_name / sizeof mon_name[0])) 601 mn = "???"; 602 else mn = mon_name[timeptr->tm_mon]; 603 printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", 604 wn, mn, 605 timeptr->tm_mday, timeptr->tm_hour, 606 timeptr->tm_min, timeptr->tm_sec); 607 #define DIVISOR 10 608 trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; 609 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + 610 trail / DIVISOR; 611 trail %= DIVISOR; 612 if (trail < 0 && lead > 0) { 613 trail += DIVISOR; 614 --lead; 615 } else if (lead < 0 && trail > 0) { 616 trail -= DIVISOR; 617 ++lead; 618 } 619 if (lead == 0) 620 printf("%d", trail); 621 else printf("%d%d", lead, ((trail < 0) ? -trail : trail)); 622 } 623