File: [local] / src / usr.sbin / zdump / zdump.c (download)
Revision 1.14, Tue Mar 15 19:50:48 2016 UTC (8 years, 2 months ago) by millert
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4, OPENBSD_6_3_BASE, OPENBSD_6_3, OPENBSD_6_2_BASE, OPENBSD_6_2, OPENBSD_6_1_BASE, OPENBSD_6_1, OPENBSD_6_0_BASE, OPENBSD_6_0, HEAD Changes since 1.13: +9 -17 lines
Don't warn about valid time zone abbreviations. POSIX through 2000
says that an abbreviation cannot start with ':', and cannot contain
',', '-', '+', NUL, or a digit. POSIX from 2001 on changes this
rule to say that an abbreviation can contain only '-', '+', and
alphanumeric characters from the portable character set in the
current locale. To be portable to both sets of rules, an abbreviation
must therefore use only ASCII letters." Adapted from tzcode2015f.
OK deraadt@ mestre@
|
/* $OpenBSD: zdump.c,v 1.14 2016/03/15 19:50:48 millert Exp $ */
/*
** This file is in the public domain, so clarified as of
** 2009-05-17 by Arthur David Olson.
*/
/*
** This code has been made independent of the rest of the time
** conversion package to increase confidence in the verification it provides.
** You can use this code to help in verifying other implementations.
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#define ZDUMP_LO_YEAR (-500)
#define ZDUMP_HI_YEAR 2500
#define MAX_STRING_LENGTH 1024
#define TRUE 1
#define FALSE 0
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define HOURSPERDAY 24
#define EPOCH_YEAR 1970
#define TM_YEAR_BASE 1900
#define DAYSPERNYEAR 365
#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
#ifndef isleap_sum
/*
** See tzfile.h for details on isleap_sum.
*/
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
#endif /* !defined isleap_sum */
extern char **environ;
extern char *tzname[2];
extern char *__progname;
time_t absolute_min_time;
time_t absolute_max_time;
size_t longest;
int warned;
static char *abbr(struct tm *tmp);
static void abbrok(const char *abbrp, const char *zone);
static long delta(struct tm *newp, struct tm *oldp);
static void dumptime(const struct tm *tmp);
static time_t hunt(char *name, time_t lot, time_t hit);
static void setabsolutes(void);
static void show(char *zone, time_t t, int v);
static const char *tformat(void);
static time_t yeartot(long y);
static void usage(void);
static void
abbrok(const char * const abbrp, const char * const zone)
{
const char *cp;
char *wp;
if (warned)
return;
cp = abbrp;
wp = NULL;
while (isascii((unsigned char)*cp) &&
(isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
++cp;
if (cp - abbrp < 3)
wp = "has fewer than 3 characters";
else if (cp - abbrp > 6)
wp = "has more than 6 characters";
else if (*cp)
wp = "has characters other than ASCII alphanumerics, '-' or '+'";
else
return;
fflush(stdout);
fprintf(stderr, "%s: warning: zone \"%s\" abbreviation \"%s\" %s\n",
__progname, zone, abbrp, wp);
warned = TRUE;
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-v] [-c [loyear,]hiyear] zonename ...\n",
__progname);
exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
int i, c, vflag = 0;
char *cutarg = NULL;
long cutloyear = ZDUMP_LO_YEAR;
long cuthiyear = ZDUMP_HI_YEAR;
time_t cutlotime = 0, cuthitime = 0;
time_t now, t, newt;
struct tm tm, newtm, *tmp, *newtmp;
char **fakeenv;
if (pledge("stdio rpath", NULL) == -1) {
perror("pledge");
exit(1);
}
while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') {
switch (c) {
case 'v':
vflag = 1;
break;
case 'c':
cutarg = optarg;
break;
default:
usage();
break;
}
}
if (c != -1 ||
(optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
usage();
}
if (vflag) {
if (cutarg != NULL) {
long lo, hi;
char dummy;
if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
cuthiyear = hi;
} else if (sscanf(cutarg, "%ld,%ld%c",
&lo, &hi, &dummy) == 2) {
cutloyear = lo;
cuthiyear = hi;
} else {
fprintf(stderr, "%s: wild -c argument %s\n",
__progname, cutarg);
exit(EXIT_FAILURE);
}
}
setabsolutes();
cutlotime = yeartot(cutloyear);
cuthitime = yeartot(cuthiyear);
}
time(&now);
longest = 0;
for (i = optind; i < argc; ++i)
if (strlen(argv[i]) > longest)
longest = strlen(argv[i]);
{
int from, to;
for (i = 0; environ[i] != NULL; ++i)
continue;
fakeenv = reallocarray(NULL, i + 2, sizeof *fakeenv);
if (fakeenv == NULL ||
(fakeenv[0] = malloc(longest + 4)) == NULL) {
perror(__progname);
exit(EXIT_FAILURE);
}
to = 0;
strlcpy(fakeenv[to++], "TZ=", longest + 4);
for (from = 0; environ[from] != NULL; ++from)
if (strncmp(environ[from], "TZ=", 3) != 0)
fakeenv[to++] = environ[from];
fakeenv[to] = NULL;
environ = fakeenv;
}
for (i = optind; i < argc; ++i) {
char buf[MAX_STRING_LENGTH];
strlcpy(&fakeenv[0][3], argv[i], longest + 1);
if (!vflag) {
show(argv[i], now, FALSE);
continue;
}
warned = FALSE;
t = absolute_min_time;
show(argv[i], t, TRUE);
t += SECSPERHOUR * HOURSPERDAY;
show(argv[i], t, TRUE);
if (t < cutlotime)
t = cutlotime;
tmp = localtime(&t);
if (tmp != NULL) {
tm = *tmp;
strlcpy(buf, abbr(&tm), sizeof buf);
}
for ( ; ; ) {
if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
break;
newt = t + SECSPERHOUR * 12;
newtmp = localtime(&newt);
if (newtmp != NULL)
newtm = *newtmp;
if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
(delta(&newtm, &tm) != (newt - t) ||
newtm.tm_isdst != tm.tm_isdst ||
strcmp(abbr(&newtm), buf) != 0)) {
newt = hunt(argv[i], t, newt);
newtmp = localtime(&newt);
if (newtmp != NULL) {
newtm = *newtmp;
strlcpy(buf, abbr(&newtm), sizeof buf);
}
}
t = newt;
tm = newtm;
tmp = newtmp;
}
t = absolute_max_time;
t -= SECSPERHOUR * HOURSPERDAY;
show(argv[i], t, TRUE);
t += SECSPERHOUR * HOURSPERDAY;
show(argv[i], t, TRUE);
}
if (fflush(stdout) || ferror(stdout)) {
fprintf(stderr, "%s: ", __progname);
perror("Error writing to standard output");
exit(EXIT_FAILURE);
}
return 0;
}
static void
setabsolutes(void)
{
time_t t = 0, t1 = 1;
while (t < t1) {
t = t1;
t1 = 2 * t1 + 1;
}
absolute_max_time = t;
t = -t;
absolute_min_time = t - 1;
if (t < absolute_min_time)
absolute_min_time = t;
}
static time_t
yeartot(const long y)
{
long myy = EPOCH_YEAR, seconds;
time_t t = 0;
while (myy != y) {
if (myy < y) {
seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
++myy;
if (t > absolute_max_time - seconds) {
t = absolute_max_time;
break;
}
t += seconds;
} else {
--myy;
seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
if (t < absolute_min_time + seconds) {
t = absolute_min_time;
break;
}
t -= seconds;
}
}
return t;
}
static time_t
hunt(char *name, time_t lot, time_t hit)
{
time_t t;
long diff;
struct tm lotm, *lotmp;
struct tm tm, *tmp;
char loab[MAX_STRING_LENGTH];
lotmp = localtime(&lot);
if (lotmp != NULL) {
lotm = *lotmp;
strlcpy(loab, abbr(&lotm), sizeof loab);
}
for ( ; ; ) {
diff = (long) (hit - lot);
if (diff < 2)
break;
t = lot;
t += diff / 2;
if (t <= lot)
++t;
else if (t >= hit)
--t;
tmp = localtime(&t);
if (tmp != NULL)
tm = *tmp;
if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
(delta(&tm, &lotm) == (t - lot) &&
tm.tm_isdst == lotm.tm_isdst &&
strcmp(abbr(&tm), loab) == 0)) {
lot = t;
lotm = tm;
lotmp = tmp;
} else
hit = t;
}
show(name, lot, TRUE);
show(name, hit, TRUE);
return hit;
}
/*
** Thanks to Paul Eggert for logic used in delta.
*/
static long
delta(struct tm *newp, struct tm *oldp)
{
long result;
int tmy;
if (newp->tm_year < oldp->tm_year)
return -delta(oldp, newp);
result = 0;
for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
result += newp->tm_yday - oldp->tm_yday;
result *= HOURSPERDAY;
result += newp->tm_hour - oldp->tm_hour;
result *= MINSPERHOUR;
result += newp->tm_min - oldp->tm_min;
result *= SECSPERMIN;
result += newp->tm_sec - oldp->tm_sec;
return result;
}
static void
show(char *zone, time_t t, int v)
{
struct tm *tmp;
printf("%-*s ", (int) longest, zone);
if (v) {
tmp = gmtime(&t);
if (tmp == NULL) {
printf(tformat(), t);
} else {
dumptime(tmp);
printf(" UTC");
}
printf(" = ");
}
tmp = localtime(&t);
dumptime(tmp);
if (tmp != NULL) {
if (*abbr(tmp) != '\0')
printf(" %s", abbr(tmp));
if (v) {
printf(" isdst=%d", tmp->tm_isdst);
#ifdef TM_GMTOFF
printf(" gmtoff=%ld", tmp->TM_GMTOFF);
#endif /* defined TM_GMTOFF */
}
}
printf("\n");
if (tmp != NULL && *abbr(tmp) != '\0')
abbrok(abbr(tmp), zone);
}
static char *
abbr(struct tm *tmp)
{
char *result;
static char nada;
if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
return &nada;
result = tzname[tmp->tm_isdst];
return (result == NULL) ? &nada : result;
}
/*
** The code below can fail on certain theoretical systems;
** it works on all known real-world systems as of 2004-12-30.
*/
static const char *
tformat(void)
{
return "%lld";
}
static void
dumptime(const struct tm *timeptr)
{
static const char wday_name[][3] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static const char mon_name[][3] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
const char *wn, *mn;
int lead, trail;
if (timeptr == NULL) {
printf("NULL");
return;
}
/*
** The packaged versions of localtime and gmtime never put out-of-range
** values in tm_wday or tm_mon, but since this code might be compiled
** with other (perhaps experimental) versions, paranoia is in order.
*/
if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
(int) (sizeof wday_name / sizeof wday_name[0]))
wn = "???";
else
wn = wday_name[timeptr->tm_wday];
if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
(int) (sizeof mon_name / sizeof mon_name[0]))
mn = "???";
else
mn = mon_name[timeptr->tm_mon];
printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec);
#define DIVISOR 10
trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
trail / DIVISOR;
trail %= DIVISOR;
if (trail < 0 && lead > 0) {
trail += DIVISOR;
--lead;
} else if (lead < 0 && trail > 0) {
trail -= DIVISOR;
++lead;
}
if (lead == 0)
printf("%d", trail);
else
printf("%d%d", lead, ((trail < 0) ? -trail : trail));
}