/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* #define LEAPSECOND_SUPPORT
*/
/*
* Regardless of the type of time_t, we do our work using this type.
*/
typedef int zic_t;
#include "private.h"
#include <tzfile.h> /* this is in system headers at Sun */
#include <ctype.h>
#include <locale.h>
#include <stdlib.h> /* for getopt */
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
#ifdef S_IRUSR
#else
#endif
struct rule {
const char *r_filename;
int r_linenum;
const char *r_name;
const char *r_yrtype;
int r_dayofmonth;
int r_wday;
/* or wall clock time if FALSE */
/* or local time if FALSE */
};
/*
* r_dycode r_dayofmonth r_wday
*/
struct zone {
const char *z_filename;
int z_linenum;
const char *z_name;
long z_gmtoff;
const char *z_rule;
const char *z_format;
long z_stdoff;
int z_nrules;
};
#ifdef LEAPSECOND_SUPPORT
static void adjleap(void);
#endif
static void associate(void);
static long eitol(int i);
#ifdef LEAPSECOND_SUPPORT
#endif
static int lowerit(int c);
static void setboundaries(void);
static void usage(void);
static int charcnt;
static int errors;
static const char *filename;
static int leapcnt;
static int linenum;
static int max_year;
static int max_year_representable;
static int min_year;
static int min_year_representable;
static int noise;
static const char *rfilename;
static int rlinenum;
static const char *progname;
static int timecnt;
static int typecnt;
/*
* Line codes.
*/
#define LC_RULE 0
/*
* Which fields are which on a Zone line.
*/
/*
* Which fields are which on a Zone continuation line.
*/
#define ZFC_GMTOFF 0
/*
* Which files are which on a Rule line.
*/
/*
* Which fields are which on a Link line.
*/
/*
* Which fields are which on a Leap line.
*/
/*
* Year synonyms.
*/
#define YR_MINIMUM 0
struct link {
const char *l_filename;
int l_linenum;
const char *l_from;
const char *l_to;
};
static int nlinks;
struct lookup {
const char *l_word;
const int l_value;
};
{ "Rule", LC_RULE },
{ "Zone", LC_ZONE },
{ "Link", LC_LINK },
{ "Leap", LC_LEAP },
{ NULL, 0}
};
{ "January", TM_JANUARY },
{ "February", TM_FEBRUARY },
{ "March", TM_MARCH },
{ "April", TM_APRIL },
{ "May", TM_MAY },
{ "June", TM_JUNE },
{ "July", TM_JULY },
{ "August", TM_AUGUST },
{ "September", TM_SEPTEMBER },
{ "October", TM_OCTOBER },
{ "November", TM_NOVEMBER },
{ "December", TM_DECEMBER },
{ NULL, 0 }
};
{ "Sunday", TM_SUNDAY },
{ "Monday", TM_MONDAY },
{ "Tuesday", TM_TUESDAY },
{ "Wednesday", TM_WEDNESDAY },
{ "Thursday", TM_THURSDAY },
{ "Friday", TM_FRIDAY },
{ "Saturday", TM_SATURDAY },
{ NULL, 0 }
};
{ "last-Sunday", TM_SUNDAY },
{ "last-Monday", TM_MONDAY },
{ "last-Tuesday", TM_TUESDAY },
{ "last-Wednesday", TM_WEDNESDAY },
{ "last-Thursday", TM_THURSDAY },
{ "last-Friday", TM_FRIDAY },
{ "last-Saturday", TM_SATURDAY },
{ NULL, 0 }
};
{ "minimum", YR_MINIMUM },
{ "maximum", YR_MAXIMUM },
{ NULL, 0 }
};
{ "minimum", YR_MINIMUM },
{ "maximum", YR_MAXIMUM },
{ "only", YR_ONLY },
{ NULL, 0 }
};
{ "Rolling", TRUE },
{ "Stationary", FALSE },
{ NULL, 0 }
};
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
};
static struct attype {
unsigned char type;
/*
* Memory allocation.
*/
static char *
char * const ptr;
{
progname, e);
}
return (ptr);
}
/*
* Error handling.
*/
static void
const char * const name;
const int num;
const char * const rname;
const int rnum;
{
}
static void
const char * const name;
const int num;
{
}
static void
const char * const string;
{
/*
* Match the format of "cc" to allow sh users to
* zic ... 2>&1 | error -t "*" -v
* on BSD systems.
*/
++errors;
}
static void
const char * const string;
{
char *cp;
--errors;
}
static void
usage(void)
{
#ifdef LEAPSECOND_SUPPORT
"[ --version ] [ -s ] [ -v ] [ -l localtime ] "
"\n\t[ -p posixrules ] [ -d directory ] [ -L leapseconds ] "
#else /* ! LEAPSECOND_SUPPORT */
"[ --version ] [ -s ] [ -v ] [ -l localtime ]"
"\n\t[ -p posixrules ] [ -d directory ] [ -y yearistype ] "
#endif /* LEAPSECOND_SUPPORT */
}
static const char *psxrules;
static const char *lcltime;
static const char *directory;
static const char *leapsec;
static const char *yitcommand;
int
int argc;
char *argv[];
{
register int i;
register int j;
register int c;
#if !defined(TEXT_DOMAIN)
#endif
(void) textdomain(TEXT_DOMAIN);
for (i = 1; i < argc; ++i)
}
#ifdef LEAPSECOND_SUPPORT
#else
#endif
switch (c) {
default:
usage();
case 'd':
else {
"%s: More than one -d option specified\n"),
progname);
}
break;
case 'l':
else {
"%s: More than one -l option specified\n"),
progname);
}
break;
case 'p':
else {
"%s: More than one -p option specified\n"),
progname);
}
break;
case 'y':
if (yitcommand == NULL)
yitcommand = optarg;
else {
"%s: More than one -y option specified\n"),
progname);
}
break;
#ifdef LEAPSECOND_SUPPORT
case 'L':
else {
"%s: More than one -L option specified\n"),
progname);
}
break;
#endif /* LEAPSECOND_SUPPORT */
case 'v':
break;
case 's':
break;
}
usage(); /* usage message by request */
if (yitcommand == NULL)
yitcommand = "yearistype";
#ifdef LEAPSECOND_SUPPORT
adjleap();
}
#endif /* LEAPSECOND_SUPPORT */
if (errors)
associate();
for (i = 0; i < nzones; i = j) {
/*
* Find the next non-continuation zone entry.
*/
continue;
}
/*
* Make links.
*/
for (i = 0; i < nlinks; ++i) {
if (noise)
for (j = 0; j < nlinks; ++j)
}
}
}
}
static void
const char * const fromfield;
const char * const tofield;
{
register char *fromname;
register char *toname;
if (fromfield[0] == '/')
else {
}
if (tofield[0] == '/')
else {
}
/*
* We get to be careful here since
* there's a fair chance of root running us.
*/
int result;
const char *s = tofield;
"../");
if (result == 0)
"hard link failed, symbolic link used"));
}
if (result != 0) {
"%s: Can't link from %s to %s: %s\n"),
}
}
}
#ifndef INT_MAX
#endif /* !defined INT_MAX */
#ifndef INT_MIN
#endif /* !defined INT_MIN */
/*
* The tz file format currently allows at most 32-bit quantities.
* This restriction should be removed before signed 32-bit values
* wrap around in 2038, but unfortunately this will require a
* change to the tz file format.
*/
/* CSTYLED */
static void
setboundaries(void)
{
register int i;
if (TYPE_SIGNED(zic_t)) {
min_time = -1;
for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
min_time *= 2;
if (sflag)
min_time = 0;
} else {
min_time = 0;
for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
max_time *= 2;
--max_time;
}
{
time_t t;
}
}
static int
const char * const name;
{
register char *myname;
register int accres;
return (accres == 0);
}
/*
* Associate sets of rules with zones.
*/
/*
* Sort by rule name.
*/
static int
const void * cp1;
const void * cp2;
{
}
static void
associate(void)
{
register int i, j;
if (nrules != 0) {
for (i = 0; i < nrules - 1; ++i) {
continue;
continue;
for (j = i + 2; j < nrules; ++j) {
break;
rules[j].r_filename) == 0)
continue;
rules[j].r_filename) == 0)
continue;
break;
}
i = j - 1;
}
}
for (i = 0; i < nzones; ++i) {
}
break;
for (i = 0; i < nzones; ++i) {
continue;
}
}
for (i = 0; i < nzones; ++i) {
/*
* Maybe we have a local standard time offset.
*/
/*
* Note, though, that if there's no rule,
* a '%s' in the format is a bad thing.
*/
}
}
if (errors)
}
static void
const char *name;
{
register char **fields;
register char *cp;
register int nfields;
register int wantcont;
register int num;
}
break;
}
*cp = '\0';
nfields = 0;
static char nada;
++nfields;
}
if (nfields == 0) {
/* nothing to do */
} else if (wantcont) {
} else {
case LC_RULE:
break;
case LC_ZONE:
break;
case LC_LINK:
break;
#ifdef LEAPSECOND_SUPPORT
case LC_LEAP:
"%s: Leap line in non leap seconds file %s\n"),
break;
#endif /* LEAPSECOND_SUPPORT */
default: /* "cannot happen" */
"%s: panic: Invalid l_value %d\n"),
}
}
}
}
}
if (wantcont)
}
/*
* Convert a string of one of the forms
* h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
* into a number of seconds.
* A null string maps to zero.
* Call error with errstring and return zero on errors.
*/
static long
const char *string;
const char * const errstring;
const int signable;
{
long hh;
return (0);
if (!signable)
sign = 1;
else if (*string == '-') {
sign = -1;
++string;
} else sign = 1;
ss = 0;
return (0);
}
if (hh < 0 ||
return (0);
}
return (0);
}
gettext("24:00 not handled by pre-1998 versions of zic"));
"pre-2007 versions of zic"));
}
static void
register char ** const fields;
const int nfields;
{
static struct rule r;
if (nfields != RULE_FIELDS) {
return;
}
return;
}
r.r_filename = filename;
TRUE);
}
static int
register char ** const fields;
const int nfields;
{
register int i;
static char *buf;
return (FALSE);
}
gettext("\"Zone %s\" line and -l option are mutually exclusive"),
return (FALSE);
}
gettext("\"Zone %s\" line and -p option are mutually exclusive"),
return (FALSE);
}
for (i = 0; i < nzones; ++i)
gettext("duplicate zone name %s (file \"%s\", line %d)"),
zones[i].z_filename,
return (FALSE);
}
}
static int
register char ** const fields;
const int nfields;
{
"wrong number of fields on Zone continuation line"));
return (FALSE);
}
}
static int
register char ** const fields;
const int nfields;
const int iscont;
{
register char *cp;
static struct zone z;
register int hasuntil;
if (iscont) {
} else {
}
z.z_filename = filename;
TRUE);
return (FALSE);
}
}
if (hasuntil) {
rulesub(&z.z_untilrule,
"only",
"",
(nfields > i_untilmonth) ?
z.z_untilrule.r_loyear);
z.z_untiltime > min_time &&
z.z_untiltime < max_time &&
"Zone continuation line end time is not after end time of previous line"));
return (FALSE);
}
}
/*
* If there was an UNTIL field on this line,
* there's more information about the zone on the next line.
*/
return (hasuntil);
}
#ifdef LEAPSECOND_SUPPORT
static void
register char ** const fields;
const int nfields;
{
register const char *cp;
register int i, j;
zic_t t;
if (nfields != LEAP_FIELDS) {
return;
}
dayoff = 0;
/*
* Leapin' Lizards!
*/
return;
}
j = EPOCH_YEAR;
while (j != year) {
if (year > j) {
++j;
} else {
--j;
}
}
return;
}
j = TM_JANUARY;
while (j != month) {
++j;
}
return;
}
return;
}
return;
}
return;
}
{
register int positive;
int count;
count = 1;
count = 2;
count = 1;
count = 2;
} else {
return;
}
"illegal Rolling/Stationary field on Leap line"));
return;
}
}
}
#endif /* LEAPSECOND_SUPPORT */
static void
register char ** const fields;
const int nfields;
{
struct link l;
if (nfields != LINK_FIELDS) {
return;
}
return;
}
return;
}
l.l_filename = filename;
}
static void
const char * const loyearp;
const char * const hiyearp;
const char * const typep;
const char * const monthp;
const char * const dayp;
const char * const timep;
{
register const char *cp;
register char *dp;
register char *ep;
return;
}
if (*dp != '\0') {
case 's': /* Standard */
*ep = '\0';
break;
case 'w': /* Wall */
*ep = '\0';
break;
case 'g': /* Greenwich */
case 'u': /* Universal */
case 'z': /* Zulu */
*ep = '\0';
break;
}
}
/*
* Year work.
*/
case YR_MINIMUM:
break;
case YR_MAXIMUM:
break;
default: /* "cannot happen" */
gettext("%s: panic: Invalid l_value %d\n"),
}
return;
} else if (noise) {
"starting year too low to be represented"));
"starting year too high to be represented"));
}
case YR_MINIMUM:
break;
case YR_MAXIMUM:
break;
case YR_ONLY:
break;
default: /* "cannot happen" */
gettext("%s: panic: Invalid l_value %d\n"),
}
return;
} else if (noise) {
"ending year too low to be represented"));
"ending year too high to be represented"));
}
return;
}
if (*typep == '\0')
else {
return;
}
}
/*
* Day work.
* Accept things such as:
* 1
* last-Sunday
* Sun<=20
* Sun>=7
*/
} else {
else {
}
*ep++ = 0;
if (*ep++ != '=') {
return;
}
return;
}
}
rp->r_dayofmonth <= 0 ||
return;
}
}
}
static void
const long val;
char * const buf;
{
register int i;
register long shift;
}
static void
const long val;
{
}
static int
const void *avp;
const void *bvp;
{
return (-1);
return (1);
else return (0);
}
static void
const char * const name;
{
register int i, j;
static char *fullname;
/*
* Sort.
*/
if (timecnt > 1)
/*
* Optimize.
*/
{
int fromi;
int toi;
toi = 0;
fromi = 0;
++fromi;
if (isdsts[0] == 0)
++fromi; /* handled by default rule */
continue;
}
if (toi == 0 ||
}
}
/*
* Transfer.
*/
for (i = 0; i < timecnt; ++i) {
}
/*
* Remove old file, if any, to snap links.
*/
}
"%s: Can't create %s: %s\n"),
}
}
for (i = 0; i < timecnt; ++i) {
j = leapcnt;
while (--j >= 0)
break;
}
}
if (timecnt > 0)
for (i = 0; i < typecnt; ++i) {
}
if (charcnt != 0)
for (i = 0; i < leapcnt; ++i) {
if (roll[i]) {
j = 0;
while (isdsts[j])
if (++j >= typecnt) {
j = 0;
break;
}
} else {
j = 1;
++j;
j = types[j - 1];
}
}
for (i = 0; i < typecnt; ++i)
for (i = 0; i < typecnt; ++i)
}
}
static void
char * const abbr;
const char * const format;
const char * const letters;
const int isdst;
{
else
} else if (isdst)
else {
}
}
static void
const int zonecount;
{
register int i, j;
register long gmtoff;
register long stdoff;
register int year;
register long startoff;
register int startttisstd;
register int startttisgmt;
register int type;
/*
* Now. . .finally. . .generate some useful data!
*/
timecnt = 0;
typecnt = 0;
charcnt = 0;
/*
* Thanks to Earl Chew
* for noting the need to unconditionally initialize startttisstd.
*/
for (i = 0; i < zonecount; ++i) {
/*
* A guess that may well be corrected later.
*/
stdoff = 0;
continue;
*startbuf = '\0';
if (usestart) {
} else if (stdoff != 0)
} else
break;
/*
* Mark which rules to do in the current year.
* For those to do, calculate rpytime(rp, year);
*/
}
for (;;) {
register int k;
register long offset;
if (useuntil) {
/*
* Turn untiltime into UTC * assuming
* the current gmtoff and stdoff values.
*/
-gmtoff);
-stdoff);
}
/*
* Find the rule (of those to do, if any)
* that takes effect earliest in the year.
*/
k = -1;
continue;
if (!rp->r_todisstd)
continue;
k = j;
}
}
if (k < 0)
break; /* go on to next year */
break;
if (usestart) {
stdoff);
continue;
}
if (*startbuf == '\0' &&
stdoff)) {
}
}
}
}
if (usestart) {
if (*startbuf == '\0' &&
if (*startbuf == '\0')
"can't determine time zone abbrevation to use just after until time"));
startttisgmt));
}
/*
* Now we may get to set starttime for the next zone line.
*/
if (useuntil) {
if (!startttisstd)
if (!startttisgmt)
}
}
}
static void
int type;
{
abbrinds[0] = 0;
typecnt = 1;
timecnt = 0;
type = 0;
}
if (timecnt >= TZ_MAX_TIMES) {
}
++timecnt;
}
static int
const long gmtoff;
const char * const abbr;
const int isdst;
const int ttisstd;
const int ttisgmt;
{
register int i, j;
"internal error - addtype called with bad isdst"));
}
"internal error - addtype called with bad ttisstd"));
}
"internal error - addtype called with bad ttisgmt"));
}
/*
* See if there's already an entry for this zone type.
* If so, just return its index.
*/
for (i = 0; i < typecnt; ++i) {
return (i);
}
/*
* There isn't one; add a new one, unless there are already too
* many.
*/
if (typecnt >= TZ_MAX_TYPES) {
}
}
for (j = 0; j < charcnt; ++j)
break;
if (j == charcnt)
abbrinds[i] = j;
++typecnt;
return (i);
}
#ifdef LEAPSECOND_SUPPORT
static void
const zic_t t;
const int positive;
const int rolling;
int count;
{
register int i, j;
}
for (i = 0; i < leapcnt; ++i)
if (t <= trans[i]) {
if (t == trans[i]) {
}
break;
}
do {
for (j = leapcnt; j > i; --j) {
}
trans[i] = t;
++leapcnt;
}
#endif /* LEAPSECOND_SUPPORT */
#ifdef LEAPSECOND_SUPPORT
static void
adjleap(void)
{
register int i;
register long last = 0;
/*
* propagate leap seconds forward
*/
for (i = 0; i < leapcnt; ++i) {
}
}
#endif /* LEAPSECOND_SUPPORT */
static int
const int year;
const char * const type;
{
static char *buf;
int result;
return (TRUE);
#if defined(sun)
return ((year % 4) == 0);
return ((year % 4) != 0);
return ((year % 2) == 0);
return ((year % 2) != 0);
#endif /* defined(sun) */
switch (WEXITSTATUS(result)) {
case 0:
return (TRUE);
case 1:
return (FALSE);
}
}
for (;;)
}
static int
lowerit(a)
int a;
{
a = (unsigned char) a;
}
static int
register const char *ap;
register const char *bp;
{
if (*ap++ == '\0')
return (TRUE);
return (FALSE);
}
static int
register const char *abbr;
register const char *word;
{
return (FALSE);
++word;
while (*++abbr != '\0')
do {
if (*word == '\0')
return (FALSE);
return (TRUE);
}
static const struct lookup *
register const char * const word;
{
return (NULL);
/*
* Look for exact match.
*/
return (lp);
/*
* Look for inexact match.
*/
else return (NULL); /* multiple inexact matches */
}
return (foundlp);
}
static char **
register char *cp;
{
register char *dp;
register char **array;
register int nsubs;
return (NULL);
array = (char **)(void *)
nsubs = 0;
for (;;) {
++cp;
break;
do {
++dp;
if (*dp != '\0')
++dp;
else {
"Odd number of quotation marks"));
exit(1);
}
++cp;
*dp = '\0';
}
return (array);
}
static long
const long t1;
const long t2;
{
register long t;
}
return (t);
}
static zic_t
const long t2;
{
register zic_t t;
return (max_time);
return (min_time);
}
return (t);
}
/*
* Given a rule, and a year, compute the date - in seconds since January 1,
* 1970, 00:00 LOCAL time - in that year that the rule refers to.
*/
static zic_t
register const int wantedy;
{
register int y, m, i;
register zic_t t;
return (min_time);
return (max_time);
dayoff = 0;
m = TM_JANUARY;
y = EPOCH_YEAR;
while (wantedy != y) {
if (wantedy > y) {
++y;
} else {
--y;
}
}
i = len_months[isleap(y)][m];
++m;
}
i = rp->r_dayofmonth;
--i;
else {
}
}
--i;
register long wday;
/*
* Don't trust mod of negative numbers.
*/
if (dayoff >= 0)
else {
if (wday < 0)
wday += LDAYSPERWEEK;
}
if (++wday >= LDAYSPERWEEK)
wday = 0;
++i;
} else {
if (--wday < 0)
--i;
}
if (i < 0 || i >= len_months[isleap(y)][m]) {
if (noise)
"month--will not work with pre-2004 "
"versions of zic"));
}
}
return (min_time);
return (min_time);
return (max_time);
}
static void
{
register int i;
register const char *cp;
register char *wp;
++cp;
}
"alphabetics"));
"characters"));
++cp;
++cp;
}
"POSIX standard");
}
}
if (charcnt + i > TZ_MAX_CHARS) {
"abbreviations"));
}
}
static int
char *argname;
{
register char *name;
register char *cp;
return (0);
*cp = '\0';
/*
* It doesn't seem to exist, so we try to create it.
* Creation may fail because of the directory being
* created by some other multiprocessor, so we get
* to do extra checking.
*/
"%s: Can't create directory %s: %s\n"),
return (-1);
}
}
}
*cp = '/';
}
return (0);
}
static long
eitol(i)
const int i;
{
long l;
l = i;
if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) {
gettext("%s: %d did not sign extend correctly\n"),
progname, i);
}
return (l);
}
/*
* UNIX was a registered trademark of The Open Group in 2003.
*/