Changeset View
Changeset View
Standalone View
Standalone View
contrib/tzcode/zdump.c
Show All 9 Lines | |||||
#ifndef NETBSD_INSPIRED | #ifndef NETBSD_INSPIRED | ||||
# define NETBSD_INSPIRED 1 | # define NETBSD_INSPIRED 1 | ||||
#endif | #endif | ||||
#include "private.h" | #include "private.h" | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#ifndef HAVE_SNPRINTF | #ifndef HAVE_SNPRINTF | ||||
# define HAVE_SNPRINTF (199901 <= __STDC_VERSION__) | # define HAVE_SNPRINTF (!PORT_TO_C89 || 199901 <= __STDC_VERSION__) | ||||
#endif | #endif | ||||
#ifndef HAVE_LOCALTIME_R | #ifndef HAVE_LOCALTIME_R | ||||
# define HAVE_LOCALTIME_R 1 | # define HAVE_LOCALTIME_R 1 | ||||
#endif | #endif | ||||
#ifndef HAVE_LOCALTIME_RZ | #ifndef HAVE_LOCALTIME_RZ | ||||
# ifdef TM_ZONE | # ifdef TM_ZONE | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | static time_t const absolute_max_time = | ||||
? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)) | ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)) | ||||
: -1); | : -1); | ||||
static size_t longest; | static size_t longest; | ||||
static char const *progname; | static char const *progname; | ||||
static bool warned; | static bool warned; | ||||
static bool errout; | static bool errout; | ||||
static char const *abbr(struct tm const *); | static char const *abbr(struct tm const *); | ||||
static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_REPRODUCIBLE; | ATTRIBUTE_REPRODUCIBLE static intmax_t delta(struct tm *, struct tm *); | ||||
static void dumptime(struct tm const *); | static void dumptime(struct tm const *); | ||||
static time_t hunt(timezone_t, time_t, time_t, bool); | static time_t hunt(timezone_t, time_t, time_t, bool); | ||||
static void show(timezone_t, char *, time_t, bool); | static void show(timezone_t, char *, time_t, bool); | ||||
static void showextrema(timezone_t, char *, time_t, struct tm *, time_t); | static void showextrema(timezone_t, char *, time_t, struct tm *, time_t); | ||||
static void showtrans(char const *, struct tm const *, time_t, char const *, | static void showtrans(char const *, struct tm const *, time_t, char const *, | ||||
char const *); | char const *); | ||||
static const char *tformat(void); | static const char *tformat(void); | ||||
static time_t yeartot(intmax_t) ATTRIBUTE_REPRODUCIBLE; | ATTRIBUTE_REPRODUCIBLE static time_t yeartot(intmax_t); | ||||
/* Is C an ASCII digit? */ | /* Is C an ASCII digit? */ | ||||
static bool | static bool | ||||
is_digit(char c) | is_digit(char c) | ||||
{ | { | ||||
return '0' <= c && c <= '9'; | return '0' <= c && c <= '9'; | ||||
} | } | ||||
Show All 11 Lines | switch (a) { | ||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': | ||||
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': | case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': | ||||
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': | case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': | ||||
case 'v': case 'w': case 'x': case 'y': case 'z': | case 'v': case 'w': case 'x': case 'y': case 'z': | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
static ATTRIBUTE_NORETURN void | ATTRIBUTE_NORETURN static void | ||||
size_overflow(void) | size_overflow(void) | ||||
{ | { | ||||
fprintf(stderr, _("%s: size overflow\n"), progname); | fprintf(stderr, _("%s: size overflow\n"), progname); | ||||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||||
} | } | ||||
/* Return A + B, exiting if the result would overflow either ptrdiff_t | /* Return A + B, exiting if the result would overflow either ptrdiff_t | ||||
or size_t. */ | or size_t. A and B are both nonnegative. */ | ||||
static ATTRIBUTE_REPRODUCIBLE size_t | ATTRIBUTE_REPRODUCIBLE static ptrdiff_t | ||||
sumsize(size_t a, size_t b) | sumsize(ptrdiff_t a, ptrdiff_t b) | ||||
{ | { | ||||
#ifdef ckd_add | #ifdef ckd_add | ||||
size_t sum; | ptrdiff_t sum; | ||||
if (!ckd_add(&sum, a, b)) | if (!ckd_add(&sum, a, b) && sum <= INDEX_MAX) | ||||
return sum; | return sum; | ||||
#else | #else | ||||
if (a <= SIZE_MAX && b <= SIZE_MAX - a) | if (a <= INDEX_MAX && b <= INDEX_MAX - a) | ||||
return a + b; | return a + b; | ||||
#endif | #endif | ||||
size_overflow(); | size_overflow(); | ||||
} | } | ||||
/* Return the size of of the string STR, including its trailing NUL. | |||||
Report an error and exit if this would exceed INDEX_MAX which means | |||||
pointer subtraction wouldn't work. */ | |||||
static ptrdiff_t | |||||
xstrsize(char const *str) | |||||
{ | |||||
size_t len = strlen(str); | |||||
if (len < INDEX_MAX) | |||||
return len + 1; | |||||
size_overflow(); | |||||
} | |||||
/* Return a pointer to a newly allocated buffer of size SIZE, exiting | /* Return a pointer to a newly allocated buffer of size SIZE, exiting | ||||
on failure. SIZE should be nonzero. */ | on failure. SIZE should be positive. */ | ||||
static void * ATTRIBUTE_MALLOC | ATTRIBUTE_MALLOC static void * | ||||
xmalloc(size_t size) | xmalloc(ptrdiff_t size) | ||||
{ | { | ||||
void *p = malloc(size); | void *p = malloc(size); | ||||
if (!p) { | if (!p) { | ||||
fprintf(stderr, _("%s: Memory exhausted\n"), progname); | fprintf(stderr, _("%s: Memory exhausted\n"), progname); | ||||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||||
} | } | ||||
return p; | return p; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | localtime_r(time_t *tp, struct tm *tmp) | ||||
} | } | ||||
return r; | return r; | ||||
} | } | ||||
# endif | # endif | ||||
# undef localtime_rz | # undef localtime_rz | ||||
# define localtime_rz zdump_localtime_rz | # define localtime_rz zdump_localtime_rz | ||||
static struct tm * | static struct tm * | ||||
localtime_rz(timezone_t rz __unused, time_t *tp, struct tm *tmp) | localtime_rz(ATTRIBUTE_MAYBE_UNUSED timezone_t rz, time_t *tp, struct tm *tmp) | ||||
{ | { | ||||
return localtime_r(tp, tmp); | return localtime_r(tp, tmp); | ||||
} | } | ||||
# ifdef TYPECHECK | # ifdef TYPECHECK | ||||
# undef mktime_z | # undef mktime_z | ||||
# define mktime_z zdump_mktime_z | # define mktime_z zdump_mktime_z | ||||
static time_t | static time_t | ||||
mktime_z(timezone_t tz, struct tm *tmp) | mktime_z(timezone_t tz, struct tm *tmp) | ||||
{ | { | ||||
return mktime(tmp); | return mktime(tmp); | ||||
} | } | ||||
# endif | # endif | ||||
# undef tzalloc | # undef tzalloc | ||||
# undef tzfree | # undef tzfree | ||||
# define tzalloc zdump_tzalloc | # define tzalloc zdump_tzalloc | ||||
# define tzfree zdump_tzfree | # define tzfree zdump_tzfree | ||||
static timezone_t | static timezone_t | ||||
tzalloc(char const *val) | tzalloc(char const *val) | ||||
{ | { | ||||
# if HAVE_SETENV | # if HAVE_SETENV | ||||
if (setenv("TZ", val, 1) != 0) { | if (setenv("TZ", val, 1) != 0) { | ||||
perror("setenv"); | char const *e = strerror(errno); | ||||
fprintf(stderr, _("%s: setenv: %s\n"), progname, e); | |||||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||||
} | } | ||||
tzset(); | tzset(); | ||||
return &optarg; /* Any valid non-null char ** will do. */ | return &optarg; /* Any valid non-null char ** will do. */ | ||||
# else | # else | ||||
enum { TZeqlen = 3 }; | enum { TZeqlen = 3 }; | ||||
static char const TZeq[TZeqlen] = "TZ="; | static char const TZeq[TZeqlen] = "TZ="; | ||||
static char **fakeenv; | static char **fakeenv; | ||||
static ptrdiff_t fakeenv0size; | static ptrdiff_t fakeenv0size; | ||||
void *freeable = NULL; | void *freeable = NULL; | ||||
char **env = fakeenv, **initial_environ; | char **env = fakeenv, **initial_environ; | ||||
size_t valsize = strlen(val) + 1; | ptrdiff_t valsize = xstrsize(val); | ||||
if (fakeenv0size < valsize) { | if (fakeenv0size < valsize) { | ||||
char **e = environ, **to; | char **e = environ, **to; | ||||
ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */ | ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */ | ||||
while (*e++) { | while (*e++) { | ||||
# ifdef ckd_add | # ifdef ckd_add | ||||
if (ckd_add(&initial_nenvptrs, initial_envptrs, 1) | if (ckd_add(&initial_nenvptrs, initial_nenvptrs, 1) | ||||
|| SIZE_MAX < initial_envptrs) | || INDEX_MAX < initial_nenvptrs) | ||||
size_overflow(); | size_overflow(); | ||||
# else | # else | ||||
if (initial_nenvptrs == min(PTRDIFF_MAX, SIZE_MAX) / sizeof *environ) | if (initial_nenvptrs == INDEX_MAX / sizeof *environ) | ||||
size_overflow(); | size_overflow(); | ||||
initial_nenvptrs++; | initial_nenvptrs++; | ||||
# endif | # endif | ||||
} | } | ||||
fakeenv0size = sumsize(valsize, valsize); | fakeenv0size = sumsize(valsize, valsize); | ||||
fakeenv0size = max(fakeenv0size, 64); | fakeenv0size = max(fakeenv0size, 64); | ||||
freeable = env; | freeable = env; | ||||
fakeenv = env = | fakeenv = env = | ||||
Show All 10 Lines | # endif | ||||
environ = env; | environ = env; | ||||
tzset(); | tzset(); | ||||
free(freeable); | free(freeable); | ||||
return initial_environ; | return initial_environ; | ||||
# endif | # endif | ||||
} | } | ||||
static void | static void | ||||
tzfree(timezone_t initial_environ) | tzfree(ATTRIBUTE_MAYBE_UNUSED timezone_t initial_environ) | ||||
{ | { | ||||
# if !HAVE_SETENV | # if !HAVE_SETENV | ||||
environ = initial_environ; | environ = initial_environ; | ||||
tzset(); | tzset(); | ||||
# else | # else | ||||
(void)initial_environ; | (void)initial_environ; | ||||
# endif | # endif | ||||
} | } | ||||
#endif /* ! USE_LOCALTIME_RZ */ | #endif /* ! USE_LOCALTIME_RZ */ | ||||
/* A UT time zone, and its initializer. */ | /* A UT time zone, and its initializer. */ | ||||
static timezone_t gmtz; | static timezone_t gmtz; | ||||
static void | static void | ||||
gmtzinit(void) | gmtzinit(void) | ||||
{ | { | ||||
if (USE_LOCALTIME_RZ) { | if (USE_LOCALTIME_RZ) { | ||||
/* Try "GMT" first to find out whether this is one of the rare | /* Try "GMT" first to find out whether this is one of the rare | ||||
platforms where time_t counts leap seconds; this works due to | platforms where time_t counts leap seconds; this works due to | ||||
the "Zone GMT 0 - GMT" line in the "etcetera" file. If "GMT" | the "Zone GMT 0 - GMT" line in the "etcetera" file. If "GMT" | ||||
fails, fall back on "GMT0" which might be similar due to the | fails, fall back on "GMT0" which might be similar due to the | ||||
"Link GMT GMT0" line in the "backward" file, and which | "Link GMT GMT0" line in the "backward" file, and which | ||||
should work on all POSIX platforms. The rest of zdump does not | should work on all POSIX platforms. The rest of zdump does not | ||||
use the "GMT" abbreviation that comes from this setting, so it | use the "GMT" abbreviation that comes from this setting, so it | ||||
is OK to use "GMT" here rather than the more-modern "UTC" which | is OK to use "GMT" here rather than the modern "UTC" which | ||||
would not work on platforms that omit the "backward" file. */ | would not work on platforms that omit the "backward" file. */ | ||||
gmtz = tzalloc("GMT"); | gmtz = tzalloc("GMT"); | ||||
if (!gmtz) { | if (!gmtz) { | ||||
static char const gmt0[] = "GMT0"; | static char const gmt0[] = "GMT0"; | ||||
gmtz = tzalloc(gmt0); | gmtz = tzalloc(gmt0); | ||||
if (!gmtz) { | if (!gmtz) { | ||||
perror(gmt0); | char const *e = strerror(errno); | ||||
fprintf(stderr, _("%s: unknown timezone '%s': %s\n"), | |||||
progname, gmt0, e); | |||||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* Convert *TP to UT, storing the broken-down time into *TMP. | /* Convert *TP to UT, storing the broken-down time into *TMP. | ||||
Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP), | Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP), | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | abbrok(const char *const abbrp, const char *const zone) | ||||
fprintf(stderr, | fprintf(stderr, | ||||
_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), | _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"), | ||||
progname, zone, abbrp, wp); | progname, zone, abbrp, wp); | ||||
warned = errout = true; | warned = errout = true; | ||||
} | } | ||||
/* Return a time zone abbreviation. If the abbreviation needs to be | /* Return a time zone abbreviation. If the abbreviation needs to be | ||||
saved, use *BUF (of size *BUFALLOC) to save it, and return the | saved, use *BUF (of size *BUFALLOC) to save it, and return the | ||||
abbreviation in the possibly-reallocated *BUF. Otherwise, just | abbreviation in the possibly reallocated *BUF. Otherwise, just | ||||
return the abbreviation. Get the abbreviation from TMP. | return the abbreviation. Get the abbreviation from TMP. | ||||
Exit on memory allocation failure. */ | Exit on memory allocation failure. */ | ||||
static char const * | static char const * | ||||
saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp) | saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp) | ||||
{ | { | ||||
char const *ab = abbr(tmp); | char const *ab = abbr(tmp); | ||||
if (HAVE_LOCALTIME_RZ) | if (HAVE_LOCALTIME_RZ) | ||||
return ab; | return ab; | ||||
else { | else { | ||||
size_t ablen = strlen(ab); | ptrdiff_t absize = xstrsize(ab); | ||||
if ((size_t)*bufalloc <= ablen) { | if (*bufalloc < absize) { | ||||
free(*buf); | free(*buf); | ||||
/* Make the new buffer at least twice as long as the old, | /* Make the new buffer at least twice as long as the old, | ||||
to avoid O(N**2) behavior on repeated calls. */ | to avoid O(N**2) behavior on repeated calls. */ | ||||
*bufalloc = sumsize(*bufalloc, ablen + 1); | *bufalloc = sumsize(*bufalloc, absize); | ||||
*buf = xmalloc(*bufalloc); | *buf = xmalloc(*bufalloc); | ||||
} | } | ||||
return strcpy(*buf, ab); | return strcpy(*buf, ab); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 152 Lines • ▼ Show 20 Lines | arg_processing_done:; | ||||
for (i = optind; i < argc; ++i) { | for (i = optind; i < argc; ++i) { | ||||
timezone_t tz = tzalloc(argv[i]); | timezone_t tz = tzalloc(argv[i]); | ||||
char const *ab; | char const *ab; | ||||
time_t t; | time_t t; | ||||
struct tm tm, newtm; | struct tm tm, newtm; | ||||
bool tm_ok; | bool tm_ok; | ||||
if (!tz) { | if (!tz) { | ||||
perror(argv[i]); | char const *e = strerror(errno); | ||||
fprintf(stderr, _("%s: unknown timezone '%s': %s\n"), | |||||
progname, argv[1], e); | |||||
return EXIT_FAILURE; | return EXIT_FAILURE; | ||||
} | } | ||||
if (now) { | if (now) { | ||||
show(tz, argv[i], now, false); | show(tz, argv[i], now, false); | ||||
tzfree(tz); | tzfree(tz); | ||||
continue; | continue; | ||||
} | } | ||||
warned = false; | warned = false; | ||||
▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | /* Convert LOT into a broken-down time here, even though our | ||||
tzname may have been altered since our caller broke down | tzname may have been altered since our caller broke down | ||||
LOT, and tzname needs to be changed back. */ | LOT, and tzname needs to be changed back. */ | ||||
bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL; | bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL; | ||||
bool tm_ok; | bool tm_ok; | ||||
char const *ab = lotm_ok ? saveabbr(&loab, &loabsize, &lotm) : NULL; | char const *ab = lotm_ok ? saveabbr(&loab, &loabsize, &lotm) : NULL; | ||||
for ( ; ; ) { | for ( ; ; ) { | ||||
/* T = average of LOT and HIT, rounding down. | /* T = average of LOT and HIT, rounding down. | ||||
Avoid overflow, even on oddball C89 platforms | Avoid overflow. */ | ||||
where / rounds down and TIME_T_MIN == -TIME_T_MAX | int rem_sum = lot % 2 + hit % 2; | ||||
so lot / 2 + hit / 2 might overflow. */ | time_t t = (rem_sum == 2) - (rem_sum < 0) + lot / 2 + hit / 2; | ||||
time_t t = (lot / 2 | |||||
- ((lot % 2 + hit % 2) < 0) | |||||
+ ((lot % 2 + hit % 2) == 2) | |||||
+ hit / 2); | |||||
if (t == lot) | if (t == lot) | ||||
break; | break; | ||||
tm_ok = my_localtime_rz(tz, &t, &tm) != NULL; | tm_ok = my_localtime_rz(tz, &t, &tm) != NULL; | ||||
if (lotm_ok == tm_ok | if (lotm_ok == tm_ok | ||||
&& (only_ok | && (only_ok | ||||
|| (ab && tm.tm_isdst == lotm.tm_isdst | || (ab && tm.tm_isdst == lotm.tm_isdst | ||||
&& delta(&tm, &lotm) == t - lot | && delta(&tm, &lotm) == t - lot | ||||
&& strcmp(abbr(&tm), ab) == 0))) { | && strcmp(abbr(&tm), ab) == 0))) { | ||||
▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
#if HAVE_SNPRINTF | #if HAVE_SNPRINTF | ||||
# define my_snprintf snprintf | # define my_snprintf snprintf | ||||
#else | #else | ||||
# include <stdarg.h> | # include <stdarg.h> | ||||
/* A substitute for snprintf that is good enough for zdump. */ | /* A substitute for snprintf that is good enough for zdump. */ | ||||
static int ATTRIBUTE_FORMAT((printf, 3, 4)) | ATTRIBUTE_FORMAT((printf, 3, 4)) static int | ||||
my_snprintf(char *s, size_t size, char const *format, ...) | my_snprintf(char *s, size_t size, char const *format, ...) | ||||
{ | { | ||||
int n; | int n; | ||||
va_list args; | va_list args; | ||||
char const *arg; | char const *arg; | ||||
size_t arglen, slen; | size_t arglen, slen; | ||||
char buf[1024]; | char buf[1024]; | ||||
va_start(args, format); | va_start(args, format); | ||||
▲ Show 20 Lines • Show All 236 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
** The code below can fail on certain theoretical systems; | ** The code below can fail on certain theoretical systems; | ||||
** it works on all known real-world systems as of 2022-01-25. | ** it works on all known real-world systems as of 2022-01-25. | ||||
*/ | */ | ||||
static const char * | static const char * | ||||
tformat(void) | tformat(void) | ||||
{ | { | ||||
#if HAVE_GENERIC | #if HAVE__GENERIC | ||||
/* C11-style _Generic is more likely to return the correct | /* C11-style _Generic is more likely to return the correct | ||||
format when distinct types have the same size. */ | format when distinct types have the same size. */ | ||||
char const *fmt = | char const *fmt = | ||||
_Generic(+ (time_t) 0, | _Generic(+ (time_t) 0, | ||||
int: "%d", long: "%ld", long long: "%lld", | int: "%d", long: "%ld", long long: "%lld", | ||||
unsigned: "%u", unsigned long: "%lu", | unsigned: "%u", unsigned long: "%lu", | ||||
unsigned long long: "%llu", | unsigned long long: "%llu", | ||||
default: NULL); | default: NULL); | ||||
▲ Show 20 Lines • Show All 71 Lines • Show Last 20 Lines |