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 <float.h> /* for FLT_MAX and DBL_MAX */ 13 #include <stdio.h> /* for stdout, stderr */ 14 #include <stdlib.h> /* for exit, malloc, atoi */ 15 #include <string.h> /* for strcpy */ 16 #include <sys/types.h> /* for time_t */ 17 #include <time.h> /* for struct tm */ 18 #include <unistd.h> 19 20 #ifndef ZDUMP_LO_YEAR 21 #define ZDUMP_LO_YEAR (-500) 22 #endif /* !defined ZDUMP_LO_YEAR */ 23 24 #ifndef ZDUMP_HI_YEAR 25 #define ZDUMP_HI_YEAR 2500 26 #endif /* !defined ZDUMP_HI_YEAR */ 27 28 #ifndef MAX_STRING_LENGTH 29 #define MAX_STRING_LENGTH 1024 30 #endif /* !defined MAX_STRING_LENGTH */ 31 32 #ifndef TRUE 33 #define TRUE 1 34 #endif /* !defined TRUE */ 35 36 #ifndef FALSE 37 #define FALSE 0 38 #endif /* !defined FALSE */ 39 40 #ifndef EXIT_SUCCESS 41 #define EXIT_SUCCESS 0 42 #endif /* !defined EXIT_SUCCESS */ 43 44 #ifndef EXIT_FAILURE 45 #define EXIT_FAILURE 1 46 #endif /* !defined EXIT_FAILURE */ 47 48 #ifndef SECSPERMIN 49 #define SECSPERMIN 60 50 #endif /* !defined SECSPERMIN */ 51 52 #ifndef MINSPERHOUR 53 #define MINSPERHOUR 60 54 #endif /* !defined MINSPERHOUR */ 55 56 #ifndef SECSPERHOUR 57 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 58 #endif /* !defined SECSPERHOUR */ 59 60 #ifndef HOURSPERDAY 61 #define HOURSPERDAY 24 62 #endif /* !defined HOURSPERDAY */ 63 64 #ifndef EPOCH_YEAR 65 #define EPOCH_YEAR 1970 66 #endif /* !defined EPOCH_YEAR */ 67 68 #ifndef TM_YEAR_BASE 69 #define TM_YEAR_BASE 1900 70 #endif /* !defined TM_YEAR_BASE */ 71 72 #ifndef DAYSPERNYEAR 73 #define DAYSPERNYEAR 365 74 #endif /* !defined DAYSPERNYEAR */ 75 76 #ifndef isleap 77 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 78 #endif /* !defined isleap */ 79 80 #ifndef isleap_sum 81 /* 82 ** See tzfile.h for details on isleap_sum. 83 */ 84 #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) 85 #endif /* !defined isleap_sum */ 86 87 #define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) 88 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) 89 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) 90 91 #ifndef GNUC_or_lint 92 #ifdef lint 93 #define GNUC_or_lint 94 #else /* !defined lint */ 95 #ifdef __GNUC__ 96 #define GNUC_or_lint 97 #endif /* defined __GNUC__ */ 98 #endif /* !defined lint */ 99 #endif /* !defined GNUC_or_lint */ 100 101 #ifndef INITIALIZE 102 #ifdef GNUC_or_lint 103 #define INITIALIZE(x) ((x) = 0) 104 #else /* !defined GNUC_or_lint */ 105 #define INITIALIZE(x) 106 #endif /* !defined GNUC_or_lint */ 107 #endif /* !defined INITIALIZE */ 108 109 /* 110 ** For the benefit of GNU folk... 111 ** `_(MSGID)' uses the current locale's message library string for MSGID. 112 ** The default is to use gettext if available, and use MSGID otherwise. 113 */ 114 115 #ifndef _ 116 #define _(msgid) msgid 117 #endif /* !defined _ */ 118 119 #ifndef TZ_DOMAIN 120 #define TZ_DOMAIN "tz" 121 #endif /* !defined TZ_DOMAIN */ 122 123 extern char ** environ; 124 extern char * tzname[2]; 125 126 static time_t absolute_min_time; 127 static time_t absolute_max_time; 128 static size_t longest; 129 static int warned; 130 131 static char *abbr(struct tm *tmp); 132 static void abbrok(const char *abbrp, const char *zone); 133 static long delta(struct tm *newp, struct tm *oldp); 134 static void dumptime(const struct tm *tmp); 135 static time_t hunt(char *name, time_t lot, time_t hit); 136 static void setabsolutes(void); 137 static void show(char *zone, time_t t, int v); 138 static const char *tformat(void); 139 static time_t yeartot(long y); 140 static void usage(void); 141 142 #ifndef TYPECHECK 143 #define my_localtime localtime 144 #else /* !defined TYPECHECK */ 145 static struct tm * 146 my_localtime(time_t *tp) 147 { 148 struct tm * tmp; 149 150 tmp = localtime(tp); 151 if (tp != NULL && tmp != NULL) { 152 struct tm tm; 153 time_t t; 154 155 tm = *tmp; 156 t = mktime(&tm); 157 if (t - *tp >= 1 || *tp - t >= 1) { 158 fflush(stdout); 159 fprintf(stderr, "\n%s: ", progname); 160 fprintf(stderr, tformat(), *tp); 161 fprintf(stderr, " ->"); 162 fprintf(stderr, " year=%d", tmp->tm_year); 163 fprintf(stderr, " mon=%d", tmp->tm_mon); 164 fprintf(stderr, " mday=%d", tmp->tm_mday); 165 fprintf(stderr, " hour=%d", tmp->tm_hour); 166 fprintf(stderr, " min=%d", tmp->tm_min); 167 fprintf(stderr, " sec=%d", tmp->tm_sec); 168 fprintf(stderr, " isdst=%d", tmp->tm_isdst); 169 fprintf(stderr, " -> "); 170 fprintf(stderr, tformat(), t); 171 fprintf(stderr, "\n"); 172 } 173 } 174 return tmp; 175 } 176 #endif /* !defined TYPECHECK */ 177 178 static void 179 abbrok(const char * const abbrp, const char * const zone) 180 { 181 const char *cp; 182 char *wp; 183 184 if (warned) 185 return; 186 cp = abbrp; 187 wp = NULL; 188 while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp)) 189 ++cp; 190 if (cp - abbrp == 0) 191 wp = _("lacks alphabetic at start"); 192 else if (cp - abbrp < 3) 193 wp = _("has fewer than 3 alphabetics"); 194 else if (cp - abbrp > 6) 195 wp = _("has more than 6 alphabetics"); 196 if (wp == NULL && (*cp == '+' || *cp == '-')) { 197 ++cp; 198 if (isascii((unsigned char) *cp) && 199 isdigit((unsigned char) *cp)) 200 if (*cp++ == '1' && *cp >= '0' && *cp <= '4') 201 ++cp; 202 if (*cp != '\0') 203 wp = _("differs from POSIX standard"); 204 } 205 if (wp == NULL) 206 return; 207 fflush(stdout); 208 warnx(_("warning: zone \"%s\" abbreviation \"%s\" %s\n"), 209 zone, abbrp, wp); 210 warned = TRUE; 211 } 212 213 int 214 main(int argc, char *argv[]) 215 { 216 int i; 217 int c; 218 int vflag; 219 char *cutarg; 220 long cutloyear = ZDUMP_LO_YEAR; 221 long cuthiyear = ZDUMP_HI_YEAR; 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 INITIALIZE(cutlotime); 234 INITIALIZE(cuthitime); 235 vflag = 0; 236 cutarg = NULL; 237 while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') 238 if (c == 'v') 239 vflag = 1; 240 else cutarg = optarg; 241 if ((c != EOF && c != -1) || 242 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { 243 usage(); 244 } 245 if (vflag) { 246 if (cutarg != NULL) { 247 long lo; 248 long hi; 249 char dummy; 250 251 if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) { 252 cuthiyear = hi; 253 } else if (sscanf(cutarg, "%ld,%ld%c", 254 &lo, &hi, &dummy) == 2) { 255 cutloyear = lo; 256 cuthiyear = hi; 257 } else { 258 errx(EXIT_FAILURE, 259 _("wild -c argument %s\n"), 260 cutarg); 261 } 262 } 263 setabsolutes(); 264 cutlotime = yeartot(cutloyear); 265 cuthitime = yeartot(cuthiyear); 266 } 267 time(&now); 268 longest = 0; 269 for (i = optind; i < argc; ++i) 270 if (strlen(argv[i]) > longest) 271 longest = strlen(argv[i]); 272 { 273 int from; 274 int to; 275 276 for (i = 0; environ[i] != NULL; ++i) 277 continue; 278 fakeenv = (char **) malloc((size_t) ((i + 2) * 279 sizeof *fakeenv)); 280 if (fakeenv == NULL || 281 (fakeenv[0] = (char *) malloc((size_t) (longest + 282 4))) == NULL) 283 errx(EXIT_FAILURE, 284 _("malloc() failed")); 285 to = 0; 286 strcpy(fakeenv[to++], "TZ="); 287 for (from = 0; environ[from] != NULL; ++from) 288 if (strncmp(environ[from], "TZ=", 3) != 0) 289 fakeenv[to++] = environ[from]; 290 fakeenv[to] = NULL; 291 environ = fakeenv; 292 } 293 for (i = optind; i < argc; ++i) { 294 static char buf[MAX_STRING_LENGTH]; 295 296 strcpy(&fakeenv[0][3], argv[i]); 297 if (!vflag) { 298 show(argv[i], now, FALSE); 299 continue; 300 } 301 warned = FALSE; 302 t = absolute_min_time; 303 show(argv[i], t, TRUE); 304 t += SECSPERHOUR * HOURSPERDAY; 305 show(argv[i], t, TRUE); 306 if (t < cutlotime) 307 t = cutlotime; 308 tmp = my_localtime(&t); 309 if (tmp != NULL) { 310 tm = *tmp; 311 strncpy(buf, abbr(&tm), (sizeof buf) - 1); 312 } 313 for ( ; ; ) { 314 if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12) 315 break; 316 newt = t + SECSPERHOUR * 12; 317 newtmp = localtime(&newt); 318 if (newtmp != NULL) 319 newtm = *newtmp; 320 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : 321 (delta(&newtm, &tm) != (newt - t) || 322 newtm.tm_isdst != tm.tm_isdst || 323 strcmp(abbr(&newtm), buf) != 0)) { 324 newt = hunt(argv[i], t, newt); 325 newtmp = localtime(&newt); 326 if (newtmp != NULL) { 327 newtm = *newtmp; 328 strncpy(buf, 329 abbr(&newtm), 330 (sizeof buf) - 1); 331 } 332 } 333 t = newt; 334 tm = newtm; 335 tmp = newtmp; 336 } 337 t = absolute_max_time; 338 t -= SECSPERHOUR * HOURSPERDAY; 339 show(argv[i], t, TRUE); 340 t += SECSPERHOUR * HOURSPERDAY; 341 show(argv[i], t, TRUE); 342 } 343 if (fflush(stdout) || ferror(stdout)) 344 errx(EXIT_FAILURE, _("error writing standard output")); 345 exit(EXIT_SUCCESS); 346 /* If exit fails to exit... */ 347 return EXIT_FAILURE; 348 } 349 350 static void 351 setabsolutes(void) 352 { 353 if (0.5 == (time_t) 0.5) { 354 /* 355 ** time_t is floating. 356 */ 357 if (sizeof (time_t) == sizeof (float)) { 358 absolute_min_time = (time_t) -FLT_MAX; 359 absolute_max_time = (time_t) FLT_MAX; 360 } else if (sizeof (time_t) == sizeof (double)) { 361 absolute_min_time = (time_t) -DBL_MAX; 362 absolute_max_time = (time_t) DBL_MAX; 363 } else { 364 errx(EXIT_FAILURE, 365 _("use of -v on system with floating time_t other than float or double\n")); 366 } 367 } else if (0 > (time_t) -1) { 368 /* 369 ** time_t is signed. Assume overflow wraps around. 370 */ 371 time_t t = 0; 372 time_t t1 = 1; 373 374 while (t < t1) { 375 t = t1; 376 t1 = 2 * t1 + 1; 377 } 378 379 absolute_max_time = t; 380 t = -t; 381 absolute_min_time = t - 1; 382 if (t < absolute_min_time) 383 absolute_min_time = t; 384 } else { 385 /* 386 ** time_t is unsigned. 387 */ 388 absolute_min_time = 0; 389 absolute_max_time = absolute_min_time - 1; 390 } 391 } 392 393 static void 394 usage(void) 395 { 396 fprintf(stderr, _("usage: zdump [-v] [-c [loyear,]hiyear] zonename ...\n")); 397 exit(EXIT_FAILURE); 398 } 399 400 static time_t 401 yeartot(const long y) 402 { 403 long myy; 404 long seconds; 405 time_t t; 406 407 myy = EPOCH_YEAR; 408 t = 0; 409 while (myy != y) { 410 if (myy < y) { 411 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 412 ++myy; 413 if (t > absolute_max_time - seconds) { 414 t = absolute_max_time; 415 break; 416 } 417 t += seconds; 418 } else { 419 --myy; 420 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 421 if (t < absolute_min_time + seconds) { 422 t = absolute_min_time; 423 break; 424 } 425 t -= seconds; 426 } 427 } 428 return t; 429 } 430 431 static time_t 432 hunt(char *name, time_t lot, time_t hit) 433 { 434 time_t t; 435 long diff; 436 struct tm lotm; 437 struct tm * lotmp; 438 struct tm tm; 439 struct tm * tmp; 440 char loab[MAX_STRING_LENGTH]; 441 442 lotmp = my_localtime(&lot); 443 if (lotmp != NULL) { 444 lotm = *lotmp; 445 strncpy(loab, abbr(&lotm), (sizeof loab) - 1); 446 } 447 for ( ; ; ) { 448 diff = (long) (hit - lot); 449 if (diff < 2) 450 break; 451 t = lot; 452 t += diff / 2; 453 if (t <= lot) 454 ++t; 455 else if (t >= hit) 456 --t; 457 tmp = my_localtime(&t); 458 if (tmp != NULL) 459 tm = *tmp; 460 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : 461 (delta(&tm, &lotm) == (t - lot) && 462 tm.tm_isdst == lotm.tm_isdst && 463 strcmp(abbr(&tm), loab) == 0)) { 464 lot = t; 465 lotm = tm; 466 lotmp = tmp; 467 } else hit = t; 468 } 469 show(name, lot, TRUE); 470 show(name, hit, TRUE); 471 return hit; 472 } 473 474 /* 475 ** Thanks to Paul Eggert for logic used in delta. 476 */ 477 478 static long 479 delta(struct tm *newp, struct tm *oldp) 480 { 481 long result; 482 int tmy; 483 484 if (newp->tm_year < oldp->tm_year) 485 return -delta(oldp, newp); 486 result = 0; 487 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 488 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); 489 result += newp->tm_yday - oldp->tm_yday; 490 result *= HOURSPERDAY; 491 result += newp->tm_hour - oldp->tm_hour; 492 result *= MINSPERHOUR; 493 result += newp->tm_min - oldp->tm_min; 494 result *= SECSPERMIN; 495 result += newp->tm_sec - oldp->tm_sec; 496 return result; 497 } 498 499 static void 500 show(char *zone, time_t t, int v) 501 { 502 struct tm * tmp; 503 504 printf("%-*s ", (int) longest, zone); 505 if (v) { 506 tmp = gmtime(&t); 507 if (tmp == NULL) { 508 printf(tformat(), t); 509 } else { 510 dumptime(tmp); 511 printf(" UTC"); 512 } 513 printf(" = "); 514 } 515 tmp = my_localtime(&t); 516 dumptime(tmp); 517 if (tmp != NULL) { 518 if (*abbr(tmp) != '\0') 519 printf(" %s", abbr(tmp)); 520 if (v) { 521 printf(" isdst=%d", tmp->tm_isdst); 522 #ifdef TM_GMTOFF 523 printf(" gmtoff=%ld", tmp->TM_GMTOFF); 524 #endif /* defined TM_GMTOFF */ 525 } 526 } 527 printf("\n"); 528 if (tmp != NULL && *abbr(tmp) != '\0') 529 abbrok(abbr(tmp), zone); 530 } 531 532 static char * 533 abbr(struct tm *tmp) 534 { 535 char * result; 536 static char nada; 537 538 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) 539 return &nada; 540 result = tzname[tmp->tm_isdst]; 541 return (result == NULL) ? &nada : result; 542 } 543 544 /* 545 ** The code below can fail on certain theoretical systems; 546 ** it works on all known real-world systems as of 2004-12-30. 547 */ 548 549 static const char * 550 tformat(void) 551 { 552 if (0.5 == (time_t) 0.5) { /* floating */ 553 if (sizeof (time_t) > sizeof (double)) 554 return "%Lg"; 555 return "%g"; 556 } 557 if (0 > (time_t) -1) { /* signed */ 558 if (sizeof (time_t) > sizeof (long)) 559 return "%lld"; 560 if (sizeof (time_t) > sizeof (int)) 561 return "%ld"; 562 return "%d"; 563 } 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