zdump.c 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. /* Dump time zone data in a textual format. */
  2. /*
  3. ** This file is in the public domain, so clarified as of
  4. ** 2009-05-17 by Arthur David Olson.
  5. */
  6. #include "version.h"
  7. #ifndef NETBSD_INSPIRED
  8. # define NETBSD_INSPIRED 1
  9. #endif
  10. #include "private.h"
  11. #include <stdio.h>
  12. #ifndef HAVE_SNPRINTF
  13. # define HAVE_SNPRINTF (199901 <= __STDC_VERSION__)
  14. #endif
  15. #ifndef HAVE_LOCALTIME_R
  16. # define HAVE_LOCALTIME_R 1
  17. #endif
  18. #ifndef HAVE_LOCALTIME_RZ
  19. # ifdef TM_ZONE
  20. # define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
  21. # else
  22. # define HAVE_LOCALTIME_RZ 0
  23. # endif
  24. #endif
  25. #ifndef HAVE_TZSET
  26. # define HAVE_TZSET 1
  27. #endif
  28. #ifndef ZDUMP_LO_YEAR
  29. #define ZDUMP_LO_YEAR (-500)
  30. #endif /* !defined ZDUMP_LO_YEAR */
  31. #ifndef ZDUMP_HI_YEAR
  32. #define ZDUMP_HI_YEAR 2500
  33. #endif /* !defined ZDUMP_HI_YEAR */
  34. #ifndef MAX_STRING_LENGTH
  35. #define MAX_STRING_LENGTH 1024
  36. #endif /* !defined MAX_STRING_LENGTH */
  37. #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
  38. #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
  39. #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
  40. + SECSPERLYEAR * (intmax_t) (100 - 3))
  41. /*
  42. ** True if SECSPER400YEARS is known to be representable as an
  43. ** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false
  44. ** even if SECSPER400YEARS is representable, because when that happens
  45. ** the code merely runs a bit more slowly, and this slowness doesn't
  46. ** occur on any practical platform.
  47. */
  48. enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
  49. #if HAVE_GETTEXT
  50. #include <locale.h> /* for setlocale */
  51. #endif /* HAVE_GETTEXT */
  52. #if ! HAVE_LOCALTIME_RZ
  53. # undef timezone_t
  54. # define timezone_t char **
  55. #endif
  56. #if !HAVE_POSIX_DECLS
  57. extern int getopt(int argc, char * const argv[],
  58. const char * options);
  59. extern char * optarg;
  60. extern int optind;
  61. #endif
  62. /* The minimum and maximum finite time values. */
  63. enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 };
  64. static time_t const absolute_min_time =
  65. ((time_t) -1 < 0
  66. ? (- ((time_t) ~ (time_t) 0 < 0)
  67. - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)))
  68. : 0);
  69. static time_t const absolute_max_time =
  70. ((time_t) -1 < 0
  71. ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
  72. : -1);
  73. static int longest;
  74. static char * progname;
  75. static bool warned;
  76. static bool errout;
  77. static char const *abbr(struct tm const *);
  78. static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
  79. static void dumptime(struct tm const *);
  80. static time_t hunt(timezone_t, char *, time_t, time_t);
  81. static void show(timezone_t, char *, time_t, bool);
  82. static void showtrans(char const *, struct tm const *, time_t, char const *,
  83. char const *);
  84. static const char *tformat(void);
  85. static time_t yeartot(intmax_t) ATTRIBUTE_PURE;
  86. /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
  87. #define is_digit(c) ((unsigned)(c) - '0' <= 9)
  88. /* Is A an alphabetic character in the C locale? */
  89. static bool
  90. is_alpha(char a)
  91. {
  92. switch (a) {
  93. default:
  94. return false;
  95. case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
  96. case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
  97. case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
  98. case 'V': case 'W': case 'X': case 'Y': case 'Z':
  99. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
  100. case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
  101. case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
  102. case 'v': case 'w': case 'x': case 'y': case 'z':
  103. return true;
  104. }
  105. }
  106. /* Return A + B, exiting if the result would overflow. */
  107. static size_t
  108. sumsize(size_t a, size_t b)
  109. {
  110. size_t sum = a + b;
  111. if (sum < a) {
  112. fprintf(stderr, "%s: size overflow\n", progname);
  113. exit(EXIT_FAILURE);
  114. }
  115. return sum;
  116. }
  117. /* Return a pointer to a newly allocated buffer of size SIZE, exiting
  118. on failure. SIZE should be nonzero. */
  119. static void * ATTRIBUTE_MALLOC
  120. xmalloc(size_t size)
  121. {
  122. void *p = malloc(size);
  123. if (!p) {
  124. perror(progname);
  125. exit(EXIT_FAILURE);
  126. }
  127. return p;
  128. }
  129. #if ! HAVE_TZSET
  130. # undef tzset
  131. # define tzset zdump_tzset
  132. static void tzset(void) { }
  133. #endif
  134. /* Assume gmtime_r works if localtime_r does.
  135. A replacement localtime_r is defined below if needed. */
  136. #if ! HAVE_LOCALTIME_R
  137. # undef gmtime_r
  138. # define gmtime_r zdump_gmtime_r
  139. static struct tm *
  140. gmtime_r(time_t *tp, struct tm *tmp)
  141. {
  142. struct tm *r = gmtime(tp);
  143. if (r) {
  144. *tmp = *r;
  145. r = tmp;
  146. }
  147. return r;
  148. }
  149. #endif
  150. /* Platforms with TM_ZONE don't need tzname, so they can use the
  151. faster localtime_rz or localtime_r if available. */
  152. #if defined TM_ZONE && HAVE_LOCALTIME_RZ
  153. # define USE_LOCALTIME_RZ true
  154. #else
  155. # define USE_LOCALTIME_RZ false
  156. #endif
  157. #if ! USE_LOCALTIME_RZ
  158. # if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
  159. # undef localtime_r
  160. # define localtime_r zdump_localtime_r
  161. static struct tm *
  162. localtime_r(time_t *tp, struct tm *tmp)
  163. {
  164. struct tm *r = localtime(tp);
  165. if (r) {
  166. *tmp = *r;
  167. r = tmp;
  168. }
  169. return r;
  170. }
  171. # endif
  172. # undef localtime_rz
  173. # define localtime_rz zdump_localtime_rz
  174. static struct tm *
  175. localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp)
  176. {
  177. return localtime_r(tp, tmp);
  178. }
  179. # ifdef TYPECHECK
  180. # undef mktime_z
  181. # define mktime_z zdump_mktime_z
  182. static time_t
  183. mktime_z(timezone_t tz, struct tm *tmp)
  184. {
  185. return mktime(tmp);
  186. }
  187. # endif
  188. # undef tzalloc
  189. # undef tzfree
  190. # define tzalloc zdump_tzalloc
  191. # define tzfree zdump_tzfree
  192. static timezone_t
  193. tzalloc(char const *val)
  194. {
  195. static char **fakeenv;
  196. char **env = fakeenv;
  197. char *env0;
  198. if (! env) {
  199. char **e = environ;
  200. int to;
  201. while (*e++)
  202. continue;
  203. env = xmalloc(sumsize(sizeof *environ,
  204. (e - environ) * sizeof *environ));
  205. to = 1;
  206. for (e = environ; (env[to] = *e); e++)
  207. to += strncmp(*e, "TZ=", 3) != 0;
  208. }
  209. env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val)));
  210. env[0] = strcat(strcpy(env0, "TZ="), val);
  211. environ = fakeenv = env;
  212. tzset();
  213. return env;
  214. }
  215. static void
  216. tzfree(timezone_t env)
  217. {
  218. environ = env + 1;
  219. free(env[0]);
  220. }
  221. #endif /* ! USE_LOCALTIME_RZ */
  222. /* A UT time zone, and its initializer. */
  223. static timezone_t gmtz;
  224. static void
  225. gmtzinit(void)
  226. {
  227. if (USE_LOCALTIME_RZ) {
  228. static char const utc[] = "UTC0";
  229. gmtz = tzalloc(utc);
  230. if (!gmtz) {
  231. perror(utc);
  232. exit(EXIT_FAILURE);
  233. }
  234. }
  235. }
  236. /* Convert *TP to UT, storing the broken-down time into *TMP.
  237. Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP),
  238. except typically faster if USE_LOCALTIME_RZ. */
  239. static struct tm *
  240. my_gmtime_r(time_t *tp, struct tm *tmp)
  241. {
  242. return USE_LOCALTIME_RZ ? localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
  243. }
  244. #ifndef TYPECHECK
  245. # define my_localtime_rz localtime_rz
  246. #else /* !defined TYPECHECK */
  247. static struct tm *
  248. my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
  249. {
  250. tmp = localtime_rz(tz, tp, tmp);
  251. if (tmp) {
  252. struct tm tm;
  253. register time_t t;
  254. tm = *tmp;
  255. t = mktime_z(tz, &tm);
  256. if (t != *tp) {
  257. fflush(stdout);
  258. fprintf(stderr, "\n%s: ", progname);
  259. fprintf(stderr, tformat(), *tp);
  260. fprintf(stderr, " ->");
  261. fprintf(stderr, " year=%d", tmp->tm_year);
  262. fprintf(stderr, " mon=%d", tmp->tm_mon);
  263. fprintf(stderr, " mday=%d", tmp->tm_mday);
  264. fprintf(stderr, " hour=%d", tmp->tm_hour);
  265. fprintf(stderr, " min=%d", tmp->tm_min);
  266. fprintf(stderr, " sec=%d", tmp->tm_sec);
  267. fprintf(stderr, " isdst=%d", tmp->tm_isdst);
  268. fprintf(stderr, " -> ");
  269. fprintf(stderr, tformat(), t);
  270. fprintf(stderr, "\n");
  271. errout = true;
  272. }
  273. }
  274. return tmp;
  275. }
  276. #endif /* !defined TYPECHECK */
  277. static void
  278. abbrok(const char *const abbrp, const char *const zone)
  279. {
  280. register const char * cp;
  281. register const char * wp;
  282. if (warned)
  283. return;
  284. cp = abbrp;
  285. while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
  286. ++cp;
  287. if (cp - abbrp < 3)
  288. wp = _("has fewer than 3 characters");
  289. else if (cp - abbrp > 6)
  290. wp = _("has more than 6 characters");
  291. else if (*cp)
  292. wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
  293. else
  294. return;
  295. fflush(stdout);
  296. fprintf(stderr,
  297. _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
  298. progname, zone, abbrp, wp);
  299. warned = errout = true;
  300. }
  301. /* Return a time zone abbreviation. If the abbreviation needs to be
  302. saved, use *BUF (of size *BUFALLOC) to save it, and return the
  303. abbreviation in the possibly-reallocated *BUF. Otherwise, just
  304. return the abbreviation. Get the abbreviation from TMP.
  305. Exit on memory allocation failure. */
  306. static char const *
  307. saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp)
  308. {
  309. char const *ab = abbr(tmp);
  310. if (HAVE_LOCALTIME_RZ)
  311. return ab;
  312. else {
  313. size_t ablen = strlen(ab);
  314. if (*bufalloc <= ablen) {
  315. free(*buf);
  316. /* Make the new buffer at least twice as long as the old,
  317. to avoid O(N**2) behavior on repeated calls. */
  318. *bufalloc = sumsize(*bufalloc, ablen + 1);
  319. *buf = xmalloc(*bufalloc);
  320. }
  321. return strcpy(*buf, ab);
  322. }
  323. }
  324. static void
  325. close_file(FILE *stream)
  326. {
  327. char const *e = (ferror(stream) ? _("I/O error")
  328. : fclose(stream) != 0 ? strerror(errno) : NULL);
  329. if (e) {
  330. fprintf(stderr, "%s: %s\n", progname, e);
  331. exit(EXIT_FAILURE);
  332. }
  333. }
  334. static void
  335. usage(FILE * const stream, const int status)
  336. {
  337. fprintf(stream,
  338. _("%s: usage: %s OPTIONS TIMEZONE ...\n"
  339. "Options include:\n"
  340. " -c [L,]U Start at year L (default -500), end before year U (default 2500)\n"
  341. " -t [L,]U Start at time L, end before time U (in seconds since 1970)\n"
  342. " -i List transitions briefly (format is experimental)\n" \
  343. " -v List transitions verbosely\n"
  344. " -V List transitions a bit less verbosely\n"
  345. " --help Output this help\n"
  346. " --version Output version info\n"
  347. "\n"
  348. "Report bugs to %s.\n"),
  349. progname, progname, REPORT_BUGS_TO);
  350. if (status == EXIT_SUCCESS)
  351. close_file(stream);
  352. exit(status);
  353. }
  354. int
  355. main(int argc, char *argv[])
  356. {
  357. /* These are static so that they're initially zero. */
  358. static char * abbrev;
  359. static size_t abbrevsize;
  360. register int i;
  361. register bool vflag;
  362. register bool Vflag;
  363. register char * cutarg;
  364. register char * cuttimes;
  365. register time_t cutlotime;
  366. register time_t cuthitime;
  367. time_t now;
  368. bool iflag = false;
  369. cutlotime = absolute_min_time;
  370. cuthitime = absolute_max_time;
  371. #if HAVE_GETTEXT
  372. setlocale(LC_ALL, "");
  373. #ifdef TZ_DOMAINDIR
  374. bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
  375. #endif /* defined TEXTDOMAINDIR */
  376. textdomain(TZ_DOMAIN);
  377. #endif /* HAVE_GETTEXT */
  378. progname = argv[0];
  379. for (i = 1; i < argc; ++i)
  380. if (strcmp(argv[i], "--version") == 0) {
  381. printf("zdump %s%s\n", PKGVERSION, TZVERSION);
  382. return EXIT_SUCCESS;
  383. } else if (strcmp(argv[i], "--help") == 0) {
  384. usage(stdout, EXIT_SUCCESS);
  385. }
  386. vflag = Vflag = false;
  387. cutarg = cuttimes = NULL;
  388. for (;;)
  389. switch (getopt(argc, argv, "c:it:vV")) {
  390. case 'c': cutarg = optarg; break;
  391. case 't': cuttimes = optarg; break;
  392. case 'i': iflag = true; break;
  393. case 'v': vflag = true; break;
  394. case 'V': Vflag = true; break;
  395. case -1:
  396. if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
  397. goto arg_processing_done;
  398. /* Fall through. */
  399. default:
  400. usage(stderr, EXIT_FAILURE);
  401. }
  402. arg_processing_done:;
  403. if (iflag | vflag | Vflag) {
  404. intmax_t lo;
  405. intmax_t hi;
  406. char *loend, *hiend;
  407. register intmax_t cutloyear = ZDUMP_LO_YEAR;
  408. register intmax_t cuthiyear = ZDUMP_HI_YEAR;
  409. if (cutarg != NULL) {
  410. lo = strtoimax(cutarg, &loend, 10);
  411. if (cutarg != loend && !*loend) {
  412. hi = lo;
  413. cuthiyear = hi;
  414. } else if (cutarg != loend && *loend == ','
  415. && (hi = strtoimax(loend + 1, &hiend, 10),
  416. loend + 1 != hiend && !*hiend)) {
  417. cutloyear = lo;
  418. cuthiyear = hi;
  419. } else {
  420. fprintf(stderr, _("%s: wild -c argument %s\n"),
  421. progname, cutarg);
  422. return EXIT_FAILURE;
  423. }
  424. }
  425. if (cutarg != NULL || cuttimes == NULL) {
  426. cutlotime = yeartot(cutloyear);
  427. cuthitime = yeartot(cuthiyear);
  428. }
  429. if (cuttimes != NULL) {
  430. lo = strtoimax(cuttimes, &loend, 10);
  431. if (cuttimes != loend && !*loend) {
  432. hi = lo;
  433. if (hi < cuthitime) {
  434. if (hi < absolute_min_time)
  435. hi = absolute_min_time;
  436. cuthitime = hi;
  437. }
  438. } else if (cuttimes != loend && *loend == ','
  439. && (hi = strtoimax(loend + 1, &hiend, 10),
  440. loend + 1 != hiend && !*hiend)) {
  441. if (cutlotime < lo) {
  442. if (absolute_max_time < lo)
  443. lo = absolute_max_time;
  444. cutlotime = lo;
  445. }
  446. if (hi < cuthitime) {
  447. if (hi < absolute_min_time)
  448. hi = absolute_min_time;
  449. cuthitime = hi;
  450. }
  451. } else {
  452. fprintf(stderr,
  453. _("%s: wild -t argument %s\n"),
  454. progname, cuttimes);
  455. return EXIT_FAILURE;
  456. }
  457. }
  458. }
  459. gmtzinit();
  460. INITIALIZE (now);
  461. if (! (iflag | vflag | Vflag))
  462. now = time(NULL);
  463. longest = 0;
  464. for (i = optind; i < argc; i++) {
  465. size_t arglen = strlen(argv[i]);
  466. if (longest < arglen)
  467. longest = arglen < INT_MAX ? arglen : INT_MAX;
  468. }
  469. for (i = optind; i < argc; ++i) {
  470. timezone_t tz = tzalloc(argv[i]);
  471. char const *ab;
  472. time_t t;
  473. struct tm tm, newtm;
  474. bool tm_ok;
  475. if (!tz) {
  476. perror(argv[i]);
  477. return EXIT_FAILURE;
  478. }
  479. if (! (iflag | vflag | Vflag)) {
  480. show(tz, argv[i], now, false);
  481. tzfree(tz);
  482. continue;
  483. }
  484. warned = false;
  485. t = absolute_min_time;
  486. if (! (iflag | Vflag)) {
  487. show(tz, argv[i], t, true);
  488. t += SECSPERDAY;
  489. show(tz, argv[i], t, true);
  490. }
  491. if (t < cutlotime)
  492. t = cutlotime;
  493. INITIALIZE (ab);
  494. tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
  495. if (tm_ok) {
  496. ab = saveabbr(&abbrev, &abbrevsize, &tm);
  497. if (iflag) {
  498. showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
  499. showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
  500. }
  501. }
  502. while (t < cuthitime) {
  503. time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
  504. && t + SECSPERDAY / 2 < cuthitime)
  505. ? t + SECSPERDAY / 2
  506. : cuthitime);
  507. struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
  508. bool newtm_ok = newtmp != NULL;
  509. if (tm_ok != newtm_ok
  510. || (tm_ok && (delta(&newtm, &tm) != newt - t
  511. || newtm.tm_isdst != tm.tm_isdst
  512. || strcmp(abbr(&newtm), ab) != 0))) {
  513. newt = hunt(tz, argv[i], t, newt);
  514. newtmp = localtime_rz(tz, &newt, &newtm);
  515. newtm_ok = newtmp != NULL;
  516. if (iflag)
  517. showtrans("%Y-%m-%d\t%L\t%Q", newtmp, newt,
  518. newtm_ok ? abbr(&newtm) : NULL, argv[i]);
  519. else {
  520. show(tz, argv[i], newt - 1, true);
  521. show(tz, argv[i], newt, true);
  522. }
  523. }
  524. t = newt;
  525. tm_ok = newtm_ok;
  526. if (newtm_ok) {
  527. ab = saveabbr(&abbrev, &abbrevsize, &newtm);
  528. tm = newtm;
  529. }
  530. }
  531. if (! (iflag | Vflag)) {
  532. t = absolute_max_time;
  533. t -= SECSPERDAY;
  534. show(tz, argv[i], t, true);
  535. t += SECSPERDAY;
  536. show(tz, argv[i], t, true);
  537. }
  538. tzfree(tz);
  539. }
  540. close_file(stdout);
  541. if (errout && (ferror(stderr) || fclose(stderr) != 0))
  542. return EXIT_FAILURE;
  543. return EXIT_SUCCESS;
  544. }
  545. static time_t
  546. yeartot(intmax_t y)
  547. {
  548. register intmax_t myy, seconds, years;
  549. register time_t t;
  550. myy = EPOCH_YEAR;
  551. t = 0;
  552. while (myy < y) {
  553. if (SECSPER400YEARS_FITS && 400 <= y - myy) {
  554. intmax_t diff400 = (y - myy) / 400;
  555. if (INTMAX_MAX / SECSPER400YEARS < diff400)
  556. return absolute_max_time;
  557. seconds = diff400 * SECSPER400YEARS;
  558. years = diff400 * 400;
  559. } else {
  560. seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
  561. years = 1;
  562. }
  563. myy += years;
  564. if (t > absolute_max_time - seconds)
  565. return absolute_max_time;
  566. t += seconds;
  567. }
  568. while (y < myy) {
  569. if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
  570. intmax_t diff400 = (myy - y) / 400;
  571. if (INTMAX_MAX / SECSPER400YEARS < diff400)
  572. return absolute_min_time;
  573. seconds = diff400 * SECSPER400YEARS;
  574. years = diff400 * 400;
  575. } else {
  576. seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
  577. years = 1;
  578. }
  579. myy -= years;
  580. if (t < absolute_min_time + seconds)
  581. return absolute_min_time;
  582. t -= seconds;
  583. }
  584. return t;
  585. }
  586. static time_t
  587. hunt(timezone_t tz, char *name, time_t lot, time_t hit)
  588. {
  589. static char * loab;
  590. static size_t loabsize;
  591. char const * ab;
  592. time_t t;
  593. struct tm lotm;
  594. struct tm tm;
  595. bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
  596. bool tm_ok;
  597. if (lotm_ok)
  598. ab = saveabbr(&loab, &loabsize, &lotm);
  599. for ( ; ; ) {
  600. time_t diff = hit - lot;
  601. if (diff < 2)
  602. break;
  603. t = lot;
  604. t += diff / 2;
  605. if (t <= lot)
  606. ++t;
  607. else if (t >= hit)
  608. --t;
  609. tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
  610. if (lotm_ok & tm_ok
  611. ? (delta(&tm, &lotm) == t - lot
  612. && tm.tm_isdst == lotm.tm_isdst
  613. && strcmp(abbr(&tm), ab) == 0)
  614. : lotm_ok == tm_ok) {
  615. lot = t;
  616. if (tm_ok)
  617. lotm = tm;
  618. } else hit = t;
  619. }
  620. return hit;
  621. }
  622. /*
  623. ** Thanks to Paul Eggert for logic used in delta_nonneg.
  624. */
  625. static intmax_t
  626. delta_nonneg(struct tm *newp, struct tm *oldp)
  627. {
  628. register intmax_t result;
  629. register int tmy;
  630. result = 0;
  631. for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
  632. result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
  633. result += newp->tm_yday - oldp->tm_yday;
  634. result *= HOURSPERDAY;
  635. result += newp->tm_hour - oldp->tm_hour;
  636. result *= MINSPERHOUR;
  637. result += newp->tm_min - oldp->tm_min;
  638. result *= SECSPERMIN;
  639. result += newp->tm_sec - oldp->tm_sec;
  640. return result;
  641. }
  642. static intmax_t
  643. delta(struct tm *newp, struct tm *oldp)
  644. {
  645. return (newp->tm_year < oldp->tm_year
  646. ? -delta_nonneg(oldp, newp)
  647. : delta_nonneg(newp, oldp));
  648. }
  649. #ifndef TM_GMTOFF
  650. /* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
  651. Assume A and B differ by at most one year. */
  652. static int
  653. adjusted_yday(struct tm const *a, struct tm const *b)
  654. {
  655. int yday = a->tm_yday;
  656. if (b->tm_year < a->tm_year)
  657. yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE);
  658. return yday;
  659. }
  660. #endif
  661. /* If A is the broken-down local time and B the broken-down UT for
  662. the same instant, return A's UT offset in seconds, where positive
  663. offsets are east of Greenwich. On failure, return LONG_MIN.
  664. If T is nonnull, *T is the timestamp that corresponds to A; call
  665. my_gmtime_r and use its result instead of B. Otherwise, B is the
  666. possibly nonnull result of an earlier call to my_gmtime_r. */
  667. static long
  668. gmtoff(struct tm const *a, time_t *t, struct tm const *b)
  669. {
  670. #ifdef TM_GMTOFF
  671. return a->TM_GMTOFF;
  672. #else
  673. struct tm tm;
  674. if (t)
  675. b = my_gmtime_r(t, &tm);
  676. if (! b)
  677. return LONG_MIN;
  678. else {
  679. int ayday = adjusted_yday(a, b);
  680. int byday = adjusted_yday(b, a);
  681. int days = ayday - byday;
  682. long hours = a->tm_hour - b->tm_hour + 24 * days;
  683. long minutes = a->tm_min - b->tm_min + 60 * hours;
  684. long seconds = a->tm_sec - b->tm_sec + 60 * minutes;
  685. return seconds;
  686. }
  687. #endif
  688. }
  689. static void
  690. show(timezone_t tz, char *zone, time_t t, bool v)
  691. {
  692. register struct tm * tmp;
  693. register struct tm * gmtmp;
  694. struct tm tm, gmtm;
  695. printf("%-*s ", longest, zone);
  696. if (v) {
  697. gmtmp = my_gmtime_r(&t, &gmtm);
  698. if (gmtmp == NULL) {
  699. printf(tformat(), t);
  700. } else {
  701. dumptime(gmtmp);
  702. printf(" UT");
  703. }
  704. printf(" = ");
  705. }
  706. tmp = my_localtime_rz(tz, &t, &tm);
  707. dumptime(tmp);
  708. if (tmp != NULL) {
  709. if (*abbr(tmp) != '\0')
  710. printf(" %s", abbr(tmp));
  711. if (v) {
  712. long off = gmtoff(tmp, NULL, gmtmp);
  713. printf(" isdst=%d", tmp->tm_isdst);
  714. if (off != LONG_MIN)
  715. printf(" gmtoff=%ld", off);
  716. }
  717. }
  718. printf("\n");
  719. if (tmp != NULL && *abbr(tmp) != '\0')
  720. abbrok(abbr(tmp), zone);
  721. }
  722. #if HAVE_SNPRINTF
  723. # define my_snprintf snprintf
  724. #else
  725. # include <stdarg.h>
  726. /* A substitute for snprintf that is good enough for zdump. */
  727. static int ATTRIBUTE_FORMAT((printf, 3, 4))
  728. my_snprintf(char *s, size_t size, char const *format, ...)
  729. {
  730. int n;
  731. va_list args;
  732. char const *arg;
  733. size_t arglen, slen;
  734. char buf[1024];
  735. va_start(args, format);
  736. if (strcmp(format, "%s") == 0) {
  737. arg = va_arg(args, char const *);
  738. arglen = strlen(arg);
  739. } else {
  740. n = vsprintf(buf, format, args);
  741. if (n < 0) {
  742. va_end(args);
  743. return n;
  744. }
  745. arg = buf;
  746. arglen = n;
  747. }
  748. slen = arglen < size ? arglen : size - 1;
  749. memcpy(s, arg, slen);
  750. s[slen] = '\0';
  751. n = arglen <= INT_MAX ? arglen : -1;
  752. va_end(args);
  753. return n;
  754. }
  755. #endif
  756. /* Store into BUF, of size SIZE, a formatted local time taken from *TM.
  757. Use ISO 8601 format +HH:MM:SS. Omit :SS if SS is zero, and omit
  758. :MM too if MM is also zero.
  759. Return the length of the resulting string. If the string does not
  760. fit, return the length that the string would have been if it had
  761. fit; do not overrun the output buffer. */
  762. static int
  763. format_local_time(char *buf, size_t size, struct tm const *tm)
  764. {
  765. int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
  766. return (ss
  767. ? my_snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
  768. : mm
  769. ? my_snprintf(buf, size, "%02d:%02d", hh, mm)
  770. : my_snprintf(buf, size, "%02d", hh));
  771. }
  772. /* Store into BUF, of size SIZE, a formatted UT offset for the
  773. localtime *TM corresponding to time T. Use ISO 8601 format
  774. +HHMMSS, or -HHMMSS for timestamps west of Greenwich; use the
  775. format -00 for unknown UT offsets. If the hour needs more than
  776. two digits to represent, extend the length of HH as needed.
  777. Otherwise, omit SS if SS is zero, and omit MM too if MM is also
  778. zero.
  779. Return the length of the resulting string, or -1 if the result is
  780. not representable as a string. If the string does not fit, return
  781. the length that the string would have been if it had fit; do not
  782. overrun the output buffer. */
  783. static int
  784. format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t)
  785. {
  786. long off = gmtoff(tm, &t, NULL);
  787. char sign = ((off < 0
  788. || (off == 0
  789. && (*abbr(tm) == '-' || strcmp(abbr(tm), "zzz") == 0)))
  790. ? '-' : '+');
  791. long hh;
  792. int mm, ss;
  793. if (off < 0)
  794. {
  795. if (off == LONG_MIN)
  796. return -1;
  797. off = -off;
  798. }
  799. ss = off % 60;
  800. mm = off / 60 % 60;
  801. hh = off / 60 / 60;
  802. return (ss || 100 <= hh
  803. ? my_snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
  804. : mm
  805. ? my_snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
  806. : my_snprintf(buf, size, "%c%02ld", sign, hh));
  807. }
  808. /* Store into BUF (of size SIZE) a quoted string representation of P.
  809. If the representation's length is less than SIZE, return the
  810. length; the representation is not null terminated. Otherwise
  811. return SIZE, to indicate that BUF is too small. */
  812. static size_t
  813. format_quoted_string(char *buf, size_t size, char const *p)
  814. {
  815. char *b = buf;
  816. size_t s = size;
  817. if (!s)
  818. return size;
  819. *b++ = '"', s--;
  820. for (;;) {
  821. char c = *p++;
  822. if (s <= 1)
  823. return size;
  824. switch (c) {
  825. default: *b++ = c, s--; continue;
  826. case '\0': *b++ = '"', s--; return size - s;
  827. case '"': case '\\': break;
  828. case ' ': c = 's'; break;
  829. case '\f': c = 'f'; break;
  830. case '\n': c = 'n'; break;
  831. case '\r': c = 'r'; break;
  832. case '\t': c = 't'; break;
  833. case '\v': c = 'v'; break;
  834. }
  835. *b++ = '\\', *b++ = c, s -= 2;
  836. }
  837. }
  838. /* Store into BUF (of size SIZE) a timestamp formatted by TIME_FMT.
  839. TM is the broken-down time, T the seconds count, AB the time zone
  840. abbreviation, and ZONE_NAME the zone name. Return true if
  841. successful, false if the output would require more than SIZE bytes.
  842. TIME_FMT uses the same format that strftime uses, with these
  843. additions:
  844. %f zone name
  845. %L local time as per format_local_time
  846. %Q like "U\t%Z\tD" where U is the UT offset as for format_utc_offset
  847. and D is the isdst flag; except omit D if it is zero, omit %Z if
  848. it equals U, quote and escape %Z if it contains nonalphabetics,
  849. and omit any trailing tabs. */
  850. static bool
  851. istrftime(char *buf, size_t size, char const *time_fmt,
  852. struct tm const *tm, time_t t, char const *ab, char const *zone_name)
  853. {
  854. char *b = buf;
  855. size_t s = size;
  856. char const *f = time_fmt, *p;
  857. for (p = f; ; p++)
  858. if (*p == '%' && p[1] == '%')
  859. p++;
  860. else if (!*p
  861. || (*p == '%'
  862. && (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) {
  863. size_t formatted_len;
  864. size_t f_prefix_len = p - f;
  865. size_t f_prefix_copy_size = p - f + 2;
  866. char fbuf[100];
  867. bool oversized = sizeof fbuf <= f_prefix_copy_size;
  868. char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf;
  869. memcpy(f_prefix_copy, f, f_prefix_len);
  870. strcpy(f_prefix_copy + f_prefix_len, "X");
  871. formatted_len = strftime(b, s, f_prefix_copy, tm);
  872. if (oversized)
  873. free(f_prefix_copy);
  874. if (formatted_len == 0)
  875. return false;
  876. formatted_len--;
  877. b += formatted_len, s -= formatted_len;
  878. if (!*p++)
  879. break;
  880. switch (*p) {
  881. case 'f':
  882. formatted_len = format_quoted_string(b, s, zone_name);
  883. break;
  884. case 'L':
  885. formatted_len = format_local_time(b, s, tm);
  886. break;
  887. case 'Q':
  888. {
  889. bool show_abbr;
  890. int offlen = format_utc_offset(b, s, tm, t);
  891. if (! (0 <= offlen && offlen < s))
  892. return false;
  893. show_abbr = strcmp(b, ab) != 0;
  894. b += offlen, s -= offlen;
  895. if (show_abbr) {
  896. char const *abp;
  897. size_t len;
  898. if (s <= 1)
  899. return false;
  900. *b++ = '\t', s--;
  901. for (abp = ab; is_alpha(*abp); abp++)
  902. continue;
  903. len = (!*abp && *ab
  904. ? my_snprintf(b, s, "%s", ab)
  905. : format_quoted_string(b, s, ab));
  906. if (s <= len)
  907. return false;
  908. b += len, s -= len;
  909. }
  910. formatted_len
  911. = (tm->tm_isdst
  912. ? my_snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
  913. : 0);
  914. }
  915. break;
  916. }
  917. if (s <= formatted_len)
  918. return false;
  919. b += formatted_len, s -= formatted_len;
  920. f = p + 1;
  921. }
  922. *b = '\0';
  923. return true;
  924. }
  925. /* Show a time transition. */
  926. static void
  927. showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
  928. char const *zone_name)
  929. {
  930. if (!tm) {
  931. printf(tformat(), t);
  932. putchar('\n');
  933. } else {
  934. char stackbuf[1000];
  935. size_t size = sizeof stackbuf;
  936. char *buf = stackbuf;
  937. char *bufalloc = NULL;
  938. while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
  939. size = sumsize(size, size);
  940. free(bufalloc);
  941. buf = bufalloc = xmalloc(size);
  942. }
  943. puts(buf);
  944. free(bufalloc);
  945. }
  946. }
  947. static char const *
  948. abbr(struct tm const *tmp)
  949. {
  950. #ifdef TM_ZONE
  951. return tmp->TM_ZONE;
  952. #else
  953. # if HAVE_TZNAME
  954. if (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst])
  955. return tzname[0 < tmp->tm_isdst];
  956. # endif
  957. return "";
  958. #endif
  959. }
  960. /*
  961. ** The code below can fail on certain theoretical systems;
  962. ** it works on all known real-world systems as of 2004-12-30.
  963. */
  964. static const char *
  965. tformat(void)
  966. {
  967. if (0 > (time_t) -1) { /* signed */
  968. if (sizeof (time_t) == sizeof (intmax_t))
  969. return "%"PRIdMAX;
  970. if (sizeof (time_t) > sizeof (long))
  971. return "%lld";
  972. if (sizeof (time_t) > sizeof (int))
  973. return "%ld";
  974. return "%d";
  975. }
  976. #ifdef PRIuMAX
  977. if (sizeof (time_t) == sizeof (uintmax_t))
  978. return "%"PRIuMAX;
  979. #endif
  980. if (sizeof (time_t) > sizeof (unsigned long))
  981. return "%llu";
  982. if (sizeof (time_t) > sizeof (unsigned int))
  983. return "%lu";
  984. return "%u";
  985. }
  986. static void
  987. dumptime(register const struct tm *timeptr)
  988. {
  989. static const char wday_name[][4] = {
  990. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  991. };
  992. static const char mon_name[][4] = {
  993. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  994. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  995. };
  996. register const char * wn;
  997. register const char * mn;
  998. register int lead;
  999. register int trail;
  1000. if (timeptr == NULL) {
  1001. printf("NULL");
  1002. return;
  1003. }
  1004. /*
  1005. ** The packaged localtime_rz and gmtime_r never put out-of-range
  1006. ** values in tm_wday or tm_mon, but since this code might be compiled
  1007. ** with other (perhaps experimental) versions, paranoia is in order.
  1008. */
  1009. if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
  1010. (int) (sizeof wday_name / sizeof wday_name[0]))
  1011. wn = "???";
  1012. else wn = wday_name[timeptr->tm_wday];
  1013. if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
  1014. (int) (sizeof mon_name / sizeof mon_name[0]))
  1015. mn = "???";
  1016. else mn = mon_name[timeptr->tm_mon];
  1017. printf("%s %s%3d %.2d:%.2d:%.2d ",
  1018. wn, mn,
  1019. timeptr->tm_mday, timeptr->tm_hour,
  1020. timeptr->tm_min, timeptr->tm_sec);
  1021. #define DIVISOR 10
  1022. trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
  1023. lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
  1024. trail / DIVISOR;
  1025. trail %= DIVISOR;
  1026. if (trail < 0 && lead > 0) {
  1027. trail += DIVISOR;
  1028. --lead;
  1029. } else if (lead < 0 && trail > 0) {
  1030. trail -= DIVISOR;
  1031. ++lead;
  1032. }
  1033. if (lead == 0)
  1034. printf("%d", trail);
  1035. else printf("%d%d", lead, ((trail < 0) ? -trail : trail));
  1036. }