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