1static const char elsieid[] = "@(#)zdump.c 7.31"; 2 3#ifndef lint 4static const char rcsid[] = 5 "$FreeBSD: src/usr.sbin/zic/zdump.c,v 1.10 2008/02/19 07:09:19 ru Exp $"; 6#endif /* not lint */ 7 8/* 9** This code has been made independent of the rest of the time 10** conversion package to increase confidence in the verification it provides. 11** You can use this code to help in verifying other implementations. 12*/ 13 14#include <err.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#include <limits.h> 22 23#ifndef MAX_STRING_LENGTH 24#define MAX_STRING_LENGTH 1024 25#endif /* !defined MAX_STRING_LENGTH */ 26 27#ifndef TRUE 28#define TRUE 1 29#endif /* !defined TRUE */ 30 31#ifndef FALSE 32#define FALSE 0 33#endif /* !defined FALSE */ 34 35#ifndef EXIT_SUCCESS 36#define EXIT_SUCCESS 0 37#endif /* !defined EXIT_SUCCESS */ 38 39#ifndef EXIT_FAILURE 40#define EXIT_FAILURE 1 41#endif /* !defined EXIT_FAILURE */ 42 43#ifndef SECSPERMIN 44#define SECSPERMIN 60 45#endif /* !defined SECSPERMIN */ 46 47#ifndef MINSPERHOUR 48#define MINSPERHOUR 60 49#endif /* !defined MINSPERHOUR */ 50 51#ifndef SECSPERHOUR 52#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 53#endif /* !defined SECSPERHOUR */ 54 55#ifndef HOURSPERDAY 56#define HOURSPERDAY 24 57#endif /* !defined HOURSPERDAY */ 58 59#ifndef EPOCH_YEAR 60#define EPOCH_YEAR 1970 61#endif /* !defined EPOCH_YEAR */ 62 63#ifndef TM_YEAR_BASE 64#define TM_YEAR_BASE 1900 65#endif /* !defined TM_YEAR_BASE */ 66 67#ifndef DAYSPERNYEAR 68#define DAYSPERNYEAR 365 69#endif /* !defined DAYSPERNYEAR */ 70 71#ifndef isleap 72#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) 73#endif /* !defined isleap */ 74 75#if HAVE_GETTEXT - 0 76#include "locale.h" /* for setlocale */ 77#include "libintl.h" 78#endif /* HAVE_GETTEXT - 0 */ 79 80#ifndef GNUC_or_lint 81#ifdef lint 82#define GNUC_or_lint 83#endif /* defined lint */ 84#ifndef lint 85#ifdef __GNUC__ 86#define GNUC_or_lint 87#endif /* defined __GNUC__ */ 88#endif /* !defined lint */ 89#endif /* !defined GNUC_or_lint */ 90 91#ifndef INITIALIZE 92#ifdef GNUC_or_lint 93#define INITIALIZE(x) ((x) = 0) 94#endif /* defined GNUC_or_lint */ 95#ifndef GNUC_or_lint 96#define INITIALIZE(x) 97#endif /* !defined GNUC_or_lint */ 98#endif /* !defined INITIALIZE */ 99 100/* 101** For the benefit of GNU folk... 102** `_(MSGID)' uses the current locale's message library string for MSGID. 103** The default is to use gettext if available, and use MSGID otherwise. 104*/ 105 106#ifndef _ 107#if HAVE_GETTEXT - 0 108#define _(msgid) gettext(msgid) 109#else /* !(HAVE_GETTEXT - 0) */ 110#define _(msgid) msgid 111#endif /* !(HAVE_GETTEXT - 0) */ 112#endif /* !defined _ */ 113 114#ifndef TZ_DOMAIN 115#define TZ_DOMAIN "tz" 116#endif /* !defined TZ_DOMAIN */ 117 118#ifndef P 119#ifdef __STDC__ 120#define P(x) x 121#endif /* defined __STDC__ */ 122#ifndef __STDC__ 123#define P(x) () 124#endif /* !defined __STDC__ */ 125#endif /* !defined P */ 126 127extern char ** environ; 128extern char * tzname[2]; 129 130static char * abbr P((struct tm * tmp)); 131static long delta P((struct tm * newp, struct tm * oldp)); 132static time_t hunt P((char * name, time_t lot, time_t hit)); 133static size_t longest; 134static void show P((char * zone, time_t t, int v)); 135static void usage(void); 136 137int 138main(argc, argv) 139int argc; 140char * argv[]; 141{ 142 register int i; 143 register int c; 144 register int vflag; 145 register char * cutoff; 146 register int cutyear; 147 register long cuttime; 148 char ** fakeenv; 149 time_t now; 150 time_t t; 151 time_t newt; 152#ifndef __APPLE__ 153// <rdar://problem/6013740> 154// The approach of walking through every day from the minimum 155// possible time_t value to the maximum possible time_t value 156// falls apart with 64-bit time_t (takes too long to iterate, 157// and causes gmtime(3) and localtime(3) to return EOVERFLOW 158// which this code does not anticipate). Limiting the time_t 159// range to [INT_MIN:INT_MAX] even on LP64. 160 time_t hibit; 161#endif 162 struct tm tm; 163 struct tm newtm; 164 165 INITIALIZE(cuttime); 166#if HAVE_GETTEXT - 0 167 (void) setlocale(LC_MESSAGES, ""); 168#ifdef TZ_DOMAINDIR 169 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); 170#endif /* defined(TEXTDOMAINDIR) */ 171 (void) textdomain(TZ_DOMAIN); 172#endif /* HAVE_GETTEXT - 0 */ 173 for (i = 1; i < argc; ++i) 174 if (strcmp(argv[i], "--version") == 0) { 175 errx(EXIT_SUCCESS, "%s", elsieid); 176 } 177 vflag = 0; 178 cutoff = NULL; 179 while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') 180 if (c == 'v') 181 vflag = 1; 182 else cutoff = optarg; 183 if ((c != -1) || 184 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { 185 usage(); 186 } 187 if (cutoff != NULL) { 188 int y; 189 190 cutyear = atoi(cutoff); 191 cuttime = 0; 192 for (y = EPOCH_YEAR; y < cutyear; ++y) 193 cuttime += DAYSPERNYEAR + isleap(y); 194 cuttime *= SECSPERHOUR * HOURSPERDAY; 195 } 196 (void) time(&now); 197 longest = 0; 198 for (i = optind; i < argc; ++i) 199 if (strlen(argv[i]) > longest) 200 longest = strlen(argv[i]); 201#ifndef __APPLE__ 202 for (hibit = 1; (hibit << 1) != 0; hibit <<= 1) 203 continue; 204#endif 205 { 206 register int from; 207 register int to; 208 209 for (i = 0; environ[i] != NULL; ++i) 210 continue; 211 fakeenv = (char **) malloc((size_t) ((i + 2) * 212 sizeof *fakeenv)); 213 if (fakeenv == NULL || 214 (fakeenv[0] = (char *) malloc((size_t) (longest + 215 4))) == NULL) 216 errx(EXIT_FAILURE, 217 _("malloc() failed")); 218 to = 0; 219 (void) strcpy(fakeenv[to++], "TZ="); 220 for (from = 0; environ[from] != NULL; ++from) 221 if (strncmp(environ[from], "TZ=", 3) != 0) 222 fakeenv[to++] = environ[from]; 223 fakeenv[to] = NULL; 224 environ = fakeenv; 225 } 226 for (i = optind; i < argc; ++i) { 227 static char buf[MAX_STRING_LENGTH]; 228 229 (void) strcpy(&fakeenv[0][3], argv[i]); 230 if (!vflag) { 231 show(argv[i], now, FALSE); 232 continue; 233 } 234 /* 235 ** Get lowest value of t. 236 */ 237#ifdef __APPLE__ 238 t = INT_MIN; 239#else 240 t = hibit; 241 if (t > 0) /* time_t is unsigned */ 242 t = 0; 243#endif 244 show(argv[i], t, TRUE); 245 t += SECSPERHOUR * HOURSPERDAY; 246 show(argv[i], t, TRUE); 247 tm = *localtime(&t); 248 (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1); 249 for ( ; ; ) { 250 if (cutoff != NULL && t >= cuttime) 251 break; 252 newt = t + SECSPERHOUR * 12; 253 if (cutoff != NULL && newt >= cuttime) 254 break; 255#ifdef __APPLE__ 256 if (newt > INT_MAX) 257 break; 258#else 259 if (newt <= t) 260 break; 261#endif 262 newtm = *localtime(&newt); 263 if (delta(&newtm, &tm) != (newt - t) || 264 newtm.tm_isdst != tm.tm_isdst || 265 strcmp(abbr(&newtm), buf) != 0) { 266 newt = hunt(argv[i], t, newt); 267 newtm = *localtime(&newt); 268 (void) strncpy(buf, abbr(&newtm), 269 (sizeof buf) - 1); 270 } 271 t = newt; 272 tm = newtm; 273 } 274 /* 275 ** Get highest value of t. 276 */ 277#ifdef __APPLE__ 278 t = INT_MAX; 279#else 280 t = ~((time_t) 0); 281 if (t < 0) /* time_t is signed */ 282 t &= ~hibit; 283#endif 284 t -= SECSPERHOUR * HOURSPERDAY; 285 show(argv[i], t, TRUE); 286 t += SECSPERHOUR * HOURSPERDAY; 287 show(argv[i], t, TRUE); 288 } 289 if (fflush(stdout) || ferror(stdout)) 290 errx(EXIT_FAILURE, _("error writing standard output")); 291 exit(EXIT_SUCCESS); 292 293 /* gcc -Wall pacifier */ 294 for ( ; ; ) 295 continue; 296} 297 298static void 299usage(void) 300{ 301 fprintf(stderr, 302_("usage: zdump [--version] [-v] [-c cutoff] zonename ...\n")); 303 exit(EXIT_FAILURE); 304} 305 306static time_t 307hunt(name, lot, hit) 308char * name; 309time_t lot; 310time_t hit; 311{ 312 time_t t; 313 struct tm lotm; 314 struct tm tm; 315 static char loab[MAX_STRING_LENGTH]; 316 317 lotm = *localtime(&lot); 318 (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1); 319 while ((hit - lot) >= 2) { 320 t = lot / 2 + hit / 2; 321 if (t <= lot) 322 ++t; 323 else if (t >= hit) 324 --t; 325 tm = *localtime(&t); 326 if (delta(&tm, &lotm) == (t - lot) && 327 tm.tm_isdst == lotm.tm_isdst && 328 strcmp(abbr(&tm), loab) == 0) { 329 lot = t; 330 lotm = tm; 331 } else hit = t; 332 } 333 show(name, lot, TRUE); 334 show(name, hit, TRUE); 335 return hit; 336} 337 338/* 339** Thanks to Paul Eggert (eggert@twinsun.com) for logic used in delta. 340*/ 341 342static long 343delta(newp, oldp) 344struct tm * newp; 345struct tm * oldp; 346{ 347 long result; 348 int tmy; 349 350 if (newp->tm_year < oldp->tm_year) 351 return -delta(oldp, newp); 352 result = 0; 353 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 354 result += DAYSPERNYEAR + isleap(tmy + TM_YEAR_BASE); 355 result += newp->tm_yday - oldp->tm_yday; 356 result *= HOURSPERDAY; 357 result += newp->tm_hour - oldp->tm_hour; 358 result *= MINSPERHOUR; 359 result += newp->tm_min - oldp->tm_min; 360 result *= SECSPERMIN; 361 result += newp->tm_sec - oldp->tm_sec; 362 return result; 363} 364 365static void 366show(zone, t, v) 367char * zone; 368time_t t; 369int v; 370{ 371 struct tm * tmp; 372 373 (void) printf("%-*s ", (int) longest, zone); 374 if (v) 375 (void) printf("%.24s UTC = ", asctime(gmtime(&t))); 376 tmp = localtime(&t); 377 (void) printf("%.24s", asctime(tmp)); 378 if (*abbr(tmp) != '\0') 379 (void) printf(" %s", abbr(tmp)); 380 if (v) { 381 (void) printf(" isdst=%d", tmp->tm_isdst); 382#ifdef TM_GMTOFF 383 (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); 384#endif /* defined TM_GMTOFF */ 385 } 386 (void) printf("\n"); 387} 388 389static char * 390abbr(tmp) 391struct tm * tmp; 392{ 393 register char * result; 394 static char nada; 395 396 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) 397 return &nada; 398 result = tzname[tmp->tm_isdst]; 399 return (result == NULL) ? &nada : result; 400} 401