Commit 1c1a7cbd authored by Tom Lane's avatar Tom Lane

Sync our copy of the timezone library with IANA release tzcode2016c.

We hadn't done this in about six years, which proves to have been a mistake
because there's been a lot of code churn upstream, making the merge rather
painful.  But putting it off any further isn't going to lessen the pain,
and there are at least two incompatible changes that we need to absorb
before someone starts complaining that --with-system-tzdata doesn't work
at all on their platform, or we get blindsided by a tzdata release that
our out-of-date zic can't compile.  Last week's "time zone abbreviation
differs from POSIX standard" mess was a wake-up call in that regard.

This is a sufficiently large patch that I'm afraid to back-patch it
immediately, though the foregoing considerations imply that we probably
should do so eventually.  For the moment, just put it in HEAD so that
it can get some testing.  Maybe we can wait till the end of the 9.6
beta cycle before deeming it okay.
parent e5a4dea8
...@@ -19,7 +19,7 @@ include $(top_builddir)/src/Makefile.global ...@@ -19,7 +19,7 @@ include $(top_builddir)/src/Makefile.global
OBJS= localtime.o strftime.o pgtz.o OBJS= localtime.o strftime.o pgtz.o
# files needed to build zic utility program # files needed to build zic utility program
ZICOBJS= zic.o ialloc.o scheck.o localtime.o $(WIN32RES) ZICOBJS= zic.o $(WIN32RES)
# timezone data files # timezone data files
TZDATA = africa antarctica asia australasia europe northamerica southamerica \ TZDATA = africa antarctica asia australasia europe northamerica southamerica \
......
src/timezone/README src/timezone/README
Timezone This is a PostgreSQL adapted version of the IANA timezone library from
========
This is a PostgreSQL adapted version of the timezone library from http://www.iana.org/time-zones
http://www.iana.org/time-zones
The source code can be found at: The latest versions of both the tzdata and tzcode tarballs are normally
available right from that page. Historical versions can be found
elsewhere on the site.
ftp://ftp.iana.org/tz/releases/tzcode*.tar.gz Since time zone rules change frequently in some parts of the world,
we should endeavor to update the data files before each PostgreSQL
The code is currently synced with release 2010c. There are many cosmetic release. The code need not be updated as often, but we must track
(and not so cosmetic) differences from the original tzcode library, but changes that might affect interpretation of the data files.
diffs in the upstream version should usually be propagated to our version.
The data files under data/ are an exact copy of the latest data set from:
ftp://ftp.iana.org/tz/releases/tzdata*.tar.gz Time Zone data
==============
Since time zone rules change frequently in some parts of the world, The data files under data/ are an exact copy of the latest tzdata set,
we should endeavor to update the data files before each PostgreSQL except that we omit some files that are not of interest for our purposes.
release.
While the files under data/ can just be duplicated when updating, manual While the files under data/ can just be duplicated when updating, manual
effort is needed to update the time zone abbreviation lists under tznames/. effort is needed to update the time zone abbreviation lists under tznames/.
...@@ -32,7 +30,9 @@ which will produce a file showing all abbreviations that are in current ...@@ -32,7 +30,9 @@ which will produce a file showing all abbreviations that are in current
use according to the data/ files. Compare this to known_abbrevs.txt, use according to the data/ files. Compare this to known_abbrevs.txt,
which is the list that existed last time the tznames/ files were updated. which is the list that existed last time the tznames/ files were updated.
Update tznames/ as seems appropriate, then replace known_abbrevs.txt Update tznames/ as seems appropriate, then replace known_abbrevs.txt
in the same commit. in the same commit. Usually, if a known abbreviation has changed meaning,
the appropriate fix is to make it refer to a long-form zone name instead
of a fixed GMT offset.
When there has been a new release of Windows (probably including Service When there has been a new release of Windows (probably including Service
Packs), the list of matching timezones need to be updated. Run the Packs), the list of matching timezones need to be updated. Run the
...@@ -40,3 +40,64 @@ script in src/tools/win32tzlist.pl on a Windows machine running this new ...@@ -40,3 +40,64 @@ script in src/tools/win32tzlist.pl on a Windows machine running this new
release and apply any new timezones that it detects. Never remove any release and apply any new timezones that it detects. Never remove any
mappings in case they are removed in Windows, since we still need to mappings in case they are removed in Windows, since we still need to
match properly on the old version. match properly on the old version.
Time Zone code
==============
The code in this directory is currently synced with tzcode release 2016c.
There are many cosmetic (and not so cosmetic) differences from the
original tzcode library, but diffs in the upstream version should usually
be propagated to our version. Here are some notes about that.
For the most part we want to use the upstream code as-is, but there are
several considerations preventing an exact match:
* For readability/maintainability we reformat the code to match our own
conventions; this includes pgindent'ing it and getting rid of upstream's
overuse of "register" declarations. (It used to include conversion of
old-style function declarations to C89 style, but thank goodness they
fixed that.)
* We need the code to follow Postgres' portability conventions; this
includes relying on configure's results rather than hand-hacked #defines,
and not relying on <stdint.h> features that may not exist on old systems.
(In particular this means using Postgres' definitions of the int32 and
int64 typedefs, not int_fast32_t/int_fast64_t.)
* Since Postgres is typically built on a system that has its own copy
of the <time.h> functions, we must avoid conflicting with those. This
mandates renaming typedef time_t to pg_time_t, and similarly for most
other exposed names.
* We have exposed the tzload() and tzparse() internal functions, and
slightly modified the API of the former, in part because it now relies
on our own pg_open_tzfile() rather than opening files for itself.
* There's a fair amount of code we don't need and have removed,
including all the nonstandard optional APIs. We have also added
a few functions of our own at the bottom of localtime.c.
* In zic.c, we have added support for a -P (print_abbrevs) switch, which
is used to create the "abbrevs.txt" summary of currently-in-use zone
abbreviations that was described above.
The most convenient way to compare a new tzcode release to our code is
to first run the tzcode source files through a sed filter like this:
sed -r \
-e 's/^([ \t]*)\*\*([ \t])/\1 *\2/' \
-e 's/^([ \t]*)\*\*$/\1 */' \
-e 's|^\*/| */|' \
-e 's/\bregister[ \t]//g' \
-e 's/int_fast32_t/int32/g' \
-e 's/int_fast64_t/int64/g' \
-e 's/struct[ \t]+tm\b/struct pg_tm/g' \
-e 's/\btime_t\b/pg_time_t/g' \
and then run them through pgindent. (The first three sed patterns deal
with conversion of their block comment style to something pgindent
won't make a hash of; the remainder address other points noted above.)
After that, the files can be diff'd directly against our corresponding
files.
/*
* This file is in the public domain, so clarified as of
* 2006-07-17 by Arthur David Olson.
*
* IDENTIFICATION
* src/timezone/ialloc.c
*/
#include "postgres_fe.h"
#include "private.h"
#define nonzero(n) (((n) == 0) ? 1 : (n))
char *
imalloc(int n)
{
return malloc((size_t) nonzero(n));
}
char *
icalloc(int nelem, int elsize)
{
if (nelem == 0 || elsize == 0)
nelem = elsize = 1;
return calloc((size_t) nelem, (size_t) elsize);
}
void *
irealloc(void *pointer, int size)
{
if (pointer == NULL)
return imalloc(size);
return realloc((void *) pointer, (size_t) nonzero(size));
}
char *
icatalloc(char *old, const char *new)
{
char *result;
int oldsize,
newsize;
newsize = (new == NULL) ? 0 : strlen(new);
if (old == NULL)
oldsize = 0;
else if (newsize == 0)
return old;
else
oldsize = strlen(old);
if ((result = irealloc(old, oldsize + newsize + 1)) != NULL)
if (new != NULL)
(void) strcpy(result + oldsize, new);
return result;
}
char *
icpyalloc(const char *string)
{
return icatalloc((char *) NULL, string);
}
void
ifree(char *p)
{
if (p != NULL)
(void) free(p);
}
void
icfree(char *p)
{
if (p != NULL)
(void) free(p);
}
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#ifndef WILDABBR #ifndef WILDABBR
/*---------- /*
* Someone might make incorrect use of a time zone abbreviation: * Someone might make incorrect use of a time zone abbreviation:
* 1. They might reference tzname[0] before calling tzset (explicitly * 1. They might reference tzname[0] before calling tzset (explicitly
* or implicitly). * or implicitly).
...@@ -36,20 +36,23 @@ ...@@ -36,20 +36,23 @@
* 5. They might reference tm.TM_ZONE after calling offtime. * 5. They might reference tm.TM_ZONE after calling offtime.
* What's best to do in the above cases is open to debate; * What's best to do in the above cases is open to debate;
* for now, we just set things up so that in any of the five cases * for now, we just set things up so that in any of the five cases
* WILDABBR is used. Another possibility: initialize tzname[0] to the * WILDABBR is used. Another possibility: initialize tzname[0] to the
* string "tzname[0] used before set", and similarly for the other cases. * string "tzname[0] used before set", and similarly for the other cases.
* And another: initialize tzname[0] to "ERA", with an explanation in the * And another: initialize tzname[0] to "ERA", with an explanation in the
* manual page of what this "time zone abbreviation" means (doing this so * manual page of what this "time zone abbreviation" means (doing this so
* that tzname[0] has the "normal" length of three characters). * that tzname[0] has the "normal" length of three characters).
*----------
*/ */
#define WILDABBR " " #define WILDABBR " "
#endif /* !defined WILDABBR */ #endif /* !defined WILDABBR */
static char wildabbr[] = WILDABBR; static const char wildabbr[] = WILDABBR;
static const char gmt[] = "GMT"; static const char gmt[] = "GMT";
/* The minimum and maximum finite time values. This assumes no padding. */
static const pg_time_t time_t_min = MINVAL(pg_time_t, TYPE_BIT(pg_time_t));
static const pg_time_t time_t_max = MAXVAL(pg_time_t, TYPE_BIT(pg_time_t));
/* /*
* The DST rules to use if TZ has no rules and we can't load TZDEFRULES. * The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
* We default to US rules as of 1999-08-17. * We default to US rules as of 1999-08-17.
...@@ -59,51 +62,35 @@ static const char gmt[] = "GMT"; ...@@ -59,51 +62,35 @@ static const char gmt[] = "GMT";
*/ */
#define TZDEFRULESTRING ",M4.1.0,M10.5.0" #define TZDEFRULESTRING ",M4.1.0,M10.5.0"
/* structs ttinfo, lsinfo, state have been moved to pgtz.h */
enum r_type
{
JULIAN_DAY, /* Jn = Julian day */
DAY_OF_YEAR, /* n = day of year */
MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */
};
struct rule struct rule
{ {
int r_type; /* type of rule--see below */ enum r_type r_type; /* type of rule */
int r_day; /* day number of rule */ int r_day; /* day number of rule */
int r_week; /* week number of rule */ int r_week; /* week number of rule */
int r_mon; /* month number of rule */ int r_mon; /* month number of rule */
long r_time; /* transition time of rule */ int32 r_time; /* transition time of rule */
}; };
#define JULIAN_DAY 0 /* Jn - Julian day */
#define DAY_OF_YEAR 1 /* n - day of year */
#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
/* /*
* Prototypes for static functions. * Prototypes for static functions.
*/ */
static long detzcode(const char *codep); static struct pg_tm *gmtsub(pg_time_t const *, int32, struct pg_tm *);
static pg_time_t detzcode64(const char *codep); static bool increment_overflow(int *, int);
static int differ_by_repeat(pg_time_t t1, pg_time_t t0); static bool increment_overflow_time(pg_time_t *, int32);
static const char *getzname(const char *strp); static struct pg_tm *timesub(pg_time_t const *, int32, struct state const *,
static const char *getqzname(const char *strp, int delim); struct pg_tm *);
static const char *getnum(const char *strp, int *nump, int min, int max); static bool typesequiv(struct state const *, int, int);
static const char *getsecs(const char *strp, long *secsp);
static const char *getoffset(const char *strp, long *offsetp);
static const char *getrule(const char *strp, struct rule * rulep);
static void gmtload(struct state * sp);
static struct pg_tm *gmtsub(const pg_time_t *timep, long offset,
struct pg_tm * tmp);
static struct pg_tm *localsub(const pg_time_t *timep, long offset,
struct pg_tm * tmp, const pg_tz *tz);
static int increment_overflow(int *number, int delta);
static pg_time_t transtime(pg_time_t janfirst, int year,
const struct rule * rulep, long offset);
static int typesequiv(const struct state * sp, int a, int b);
static struct pg_tm *timesub(const pg_time_t *timep, long offset,
const struct state * sp, struct pg_tm * tmp);
/* GMT timezone */
static struct state gmtmem;
#define gmtptr (&gmtmem)
static int gmt_is_set = 0;
/* /*
* Section 4.12.3 of X3.159-1989 requires that * Section 4.12.3 of X3.159-1989 requires that
...@@ -115,147 +102,281 @@ static int gmt_is_set = 0; ...@@ -115,147 +102,281 @@ static int gmt_is_set = 0;
static struct pg_tm tm; static struct pg_tm tm;
/* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND. */
static void
init_ttinfo(struct ttinfo * s, int32 gmtoff, bool isdst, int abbrind)
{
s->tt_gmtoff = gmtoff;
s->tt_isdst = isdst;
s->tt_abbrind = abbrind;
s->tt_ttisstd = false;
s->tt_ttisgmt = false;
}
static long static int32
detzcode(const char *codep) detzcode(const char *codep)
{ {
long result; int32 result;
int i; int i;
int32 one = 1;
int32 halfmaxval = one << (32 - 2);
int32 maxval = halfmaxval - 1 + halfmaxval;
int32 minval = -1 - maxval;
result = (codep[0] & 0x80) ? ~0L : 0; result = codep[0] & 0x7f;
for (i = 0; i < 4; ++i) for (i = 1; i < 4; ++i)
result = (result << 8) | (codep[i] & 0xff); result = (result << 8) | (codep[i] & 0xff);
if (codep[0] & 0x80)
{
/*
* Do two's-complement negation even on non-two's-complement machines.
* If the result would be minval - 1, return minval.
*/
result -= !TWOS_COMPLEMENT(int32) &&result != 0;
result += minval;
}
return result; return result;
} }
static pg_time_t static int64
detzcode64(const char *codep) detzcode64(const char *codep)
{ {
pg_time_t result; uint64 result;
int i; int i;
int64 one = 1;
int64 halfmaxval = one << (64 - 2);
int64 maxval = halfmaxval - 1 + halfmaxval;
int64 minval = -TWOS_COMPLEMENT(int64) -maxval;
result = codep[0] & 0x7f;
for (i = 1; i < 8; ++i)
result = (result << 8) | (codep[i] & 0xff);
result = (codep[0] & 0x80) ? (~(int64) 0) : 0; if (codep[0] & 0x80)
for (i = 0; i < 8; ++i) {
result = result * 256 + (codep[i] & 0xff); /*
* Do two's-complement negation even on non-two's-complement machines.
* If the result would be minval - 1, return minval.
*/
result -= !TWOS_COMPLEMENT(int64) &&result != 0;
result += minval;
}
return result; return result;
} }
static int static bool
differ_by_repeat(pg_time_t t1, pg_time_t t0) differ_by_repeat(const pg_time_t t1, const pg_time_t t0)
{ {
if (TYPE_INTEGRAL(pg_time_t) && if (TYPE_BIT(pg_time_t) -TYPE_SIGNED(pg_time_t) <SECSPERREPEAT_BITS)
TYPE_BIT(pg_time_t) -TYPE_SIGNED(pg_time_t) <SECSPERREPEAT_BITS)
return 0; return 0;
return t1 - t0 == SECSPERREPEAT; return t1 - t0 == SECSPERREPEAT;
} }
int /* Input buffer for data read from a compiled tz file. */
tzload(const char *name, char *canonname, struct state * sp, int doextend) union input_buffer
{
/* The first part of the buffer, interpreted as a header. */
struct tzhead tzhead;
/* The entire buffer. */
char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state)
+ 4 * TZ_MAX_TIMES];
};
/* Local storage needed for 'tzloadbody'. */
union local_storage
{
/* We don't need the "fullname" member */
/* The results of analyzing the file's contents after it is opened. */
struct
{
/* The input buffer. */
union input_buffer u;
/* A temporary state used for parsing a TZ string in the file. */
struct state st;
} u;
};
/* Load tz data from the file named NAME into *SP. Read extended
* format if DOEXTEND. Use *LSP for temporary storage. Return 0 on
* success, an errno value on failure.
* PG: If "canonname" is not NULL, then on success the canonical spelling of
* given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
*/
static int
tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
union local_storage * lsp)
{ {
const char *p;
int i; int i;
int fid; int fid;
int stored; int stored;
int nread; ssize_t nread;
union union input_buffer *up = &lsp->u.u;
int tzheadsize = sizeof(struct tzhead);
sp->goback = sp->goahead = false;
if (!name)
{ {
struct tzhead tzhead; name = TZDEFAULT;
char buf[2 * sizeof(struct tzhead) + if (!name)
2 * sizeof *sp + return EINVAL;
4 * TZ_MAX_TIMES]; }
} u;
sp->goback = sp->goahead = FALSE;
if (name == NULL && (name = TZDEFAULT) == NULL)
return -1;
if (name[0] == ':') if (name[0] == ':')
++name; ++name;
fid = pg_open_tzfile(name, canonname); fid = pg_open_tzfile(name, canonname);
if (fid < 0) if (fid < 0)
return -1; return ENOENT; /* pg_open_tzfile may not set errno */
nread = read(fid, u.buf, sizeof u.buf);
if (close(fid) != 0 || nread <= 0) nread = read(fid, up->buf, sizeof up->buf);
return -1; if (nread < tzheadsize)
{
int err = nread < 0 ? errno : EINVAL;
close(fid);
return err;
}
if (close(fid) < 0)
return errno;
for (stored = 4; stored <= 8; stored *= 2) for (stored = 4; stored <= 8; stored *= 2)
{ {
int ttisstdcnt; int32 ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
int ttisgmtcnt; int32 ttisgmtcnt = detzcode(up->tzhead.tzh_ttisgmtcnt);
int32 leapcnt = detzcode(up->tzhead.tzh_leapcnt);
ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); int32 timecnt = detzcode(up->tzhead.tzh_timecnt);
ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); int32 typecnt = detzcode(up->tzhead.tzh_typecnt);
sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); int32 charcnt = detzcode(up->tzhead.tzh_charcnt);
sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); char const *p = up->buf + tzheadsize;
sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; && 0 < typecnt && typecnt < TZ_MAX_TYPES
if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || && 0 <= timecnt && timecnt < TZ_MAX_TIMES
sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || && 0 <= charcnt && charcnt < TZ_MAX_CHARS
sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || && (ttisstdcnt == typecnt || ttisstdcnt == 0)
sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || && (ttisgmtcnt == typecnt || ttisgmtcnt == 0)))
(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || return EINVAL;
(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) if (nread
return -1; < (tzheadsize /* struct tzhead */
if (nread - (p - u.buf) < + timecnt * stored /* ats */
sp->timecnt * stored + /* ats */ + timecnt /* types */
sp->timecnt + /* types */ + typecnt * 6 /* ttinfos */
sp->typecnt * 6 + /* ttinfos */ + charcnt /* chars */
sp->charcnt + /* chars */ + leapcnt * (stored + 4) /* lsinfos */
sp->leapcnt * (stored + 4) + /* lsinfos */ + ttisstdcnt /* ttisstds */
ttisstdcnt + /* ttisstds */ + ttisgmtcnt)) /* ttisgmts */
ttisgmtcnt) /* ttisgmts */ return EINVAL;
return -1; sp->leapcnt = leapcnt;
sp->timecnt = timecnt;
sp->typecnt = typecnt;
sp->charcnt = charcnt;
/*
* Read transitions, discarding those out of pg_time_t range. But
* pretend the last transition before time_t_min occurred at
* time_t_min.
*/
timecnt = 0;
for (i = 0; i < sp->timecnt; ++i) for (i = 0; i < sp->timecnt; ++i)
{ {
sp->ats[i] = (stored == 4) ? detzcode(p) : detzcode64(p); int64 at
= stored == 4 ? detzcode(p) : detzcode64(p);
sp->types[i] = at <= time_t_max;
if (sp->types[i])
{
pg_time_t attime
= ((TYPE_SIGNED(pg_time_t) ? at < time_t_min : at < 0)
? time_t_min : at);
if (timecnt && attime <= sp->ats[timecnt - 1])
{
if (attime < sp->ats[timecnt - 1])
return EINVAL;
sp->types[i - 1] = 0;
timecnt--;
}
sp->ats[timecnt++] = attime;
}
p += stored; p += stored;
} }
timecnt = 0;
for (i = 0; i < sp->timecnt; ++i) for (i = 0; i < sp->timecnt; ++i)
{ {
sp->types[i] = (unsigned char) *p++; unsigned char typ = *p++;
if (sp->types[i] >= sp->typecnt)
return -1; if (sp->typecnt <= typ)
return EINVAL;
if (sp->types[i])
sp->types[timecnt++] = typ;
} }
sp->timecnt = timecnt;
for (i = 0; i < sp->typecnt; ++i) for (i = 0; i < sp->typecnt; ++i)
{ {
struct ttinfo *ttisp; struct ttinfo *ttisp;
unsigned char isdst,
abbrind;
ttisp = &sp->ttis[i]; ttisp = &sp->ttis[i];
ttisp->tt_gmtoff = detzcode(p); ttisp->tt_gmtoff = detzcode(p);
p += 4; p += 4;
ttisp->tt_isdst = (unsigned char) *p++; isdst = *p++;
if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) if (!(isdst < 2))
return -1; return EINVAL;
ttisp->tt_abbrind = (unsigned char) *p++; ttisp->tt_isdst = isdst;
if (ttisp->tt_abbrind < 0 || abbrind = *p++;
ttisp->tt_abbrind > sp->charcnt) if (!(abbrind < sp->charcnt))
return -1; return EINVAL;
ttisp->tt_abbrind = abbrind;
} }
for (i = 0; i < sp->charcnt; ++i) for (i = 0; i < sp->charcnt; ++i)
sp->chars[i] = *p++; sp->chars[i] = *p++;
sp->chars[i] = '\0'; /* ensure '\0' at end */ sp->chars[i] = '\0'; /* ensure '\0' at end */
/* Read leap seconds, discarding those out of pg_time_t range. */
leapcnt = 0;
for (i = 0; i < sp->leapcnt; ++i) for (i = 0; i < sp->leapcnt; ++i)
{ {
struct lsinfo *lsisp; int64 tr = stored == 4 ? detzcode(p) : detzcode64(p);
int32 corr = detzcode(p + stored);
lsisp = &sp->lsis[i]; p += stored + 4;
lsisp->ls_trans = (stored == 4) ? detzcode(p) : detzcode64(p); if (tr <= time_t_max)
p += stored; {
lsisp->ls_corr = detzcode(p); pg_time_t trans
p += 4; = ((TYPE_SIGNED(pg_time_t) ? tr < time_t_min : tr < 0)
? time_t_min : tr);
if (leapcnt && trans <= sp->lsis[leapcnt - 1].ls_trans)
{
if (trans < sp->lsis[leapcnt - 1].ls_trans)
return EINVAL;
leapcnt--;
}
sp->lsis[leapcnt].ls_trans = trans;
sp->lsis[leapcnt].ls_corr = corr;
leapcnt++;
}
} }
sp->leapcnt = leapcnt;
for (i = 0; i < sp->typecnt; ++i) for (i = 0; i < sp->typecnt; ++i)
{ {
struct ttinfo *ttisp; struct ttinfo *ttisp;
ttisp = &sp->ttis[i]; ttisp = &sp->ttis[i];
if (ttisstdcnt == 0) if (ttisstdcnt == 0)
ttisp->tt_ttisstd = FALSE; ttisp->tt_ttisstd = false;
else else
{ {
if (*p != true && *p != false)
return EINVAL;
ttisp->tt_ttisstd = *p++; ttisp->tt_ttisstd = *p++;
if (ttisp->tt_ttisstd != TRUE &&
ttisp->tt_ttisstd != FALSE)
return -1;
} }
} }
for (i = 0; i < sp->typecnt; ++i) for (i = 0; i < sp->typecnt; ++i)
...@@ -264,99 +385,87 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend) ...@@ -264,99 +385,87 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend)
ttisp = &sp->ttis[i]; ttisp = &sp->ttis[i];
if (ttisgmtcnt == 0) if (ttisgmtcnt == 0)
ttisp->tt_ttisgmt = FALSE; ttisp->tt_ttisgmt = false;
else else
{ {
if (*p != true && *p != false)
return EINVAL;
ttisp->tt_ttisgmt = *p++; ttisp->tt_ttisgmt = *p++;
if (ttisp->tt_ttisgmt != TRUE &&
ttisp->tt_ttisgmt != FALSE)
return -1;
} }
} }
/*
* Out-of-sort ats should mean we're running on a signed time_t system
* but using a data file with unsigned values (or vice versa).
*/
for (i = 0; i < sp->timecnt - 2; ++i)
if (sp->ats[i] > sp->ats[i + 1])
{
++i;
if (TYPE_SIGNED(pg_time_t))
{
/*
* Ignore the end (easy).
*/
sp->timecnt = i;
}
else
{
/*
* Ignore the beginning (harder).
*/
int j;
for (j = 0; j + i < sp->timecnt; ++j)
{
sp->ats[j] = sp->ats[j + i];
sp->types[j] = sp->types[j + i];
}
sp->timecnt = j;
}
break;
}
/* /*
* If this is an old file, we're done. * If this is an old file, we're done.
*/ */
if (u.tzhead.tzh_version[0] == '\0') if (up->tzhead.tzh_version[0] == '\0')
break;
nread -= p - u.buf;
for (i = 0; i < nread; ++i)
u.buf[i] = p[i];
/*
* If this is a narrow integer time_t system, we're done.
*/
if (stored >= (int) sizeof(pg_time_t) && TYPE_INTEGRAL(pg_time_t))
break; break;
nread -= p - up->buf;
memmove(up->buf, p, nread);
} }
if (doextend && nread > 2 && if (doextend && nread > 2 &&
u.buf[0] == '\n' && u.buf[nread - 1] == '\n' && up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
sp->typecnt + 2 <= TZ_MAX_TYPES) sp->typecnt + 2 <= TZ_MAX_TYPES)
{ {
struct state ts; struct state *ts = &lsp->u.st;
int result;
u.buf[nread - 1] = '\0'; up->buf[nread - 1] = '\0';
result = tzparse(&u.buf[1], &ts, FALSE); if (tzparse(&up->buf[1], ts, false) == 0
if (result == 0 && ts.typecnt == 2 && && ts->typecnt == 2)
sp->charcnt + ts.charcnt <= TZ_MAX_CHARS)
{ {
for (i = 0; i < 2; ++i) /*
ts.ttis[i].tt_abbrind += * Attempt to reuse existing abbreviations. Without this,
sp->charcnt; * America/Anchorage would stop working after 2037 when
for (i = 0; i < ts.charcnt; ++i) * TZ_MAX_CHARS is 50, as sp->charcnt equals 42 (for LMT CAT CAWT
sp->chars[sp->charcnt++] = * CAPT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for
ts.chars[i]; * AKST AKDT). Reusing means sp->charcnt can stay 42 in this
i = 0; * example.
while (i < ts.timecnt && */
ts.ats[i] <= int gotabbr = 0;
sp->ats[sp->timecnt - 1]) int charcnt = sp->charcnt;
++i;
while (i < ts.timecnt && for (i = 0; i < 2; i++)
sp->timecnt < TZ_MAX_TIMES)
{ {
sp->ats[sp->timecnt] = char *tsabbr = ts->chars + ts->ttis[i].tt_abbrind;
ts.ats[i]; int j;
sp->types[sp->timecnt] =
sp->typecnt + for (j = 0; j < charcnt; j++)
ts.types[i]; if (strcmp(sp->chars + j, tsabbr) == 0)
++sp->timecnt; {
++i; ts->ttis[i].tt_abbrind = j;
gotabbr++;
break;
}
if (!(j < charcnt))
{
int tsabbrlen = strlen(tsabbr);
if (j + tsabbrlen < TZ_MAX_CHARS)
{
strcpy(sp->chars + j, tsabbr);
charcnt = j + tsabbrlen + 1;
ts->ttis[i].tt_abbrind = j;
gotabbr++;
}
}
}
if (gotabbr == 2)
{
sp->charcnt = charcnt;
for (i = 0; i < ts->timecnt; i++)
if (sp->ats[sp->timecnt - 1] < ts->ats[i])
break;
while (i < ts->timecnt
&& sp->timecnt < TZ_MAX_TIMES)
{
sp->ats[sp->timecnt] = ts->ats[i];
sp->types[sp->timecnt] = (sp->typecnt
+ ts->types[i]);
sp->timecnt++;
i++;
}
sp->ttis[sp->typecnt++] = ts->ttis[0];
sp->ttis[sp->typecnt++] = ts->ttis[1];
} }
sp->ttis[sp->typecnt++] = ts.ttis[0];
sp->ttis[sp->typecnt++] = ts.ttis[1];
} }
} }
if (sp->timecnt > 1) if (sp->timecnt > 1)
...@@ -365,7 +474,7 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend) ...@@ -365,7 +474,7 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend)
if (typesequiv(sp, sp->types[i], sp->types[0]) && if (typesequiv(sp, sp->types[i], sp->types[0]) &&
differ_by_repeat(sp->ats[i], sp->ats[0])) differ_by_repeat(sp->ats[i], sp->ats[0]))
{ {
sp->goback = TRUE; sp->goback = true;
break; break;
} }
for (i = sp->timecnt - 2; i >= 0; --i) for (i = sp->timecnt - 2; i >= 0; --i)
...@@ -374,22 +483,73 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend) ...@@ -374,22 +483,73 @@ tzload(const char *name, char *canonname, struct state * sp, int doextend)
differ_by_repeat(sp->ats[sp->timecnt - 1], differ_by_repeat(sp->ats[sp->timecnt - 1],
sp->ats[i])) sp->ats[i]))
{ {
sp->goahead = TRUE; sp->goahead = true;
break;
}
}
/*
* If type 0 is is unused in transitions, it's the type to use for early
* times.
*/
for (i = 0; i < sp->timecnt; ++i)
if (sp->types[i] == 0)
break;
i = i < sp->timecnt ? -1 : 0;
/*
* Absent the above, if there are transition times and the first
* transition is to a daylight time find the standard type less than and
* closest to the type of the first transition.
*/
if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst)
{
i = sp->types[0];
while (--i >= 0)
if (!sp->ttis[i].tt_isdst)
break;
}
/*
* If no result yet, find the first standard type. If there is none, punt
* to type zero.
*/
if (i < 0)
{
i = 0;
while (sp->ttis[i].tt_isdst)
if (++i >= sp->typecnt)
{
i = 0;
break; break;
} }
} }
sp->defaulttype = i;
return 0; return 0;
} }
static int /* Load tz data from the file named NAME into *SP. Read extended
* format if DOEXTEND. Return 0 on success, an errno value on failure.
* PG: If "canonname" is not NULL, then on success the canonical spelling of
* given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
*/
int
tzload(const char *name, char *canonname, struct state * sp, int doextend)
{
union local_storage ls;
return tzloadbody(name, canonname, sp, doextend, &ls);
}
static bool
typesequiv(const struct state * sp, int a, int b) typesequiv(const struct state * sp, int a, int b)
{ {
int result; bool result;
if (sp == NULL || if (sp == NULL ||
a < 0 || a >= sp->typecnt || a < 0 || a >= sp->typecnt ||
b < 0 || b >= sp->typecnt) b < 0 || b >= sp->typecnt)
result = FALSE; result = false;
else else
{ {
const struct ttinfo *ap = &sp->ttis[a]; const struct ttinfo *ap = &sp->ttis[a];
...@@ -484,19 +644,19 @@ getnum(const char *strp, int *nump, int min, int max) ...@@ -484,19 +644,19 @@ getnum(const char *strp, int *nump, int min, int max)
* of seconds. * of seconds.
*/ */
static const char * static const char *
getsecs(const char *strp, long *secsp) getsecs(const char *strp, int32 *secsp)
{ {
int num; int num;
/* /*
* `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like * 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
* "M10.4.6/26", which does not conform to Posix, but which specifies the * "M10.4.6/26", which does not conform to Posix, but which specifies the
* equivalent of ``02:00 on the first Sunday on or after 23 Oct''. * equivalent of "02:00 on the first Sunday on or after 23 Oct".
*/ */
strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
if (strp == NULL) if (strp == NULL)
return NULL; return NULL;
*secsp = num * (long) SECSPERHOUR; *secsp = num * (int32) SECSPERHOUR;
if (*strp == ':') if (*strp == ':')
{ {
++strp; ++strp;
...@@ -507,7 +667,7 @@ getsecs(const char *strp, long *secsp) ...@@ -507,7 +667,7 @@ getsecs(const char *strp, long *secsp)
if (*strp == ':') if (*strp == ':')
{ {
++strp; ++strp;
/* `SECSPERMIN' allows for leap seconds. */ /* 'SECSPERMIN' allows for leap seconds. */
strp = getnum(strp, &num, 0, SECSPERMIN); strp = getnum(strp, &num, 0, SECSPERMIN);
if (strp == NULL) if (strp == NULL)
return NULL; return NULL;
...@@ -524,13 +684,13 @@ getsecs(const char *strp, long *secsp) ...@@ -524,13 +684,13 @@ getsecs(const char *strp, long *secsp)
* Otherwise, return a pointer to the first character not part of the time. * Otherwise, return a pointer to the first character not part of the time.
*/ */
static const char * static const char *
getoffset(const char *strp, long *offsetp) getoffset(const char *strp, int32 *offsetp)
{ {
int neg = 0; bool neg = false;
if (*strp == '-') if (*strp == '-')
{ {
neg = 1; neg = true;
++strp; ++strp;
} }
else if (*strp == '+') else if (*strp == '+')
...@@ -598,7 +758,7 @@ getrule(const char *strp, struct rule * rulep) ...@@ -598,7 +758,7 @@ getrule(const char *strp, struct rule * rulep)
* Time specified. * Time specified.
*/ */
++strp; ++strp;
strp = getsecs(strp, &rulep->r_time); strp = getoffset(strp, &rulep->r_time);
} }
else else
rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
...@@ -606,16 +766,15 @@ getrule(const char *strp, struct rule * rulep) ...@@ -606,16 +766,15 @@ getrule(const char *strp, struct rule * rulep)
} }
/* /*
* Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the * Given a year, a rule, and the offset from UT at the time that rule takes
* year, a rule, and the offset from UTC at the time that rule takes effect, * effect, calculate the year-relative time that rule takes effect.
* calculate the Epoch-relative time that rule takes effect.
*/ */
static pg_time_t static int32
transtime(pg_time_t janfirst, int year, transtime(int year, const struct rule * rulep,
const struct rule * rulep, long offset) int32 offset)
{ {
int leapyear; bool leapyear;
pg_time_t value = 0; int32 value;
int i, int i,
d, d,
m1, m1,
...@@ -624,6 +783,7 @@ transtime(pg_time_t janfirst, int year, ...@@ -624,6 +783,7 @@ transtime(pg_time_t janfirst, int year,
yy2, yy2,
dow; dow;
INITIALIZE(value);
leapyear = isleap(year); leapyear = isleap(year);
switch (rulep->r_type) switch (rulep->r_type)
{ {
...@@ -636,7 +796,7 @@ transtime(pg_time_t janfirst, int year, ...@@ -636,7 +796,7 @@ transtime(pg_time_t janfirst, int year,
* just add SECSPERDAY times the day number-1 to the time of * just add SECSPERDAY times the day number-1 to the time of
* January 1, midnight, to get the day. * January 1, midnight, to get the day.
*/ */
value = janfirst + (rulep->r_day - 1) * SECSPERDAY; value = (rulep->r_day - 1) * SECSPERDAY;
if (leapyear && rulep->r_day >= 60) if (leapyear && rulep->r_day >= 60)
value += SECSPERDAY; value += SECSPERDAY;
break; break;
...@@ -647,7 +807,7 @@ transtime(pg_time_t janfirst, int year, ...@@ -647,7 +807,7 @@ transtime(pg_time_t janfirst, int year,
* n - day of year. Just add SECSPERDAY times the day number to * n - day of year. Just add SECSPERDAY times the day number to
* the time of January 1, midnight, to get the day. * the time of January 1, midnight, to get the day.
*/ */
value = janfirst + rulep->r_day * SECSPERDAY; value = rulep->r_day * SECSPERDAY;
break; break;
case MONTH_NTH_DAY_OF_WEEK: case MONTH_NTH_DAY_OF_WEEK:
...@@ -655,9 +815,6 @@ transtime(pg_time_t janfirst, int year, ...@@ -655,9 +815,6 @@ transtime(pg_time_t janfirst, int year,
/* /*
* Mm.n.d - nth "dth day" of month m. * Mm.n.d - nth "dth day" of month m.
*/ */
value = janfirst;
for (i = 0; i < rulep->r_mon - 1; ++i)
value += mon_lengths[leapyear][i] * SECSPERDAY;
/* /*
* Use Zeller's Congruence to get day-of-week of first day of * Use Zeller's Congruence to get day-of-week of first day of
...@@ -682,7 +839,7 @@ transtime(pg_time_t janfirst, int year, ...@@ -682,7 +839,7 @@ transtime(pg_time_t janfirst, int year,
for (i = 1; i < rulep->r_week; ++i) for (i = 1; i < rulep->r_week; ++i)
{ {
if (d + DAYSPERWEEK >= if (d + DAYSPERWEEK >=
mon_lengths[leapyear][rulep->r_mon - 1]) mon_lengths[(int) leapyear][rulep->r_mon - 1])
break; break;
d += DAYSPERWEEK; d += DAYSPERWEEK;
} }
...@@ -690,14 +847,16 @@ transtime(pg_time_t janfirst, int year, ...@@ -690,14 +847,16 @@ transtime(pg_time_t janfirst, int year,
/* /*
* "d" is the day-of-month (zero-origin) of the day we want. * "d" is the day-of-month (zero-origin) of the day we want.
*/ */
value += d * SECSPERDAY; value = d * SECSPERDAY;
for (i = 0; i < rulep->r_mon - 1; ++i)
value += mon_lengths[(int) leapyear][i] * SECSPERDAY;
break; break;
} }
/* /*
* "value" is the Epoch-relative time of 00:00:00 UTC on the day in * "value" is the year-relative time of 00:00:00 UT on the day in
* question. To get the Epoch-relative time of the specified local time * question. To get the year-relative time of the specified local time on
* on that day, add the transition time and the current offset from UTC. * that day, add the transition time and the current offset from UT.
*/ */
return value + rulep->r_time + offset; return value + rulep->r_time + offset;
} }
...@@ -705,8 +864,11 @@ transtime(pg_time_t janfirst, int year, ...@@ -705,8 +864,11 @@ transtime(pg_time_t janfirst, int year,
/* /*
* Given a POSIX section 8-style TZ string, fill in the rule tables as * Given a POSIX section 8-style TZ string, fill in the rule tables as
* appropriate. * appropriate.
*
* Returns 0 on success, -1 on failure. (Note: tzcode has converted this
* to a bool true-on-success convention, but we're holding the line in PG
* for the moment, to avoid external API changes.)
*/ */
int int
tzparse(const char *name, struct state * sp, int lastditch) tzparse(const char *name, struct state * sp, int lastditch)
{ {
...@@ -714,29 +876,29 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -714,29 +876,29 @@ tzparse(const char *name, struct state * sp, int lastditch)
const char *dstname = NULL; const char *dstname = NULL;
size_t stdlen; size_t stdlen;
size_t dstlen; size_t dstlen;
long stdoffset; size_t charcnt;
long dstoffset; int32 stdoffset;
pg_time_t *atp; int32 dstoffset;
unsigned char *typep;
char *cp; char *cp;
int load_result; bool load_ok;
stdname = name; stdname = name;
if (lastditch) if (lastditch)
{ {
/*
* This is intentionally somewhat different from the IANA code. We do
* not want to invoke tzload() in the lastditch case: we can't assume
* pg_open_tzfile() is sane yet, and we don't care about leap seconds
* anyway.
*/
stdlen = strlen(name); /* length of standard zone name */ stdlen = strlen(name); /* length of standard zone name */
name += stdlen; name += stdlen;
if (stdlen >= sizeof sp->chars) if (stdlen >= sizeof sp->chars)
stdlen = (sizeof sp->chars) - 1; stdlen = (sizeof sp->chars) - 1;
charcnt = stdlen + 1;
stdoffset = 0; stdoffset = 0;
sp->goback = sp->goahead = false; /* simulate failed tzload() */
/* load_ok = false;
* Unlike the original zic library, do NOT invoke tzload() here; we
* can't assume pg_open_tzfile() is sane yet, and we don't care about
* leap seconds anyway.
*/
sp->goback = sp->goahead = FALSE;
load_result = -1;
} }
else else
{ {
...@@ -746,7 +908,7 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -746,7 +908,7 @@ tzparse(const char *name, struct state * sp, int lastditch)
stdname = name; stdname = name;
name = getqzname(name, '>'); name = getqzname(name, '>');
if (*name != '>') if (*name != '>')
return (-1); return -1;
stdlen = name - stdname; stdlen = name - stdname;
name++; name++;
} }
...@@ -755,14 +917,17 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -755,14 +917,17 @@ tzparse(const char *name, struct state * sp, int lastditch)
name = getzname(name); name = getzname(name);
stdlen = name - stdname; stdlen = name - stdname;
} }
if (*name == '\0') if (*name == '\0') /* we allow empty STD abbrev, unlike IANA */
return -1; return -1;
name = getoffset(name, &stdoffset); name = getoffset(name, &stdoffset);
if (name == NULL) if (name == NULL)
return -1; return -1;
load_result = tzload(TZDEFRULES, NULL, sp, FALSE); charcnt = stdlen + 1;
if (sizeof sp->chars < charcnt)
return -1;
load_ok = tzload(TZDEFRULES, NULL, sp, false) == 0;
} }
if (load_result != 0) if (!load_ok)
sp->leapcnt = 0; /* so, we're off a little */ sp->leapcnt = 0; /* so, we're off a little */
if (*name != '\0') if (*name != '\0')
{ {
...@@ -781,6 +946,11 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -781,6 +946,11 @@ tzparse(const char *name, struct state * sp, int lastditch)
name = getzname(name); name = getzname(name);
dstlen = name - dstname; /* length of DST zone name */ dstlen = name - dstname; /* length of DST zone name */
} }
if (!dstlen)
return -1;
charcnt += dstlen + 1;
if (sizeof sp->chars < charcnt)
return -1;
if (*name != '\0' && *name != ',' && *name != ';') if (*name != '\0' && *name != ',' && *name != ';')
{ {
name = getoffset(name, &dstoffset); name = getoffset(name, &dstoffset);
...@@ -789,16 +959,16 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -789,16 +959,16 @@ tzparse(const char *name, struct state * sp, int lastditch)
} }
else else
dstoffset = stdoffset - SECSPERHOUR; dstoffset = stdoffset - SECSPERHOUR;
if (*name == '\0' && load_result != 0) if (*name == '\0' && !load_ok)
name = TZDEFRULESTRING; name = TZDEFRULESTRING;
if (*name == ',' || *name == ';') if (*name == ',' || *name == ';')
{ {
struct rule start; struct rule start;
struct rule end; struct rule end;
int year; int year;
int yearlim;
int timecnt;
pg_time_t janfirst; pg_time_t janfirst;
pg_time_t starttime;
pg_time_t endtime;
++name; ++name;
if ((name = getrule(name, &start)) == NULL) if ((name = getrule(name, &start)) == NULL)
...@@ -814,55 +984,62 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -814,55 +984,62 @@ tzparse(const char *name, struct state * sp, int lastditch)
/* /*
* Two transitions per year, from EPOCH_YEAR forward. * Two transitions per year, from EPOCH_YEAR forward.
*/ */
sp->ttis[0].tt_gmtoff = -dstoffset; init_ttinfo(&sp->ttis[0], -dstoffset, true, stdlen + 1);
sp->ttis[0].tt_isdst = 1; init_ttinfo(&sp->ttis[1], -stdoffset, false, 0);
sp->ttis[0].tt_abbrind = stdlen + 1; sp->defaulttype = 0;
sp->ttis[1].tt_gmtoff = -stdoffset; timecnt = 0;
sp->ttis[1].tt_isdst = 0;
sp->ttis[1].tt_abbrind = 0;
atp = sp->ats;
typep = sp->types;
janfirst = 0; janfirst = 0;
sp->timecnt = 0; yearlim = EPOCH_YEAR + YEARSPERREPEAT;
for (year = EPOCH_YEAR; for (year = EPOCH_YEAR; year < yearlim; year++)
sp->timecnt + 2 <= TZ_MAX_TIMES;
++year)
{ {
pg_time_t newfirst; int32
starttime = transtime(year, &start, stdoffset),
starttime = transtime(janfirst, year, &start, endtime = transtime(year, &end, dstoffset);
stdoffset); int32
endtime = transtime(janfirst, year, &end, yearsecs = (year_lengths[isleap(year)]
dstoffset); * SECSPERDAY);
if (starttime > endtime) bool reversed = endtime < starttime;
if (reversed)
{ {
*atp++ = endtime; int32 swap = starttime;
*typep++ = 1; /* DST ends */
*atp++ = starttime; starttime = endtime;
*typep++ = 0; /* DST begins */ endtime = swap;
} }
else if (reversed
|| (starttime < endtime
&& (endtime - starttime
< (yearsecs
+ (stdoffset - dstoffset)))))
{ {
*atp++ = starttime; if (TZ_MAX_TIMES - 2 < timecnt)
*typep++ = 0; /* DST begins */ break;
*atp++ = endtime; yearlim = year + YEARSPERREPEAT + 1;
*typep++ = 1; /* DST ends */ sp->ats[timecnt] = janfirst;
if (increment_overflow_time
(&sp->ats[timecnt], starttime))
break;
sp->types[timecnt++] = reversed;
sp->ats[timecnt] = janfirst;
if (increment_overflow_time
(&sp->ats[timecnt], endtime))
break;
sp->types[timecnt++] = !reversed;
} }
sp->timecnt += 2; if (increment_overflow_time(&janfirst, yearsecs))
newfirst = janfirst;
newfirst += year_lengths[isleap(year)] *
SECSPERDAY;
if (newfirst <= janfirst)
break; break;
janfirst = newfirst;
} }
sp->timecnt = timecnt;
if (!timecnt)
sp->typecnt = 1; /* Perpetual DST. */
} }
else else
{ {
long theirstdoffset; int32 theirstdoffset;
long theirdstoffset; int32 theirdstoffset;
long theiroffset; int32 theiroffset;
int isdst; bool isdst;
int i; int i;
int j; int j;
...@@ -898,7 +1075,7 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -898,7 +1075,7 @@ tzparse(const char *name, struct state * sp, int lastditch)
/* /*
* Initially we're assumed to be in standard time. * Initially we're assumed to be in standard time.
*/ */
isdst = FALSE; isdst = false;
theiroffset = theirstdoffset; theiroffset = theirstdoffset;
/* /*
...@@ -945,15 +1122,12 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -945,15 +1122,12 @@ tzparse(const char *name, struct state * sp, int lastditch)
} }
/* /*
* Finally, fill in ttis. ttisstd and ttisgmt need not be handled. * Finally, fill in ttis.
*/ */
sp->ttis[0].tt_gmtoff = -stdoffset; init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
sp->ttis[0].tt_isdst = FALSE; init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
sp->ttis[0].tt_abbrind = 0;
sp->ttis[1].tt_gmtoff = -dstoffset;
sp->ttis[1].tt_isdst = TRUE;
sp->ttis[1].tt_abbrind = stdlen + 1;
sp->typecnt = 2; sp->typecnt = 2;
sp->defaulttype = 0;
} }
} }
else else
...@@ -961,22 +1135,17 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -961,22 +1135,17 @@ tzparse(const char *name, struct state * sp, int lastditch)
dstlen = 0; dstlen = 0;
sp->typecnt = 1; /* only standard time */ sp->typecnt = 1; /* only standard time */
sp->timecnt = 0; sp->timecnt = 0;
sp->ttis[0].tt_gmtoff = -stdoffset; init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
sp->ttis[0].tt_isdst = 0; sp->defaulttype = 0;
sp->ttis[0].tt_abbrind = 0;
} }
sp->charcnt = stdlen + 1; sp->charcnt = charcnt;
if (dstlen != 0)
sp->charcnt += dstlen + 1;
if ((size_t) sp->charcnt > sizeof sp->chars)
return -1;
cp = sp->chars; cp = sp->chars;
(void) strncpy(cp, stdname, stdlen); memcpy(cp, stdname, stdlen);
cp += stdlen; cp += stdlen;
*cp++ = '\0'; *cp++ = '\0';
if (dstlen != 0) if (dstlen != 0)
{ {
(void) strncpy(cp, dstname, dstlen); memcpy(cp, dstname, dstlen);
*(cp + dstlen) = '\0'; *(cp + dstlen) = '\0';
} }
return 0; return 0;
...@@ -985,51 +1154,42 @@ tzparse(const char *name, struct state * sp, int lastditch) ...@@ -985,51 +1154,42 @@ tzparse(const char *name, struct state * sp, int lastditch)
static void static void
gmtload(struct state * sp) gmtload(struct state * sp)
{ {
if (tzload(gmt, NULL, sp, TRUE) != 0) if (tzload(gmt, NULL, sp, true) != 0)
(void) tzparse(gmt, sp, TRUE); tzparse(gmt, sp, true);
} }
/* /*
* The easy way to behave "as if no library function calls" localtime * The easy way to behave "as if no library function calls" localtime
* is to not call it--so we drop its guts into "localsub", which can be * is to not call it, so we drop its guts into "localsub", which can be
* freely called. (And no, the PANS doesn't require the above behavior-- * freely called. (And no, the PANS doesn't require the above behavior,
* but it *is* desirable.) * but it *is* desirable.)
*
* The unused offset argument is for the benefit of mktime variants.
*/ */
static struct pg_tm * static struct pg_tm *
localsub(const pg_time_t *timep, long offset, localsub(struct state const * sp, pg_time_t const * timep,
struct pg_tm * tmp, const pg_tz *tz) struct pg_tm * tmp)
{ {
const struct state *sp;
const struct ttinfo *ttisp; const struct ttinfo *ttisp;
int i; int i;
struct pg_tm *result; struct pg_tm *result;
const pg_time_t t = *timep; const pg_time_t t = *timep;
sp = &tz->state; if (sp == NULL)
return gmtsub(timep, 0, tmp);
if ((sp->goback && t < sp->ats[0]) || if ((sp->goback && t < sp->ats[0]) ||
(sp->goahead && t > sp->ats[sp->timecnt - 1])) (sp->goahead && t > sp->ats[sp->timecnt - 1]))
{ {
pg_time_t newt = t; pg_time_t newt = t;
pg_time_t seconds; pg_time_t seconds;
pg_time_t tcycles; pg_time_t years;
int64 icycles;
if (t < sp->ats[0]) if (t < sp->ats[0])
seconds = sp->ats[0] - t; seconds = sp->ats[0] - t;
else else
seconds = t - sp->ats[sp->timecnt - 1]; seconds = t - sp->ats[sp->timecnt - 1];
--seconds; --seconds;
tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT;
++tcycles; seconds = years * AVGSECSPERYEAR;
icycles = tcycles;
if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
return NULL;
seconds = icycles;
seconds *= YEARSPERREPEAT;
seconds *= AVGSECSPERYEAR;
if (t < sp->ats[0]) if (t < sp->ats[0])
newt += seconds; newt += seconds;
else else
...@@ -1037,31 +1197,25 @@ localsub(const pg_time_t *timep, long offset, ...@@ -1037,31 +1197,25 @@ localsub(const pg_time_t *timep, long offset,
if (newt < sp->ats[0] || if (newt < sp->ats[0] ||
newt > sp->ats[sp->timecnt - 1]) newt > sp->ats[sp->timecnt - 1])
return NULL; /* "cannot happen" */ return NULL; /* "cannot happen" */
result = localsub(&newt, offset, tmp, tz); result = localsub(sp, &newt, tmp);
if (result == tmp) if (result)
{ {
pg_time_t newy; int64 newy;
newy = tmp->tm_year; newy = result->tm_year;
if (t < sp->ats[0]) if (t < sp->ats[0])
newy -= icycles * YEARSPERREPEAT; newy -= years;
else else
newy += icycles * YEARSPERREPEAT; newy += years;
tmp->tm_year = newy; if (!(INT_MIN <= newy && newy <= INT_MAX))
if (tmp->tm_year != newy)
return NULL; return NULL;
result->tm_year = newy;
} }
return result; return result;
} }
if (sp->timecnt == 0 || t < sp->ats[0]) if (sp->timecnt == 0 || t < sp->ats[0])
{ {
i = 0; i = sp->defaulttype;
while (sp->ttis[i].tt_isdst)
if (++i >= sp->typecnt)
{
i = 0;
break;
}
} }
else else
{ {
...@@ -1082,8 +1236,11 @@ localsub(const pg_time_t *timep, long offset, ...@@ -1082,8 +1236,11 @@ localsub(const pg_time_t *timep, long offset,
ttisp = &sp->ttis[i]; ttisp = &sp->ttis[i];
result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
tmp->tm_isdst = ttisp->tt_isdst; if (result)
tmp->tm_zone = &sp->chars[ttisp->tt_abbrind]; {
result->tm_isdst = ttisp->tt_isdst;
result->tm_zone = (char *) &sp->chars[ttisp->tt_abbrind];
}
return result; return result;
} }
...@@ -1091,28 +1248,35 @@ localsub(const pg_time_t *timep, long offset, ...@@ -1091,28 +1248,35 @@ localsub(const pg_time_t *timep, long offset,
struct pg_tm * struct pg_tm *
pg_localtime(const pg_time_t *timep, const pg_tz *tz) pg_localtime(const pg_time_t *timep, const pg_tz *tz)
{ {
return localsub(timep, 0L, &tm, tz); return localsub(&tz->state, timep, &tm);
} }
/* /*
* gmtsub is to gmtime as localsub is to localtime. * gmtsub is to gmtime as localsub is to localtime.
*
* Except we have a private "struct state" for GMT, so no sp is passed in.
*/ */
static struct pg_tm * static struct pg_tm *
gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp) gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp)
{ {
struct pg_tm *result; struct pg_tm *result;
/* GMT timezone state data is kept here */
static struct state gmtmem;
static bool gmt_is_set = false;
#define gmtptr (&gmtmem)
if (!gmt_is_set) if (!gmt_is_set)
{ {
gmt_is_set = TRUE; gmt_is_set = true;
gmtload(gmtptr); gmtload(gmtptr);
} }
result = timesub(timep, offset, gmtptr, tmp); result = timesub(timep, offset, gmtptr, tmp);
/* /*
* Could get fancy here and deliver something such as "UTC+xxxx" or * Could get fancy here and deliver something such as "UT+xxxx" or
* "UTC-xxxx" if offset is non-zero, but this is no time for a treasure * "UT-xxxx" if offset is non-zero, but this is no time for a treasure
* hunt. * hunt.
*/ */
if (offset != 0) if (offset != 0)
...@@ -1126,7 +1290,7 @@ gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp) ...@@ -1126,7 +1290,7 @@ gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp)
struct pg_tm * struct pg_tm *
pg_gmtime(const pg_time_t *timep) pg_gmtime(const pg_time_t *timep)
{ {
return gmtsub(timep, 0L, &tm); return gmtsub(timep, 0, &tm);
} }
/* /*
...@@ -1140,24 +1304,23 @@ leaps_thru_end_of(const int y) ...@@ -1140,24 +1304,23 @@ leaps_thru_end_of(const int y)
-(leaps_thru_end_of(-(y + 1)) + 1); -(leaps_thru_end_of(-(y + 1)) + 1);
} }
static struct pg_tm * static struct pg_tm *
timesub(const pg_time_t *timep, long offset, timesub(const pg_time_t *timep, int32 offset,
const struct state * sp, struct pg_tm * tmp) const struct state * sp, struct pg_tm * tmp)
{ {
const struct lsinfo *lp; const struct lsinfo *lp;
pg_time_t tdays; pg_time_t tdays;
int idays; /* unsigned would be so 2003 */ int idays; /* unsigned would be so 2003 */
long rem; int64 rem;
int y; int y;
const int *ip; const int *ip;
long corr; int64 corr;
int hit; bool hit;
int i; int i;
corr = 0; corr = 0;
hit = 0; hit = false;
i = sp->leapcnt; i = (sp == NULL) ? 0 : sp->leapcnt;
while (--i >= 0) while (--i >= 0)
{ {
lp = &sp->lsis[i]; lp = &sp->lsis[i];
...@@ -1184,7 +1347,7 @@ timesub(const pg_time_t *timep, long offset, ...@@ -1184,7 +1347,7 @@ timesub(const pg_time_t *timep, long offset,
} }
y = EPOCH_YEAR; y = EPOCH_YEAR;
tdays = *timep / SECSPERDAY; tdays = *timep / SECSPERDAY;
rem = *timep - tdays * SECSPERDAY; rem = *timep % SECSPERDAY;
while (tdays < 0 || tdays >= year_lengths[isleap(y)]) while (tdays < 0 || tdays >= year_lengths[isleap(y)])
{ {
int newy; int newy;
...@@ -1193,27 +1356,21 @@ timesub(const pg_time_t *timep, long offset, ...@@ -1193,27 +1356,21 @@ timesub(const pg_time_t *timep, long offset,
int leapdays; int leapdays;
tdelta = tdays / DAYSPERLYEAR; tdelta = tdays / DAYSPERLYEAR;
if (!((!TYPE_SIGNED(pg_time_t) ||INT_MIN <= tdelta)
&& tdelta <= INT_MAX))
goto out_of_range;
idelta = tdelta; idelta = tdelta;
if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
return NULL;
if (idelta == 0) if (idelta == 0)
idelta = (tdays < 0) ? -1 : 1; idelta = (tdays < 0) ? -1 : 1;
newy = y; newy = y;
if (increment_overflow(&newy, idelta)) if (increment_overflow(&newy, idelta))
return NULL; goto out_of_range;
leapdays = leaps_thru_end_of(newy - 1) - leapdays = leaps_thru_end_of(newy - 1) -
leaps_thru_end_of(y - 1); leaps_thru_end_of(y - 1);
tdays -= ((pg_time_t) newy - y) * DAYSPERNYEAR; tdays -= ((pg_time_t) newy - y) * DAYSPERNYEAR;
tdays -= leapdays; tdays -= leapdays;
y = newy; y = newy;
} }
{
long seconds;
seconds = tdays * SECSPERDAY + 0.5;
tdays = seconds / SECSPERDAY;
rem += seconds - tdays * SECSPERDAY;
}
/* /*
* Given the range, we can now fearlessly cast... * Given the range, we can now fearlessly cast...
...@@ -1233,18 +1390,18 @@ timesub(const pg_time_t *timep, long offset, ...@@ -1233,18 +1390,18 @@ timesub(const pg_time_t *timep, long offset,
while (idays < 0) while (idays < 0)
{ {
if (increment_overflow(&y, -1)) if (increment_overflow(&y, -1))
return NULL; goto out_of_range;
idays += year_lengths[isleap(y)]; idays += year_lengths[isleap(y)];
} }
while (idays >= year_lengths[isleap(y)]) while (idays >= year_lengths[isleap(y)])
{ {
idays -= year_lengths[isleap(y)]; idays -= year_lengths[isleap(y)];
if (increment_overflow(&y, 1)) if (increment_overflow(&y, 1))
return NULL; goto out_of_range;
} }
tmp->tm_year = y; tmp->tm_year = y;
if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
return NULL; goto out_of_range;
tmp->tm_yday = idays; tmp->tm_yday = idays;
/* /*
...@@ -1275,20 +1432,49 @@ timesub(const pg_time_t *timep, long offset, ...@@ -1275,20 +1432,49 @@ timesub(const pg_time_t *timep, long offset,
tmp->tm_isdst = 0; tmp->tm_isdst = 0;
tmp->tm_gmtoff = offset; tmp->tm_gmtoff = offset;
return tmp; return tmp;
out_of_range:
errno = EOVERFLOW;
return NULL;
} }
/* /*
* Simplified normalize logic courtesy Paul Eggert. * Normalize logic courtesy Paul Eggert.
*/ */
static int static bool
increment_overflow(int *number, int delta) increment_overflow(int *ip, int j)
{ {
int number0; int const i = *ip;
/*----------
* If i >= 0 there can only be overflow if i + j > INT_MAX
* or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
* If i < 0 there can only be overflow if i + j < INT_MIN
* or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
*----------
*/
if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i))
return true;
*ip += j;
return false;
}
number0 = *number; static bool
*number += delta; increment_overflow_time(pg_time_t *tp, int32 j)
return (*number < number0) != (delta < 0); {
/*----------
* This is like
* 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...',
* except that it does the right thing even if *tp + j would overflow.
*----------
*/
if (!(j < 0
? (TYPE_SIGNED(pg_time_t) ? time_t_min - j <= *tp : -1 - j < *tp)
: *tp <= time_t_max - j))
return true;
*tp += j;
return false;
} }
/* /*
...@@ -1296,7 +1482,7 @@ increment_overflow(int *number, int delta) ...@@ -1296,7 +1482,7 @@ increment_overflow(int *number, int delta)
* *
* *timep and *tz are input arguments, the other parameters are output values. * *timep and *tz are input arguments, the other parameters are output values.
* *
* When the function result is 1, *boundary is set to the time_t * When the function result is 1, *boundary is set to the pg_time_t
* representation of the next DST transition time after *timep, * representation of the next DST transition time after *timep,
* *before_gmtoff and *before_isdst are set to the GMT offset and isdst * *before_gmtoff and *before_isdst are set to the GMT offset and isdst
* state prevailing just before that boundary (in particular, the state * state prevailing just before that boundary (in particular, the state
...@@ -1453,8 +1639,8 @@ pg_next_dst_boundary(const pg_time_t *timep, ...@@ -1453,8 +1639,8 @@ pg_next_dst_boundary(const pg_time_t *timep,
* the meaning in use at or most recently before that time, or the meaning * the meaning in use at or most recently before that time, or the meaning
* in first use after that time if the abbrev was never used before that. * in first use after that time if the abbrev was never used before that.
* *
* On success, returns TRUE and sets *gmtoff and *isdst. If the abbreviation * On success, returns true and sets *gmtoff and *isdst. If the abbreviation
* was never used at all in this zone, returns FALSE. * was never used at all in this zone, returns false.
* *
* Note: abbrev is matched case-sensitively; it should be all-upper-case. * Note: abbrev is matched case-sensitively; it should be all-upper-case.
*/ */
...@@ -1490,7 +1676,7 @@ pg_interpret_timezone_abbrev(const char *abbrev, ...@@ -1490,7 +1676,7 @@ pg_interpret_timezone_abbrev(const char *abbrev,
abbrind++; abbrind++;
} }
if (abbrind >= sp->charcnt) if (abbrind >= sp->charcnt)
return FALSE; /* not there! */ return false; /* not there! */
/* /*
* Unlike pg_next_dst_boundary, we needn't sweat about extrapolation * Unlike pg_next_dst_boundary, we needn't sweat about extrapolation
...@@ -1527,7 +1713,7 @@ pg_interpret_timezone_abbrev(const char *abbrev, ...@@ -1527,7 +1713,7 @@ pg_interpret_timezone_abbrev(const char *abbrev,
{ {
*gmtoff = ttisp->tt_gmtoff; *gmtoff = ttisp->tt_gmtoff;
*isdst = ttisp->tt_isdst; *isdst = ttisp->tt_isdst;
return TRUE; return true;
} }
} }
...@@ -1541,23 +1727,23 @@ pg_interpret_timezone_abbrev(const char *abbrev, ...@@ -1541,23 +1727,23 @@ pg_interpret_timezone_abbrev(const char *abbrev,
{ {
*gmtoff = ttisp->tt_gmtoff; *gmtoff = ttisp->tt_gmtoff;
*isdst = ttisp->tt_isdst; *isdst = ttisp->tt_isdst;
return TRUE; return true;
} }
} }
return FALSE; /* hm, not actually used in any interval? */ return false; /* hm, not actually used in any interval? */
} }
/* /*
* If the given timezone uses only one GMT offset, store that offset * If the given timezone uses only one GMT offset, store that offset
* into *gmtoff and return TRUE, else return FALSE. * into *gmtoff and return true, else return false.
*/ */
bool bool
pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff) pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff)
{ {
/* /*
* The zone could have more than one ttinfo, if it's historically used * The zone could have more than one ttinfo, if it's historically used
* more than one abbreviation. We return TRUE as long as they all have * more than one abbreviation. We return true as long as they all have
* the same gmtoff. * the same gmtoff.
*/ */
const struct state *sp; const struct state *sp;
......
...@@ -256,7 +256,7 @@ pg_tzset(const char *name) ...@@ -256,7 +256,7 @@ pg_tzset(const char *name)
*/ */
if (strcmp(uppername, "GMT") == 0) if (strcmp(uppername, "GMT") == 0)
{ {
if (tzparse(uppername, &tzstate, TRUE) != 0) if (tzparse(uppername, &tzstate, true) != 0)
{ {
/* This really, really should not happen ... */ /* This really, really should not happen ... */
elog(ERROR, "could not initialize GMT time zone"); elog(ERROR, "could not initialize GMT time zone");
...@@ -264,9 +264,9 @@ pg_tzset(const char *name) ...@@ -264,9 +264,9 @@ pg_tzset(const char *name)
/* Use uppercase name as canonical */ /* Use uppercase name as canonical */
strcpy(canonname, uppername); strcpy(canonname, uppername);
} }
else if (tzload(uppername, canonname, &tzstate, TRUE) != 0) else if (tzload(uppername, canonname, &tzstate, true) != 0)
{ {
if (uppername[0] == ':' || tzparse(uppername, &tzstate, FALSE) != 0) if (uppername[0] == ':' || tzparse(uppername, &tzstate, false) != 0)
{ {
/* Unknown timezone. Fail our call instead of loading GMT! */ /* Unknown timezone. Fail our call instead of loading GMT! */
return NULL; return NULL;
...@@ -460,7 +460,7 @@ pg_tzenumerate_next(pg_tzenum *dir) ...@@ -460,7 +460,7 @@ pg_tzenumerate_next(pg_tzenum *dir)
* the cache * the cache
*/ */
if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state, if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state,
TRUE) != 0) true) != 0)
{ {
/* Zone could not be loaded, ignore it */ /* Zone could not be loaded, ignore it */
continue; continue;
......
...@@ -16,25 +16,26 @@ ...@@ -16,25 +16,26 @@
#ifndef _PGTZ_H #ifndef _PGTZ_H
#define _PGTZ_H #define _PGTZ_H
#include "tzfile.h"
#include "pgtime.h" #include "pgtime.h"
#include "tzfile.h"
#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
struct ttinfo struct ttinfo
{ /* time type information */ { /* time type information */
long tt_gmtoff; /* UTC offset in seconds */ int32 tt_gmtoff; /* UT offset in seconds */
int tt_isdst; /* used to set tm_isdst */ bool tt_isdst; /* used to set tm_isdst */
int tt_abbrind; /* abbreviation list index */ int tt_abbrind; /* abbreviation list index */
int tt_ttisstd; /* TRUE if transition is std time */ bool tt_ttisstd; /* transition is std time */
int tt_ttisgmt; /* TRUE if transition is UTC */ bool tt_ttisgmt; /* transition is UT */
}; };
struct lsinfo struct lsinfo
{ /* leap second information */ { /* leap second information */
pg_time_t ls_trans; /* transition time */ pg_time_t ls_trans; /* transition time */
long ls_corr; /* correction to apply */ int64 ls_corr; /* correction to apply */
}; };
struct state struct state
...@@ -43,14 +44,15 @@ struct state ...@@ -43,14 +44,15 @@ struct state
int timecnt; int timecnt;
int typecnt; int typecnt;
int charcnt; int charcnt;
int goback; bool goback;
int goahead; bool goahead;
pg_time_t ats[TZ_MAX_TIMES]; pg_time_t ats[TZ_MAX_TIMES];
unsigned char types[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES];
struct ttinfo ttis[TZ_MAX_TYPES]; struct ttinfo ttis[TZ_MAX_TYPES];
char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 3 /* sizeof gmt */ ), char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 3 /* sizeof gmt */ ),
(2 * (TZ_STRLEN_MAX + 1)))]; (2 * (TZ_STRLEN_MAX + 1)))];
struct lsinfo lsis[TZ_MAX_LEAPS]; struct lsinfo lsis[TZ_MAX_LEAPS];
int defaulttype; /* for early times or if no transitions */
}; };
......
...@@ -25,6 +25,18 @@ ...@@ -25,6 +25,18 @@
#define GRANDPARENTED "Local time zone must be set--see zic manual page" #define GRANDPARENTED "Local time zone must be set--see zic manual page"
/*
* IANA has a bunch of HAVE_FOO #defines here, but in PG we want pretty
* much all of that to be done by PG's configure script.
*/
#ifndef ENOTSUP
#define ENOTSUP EINVAL
#endif
#ifndef EOVERFLOW
#define EOVERFLOW EINVAL
#endif
#ifndef WIFEXITED #ifndef WIFEXITED
#define WIFEXITED(status) (((status) & 0xff) == 0) #define WIFEXITED(status) (((status) & 0xff) == 0)
#endif /* !defined WIFEXITED */ #endif /* !defined WIFEXITED */
...@@ -35,6 +47,10 @@ ...@@ -35,6 +47,10 @@
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
#define is_digit(c) ((unsigned)(c) - '0' <= 9) #define is_digit(c) ((unsigned)(c) - '0' <= 9)
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t) -1)
#endif
/* /*
* SunOS 4.1.1 libraries lack remove. * SunOS 4.1.1 libraries lack remove.
*/ */
...@@ -45,31 +61,11 @@ extern int unlink(const char *filename); ...@@ -45,31 +61,11 @@ extern int unlink(const char *filename);
#define remove unlink #define remove unlink
#endif /* !defined remove */ #endif /* !defined remove */
/*
* Private function declarations.
*/
extern char *icalloc(int nelem, int elsize);
extern char *icatalloc(char *old, const char *new);
extern char *icpyalloc(const char *string);
extern char *imalloc(int n);
extern void *irealloc(void *pointer, int size);
extern void icfree(char *pointer);
extern void ifree(char *pointer);
extern const char *scheck(const char *string, const char *format);
/* /*
* Finally, some convenience items. * Finally, some convenience items.
*/ */
#ifndef TRUE
#define TRUE 1
#endif /* !defined TRUE */
#ifndef FALSE
#define FALSE 0
#endif /* !defined FALSE */
#ifndef TYPE_BIT #ifndef TYPE_BIT
#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
#endif /* !defined TYPE_BIT */ #endif /* !defined TYPE_BIT */
...@@ -78,14 +74,18 @@ extern const char *scheck(const char *string, const char *format); ...@@ -78,14 +74,18 @@ extern const char *scheck(const char *string, const char *format);
#define TYPE_SIGNED(type) (((type) -1) < 0) #define TYPE_SIGNED(type) (((type) -1) < 0)
#endif /* !defined TYPE_SIGNED */ #endif /* !defined TYPE_SIGNED */
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
/* /*
* Since the definition of TYPE_INTEGRAL contains floating point numbers, * Max and min values of the integer type T, of which only the bottom
* it cannot be used in preprocessor directives. * B bits are used, and where the highest-order used bit is considered
* to be a sign bit if T is signed.
*/ */
#define MAXVAL(t, b) \
#ifndef TYPE_INTEGRAL ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
#endif /* !defined TYPE_INTEGRAL */ #define MINVAL(t, b) \
((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
#ifndef INT_STRLEN_MAXIMUM #ifndef INT_STRLEN_MAXIMUM
/* /*
...@@ -95,34 +95,36 @@ extern const char *scheck(const char *string, const char *format); ...@@ -95,34 +95,36 @@ extern const char *scheck(const char *string, const char *format);
* add one more for a minus sign if the type is signed. * add one more for a minus sign if the type is signed.
*/ */
#define INT_STRLEN_MAXIMUM(type) \ #define INT_STRLEN_MAXIMUM(type) \
((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type)) ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
1 + TYPE_SIGNED(type))
#endif /* !defined INT_STRLEN_MAXIMUM */ #endif /* !defined INT_STRLEN_MAXIMUM */
/*
* INITIALIZE(x)
*/
#define INITIALIZE(x) ((x) = 0)
#undef _ #undef _
#define _(msgid) (msgid) #define _(msgid) (msgid)
#ifndef YEARSPERREPEAT #ifndef YEARSPERREPEAT
#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ #define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
#endif /* !defined YEARSPERREPEAT */ #endif /* !defined YEARSPERREPEAT */
/* /*
** The Gregorian year averages 365.2425 days, which is 31556952 seconds. * The Gregorian year averages 365.2425 days, which is 31556952 seconds.
*/ */
#ifndef AVGSECSPERYEAR #ifndef AVGSECSPERYEAR
#define AVGSECSPERYEAR 31556952L #define AVGSECSPERYEAR 31556952L
#endif /* !defined AVGSECSPERYEAR */ #endif /* !defined AVGSECSPERYEAR */
#ifndef SECSPERREPEAT #ifndef SECSPERREPEAT
#define SECSPERREPEAT ((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR) #define SECSPERREPEAT ((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR)
#endif /* !defined SECSPERREPEAT */ #endif /* !defined SECSPERREPEAT */
#ifndef SECSPERREPEAT_BITS #ifndef SECSPERREPEAT_BITS
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ #define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
#endif /* !defined SECSPERREPEAT_BITS */ #endif /* !defined SECSPERREPEAT_BITS */
/*
* UNIX was a registered trademark of The Open Group in 2003.
*/
#endif /* !defined PRIVATE_H */ #endif /* !defined PRIVATE_H */
/*
* This file is in the public domain, so clarified as of
* 2006-07-17 by Arthur David Olson.
*
* IDENTIFICATION
* src/timezone/scheck.c
*/
#include "postgres_fe.h"
#include "private.h"
const char *
scheck(const char *string, const char *format)
{
char *fbuf;
const char *fp;
char *tp;
int c;
const char *result;
char dummy;
result = "";
if (string == NULL || format == NULL)
return result;
fbuf = imalloc((int) (2 * strlen(format) + 4));
if (fbuf == NULL)
return result;
fp = format;
tp = fbuf;
while ((*tp++ = c = *fp++) != '\0')
{
if (c != '%')
continue;
if (*fp == '%')
{
*tp++ = *fp++;
continue;
}
*tp++ = '*';
if (*fp == '*')
++fp;
while (is_digit(*fp))
*tp++ = *fp++;
if (*fp == 'l' || *fp == 'h')
*tp++ = *fp++;
else if (*fp == '[')
do
*tp++ = *fp++;
while (*fp != '\0' && *fp != ']');
if ((*tp++ = *fp++) == '\0')
break;
}
*(tp - 1) = '%';
*tp++ = 'c';
*tp = '\0';
if (sscanf(string, fbuf, &dummy) != 1)
result = (char *) format;
ifree(fbuf);
return result;
}
/* Convert a broken-down time stamp to a string. */
/* /*
* Copyright (c) 1989 The Regents of the University of California. * Copyright 1989 The Regents of the University of California.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms are permitted * Redistribution and use in source and binary forms, with or without
* provided that the above copyright notice and this paragraph are * modification, are permitted provided that the following conditions
* duplicated in all such forms and that any documentation, * are met:
* advertising materials, and other materials related to such * 1. Redistributions of source code must retain the above copyright
* distribution and use acknowledge that the software was developed * notice, this list of conditions and the following disclaimer.
* by the University of California, Berkeley. The name of the * 2. Redistributions in binary form must reproduce the above copyright
* University may not be used to endorse or promote products derived * notice, this list of conditions and the following disclaimer in the
* from this software without specific prior written permission. * documentation and/or other materials provided with the distribution.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * 3. Neither the name of the University nor the names of its contributors
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * may be used to endorse or promote products derived from this software
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Based on the UCB version with the copyright notice appearing above.
*
* This is ANSIish only when "multibyte character == plain character".
* *
* IDENTIFICATION * IDENTIFICATION
* src/timezone/strftime.c * src/timezone/strftime.c
...@@ -92,8 +112,7 @@ static char *_add(const char *, char *, const char *); ...@@ -92,8 +112,7 @@ static char *_add(const char *, char *, const char *);
static char *_conv(int, const char *, char *, const char *); static char *_conv(int, const char *, char *, const char *);
static char *_fmt(const char *, const struct pg_tm *, char *, static char *_fmt(const char *, const struct pg_tm *, char *,
const char *, int *); const char *, int *);
static char *_yconv(const int, const int, const int, const int, static char *_yconv(int, int, bool, bool, char *, const char *);
char *, const char *const);
#define IN_NONE 0 #define IN_NONE 0
#define IN_SOME 1 #define IN_SOME 1
...@@ -162,8 +181,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -162,8 +181,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
* ...whereas now POSIX 1003.2 calls for something * ...whereas now POSIX 1003.2 calls for something
* completely different. (ado, 1993-05-24) * completely different. (ado, 1993-05-24)
*/ */
pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, pt = _yconv(t->tm_year, TM_YEAR_BASE,
pt, ptlim); true, false, pt, ptlim);
continue; continue;
case 'c': case 'c':
{ {
...@@ -224,7 +243,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -224,7 +243,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
case 'K': case 'K':
/* /*
* * After all this time, still unclaimed! * After all this time, still unclaimed!
*/ */
pt = _add("kitchen sink", pt, ptlim); pt = _add("kitchen sink", pt, ptlim);
continue; continue;
...@@ -296,7 +315,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -296,7 +315,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
* (01-53)." * (01-53)."
* (ado, 1993-05-24) * (ado, 1993-05-24)
* *
* From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: * From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn:
* "Week 01 of a year is per definition the first week which has the * "Week 01 of a year is per definition the first week which has the
* Thursday in this year, which is equivalent to the week which contains * Thursday in this year, which is equivalent to the week which contains
* the fourth day of January. In other words, the first week of a new year * the fourth day of January. In other words, the first week of a new year
...@@ -367,11 +386,13 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -367,11 +386,13 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
else if (*format == 'g') else if (*format == 'g')
{ {
*warnp = IN_ALL; *warnp = IN_ALL;
pt = _yconv(year, base, 0, 1, pt = _yconv(year, base,
false, true,
pt, ptlim); pt, ptlim);
} }
else else
pt = _yconv(year, base, 1, 1, pt = _yconv(year, base,
true, true,
pt, ptlim); pt, ptlim);
} }
continue; continue;
...@@ -409,11 +430,13 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -409,11 +430,13 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
continue; continue;
case 'y': case 'y':
*warnp = IN_ALL; *warnp = IN_ALL;
pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, pt = _yconv(t->tm_year, TM_YEAR_BASE,
false, true,
pt, ptlim); pt, ptlim);
continue; continue;
case 'Y': case 'Y':
pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, pt = _yconv(t->tm_year, TM_YEAR_BASE,
true, true,
pt, ptlim); pt, ptlim);
continue; continue;
case 'Z': case 'Z':
...@@ -427,7 +450,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -427,7 +450,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
continue; continue;
case 'z': case 'z':
{ {
int diff; long diff;
char const *sign; char const *sign;
if (t->tm_isdst < 0) if (t->tm_isdst < 0)
...@@ -441,9 +464,10 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, ...@@ -441,9 +464,10 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
else else
sign = "+"; sign = "+";
pt = _add(sign, pt, ptlim); pt = _add(sign, pt, ptlim);
diff /= 60; diff /= SECSPERMIN;
pt = _conv((diff / 60) * 100 + diff % 60, diff = (diff / MINSPERHOUR) * 100 +
"%04d", pt, ptlim); (diff % MINSPERHOUR);
pt = _conv(diff, "%04d", pt, ptlim);
} }
continue; continue;
case '+': case '+':
...@@ -473,7 +497,7 @@ _conv(int n, const char *format, char *pt, const char *ptlim) ...@@ -473,7 +497,7 @@ _conv(int n, const char *format, char *pt, const char *ptlim)
{ {
char buf[INT_STRLEN_MAXIMUM(int) +1]; char buf[INT_STRLEN_MAXIMUM(int) +1];
(void) sprintf(buf, format, n); sprintf(buf, format, n);
return _add(buf, pt, ptlim); return _add(buf, pt, ptlim);
} }
...@@ -493,13 +517,13 @@ _add(const char *str, char *pt, const char *ptlim) ...@@ -493,13 +517,13 @@ _add(const char *str, char *pt, const char *ptlim)
* with more only if necessary. * with more only if necessary.
*/ */
static char * static char *
_yconv(const int a, const int b, const int convert_top, _yconv(int a, int b, bool convert_top, bool convert_yy,
const int convert_yy, char *pt, const char *const ptlim) char *pt, const char *ptlim)
{ {
int lead; int lead;
int trail; int trail;
#define DIVISOR 100 #define DIVISOR 100
trail = a % DIVISOR + b % DIVISOR; trail = a % DIVISOR + b % DIVISOR;
lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
trail %= DIVISOR; trail %= DIVISOR;
......
...@@ -33,8 +33,8 @@ ...@@ -33,8 +33,8 @@
struct tzhead struct tzhead
{ {
char tzh_magic[4]; /* TZ_MAGIC */ char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' as of 2005 */ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
char tzh_reserved[15]; /* reserved--must be zero */ char tzh_reserved[15]; /* reserved; must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */ char tzh_leapcnt[4]; /* coded number of leap seconds */
...@@ -43,30 +43,29 @@ struct tzhead ...@@ -43,30 +43,29 @@ struct tzhead
char tzh_charcnt[4]; /* coded number of abbr. chars */ char tzh_charcnt[4]; /* coded number of abbr. chars */
}; };
/*---------- /*
* . . .followed by. . . * . . .followed by. . .
* *
* tzh_timecnt (char [4])s coded transition times a la time(2) * tzh_timecnt (char [4])s coded transition times a la time(2)
* tzh_timecnt (unsigned char)s types of local time starting at above * tzh_timecnt (unsigned char)s types of local time starting at above
* tzh_typecnt repetitions of * tzh_typecnt repetitions of
* one (char [4]) coded UTC offset in seconds * one (char [4]) coded UT offset in seconds
* one (unsigned char) used to set tm_isdst * one (unsigned char) used to set tm_isdst
* one (unsigned char) that's an abbreviation list index * one (unsigned char) that's an abbreviation list index
* tzh_charcnt (char)s '\0'-terminated zone abbreviations * tzh_charcnt (char)s '\0'-terminated zone abbreviations
* tzh_leapcnt repetitions of * tzh_leapcnt repetitions of
* one (char [4]) coded leap second transition times * one (char [4]) coded leap second transition times
* one (char [4]) total correction after above * one (char [4]) total correction after above
* tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition * tzh_ttisstdcnt (char)s indexed by type; if 1, transition
* time is standard time, if FALSE, * time is standard time, if 0,
* transition time is wall clock time * transition time is wall clock time
* if absent, transition times are * if absent, transition times are
* assumed to be wall clock time * assumed to be wall clock time
* tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition * tzh_ttisgmtcnt (char)s indexed by type; if 1, transition
* time is UTC, if FALSE, * time is UT, if 0,
* transition time is local time * transition time is local time
* if absent, transition times are * if absent, transition times are
* assumed to be local time * assumed to be local time
*----------
*/ */
/* /*
...@@ -77,6 +76,13 @@ struct tzhead ...@@ -77,6 +76,13 @@ struct tzhead
* instants after the last transition time stored in the file * instants after the last transition time stored in the file
* (with nothing between the newlines if there is no POSIX representation for * (with nothing between the newlines if there is no POSIX representation for
* such instants). * such instants).
*
* If tz_version is '3' or greater, the above is extended as follows.
* First, the POSIX TZ string's hour offset may range from -167
* through 167 as compared to the POSIX-required 0 through 24.
* Second, its DST start time may be January 1 at 00:00 and its stop
* time December 31 at 24:00 plus the difference between DST and
* standard time, indicating DST all year.
*/ */
/* /*
...@@ -84,8 +90,9 @@ struct tzhead ...@@ -84,8 +90,9 @@ struct tzhead
* exceed any of the limits below. * exceed any of the limits below.
*/ */
#define TZ_MAX_TIMES 1200 #define TZ_MAX_TIMES 2000
/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ #define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
...@@ -100,7 +107,7 @@ struct tzhead ...@@ -100,7 +107,7 @@ struct tzhead
#define DAYSPERNYEAR 365 #define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366 #define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) #define SECSPERDAY ((int32) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12 #define MONSPERYEAR 12
#define TM_SUNDAY 0 #define TM_SUNDAY 0
...@@ -143,6 +150,6 @@ struct tzhead ...@@ -143,6 +150,6 @@ struct tzhead
* We use this to avoid addition overflow problems. * We use this to avoid addition overflow problems.
*/ */
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
#endif /* !defined TZFILE_H */ #endif /* !defined TZFILE_H */
...@@ -8,28 +8,27 @@ ...@@ -8,28 +8,27 @@
#include "postgres_fe.h" #include "postgres_fe.h"
#include <limits.h>
#include <locale.h> #include <locale.h>
#include <sys/stat.h>
#include <time.h> #include <time.h>
#include "pg_getopt.h" #include "pg_getopt.h"
#include "private.h" #include "private.h"
#include "pgtz.h"
#include "tzfile.h" #include "tzfile.h"
#define ZIC_VERSION '2' #define ZIC_VERSION_PRE_2013 '2'
#define ZIC_VERSION '3'
typedef int64 zic_t; typedef int64 zic_t;
#define ZIC_MIN PG_INT64_MIN
#define ZIC_MAX PG_INT64_MAX
#define SCNdZIC INT64_MODIFIER "d"
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
#define ZIC_MAX_ABBR_LEN_WO_WARN 6 #define ZIC_MAX_ABBR_LEN_WO_WARN 6
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifndef WIN32 #ifndef WIN32
#ifdef S_IRUSR #ifdef S_IRUSR
#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
...@@ -38,37 +37,17 @@ typedef int64 zic_t; ...@@ -38,37 +37,17 @@ typedef int64 zic_t;
#endif #endif
#endif #endif
static char elsieid[] = "@(#)zic.c 8.20";
/*
* On some ancient hosts, predicates like `isspace(C)' are defined
* only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
* which says they are defined only if C == ((unsigned char) C) || C == EOF.
* Neither the C Standard nor Posix require that `isascii' exist.
* For portability, we check both ancient and modern requirements.
* If isascii is not defined, the isascii check succeeds trivially.
*/
#include <ctype.h>
#ifndef isascii
#define isascii(x) 1
#endif
#define OFFSET_STRLEN_MAXIMUM (7 + INT_STRLEN_MAXIMUM(long))
#define RULE_STRLEN_MAXIMUM 8 /* "Mdd.dd.d" */
#define end(cp) (strchr((cp), '\0'))
struct rule struct rule
{ {
const char *r_filename; const char *r_filename;
int r_linenum; int r_linenum;
const char *r_name; const char *r_name;
int r_loyear; /* for example, 1986 */ zic_t r_loyear; /* for example, 1986 */
int r_hiyear; /* for example, 1986 */ zic_t r_hiyear; /* for example, 1986 */
const char *r_yrtype; const char *r_yrtype;
int r_lowasnum; bool r_lowasnum;
int r_hiwasnum; bool r_hiwasnum;
int r_month; /* 0..11 */ int r_month; /* 0..11 */
...@@ -76,12 +55,11 @@ struct rule ...@@ -76,12 +55,11 @@ struct rule
int r_dayofmonth; int r_dayofmonth;
int r_wday; int r_wday;
long r_tod; /* time from midnight */ zic_t r_tod; /* time from midnight */
int r_todisstd; /* above is standard time if TRUE */ bool r_todisstd; /* above is standard time if 1 or wall clock
/* or wall clock time if FALSE */ * time if 0 */
int r_todisgmt; /* above is GMT if TRUE */ bool r_todisgmt; /* above is GMT if 1 or local time if 0 */
/* or local time if FALSE */ zic_t r_stdoff; /* offset from standard time */
long r_stdoff; /* offset from standard time */
const char *r_abbrvar; /* variable part of abbreviation */ const char *r_abbrvar; /* variable part of abbreviation */
int r_todo; /* a rule to do (used in outzone) */ int r_todo; /* a rule to do (used in outzone) */
...@@ -102,11 +80,12 @@ struct zone ...@@ -102,11 +80,12 @@ struct zone
int z_linenum; int z_linenum;
const char *z_name; const char *z_name;
long z_gmtoff; zic_t z_gmtoff;
const char *z_rule; const char *z_rule;
const char *z_format; const char *z_format;
char z_format_specifier;
long z_stdoff; zic_t z_stdoff;
struct rule *z_rules; struct rule *z_rules;
int z_nrules; int z_nrules;
...@@ -116,74 +95,69 @@ struct zone ...@@ -116,74 +95,69 @@ struct zone
}; };
extern int link(const char *fromname, const char *toname); extern int link(const char *fromname, const char *toname);
static void addtt(const pg_time_t starttime, int type);
static int addtype(long gmtoff, const char *abbr, int isdst, static void memory_exhausted(const char *msg) pg_attribute_noreturn();
int ttisstd, int ttisgmt); static void verror(const char *string, va_list args) pg_attribute_printf(1, 0);
static void leapadd(const pg_time_t t, int positive, int rolling, int count); static void error(const char *string,...) pg_attribute_printf(1, 2);
static void warning(const char *string,...) pg_attribute_printf(1, 2);
static void usage(FILE *stream, int status) pg_attribute_noreturn();
static void addtt(zic_t starttime, int type);
static int addtype(zic_t, char const *, bool, bool, bool);
static void leapadd(zic_t, bool, int, int);
static void adjleap(void); static void adjleap(void);
static void associate(void); static void associate(void);
static int ciequal(const char *ap, const char *bp); static void dolink(const char *fromfield, const char *tofield);
static void convert(long val, char *buf);
static void dolink(const char *fromfile, const char *tofile);
static void doabbr(char *abbr, const char *format,
const char *letters, int isdst, int doquotes);
static void eat(const char *name, int num);
static void eats(const char *name, int num,
const char *rname, int rnum);
static long eitol(int i);
static void error(const char *message);
static char **getfields(char *buf); static char **getfields(char *buf);
static long gethms(const char *string, const char *errstrng, static zic_t gethms(const char *string, const char *errstring,
int signable); bool);
static void infile(const char *filename); static void infile(const char *filename);
static void inleap(char **fields, int nfields); static void inleap(char **fields, int nfields);
static void inlink(char **fields, int nfields); static void inlink(char **fields, int nfields);
static void inrule(char **fields, int nfields); static void inrule(char **fields, int nfields);
static int inzcont(char **fields, int nfields); static bool inzcont(char **fields, int nfields);
static int inzone(char **fields, int nfields); static bool inzone(char **fields, int nfields);
static int inzsub(char **fields, int nfields, int iscont); static bool inzsub(char **, int, bool);
static int itsabbr(const char *abbr, const char *word);
static int itsdir(const char *name); static int itsdir(const char *name);
static int lowerit(int c); static bool is_alpha(char a);
static char *memcheck(char *tocheck); static char lowerit(char);
static int mkdirs(char *filename); static bool mkdirs(char *);
static void newabbr(const char *abbr); static void newabbr(const char *abbr);
static long oadd(long t1, long t2); static zic_t oadd(zic_t t1, zic_t t2);
static void outzone(const struct zone * zp, int ntzones); static void outzone(const struct zone * zp, int ntzones);
static void puttzcode(long code, FILE *fp); static zic_t rpytime(const struct rule * rp, zic_t wantedy);
static int rcomp(const void *leftp, const void *rightp);
static pg_time_t rpytime(const struct rule * rp, int wantedy);
static void rulesub(struct rule * rp, static void rulesub(struct rule * rp,
const char *loyearp, const char *hiyearp, const char *loyearp, const char *hiyearp,
const char *typep, const char *monthp, const char *typep, const char *monthp,
const char *dayp, const char *timep); const char *dayp, const char *timep);
static void setboundaries(void); static zic_t tadd(zic_t t1, zic_t t2);
static pg_time_t tadd(const pg_time_t t1, long t2); static bool yearistype(int year, const char *type);
static void usage(FILE *stream, int status);
static void writezone(const char *name, const char *string); /* Bound on length of what %z can expand to. */
static int yearistype(int year, const char *type); enum
{
PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1};
static int charcnt; static int charcnt;
static int errors; static bool errors;
static bool warnings;
static const char *filename; static const char *filename;
static int leapcnt; static int leapcnt;
static int leapseen; static bool leapseen;
static int leapminyear; static zic_t leapminyear;
static int leapmaxyear; static zic_t leapmaxyear;
static int linenum; static int linenum;
static int max_abbrvar_len; static int max_abbrvar_len = PERCENT_Z_LEN_BOUND;
static int max_format_len; static int max_format_len;
static zic_t max_time; static zic_t max_year;
static int max_year; static zic_t min_year;
static zic_t min_time; static bool noise;
static int min_year; static bool print_abbrevs;
static int noise;
static int print_abbrevs;
static zic_t print_cutoff; static zic_t print_cutoff;
static const char *rfilename; static const char *rfilename;
static int rlinenum; static int rlinenum;
static const char *progname; static const char *progname;
static int timecnt; static int timecnt;
static int timecnt_alloc;
static int typecnt; static int typecnt;
/* /*
...@@ -269,9 +243,11 @@ static int typecnt; ...@@ -269,9 +243,11 @@ static int typecnt;
static struct rule *rules; static struct rule *rules;
static int nrules; /* number of rules */ static int nrules; /* number of rules */
static int nrules_alloc;
static struct zone *zones; static struct zone *zones;
static int nzones; /* number of zones */ static int nzones; /* number of zones */
static int nzones_alloc;
struct link struct link
{ {
...@@ -283,6 +259,7 @@ struct link ...@@ -283,6 +259,7 @@ struct link
static struct link *links; static struct link *links;
static int nlinks; static int nlinks;
static int nlinks_alloc;
struct lookup struct lookup
{ {
...@@ -353,8 +330,8 @@ static struct lookup const end_years[] = { ...@@ -353,8 +330,8 @@ static struct lookup const end_years[] = {
}; };
static struct lookup const leap_types[] = { static struct lookup const leap_types[] = {
{"Rolling", TRUE}, {"Rolling", true},
{"Stationary", FALSE}, {"Stationary", false},
{NULL, 0} {NULL, 0}
}; };
...@@ -371,40 +348,78 @@ static struct attype ...@@ -371,40 +348,78 @@ static struct attype
{ {
zic_t at; zic_t at;
unsigned char type; unsigned char type;
} attypes[TZ_MAX_TIMES]; } *attypes;
static long gmtoffs[TZ_MAX_TYPES]; static zic_t gmtoffs[TZ_MAX_TYPES];
static char isdsts[TZ_MAX_TYPES]; static char isdsts[TZ_MAX_TYPES];
static unsigned char abbrinds[TZ_MAX_TYPES]; static unsigned char abbrinds[TZ_MAX_TYPES];
static char ttisstds[TZ_MAX_TYPES]; static bool ttisstds[TZ_MAX_TYPES];
static char ttisgmts[TZ_MAX_TYPES]; static bool ttisgmts[TZ_MAX_TYPES];
static char chars[TZ_MAX_CHARS]; static char chars[TZ_MAX_CHARS];
static zic_t trans[TZ_MAX_LEAPS]; static zic_t trans[TZ_MAX_LEAPS];
static long corr[TZ_MAX_LEAPS]; static zic_t corr[TZ_MAX_LEAPS];
static char roll[TZ_MAX_LEAPS]; static char roll[TZ_MAX_LEAPS];
/* /*
* Memory allocation. * Memory allocation.
*/ */
static char * static void
memcheck(char *ptr) memory_exhausted(const char *msg)
{
fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
exit(EXIT_FAILURE);
}
static size_t
size_product(size_t nitems, size_t itemsize)
{
if (SIZE_MAX / itemsize < nitems)
memory_exhausted(_("size overflow"));
return nitems * itemsize;
}
static void *
memcheck(void *ptr)
{ {
if (ptr == NULL) if (ptr == NULL)
memory_exhausted(strerror(errno));
return ptr;
}
static void *
emalloc(size_t size)
{
return memcheck(malloc(size));
}
static void *
erealloc(void *ptr, size_t size)
{
return memcheck(realloc(ptr, size));
}
static char *
ecpyalloc(char const * str)
{
return memcheck(strdup(str));
}
static void *
growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
{
if (nitems < *nitems_alloc)
return ptr;
else
{ {
const char *e = strerror(errno); int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX;
(void) fprintf(stderr, _("%s: Memory exhausted: %s\n"), if ((amax - 1) / 3 * 2 < *nitems_alloc)
progname, e); memory_exhausted(_("int overflow"));
exit(EXIT_FAILURE); *nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1;
return erealloc(ptr, size_product(*nitems_alloc, itemsize));
} }
return ptr;
} }
#define emalloc(size) memcheck(imalloc(size))
#define erealloc(ptr, size) memcheck(irealloc((ptr), (size)))
#define ecpyalloc(ptr) memcheck(icpyalloc(ptr))
#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp)))
/* /*
* Error handling. * Error handling.
*/ */
...@@ -421,46 +436,75 @@ eats(const char *name, int num, const char *rname, int rnum) ...@@ -421,46 +436,75 @@ eats(const char *name, int num, const char *rname, int rnum)
static void static void
eat(const char *name, int num) eat(const char *name, int num)
{ {
eats(name, num, (char *) NULL, -1); eats(name, num, NULL, -1);
} }
static void static void
error(const char *string) verror(const char *string, va_list args)
{ {
/* /*
* Match the format of "cc" to allow sh users to zic ... 2>&1 | error -t * Match the format of "cc" to allow sh users to zic ... 2>&1 | error -t
* "*" -v on BSD systems. * "*" -v on BSD systems.
*/ */
(void) fprintf(stderr, _("\"%s\", line %d: %s"), if (filename)
filename, linenum, string); fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
vfprintf(stderr, string, args);
if (rfilename != NULL) if (rfilename != NULL)
(void) fprintf(stderr, _(" (rule from \"%s\", line %d)"), fprintf(stderr, _(" (rule from \"%s\", line %d)"),
rfilename, rlinenum); rfilename, rlinenum);
(void) fprintf(stderr, "\n"); fprintf(stderr, "\n");
++errors;
} }
static void static void
warning(const char *string) error(const char *string,...)
{ {
char *cp; va_list args;
va_start(args, string);
verror(string, args);
va_end(args);
errors = true;
}
static void
warning(const char *string,...)
{
va_list args;
fprintf(stderr, _("warning: "));
va_start(args, string);
verror(string, args);
va_end(args);
warnings = true;
}
static void
close_file(FILE *stream, char const * name)
{
char const *e = (ferror(stream) ? _("I/O error")
: fclose(stream) != 0 ? strerror(errno) : NULL);
cp = ecpyalloc(_("warning: ")); if (e)
cp = ecatalloc(cp, string); {
error(cp); fprintf(stderr, "%s: ", progname);
ifree(cp); if (name)
--errors; fprintf(stderr, "%s: ", name);
fprintf(stderr, "%s\n", e);
exit(EXIT_FAILURE);
}
} }
static void static void
usage(FILE *stream, int status) usage(FILE *stream, int status)
{ {
(void) fprintf(stream, _("%s: usage is %s \ fprintf(stream,
[ --version ] [ --help ] [ -v ] [ -P ] [ -l localtime ] [ -p posixrules ] \\\n\ _("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -P ] \\\n"
\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\ "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n"
\n\ "\t[ -L leapseconds ] [ filename ... ]\n\n"
Report bugs to tz@elsie.nci.nih.gov.\n"), "Report bugs to %s.\n"),
progname, progname); progname, progname, PACKAGE_BUGREPORT);
if (status == EXIT_SUCCESS)
close_file(stream, NULL);
exit(status); exit(status);
} }
...@@ -478,20 +522,21 @@ main(int argc, char *argv[]) ...@@ -478,20 +522,21 @@ main(int argc, char *argv[])
int c; int c;
#ifndef WIN32 #ifndef WIN32
(void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
#endif /* !WIN32 */ #endif /* !WIN32 */
progname = argv[0]; progname = argv[0];
if (TYPE_BIT(zic_t) <64) if (TYPE_BIT(zic_t) <64)
{ {
(void) fprintf(stderr, "%s: %s\n", progname, fprintf(stderr, "%s: %s\n", progname,
_("wild compilation-time specification of zic_t")); _("wild compilation-time specification of zic_t"));
exit(EXIT_FAILURE); return EXIT_FAILURE;
} }
for (i = 1; i < argc; ++i) for (i = 1; i < argc; ++i)
if (strcmp(argv[i], "--version") == 0) if (strcmp(argv[i], "--version") == 0)
{ {
(void) printf("%s\n", elsieid); printf("zic %s\n", PG_VERSION);
exit(EXIT_SUCCESS); close_file(stdout, NULL);
return EXIT_SUCCESS;
} }
else if (strcmp(argv[i], "--help") == 0) else if (strcmp(argv[i], "--help") == 0)
{ {
...@@ -507,10 +552,10 @@ main(int argc, char *argv[]) ...@@ -507,10 +552,10 @@ main(int argc, char *argv[])
directory = strdup(optarg); directory = strdup(optarg);
else else
{ {
(void) fprintf(stderr, fprintf(stderr,
_("%s: More than one -d option specified\n"), _("%s: More than one -d option specified\n"),
progname); progname);
exit(EXIT_FAILURE); return EXIT_FAILURE;
} }
break; break;
case 'l': case 'l':
...@@ -518,10 +563,10 @@ main(int argc, char *argv[]) ...@@ -518,10 +563,10 @@ main(int argc, char *argv[])
lcltime = strdup(optarg); lcltime = strdup(optarg);
else else
{ {
(void) fprintf(stderr, fprintf(stderr,
_("%s: More than one -l option specified\n"), _("%s: More than one -l option specified\n"),
progname); progname);
exit(EXIT_FAILURE); return EXIT_FAILURE;
} }
break; break;
case 'p': case 'p':
...@@ -529,10 +574,10 @@ main(int argc, char *argv[]) ...@@ -529,10 +574,10 @@ main(int argc, char *argv[])
psxrules = strdup(optarg); psxrules = strdup(optarg);
else else
{ {
(void) fprintf(stderr, fprintf(stderr,
_("%s: More than one -p option specified\n"), _("%s: More than one -p option specified\n"),
progname); progname);
exit(EXIT_FAILURE); return EXIT_FAILURE;
} }
break; break;
case 'y': case 'y':
...@@ -540,10 +585,10 @@ main(int argc, char *argv[]) ...@@ -540,10 +585,10 @@ main(int argc, char *argv[])
yitcommand = strdup(optarg); yitcommand = strdup(optarg);
else else
{ {
(void) fprintf(stderr, fprintf(stderr,
_("%s: More than one -y option specified\n"), _("%s: More than one -y option specified\n"),
progname); progname);
exit(EXIT_FAILURE); return EXIT_FAILURE;
} }
break; break;
case 'L': case 'L':
...@@ -551,21 +596,21 @@ main(int argc, char *argv[]) ...@@ -551,21 +596,21 @@ main(int argc, char *argv[])
leapsec = strdup(optarg); leapsec = strdup(optarg);
else else
{ {
(void) fprintf(stderr, fprintf(stderr,
_("%s: More than one -L option specified\n"), _("%s: More than one -L option specified\n"),
progname); progname);
exit(EXIT_FAILURE); return EXIT_FAILURE;
} }
break; break;
case 'v': case 'v':
noise = TRUE; noise = true;
break; break;
case 'P': case 'P':
print_abbrevs = TRUE; print_abbrevs = true;
print_cutoff = time(NULL); print_cutoff = time(NULL);
break; break;
case 's': case 's':
(void) printf("%s: -s ignored\n", progname); warning(_("-s ignored"));
break; break;
} }
if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
...@@ -575,8 +620,6 @@ main(int argc, char *argv[]) ...@@ -575,8 +620,6 @@ main(int argc, char *argv[])
if (yitcommand == NULL) if (yitcommand == NULL)
yitcommand = "yearistype"; yitcommand = "yearistype";
setboundaries();
if (optind < argc && leapsec != NULL) if (optind < argc && leapsec != NULL)
{ {
infile(leapsec); infile(leapsec);
...@@ -586,7 +629,7 @@ main(int argc, char *argv[]) ...@@ -586,7 +629,7 @@ main(int argc, char *argv[])
for (i = optind; i < argc; ++i) for (i = optind; i < argc; ++i)
infile(argv[i]); infile(argv[i]);
if (errors) if (errors)
exit(EXIT_FAILURE); return EXIT_FAILURE;
associate(); associate();
for (i = 0; i < nzones; i = j) for (i = 0; i < nzones; i = j)
{ {
...@@ -613,110 +656,284 @@ main(int argc, char *argv[]) ...@@ -613,110 +656,284 @@ main(int argc, char *argv[])
} }
if (lcltime != NULL) if (lcltime != NULL)
{ {
eat("command line", 1); eat(_("command line"), 1);
dolink(lcltime, TZDEFAULT); dolink(lcltime, TZDEFAULT);
} }
if (psxrules != NULL) if (psxrules != NULL)
{ {
eat("command line", 1); eat(_("command line"), 1);
dolink(psxrules, TZDEFRULES); dolink(psxrules, TZDEFRULES);
} }
return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; if (warnings && (ferror(stderr) || fclose(stderr) != 0))
return EXIT_FAILURE;
return errors ? EXIT_FAILURE : EXIT_SUCCESS;
} }
static void static bool
dolink(const char *fromfield, const char *tofield) componentcheck(char const * name, char const * component,
char const * component_end)
{ {
char *fromname; enum
char *toname; {
component_len_max = 14};
size_t component_len = component_end - component;
if (fromfield[0] == '/') if (component_len == 0)
fromname = ecpyalloc(fromfield); {
else if (!*name)
error(_("empty file name"));
else
error(_(component == name
? "file name '%s' begins with '/'"
: *component_end
? "file name '%s' contains '//'"
: "file name '%s' ends with '/'"),
name);
return false;
}
if (0 < component_len && component_len <= 2
&& component[0] == '.' && component_end[-1] == '.')
{
error(_("file name '%s' contains '%.*s' component"),
name, (int) component_len, component);
return false;
}
if (noise)
{
if (0 < component_len && component[0] == '-')
warning(_("file name '%s' component contains leading '-'"),
name);
if (component_len_max < component_len)
warning(_("file name '%s' contains overlength component"
" '%.*s...'"),
name, component_len_max, component);
}
return true;
}
static bool
namecheck(const char *name)
{
char const *cp;
/* Benign characters in a portable file name. */
static char const benign[] =
"-/_"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/*
* Non-control chars in the POSIX portable character set, excluding the
* benign characters.
*/
static char const printable_and_not_benign[] =
" !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
char const *component = name;
for (cp = name; *cp; cp++)
{ {
fromname = ecpyalloc(directory); unsigned char c = *cp;
fromname = ecatalloc(fromname, "/");
fromname = ecatalloc(fromname, fromfield); if (noise && !strchr(benign, c))
{
warning((strchr(printable_and_not_benign, c)
? _("file name '%s' contains byte '%c'")
: _("file name '%s' contains byte '\\%o'")),
name, c);
}
if (c == '/')
{
if (!componentcheck(name, component, cp))
return false;
component = cp + 1;
}
} }
if (tofield[0] == '/') return componentcheck(name, component, cp);
toname = ecpyalloc(tofield); }
static char *
relname(char const * dir, char const * base)
{
if (*base == '/')
return ecpyalloc(base);
else else
{ {
toname = ecpyalloc(directory); size_t dir_len = strlen(dir);
toname = ecatalloc(toname, "/"); bool needs_slash = dir_len && dir[dir_len - 1] != '/';
toname = ecatalloc(toname, tofield); char *result = emalloc(dir_len + needs_slash + strlen(base) + 1);
result[dir_len] = '/';
strcpy(result + dir_len + needs_slash, base);
return memcpy(result, dir, dir_len);
} }
}
static void
dolink(char const * fromfield, char const * tofield)
{
char *fromname;
char *toname;
int fromisdir;
fromname = relname(directory, fromfield);
toname = relname(directory, tofield);
/* /*
* We get to be careful here since there's a fair chance of root running * We get to be careful here since there's a fair chance of root running
* us. * us.
*/ */
if (!itsdir(toname)) fromisdir = itsdir(fromname);
(void) remove(toname); if (fromisdir)
if (link(fromname, toname) != 0)
{ {
int result; char const *e = strerror(fromisdir < 0 ? errno : EPERM);
if (mkdirs(toname) != 0) fprintf(stderr, _("%s: link from %s failed: %s"),
exit(EXIT_FAILURE); progname, fromname, e);
exit(EXIT_FAILURE);
}
if (link(fromname, toname) != 0)
{
int link_errno = errno;
bool retry_if_link_supported = false;
result = link(fromname, toname); if (link_errno == ENOENT || link_errno == ENOTSUP)
#ifdef HAVE_SYMLINK
if (result != 0 &&
access(fromname, F_OK) == 0 &&
!itsdir(fromname))
{ {
const char *s = tofield; if (!mkdirs(toname))
char *symlinkcontents = NULL; exit(EXIT_FAILURE);
retry_if_link_supported = true;
while ((s = strchr(s + 1, '/')) != NULL)
symlinkcontents = ecatalloc(symlinkcontents, "../");
symlinkcontents = ecatalloc(symlinkcontents, fromfield);
result = symlink(symlinkcontents, toname);
if (result == 0)
warning(_("hard link failed, symbolic link used"));
ifree(symlinkcontents);
} }
#endif if ((link_errno == EEXIST || link_errno == ENOTSUP)
if (result != 0) && itsdir(toname) == 0
&& (remove(toname) == 0 || errno == ENOENT))
retry_if_link_supported = true;
if (retry_if_link_supported && link_errno != ENOTSUP)
link_errno = link(fromname, toname) == 0 ? 0 : errno;
if (link_errno != 0)
{ {
const char *e = strerror(errno); const char *s = fromfield;
const char *t;
char *p;
size_t dotdots = 0;
char *symlinkcontents;
int symlink_result;
do
t = s;
while ((s = strchr(s, '/'))
&& strncmp(fromfield, tofield, ++s - fromfield) == 0);
for (s = tofield + (t - fromfield); *s; s++)
dotdots += *s == '/';
symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1);
for (p = symlinkcontents; dotdots-- != 0; p += 3)
memcpy(p, "../", 3);
strcpy(p, t);
symlink_result = symlink(symlinkcontents, toname);
free(symlinkcontents);
if (symlink_result == 0)
{
if (link_errno != ENOTSUP)
warning(_("symbolic link used because hard link failed: %s"),
strerror(link_errno));
}
else
{
FILE *fp,
*tp;
int c;
(void) fprintf(stderr, fp = fopen(fromname, "rb");
_("%s: Cannot link from %s to %s: %s\n"), if (!fp)
progname, fromname, toname, e); {
exit(EXIT_FAILURE); const char *e = strerror(errno);
fprintf(stderr,
_("%s: Can't read %s: %s\n"),
progname, fromname, e);
exit(EXIT_FAILURE);
}
tp = fopen(toname, "wb");
if (!tp)
{
const char *e = strerror(errno);
fprintf(stderr,
_("%s: Can't create %s: %s\n"),
progname, toname, e);
exit(EXIT_FAILURE);
}
while ((c = getc(fp)) != EOF)
putc(c, tp);
close_file(fp, fromname);
close_file(tp, toname);
if (link_errno != ENOTSUP)
warning(_("copy used because hard link failed: %s"),
strerror(link_errno));
}
} }
} }
ifree(fromname); free(fromname);
ifree(toname); free(toname);
} }
#define TIME_T_BITS_IN_FILE 64 #define TIME_T_BITS_IN_FILE 64
static void static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
setboundaries(void) static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
{
int i;
min_time = -1; /*
for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) * Estimated time of the Big Bang, in seconds since the POSIX epoch.
min_time *= 2; * rounded downward to the negation of a power of two that is
max_time = -(min_time + 1); * comfortably outside the error bounds.
} *
* zic does not output time stamps before this, partly because they
* are physically suspect, and partly because GNOME mishandles them; see
* GNOME bug 730332 <https://bugzilla.gnome.org/show_bug.cgi?id=730332>.
*
* For the time of the Big Bang, see:
*
* Ade PAR, Aghanim N, Armitage-Caplan C et al. Planck 2013 results.
* I. Overview of products and scientific results.
* arXiv:1303.5062 2013-03-20 20:10:01 UTC
* <http://arxiv.org/pdf/1303.5062v1> [PDF]
*
* Page 36, Table 9, row Age/Gyr, column Planck+WP+highL+BAO 68% limits
* gives the value 13.798 plus-or-minus 0.037 billion years.
* Multiplying this by 1000000000 and then by 31557600 (the number of
* seconds in an astronomical year) gives a value that is comfortably
* less than 2**59, so BIG_BANG is - 2**59.
*
* BIG_BANG is approximate, and may change in future versions.
* Please do not rely on its exact value.
*/
#ifndef BIG_BANG
#define BIG_BANG (- (((zic_t) 1) << 59))
#endif
static const zic_t big_bang_time = BIG_BANG;
/* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble. */
static int static int
itsdir(const char *name) itsdir(char const * name)
{ {
char *myname; struct stat st;
int accres; int res = stat(name, &st);
#ifdef S_ISDIR
if (res == 0)
return S_ISDIR(st.st_mode) != 0;
#endif
if (res == 0 || errno == EOVERFLOW)
{
char *nameslashdot = relname(name, ".");
bool dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW;
myname = ecpyalloc(name); free(nameslashdot);
myname = ecatalloc(myname, "/."); return dir;
accres = access(myname, F_OK); }
ifree(myname); return -1;
return accres == 0;
} }
/* /*
...@@ -746,8 +963,7 @@ associate(void) ...@@ -746,8 +963,7 @@ associate(void)
if (nrules != 0) if (nrules != 0)
{ {
(void) qsort((void *) rules, (size_t) nrules, qsort(rules, nrules, sizeof *rules, rcomp);
(size_t) sizeof *rules, rcomp);
for (i = 0; i < nrules - 1; ++i) for (i = 0; i < nrules - 1; ++i)
{ {
if (strcmp(rules[i].r_name, if (strcmp(rules[i].r_name,
...@@ -807,14 +1023,14 @@ associate(void) ...@@ -807,14 +1023,14 @@ associate(void)
*/ */
eat(zp->z_filename, zp->z_linenum); eat(zp->z_filename, zp->z_linenum);
zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
TRUE); true);
/* /*
* Note, though, that if there's no rule, a '%s' in the format is * Note, though, that if there's no rule, a '%s' in the format is
* a bad thing. * a bad thing.
*/ */
if (strchr(zp->z_format, '%') != NULL) if (zp->z_format_specifier == 's')
error(_("%s in ruleless zone")); error("%s", _("%s in ruleless zone"));
} }
} }
if (errors) if (errors)
...@@ -829,7 +1045,7 @@ infile(const char *name) ...@@ -829,7 +1045,7 @@ infile(const char *name)
char *cp; char *cp;
const struct lookup *lp; const struct lookup *lp;
int nfields; int nfields;
int wantcont; bool wantcont;
int num; int num;
char buf[BUFSIZ]; char buf[BUFSIZ];
...@@ -842,15 +1058,15 @@ infile(const char *name) ...@@ -842,15 +1058,15 @@ infile(const char *name)
{ {
const char *e = strerror(errno); const char *e = strerror(errno);
(void) fprintf(stderr, _("%s: Cannot open %s: %s\n"), fprintf(stderr, _("%s: Cannot open %s: %s\n"),
progname, name, e); progname, name, e);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
wantcont = FALSE; wantcont = false;
for (num = 1;; ++num) for (num = 1;; ++num)
{ {
eat(name, num); eat(name, num);
if (fgets(buf, (int) sizeof buf, fp) != buf) if (fgets(buf, sizeof buf, fp) != buf)
break; break;
cp = strchr(buf, '\n'); cp = strchr(buf, '\n');
if (cp == NULL) if (cp == NULL)
...@@ -885,66 +1101,53 @@ infile(const char *name) ...@@ -885,66 +1101,53 @@ infile(const char *name)
{ {
case LC_RULE: case LC_RULE:
inrule(fields, nfields); inrule(fields, nfields);
wantcont = FALSE; wantcont = false;
break; break;
case LC_ZONE: case LC_ZONE:
wantcont = inzone(fields, nfields); wantcont = inzone(fields, nfields);
break; break;
case LC_LINK: case LC_LINK:
inlink(fields, nfields); inlink(fields, nfields);
wantcont = FALSE; wantcont = false;
break; break;
case LC_LEAP: case LC_LEAP:
if (name != leapsec) if (name != leapsec)
(void) fprintf(stderr, warning(_("%s: Leap line in non leap"
_("%s: Leap line in non leap seconds file %s\n"), " seconds file %s"),
progname, name); progname, name);
else else
inleap(fields, nfields); inleap(fields, nfields);
wantcont = FALSE; wantcont = false;
break; break;
default: /* "cannot happen" */ default: /* "cannot happen" */
(void) fprintf(stderr, fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"), _("%s: panic: Invalid l_value %d\n"),
progname, lp->l_value); progname, lp->l_value);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
ifree((char *) fields); free(fields);
}
if (ferror(fp))
{
(void) fprintf(stderr, _("%s: Error reading %s\n"),
progname, filename);
exit(EXIT_FAILURE);
}
if (fp != stdin && fclose(fp))
{
const char *e = strerror(errno);
(void) fprintf(stderr, _("%s: Error closing %s: %s\n"),
progname, filename, e);
exit(EXIT_FAILURE);
} }
close_file(fp, filename);
if (wantcont) if (wantcont)
error(_("expected continuation line not found")); error(_("expected continuation line not found"));
} }
/*---------- /*
* Convert a string of one of the forms * Convert a string of one of the forms
* h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss * h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
* into a number of seconds. * into a number of seconds.
* A null string maps to zero. * A null string maps to zero.
* Call error with errstring and return zero on errors. * Call error with errstring and return zero on errors.
*----------
*/ */
static long static zic_t
gethms(const char *string, const char *errstring, int signable) gethms(char const * string, char const * errstring, bool signable)
{ {
long hh; zic_t hh;
int mm, int mm,
ss, ss,
sign; sign;
char xs;
if (string == NULL || *string == '\0') if (string == NULL || *string == '\0')
return 0; return 0;
...@@ -957,35 +1160,33 @@ gethms(const char *string, const char *errstring, int signable) ...@@ -957,35 +1160,33 @@ gethms(const char *string, const char *errstring, int signable)
} }
else else
sign = 1; sign = 1;
if (sscanf(string, scheck(string, "%ld"), &hh) == 1) if (sscanf(string, "%" SCNdZIC "%c", &hh, &xs) == 1)
mm = ss = 0; mm = ss = 0;
else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2) else if (sscanf(string, "%" SCNdZIC ":%d%c", &hh, &mm, &xs) == 2)
ss = 0; ss = 0;
else if (sscanf(string, scheck(string, "%ld:%d:%d"), else if (sscanf(string, "%" SCNdZIC ":%d:%d%c", &hh, &mm, &ss, &xs)
&hh, &mm, &ss) != 3) != 3)
{ {
error(errstring); error("%s", errstring);
return 0; return 0;
} }
if (hh < 0 || if (hh < 0 ||
mm < 0 || mm >= MINSPERHOUR || mm < 0 || mm >= MINSPERHOUR ||
ss < 0 || ss > SECSPERMIN) ss < 0 || ss > SECSPERMIN)
{ {
error(errstring); error("%s", errstring);
return 0; return 0;
} }
if (LONG_MAX / SECSPERHOUR < hh) if (ZIC_MAX / SECSPERHOUR < hh)
{ {
error(_("time overflow")); error(_("time overflow"));
return 0; return 0;
} }
if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0)
warning(_("24:00 not handled by pre-1998 versions of zic"));
if (noise && (hh > HOURSPERDAY || if (noise && (hh > HOURSPERDAY ||
(hh == HOURSPERDAY && (mm != 0 || ss != 0)))) (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
warning(_("values over 24 hours not handled by pre-2007 versions of zic")); warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
return oadd(eitol(sign) * hh * eitol(SECSPERHOUR), return oadd(sign * hh * SECSPERHOUR,
eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss))); sign * (mm * SECSPERMIN + ss));
} }
static void static void
...@@ -1005,80 +1206,71 @@ inrule(char **fields, int nfields) ...@@ -1005,80 +1206,71 @@ inrule(char **fields, int nfields)
} }
r.r_filename = filename; r.r_filename = filename;
r.r_linenum = linenum; r.r_linenum = linenum;
r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE); r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true);
rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
r.r_name = ecpyalloc(fields[RF_NAME]); r.r_name = ecpyalloc(fields[RF_NAME]);
r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
if (max_abbrvar_len < strlen(r.r_abbrvar)) if (max_abbrvar_len < strlen(r.r_abbrvar))
max_abbrvar_len = strlen(r.r_abbrvar); max_abbrvar_len = strlen(r.r_abbrvar);
rules = (struct rule *) (void *) erealloc((char *) rules, rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
(int) ((nrules + 1) * sizeof *rules));
rules[nrules++] = r; rules[nrules++] = r;
} }
static int static bool
inzone(char **fields, int nfields) inzone(char **fields, int nfields)
{ {
int i; int i;
static char *buf;
if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS)
{ {
error(_("wrong number of fields on Zone line")); error(_("wrong number of fields on Zone line"));
return FALSE; return false;
} }
if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL)
{ {
buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT))); error(
(void) sprintf(buf, _("\"Zone %s\" line and -l option are mutually exclusive"),
_("\"Zone %s\" line and -l option are mutually exclusive"), TZDEFAULT);
TZDEFAULT); return false;
error(buf);
return FALSE;
} }
if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL)
{ {
buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES))); error(
(void) sprintf(buf, _("\"Zone %s\" line and -p option are mutually exclusive"),
_("\"Zone %s\" line and -p option are mutually exclusive"), TZDEFRULES);
TZDEFRULES); return false;
error(buf);
return FALSE;
} }
for (i = 0; i < nzones; ++i) for (i = 0; i < nzones; ++i)
if (zones[i].z_name != NULL && if (zones[i].z_name != NULL &&
strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) strcmp(zones[i].z_name, fields[ZF_NAME]) == 0)
{ {
buf = erealloc(buf, (int) (132 + error(
strlen(fields[ZF_NAME]) + _("duplicate zone name %s (file \"%s\", line %d)"),
strlen(zones[i].z_filename))); fields[ZF_NAME],
(void) sprintf(buf, zones[i].z_filename,
_("duplicate zone name %s (file \"%s\", line %d)"), zones[i].z_linenum);
fields[ZF_NAME], return false;
zones[i].z_filename,
zones[i].z_linenum);
error(buf);
return FALSE;
} }
return inzsub(fields, nfields, FALSE); return inzsub(fields, nfields, false);
} }
static int static bool
inzcont(char **fields, int nfields) inzcont(char **fields, int nfields)
{ {
if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS)
{ {
error(_("wrong number of fields on Zone continuation line")); error(_("wrong number of fields on Zone continuation line"));
return FALSE; return false;
} }
return inzsub(fields, nfields, TRUE); return inzsub(fields, nfields, true);
} }
static int static bool
inzsub(char **fields, int nfields, int iscont) inzsub(char **fields, int nfields, bool iscont)
{ {
char *cp; char *cp;
char *cp1;
static struct zone z; static struct zone z;
int i_gmtoff, int i_gmtoff,
i_rule, i_rule,
...@@ -1087,7 +1279,7 @@ inzsub(char **fields, int nfields, int iscont) ...@@ -1087,7 +1279,7 @@ inzsub(char **fields, int nfields, int iscont)
i_untilmonth; i_untilmonth;
int i_untilday, int i_untilday,
i_untiltime; i_untiltime;
int hasuntil; bool hasuntil;
if (iscont) if (iscont)
{ {
...@@ -1100,6 +1292,8 @@ inzsub(char **fields, int nfields, int iscont) ...@@ -1100,6 +1292,8 @@ inzsub(char **fields, int nfields, int iscont)
i_untiltime = ZFC_TILTIME; i_untiltime = ZFC_TILTIME;
z.z_name = NULL; z.z_name = NULL;
} }
else if (!namecheck(fields[ZF_NAME]))
return false;
else else
{ {
i_gmtoff = ZF_GMTOFF; i_gmtoff = ZF_GMTOFF;
...@@ -1113,17 +1307,26 @@ inzsub(char **fields, int nfields, int iscont) ...@@ -1113,17 +1307,26 @@ inzsub(char **fields, int nfields, int iscont)
} }
z.z_filename = filename; z.z_filename = filename;
z.z_linenum = linenum; z.z_linenum = linenum;
z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE); z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true);
if ((cp = strchr(fields[i_format], '%')) != NULL) if ((cp = strchr(fields[i_format], '%')) != 0)
{ {
if (*++cp != 's' || strchr(cp, '%') != NULL) if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
|| strchr(fields[i_format], '/'))
{ {
error(_("invalid abbreviation format")); error(_("invalid abbreviation format"));
return FALSE; return false;
} }
} }
z.z_rule = ecpyalloc(fields[i_rule]); z.z_rule = ecpyalloc(fields[i_rule]);
z.z_format = ecpyalloc(fields[i_format]); z.z_format = cp1 = ecpyalloc(fields[i_format]);
z.z_format_specifier = cp ? *cp : '\0';
if (z.z_format_specifier == 'z')
{
if (noise)
warning(_("format '%s' not handled by pre-2015 versions of zic"),
z.z_format);
cp1[cp - fields[i_format]] = 's';
}
if (max_format_len < strlen(z.z_format)) if (max_format_len < strlen(z.z_format))
max_format_len = strlen(z.z_format); max_format_len = strlen(z.z_format);
hasuntil = nfields > i_untilyear; hasuntil = nfields > i_untilyear;
...@@ -1149,11 +1352,10 @@ inzsub(char **fields, int nfields, int iscont) ...@@ -1149,11 +1352,10 @@ inzsub(char **fields, int nfields, int iscont)
zones[nzones - 1].z_untiltime >= z.z_untiltime) zones[nzones - 1].z_untiltime >= z.z_untiltime)
{ {
error(_("Zone continuation line end time is not after end time of previous line")); error(_("Zone continuation line end time is not after end time of previous line"));
return FALSE; return false;
} }
} }
zones = (struct zone *) (void *) erealloc((char *) zones, zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
(int) ((nzones + 1) * sizeof *zones));
zones[nzones++] = z; zones[nzones++] = z;
/* /*
...@@ -1170,12 +1372,13 @@ inleap(char **fields, int nfields) ...@@ -1170,12 +1372,13 @@ inleap(char **fields, int nfields)
const struct lookup *lp; const struct lookup *lp;
int i, int i,
j; j;
int year, zic_t year;
month, int month,
day; day;
long dayoff, zic_t dayoff,
tod; tod;
zic_t t; zic_t t;
char xs;
if (nfields != LEAP_FIELDS) if (nfields != LEAP_FIELDS)
{ {
...@@ -1184,7 +1387,7 @@ inleap(char **fields, int nfields) ...@@ -1184,7 +1387,7 @@ inleap(char **fields, int nfields)
} }
dayoff = 0; dayoff = 0;
cp = fields[LP_YEAR]; cp = fields[LP_YEAR];
if (sscanf(cp, scheck(cp, "%d"), &year) != 1) if (sscanf(cp, "%" SCNdZIC "%c", &year, &xs) != 1)
{ {
/* /*
* Leapin' Lizards! * Leapin' Lizards!
...@@ -1196,7 +1399,7 @@ inleap(char **fields, int nfields) ...@@ -1196,7 +1399,7 @@ inleap(char **fields, int nfields)
leapmaxyear = year; leapmaxyear = year;
if (!leapseen || leapminyear > year) if (!leapseen || leapminyear > year)
leapminyear = year; leapminyear = year;
leapseen = TRUE; leapseen = true;
j = EPOCH_YEAR; j = EPOCH_YEAR;
while (j != year) while (j != year)
{ {
...@@ -1210,7 +1413,7 @@ inleap(char **fields, int nfields) ...@@ -1210,7 +1413,7 @@ inleap(char **fields, int nfields)
--j; --j;
i = -len_years[isleap(j)]; i = -len_years[isleap(j)];
} }
dayoff = oadd(dayoff, eitol(i)); dayoff = oadd(dayoff, i);
} }
if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL)
{ {
...@@ -1222,17 +1425,17 @@ inleap(char **fields, int nfields) ...@@ -1222,17 +1425,17 @@ inleap(char **fields, int nfields)
while (j != month) while (j != month)
{ {
i = len_months[isleap(year)][j]; i = len_months[isleap(year)][j];
dayoff = oadd(dayoff, eitol(i)); dayoff = oadd(dayoff, i);
++j; ++j;
} }
cp = fields[LP_DAY]; cp = fields[LP_DAY];
if (sscanf(cp, scheck(cp, "%d"), &day) != 1 || if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
day <= 0 || day > len_months[isleap(year)][month]) day <= 0 || day > len_months[isleap(year)][month])
{ {
error(_("invalid day of month")); error(_("invalid day of month"));
return; return;
} }
dayoff = oadd(dayoff, eitol(day - 1)); dayoff = oadd(dayoff, day - 1);
if (dayoff < min_time / SECSPERDAY) if (dayoff < min_time / SECSPERDAY)
{ {
error(_("time too small")); error(_("time too small"));
...@@ -1243,32 +1446,31 @@ inleap(char **fields, int nfields) ...@@ -1243,32 +1446,31 @@ inleap(char **fields, int nfields)
error(_("time too large")); error(_("time too large"));
return; return;
} }
t = (zic_t) dayoff *SECSPERDAY; t = dayoff * SECSPERDAY;
tod = gethms(fields[LP_TIME], _("invalid time of day"), false);
tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
cp = fields[LP_CORR]; cp = fields[LP_CORR];
{ {
int positive; bool positive;
int count; int count;
if (strcmp(cp, "") == 0) if (strcmp(cp, "") == 0)
{ /* infile() turns "-" into "" */ { /* infile() turns "-" into "" */
positive = FALSE; positive = false;
count = 1; count = 1;
} }
else if (strcmp(cp, "--") == 0) else if (strcmp(cp, "--") == 0)
{ {
positive = FALSE; positive = false;
count = 2; count = 2;
} }
else if (strcmp(cp, "+") == 0) else if (strcmp(cp, "+") == 0)
{ {
positive = TRUE; positive = true;
count = 1; count = 1;
} }
else if (strcmp(cp, "++") == 0) else if (strcmp(cp, "++") == 0)
{ {
positive = TRUE; positive = true;
count = 2; count = 2;
} }
else else
...@@ -1281,7 +1483,13 @@ inleap(char **fields, int nfields) ...@@ -1281,7 +1483,13 @@ inleap(char **fields, int nfields)
error(_("illegal Rolling/Stationary field on Leap line")); error(_("illegal Rolling/Stationary field on Leap line"));
return; return;
} }
leapadd(tadd(t, tod), positive, lp->l_value, count); t = tadd(t, tod);
if (t < big_bang_time)
{
error(_("leap second precedes Big Bang"));
return;
}
leapadd(t, positive, lp->l_value, count);
} }
} }
...@@ -1300,17 +1508,13 @@ inlink(char **fields, int nfields) ...@@ -1300,17 +1508,13 @@ inlink(char **fields, int nfields)
error(_("blank FROM field on Link line")); error(_("blank FROM field on Link line"));
return; return;
} }
if (*fields[LF_TO] == '\0') if (!namecheck(fields[LF_TO]))
{
error(_("blank TO field on Link line"));
return; return;
}
l.l_filename = filename; l.l_filename = filename;
l.l_linenum = linenum; l.l_linenum = linenum;
l.l_from = ecpyalloc(fields[LF_FROM]); l.l_from = ecpyalloc(fields[LF_FROM]);
l.l_to = ecpyalloc(fields[LF_TO]); l.l_to = ecpyalloc(fields[LF_TO]);
links = (struct link *) (void *) erealloc((char *) links, links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
(int) ((nlinks + 1) * sizeof *links));
links[nlinks++] = l; links[nlinks++] = l;
} }
...@@ -1323,6 +1527,7 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1323,6 +1527,7 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
const char *cp; const char *cp;
char *dp; char *dp;
char *ep; char *ep;
char xs;
if ((lp = byword(monthp, mon_names)) == NULL) if ((lp = byword(monthp, mon_names)) == NULL)
{ {
...@@ -1330,8 +1535,8 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1330,8 +1535,8 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
return; return;
} }
rp->r_month = lp->l_value; rp->r_month = lp->l_value;
rp->r_todisstd = FALSE; rp->r_todisstd = false;
rp->r_todisgmt = FALSE; rp->r_todisgmt = false;
dp = ecpyalloc(timep); dp = ecpyalloc(timep);
if (*dp != '\0') if (*dp != '\0')
{ {
...@@ -1339,26 +1544,26 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1339,26 +1544,26 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
switch (lowerit(*ep)) switch (lowerit(*ep))
{ {
case 's': /* Standard */ case 's': /* Standard */
rp->r_todisstd = TRUE; rp->r_todisstd = true;
rp->r_todisgmt = FALSE; rp->r_todisgmt = false;
*ep = '\0'; *ep = '\0';
break; break;
case 'w': /* Wall */ case 'w': /* Wall */
rp->r_todisstd = FALSE; rp->r_todisstd = false;
rp->r_todisgmt = FALSE; rp->r_todisgmt = false;
*ep = '\0'; *ep = '\0';
break; break;
case 'g': /* Greenwich */ case 'g': /* Greenwich */
case 'u': /* Universal */ case 'u': /* Universal */
case 'z': /* Zulu */ case 'z': /* Zulu */
rp->r_todisstd = TRUE; rp->r_todisstd = true;
rp->r_todisgmt = TRUE; rp->r_todisgmt = true;
*ep = '\0'; *ep = '\0';
break; break;
} }
} }
rp->r_tod = gethms(dp, _("invalid time of day"), FALSE); rp->r_tod = gethms(dp, _("invalid time of day"), false);
ifree(dp); free(dp);
/* /*
* Year work. * Year work.
...@@ -1370,18 +1575,18 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1370,18 +1575,18 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
switch ((int) lp->l_value) switch ((int) lp->l_value)
{ {
case YR_MINIMUM: case YR_MINIMUM:
rp->r_loyear = INT_MIN; rp->r_loyear = ZIC_MIN;
break; break;
case YR_MAXIMUM: case YR_MAXIMUM:
rp->r_loyear = INT_MAX; rp->r_loyear = ZIC_MAX;
break; break;
default: /* "cannot happen" */ default: /* "cannot happen" */
(void) fprintf(stderr, fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"), _("%s: panic: Invalid l_value %d\n"),
progname, lp->l_value); progname, lp->l_value);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_loyear, &xs) != 1)
{ {
error(_("invalid starting year")); error(_("invalid starting year"));
return; return;
...@@ -1393,21 +1598,21 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1393,21 +1598,21 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
switch ((int) lp->l_value) switch ((int) lp->l_value)
{ {
case YR_MINIMUM: case YR_MINIMUM:
rp->r_hiyear = INT_MIN; rp->r_hiyear = ZIC_MIN;
break; break;
case YR_MAXIMUM: case YR_MAXIMUM:
rp->r_hiyear = INT_MAX; rp->r_hiyear = ZIC_MAX;
break; break;
case YR_ONLY: case YR_ONLY:
rp->r_hiyear = rp->r_loyear; rp->r_hiyear = rp->r_loyear;
break; break;
default: /* "cannot happen" */ default: /* "cannot happen" */
(void) fprintf(stderr, fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"), _("%s: panic: Invalid l_value %d\n"),
progname, lp->l_value); progname, lp->l_value);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_hiyear, &xs) != 1)
{ {
error(_("invalid ending year")); error(_("invalid ending year"));
return; return;
...@@ -1456,65 +1661,67 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, ...@@ -1456,65 +1661,67 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
if (*ep++ != '=') if (*ep++ != '=')
{ {
error(_("invalid day of month")); error(_("invalid day of month"));
ifree(dp); free(dp);
return; return;
} }
if ((lp = byword(dp, wday_names)) == NULL) if ((lp = byword(dp, wday_names)) == NULL)
{ {
error(_("invalid weekday name")); error(_("invalid weekday name"));
ifree(dp); free(dp);
return; return;
} }
rp->r_wday = lp->l_value; rp->r_wday = lp->l_value;
} }
if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 || if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
rp->r_dayofmonth <= 0 || rp->r_dayofmonth <= 0 ||
(rp->r_dayofmonth > len_months[1][rp->r_month])) (rp->r_dayofmonth > len_months[1][rp->r_month]))
{ {
error(_("invalid day of month")); error(_("invalid day of month"));
ifree(dp); free(dp);
return; return;
} }
} }
ifree(dp); free(dp);
} }
static void static void
convert(long val, char *buf) convert(const int32 val, char *const buf)
{ {
int i; int i;
int shift; int shift;
unsigned char *const b = (unsigned char *) buf;
for (i = 0, shift = 24; i < 4; ++i, shift -= 8) for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
buf[i] = val >> shift; b[i] = val >> shift;
} }
static void static void
convert64(zic_t val, char *buf) convert64(const zic_t val, char *const buf)
{ {
int i; int i;
int shift; int shift;
unsigned char *const b = (unsigned char *) buf;
for (i = 0, shift = 56; i < 8; ++i, shift -= 8) for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
buf[i] = val >> shift; b[i] = val >> shift;
} }
static void static void
puttzcode(long val, FILE *fp) puttzcode(const int32 val, FILE *const fp)
{ {
char buf[4]; char buf[4];
convert(val, buf); convert(val, buf);
(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); fwrite(buf, sizeof buf, 1, fp);
} }
static void static void
puttzcode64(zic_t val, FILE *fp) puttzcode64(const zic_t val, FILE *const fp)
{ {
char buf[8]; char buf[8];
convert64(val, buf); convert64(val, buf);
(void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); fwrite(buf, sizeof buf, 1, fp);
} }
static int static int
...@@ -1526,14 +1733,14 @@ atcomp(const void *avp, const void *bvp) ...@@ -1526,14 +1733,14 @@ atcomp(const void *avp, const void *bvp)
return (a < b) ? -1 : (a > b); return (a < b) ? -1 : (a > b);
} }
static int static bool
is32(zic_t x) is32(const zic_t x)
{ {
return x == ((zic_t) ((int32) x)); return x == ((zic_t) ((int32) x));
} }
static void static void
writezone(const char *name, const char *string) writezone(const char *const name, const char *const string, char version)
{ {
FILE *fp; FILE *fp;
int i, int i,
...@@ -1543,18 +1750,18 @@ writezone(const char *name, const char *string) ...@@ -1543,18 +1750,18 @@ writezone(const char *name, const char *string)
int timecnt32, int timecnt32,
timei32; timei32;
int pass; int pass;
static char *fullname; char *fullname;
static const struct tzhead tzh0; static const struct tzhead tzh0;
static struct tzhead tzh; static struct tzhead tzh;
zic_t ats[TZ_MAX_TIMES]; zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1));
unsigned char types[TZ_MAX_TIMES]; void *typesptr = ats + timecnt;
unsigned char *types = typesptr;
/* /*
* Sort. * Sort.
*/ */
if (timecnt > 1) if (timecnt > 1)
(void) qsort((void *) attypes, (size_t) timecnt, qsort(attypes, timecnt, sizeof *attypes, atcomp);
(size_t) sizeof *attypes, atcomp);
/* /*
* Optimize. * Optimize.
...@@ -1565,21 +1772,17 @@ writezone(const char *name, const char *string) ...@@ -1565,21 +1772,17 @@ writezone(const char *name, const char *string)
toi = 0; toi = 0;
fromi = 0; fromi = 0;
while (fromi < timecnt && attypes[fromi].at < min_time) while (fromi < timecnt && attypes[fromi].at < big_bang_time)
++fromi; ++fromi;
if (isdsts[0] == 0)
while (fromi < timecnt && attypes[fromi].type == 0)
++fromi; /* handled by default rule */
for (; fromi < timecnt; ++fromi) for (; fromi < timecnt; ++fromi)
{ {
if (toi != 0 if (toi > 1 && ((attypes[fromi].at +
&& ((attypes[fromi].at gmtoffs[attypes[toi - 1].type]) <=
+ gmtoffs[attypes[toi - 1].type]) (attypes[toi - 1].at +
<= (attypes[toi - 1].at gmtoffs[attypes[toi - 2].type])))
+ gmtoffs[toi == 1 ? 0
: attypes[toi - 2].type])))
{ {
attypes[toi - 1].type = attypes[fromi].type; attypes[toi - 1].type =
attypes[fromi].type;
continue; continue;
} }
if (toi == 0 || if (toi == 0 ||
...@@ -1588,6 +1791,9 @@ writezone(const char *name, const char *string) ...@@ -1588,6 +1791,9 @@ writezone(const char *name, const char *string)
} }
timecnt = toi; timecnt = toi;
} }
if (noise && timecnt > 1200)
warning(_("pre-2014 clients may mishandle"
" more than 1200 transition times"));
/* /*
* Transfer. * Transfer.
...@@ -1626,6 +1832,15 @@ writezone(const char *name, const char *string) ...@@ -1626,6 +1832,15 @@ writezone(const char *name, const char *string)
--timecnt32; --timecnt32;
++timei32; ++timei32;
} }
/*
* Output an INT32_MIN "transition" if appropriate; see below.
*/
if (timei32 > 0 && ats[timei32] > PG_INT32_MIN)
{
--timei32;
++timecnt32;
}
while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1])) while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
--leapcnt32; --leapcnt32;
while (leapcnt32 > 0 && !is32(trans[leapi32])) while (leapcnt32 > 0 && !is32(trans[leapi32]))
...@@ -1633,45 +1848,43 @@ writezone(const char *name, const char *string) ...@@ -1633,45 +1848,43 @@ writezone(const char *name, const char *string)
--leapcnt32; --leapcnt32;
++leapi32; ++leapi32;
} }
fullname = erealloc(fullname, fullname = relname(directory, name);
(int) (strlen(directory) + 1 + strlen(name) + 1));
(void) sprintf(fullname, "%s/%s", directory, name);
/* /*
* Remove old file, if any, to snap links. * Remove old file, if any, to snap links.
*/ */
if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) if (itsdir(fullname) == 0 && remove(fullname) != 0 && errno != ENOENT)
{ {
const char *e = strerror(errno); const char *e = strerror(errno);
(void) fprintf(stderr, _("%s: Cannot remove %s: %s\n"), fprintf(stderr, _("%s: Cannot remove %s: %s\n"),
progname, fullname, e); progname, fullname, e);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ((fp = fopen(fullname, "wb")) == NULL) if ((fp = fopen(fullname, "wb")) == NULL)
{ {
if (mkdirs(fullname) != 0) if (!mkdirs(fullname))
(void) exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if ((fp = fopen(fullname, "wb")) == NULL) if ((fp = fopen(fullname, "wb")) == NULL)
{ {
const char *e = strerror(errno); const char *e = strerror(errno);
(void) fprintf(stderr, _("%s: Cannot create %s: %s\n"), fprintf(stderr, _("%s: Cannot create %s: %s\n"),
progname, fullname, e); progname, fullname, e);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
for (pass = 1; pass <= 2; ++pass) for (pass = 1; pass <= 2; ++pass)
{ {
register int thistimei, int thistimei,
thistimecnt; thistimecnt;
register int thisleapi, int thisleapi,
thisleapcnt; thisleapcnt;
register int thistimelim, int thistimelim,
thisleaplim; thisleaplim;
int writetype[TZ_MAX_TIMES]; int writetype[TZ_MAX_TYPES];
int typemap[TZ_MAX_TYPES]; int typemap[TZ_MAX_TYPES];
register int thistypecnt; int thistypecnt;
char thischars[TZ_MAX_CHARS]; char thischars[TZ_MAX_CHARS];
char thischarcnt; char thischarcnt;
int indmap[TZ_MAX_CHARS]; int indmap[TZ_MAX_CHARS];
...@@ -1697,24 +1910,79 @@ writezone(const char *name, const char *string) ...@@ -1697,24 +1910,79 @@ writezone(const char *name, const char *string)
if (thistimecnt == 0) if (thistimecnt == 0)
{ {
/* /*
* * No transition times fall in the current * (32- or 64-bit) * No transition times fall in the current (32- or 64-bit) window.
* window.
*/ */
if (typecnt != 0) if (typecnt != 0)
writetype[typecnt - 1] = TRUE; writetype[typecnt - 1] = true;
} }
else else
{ {
for (i = thistimei - 1; i < thistimelim; ++i) for (i = thistimei - 1; i < thistimelim; ++i)
if (i >= 0) if (i >= 0)
writetype[types[i]] = TRUE; writetype[types[i]] = true;
/* /*
* * For America/Godthab and Antarctica/Palmer * For America/Godthab and Antarctica/Palmer
*/ */
if (thistimei == 0) if (thistimei == 0)
writetype[0] = TRUE; writetype[0] = true;
}
#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
/*
* For some pre-2011 systems: if the last-to-be-written standard (or
* daylight) type has an offset different from the most recently used
* offset, append an (unused) copy of the most recently used type (to
* help get global "altzone" and "timezone" variables set correctly).
*/
{
int mrudst,
mrustd,
hidst,
histd,
type;
hidst = histd = mrudst = mrustd = -1;
for (i = thistimei; i < thistimelim; ++i)
if (isdsts[types[i]])
mrudst = types[i];
else
mrustd = types[i];
for (i = 0; i < typecnt; ++i)
if (writetype[i])
{
if (isdsts[i])
hidst = i;
else
histd = i;
}
if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
gmtoffs[hidst] != gmtoffs[mrudst])
{
isdsts[mrudst] = -1;
type = addtype(gmtoffs[mrudst],
&chars[abbrinds[mrudst]],
true,
ttisstds[mrudst],
ttisgmts[mrudst]);
isdsts[mrudst] = 1;
writetype[type] = true;
}
if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
gmtoffs[histd] != gmtoffs[mrustd])
{
isdsts[mrustd] = -1;
type = addtype(gmtoffs[mrustd],
&chars[abbrinds[mrustd]],
false,
ttisstds[mrustd],
ttisgmts[mrustd]);
isdsts[mrustd] = 0;
writetype[type] = true;
}
} }
#endif /* !defined
* LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
thistypecnt = 0; thistypecnt = 0;
for (i = 0; i < typecnt; ++i) for (i = 0; i < typecnt; ++i)
typemap[i] = writetype[i] ? thistypecnt++ : -1; typemap[i] = writetype[i] ? thistypecnt++ : -1;
...@@ -1723,7 +1991,7 @@ writezone(const char *name, const char *string) ...@@ -1723,7 +1991,7 @@ writezone(const char *name, const char *string)
thischarcnt = 0; thischarcnt = 0;
for (i = 0; i < typecnt; ++i) for (i = 0; i < typecnt; ++i)
{ {
register char *thisabbr; char *thisabbr;
if (!writetype[i]) if (!writetype[i])
continue; continue;
...@@ -1735,23 +2003,22 @@ writezone(const char *name, const char *string) ...@@ -1735,23 +2003,22 @@ writezone(const char *name, const char *string)
break; break;
if (j == thischarcnt) if (j == thischarcnt)
{ {
(void) strcpy(&thischars[(int) thischarcnt], strcpy(&thischars[(int) thischarcnt],
thisabbr); thisabbr);
thischarcnt += strlen(thisabbr) + 1; thischarcnt += strlen(thisabbr) + 1;
} }
indmap[abbrinds[i]] = j; indmap[abbrinds[i]] = j;
} }
#define DO(field) (void) fwrite((void *) tzh.field, \ #define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp)
(size_t) sizeof tzh.field, (size_t) 1, fp)
tzh = tzh0; tzh = tzh0;
(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
tzh.tzh_version[0] = ZIC_VERSION; tzh.tzh_version[0] = version;
convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt); convert(thistypecnt, tzh.tzh_ttisgmtcnt);
convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt); convert(thistypecnt, tzh.tzh_ttisstdcnt);
convert(eitol(thisleapcnt), tzh.tzh_leapcnt); convert(thisleapcnt, tzh.tzh_leapcnt);
convert(eitol(thistimecnt), tzh.tzh_timecnt); convert(thistimecnt, tzh.tzh_timecnt);
convert(eitol(thistypecnt), tzh.tzh_typecnt); convert(thistypecnt, tzh.tzh_typecnt);
convert(eitol(thischarcnt), tzh.tzh_charcnt); convert(thischarcnt, tzh.tzh_charcnt);
DO(tzh_magic); DO(tzh_magic);
DO(tzh_version); DO(tzh_version);
DO(tzh_reserved); DO(tzh_reserved);
...@@ -1764,7 +2031,12 @@ writezone(const char *name, const char *string) ...@@ -1764,7 +2031,12 @@ writezone(const char *name, const char *string)
#undef DO #undef DO
for (i = thistimei; i < thistimelim; ++i) for (i = thistimei; i < thistimelim; ++i)
if (pass == 1) if (pass == 1)
puttzcode((long) ats[i], fp);
/*
* Output an INT32_MIN "transition" if appropriate; see above.
*/
puttzcode(((ats[i] < PG_INT32_MIN) ?
PG_INT32_MIN : ats[i]), fp);
else else
{ {
puttzcode64(ats[i], fp); puttzcode64(ats[i], fp);
...@@ -1779,7 +2051,7 @@ writezone(const char *name, const char *string) ...@@ -1779,7 +2051,7 @@ writezone(const char *name, const char *string)
/* filter out assorted junk entries */ /* filter out assorted junk entries */
if (strcmp(thisabbrev, GRANDPARENTED) != 0 && if (strcmp(thisabbrev, GRANDPARENTED) != 0 &&
strcmp(thisabbrev, "zzz") != 0) strcmp(thisabbrev, "zzz") != 0)
fprintf(stdout, "%s\t%ld%s\n", fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
thisabbrev, thisabbrev,
gmtoffs[tm], gmtoffs[tm],
isdsts[tm] ? "\tD" : ""); isdsts[tm] ? "\tD" : "");
...@@ -1790,25 +2062,21 @@ writezone(const char *name, const char *string) ...@@ -1790,25 +2062,21 @@ writezone(const char *name, const char *string)
unsigned char uc; unsigned char uc;
uc = typemap[types[i]]; uc = typemap[types[i]];
(void) fwrite((void *) &uc, fwrite(&uc, sizeof uc, 1, fp);
(size_t) sizeof uc,
(size_t) 1,
fp);
} }
for (i = 0; i < typecnt; ++i) for (i = 0; i < typecnt; ++i)
if (writetype[i]) if (writetype[i])
{ {
puttzcode(gmtoffs[i], fp); puttzcode(gmtoffs[i], fp);
(void) putc(isdsts[i], fp); putc(isdsts[i], fp);
(void) putc((unsigned char) indmap[abbrinds[i]], fp); putc((unsigned char) indmap[abbrinds[i]], fp);
} }
if (thischarcnt != 0) if (thischarcnt != 0)
(void) fwrite((void *) thischars, fwrite(thischars, sizeof thischars[0],
(size_t) sizeof thischars[0], thischarcnt, fp);
(size_t) thischarcnt, fp);
for (i = thisleapi; i < thisleaplim; ++i) for (i = thisleapi; i < thisleaplim; ++i)
{ {
register zic_t todo; zic_t todo;
if (roll[i]) if (roll[i])
{ {
...@@ -1835,70 +2103,113 @@ writezone(const char *name, const char *string) ...@@ -1835,70 +2103,113 @@ writezone(const char *name, const char *string)
else else
todo = trans[i]; todo = trans[i];
if (pass == 1) if (pass == 1)
puttzcode((long) todo, fp); puttzcode(todo, fp);
else else
puttzcode64(todo, fp); puttzcode64(todo, fp);
puttzcode(corr[i], fp); puttzcode(corr[i], fp);
} }
for (i = 0; i < typecnt; ++i) for (i = 0; i < typecnt; ++i)
if (writetype[i]) if (writetype[i])
(void) putc(ttisstds[i], fp); putc(ttisstds[i], fp);
for (i = 0; i < typecnt; ++i) for (i = 0; i < typecnt; ++i)
if (writetype[i]) if (writetype[i])
(void) putc(ttisgmts[i], fp); putc(ttisgmts[i], fp);
} }
(void) fprintf(fp, "\n%s\n", string); fprintf(fp, "\n%s\n", string);
if (ferror(fp) || fclose(fp)) close_file(fp, fullname);
free(ats);
free(fullname);
}
static char const *
abbroffset(char *buf, zic_t offset)
{
char sign = '+';
int seconds,
minutes;
if (offset < 0)
{ {
(void) fprintf(stderr, _("%s: Error writing %s\n"), offset = -offset;
progname, fullname); sign = '-';
exit(EXIT_FAILURE); }
seconds = offset % SECSPERMIN;
offset /= SECSPERMIN;
minutes = offset % MINSPERHOUR;
offset /= MINSPERHOUR;
if (100 <= offset)
{
error(_("%%z UTC offset magnitude exceeds 99:59:59"));
return "%z";
}
else
{
char *p = buf;
*p++ = sign;
*p++ = '0' + offset / 10;
*p++ = '0' + offset % 10;
if (minutes | seconds)
{
*p++ = '0' + minutes / 10;
*p++ = '0' + minutes % 10;
if (seconds)
{
*p++ = '0' + seconds / 10;
*p++ = '0' + seconds % 10;
}
}
*p = '\0';
return buf;
} }
} }
static void static size_t
doabbr(char *abbr, const char *format, const char *letters, int isdst, doabbr(char *abbr, struct zone const * zp, char const * letters,
int doquotes) zic_t stdoff, bool doquotes)
{ {
char *cp; char *cp;
char *slashp; char *slashp;
int len; size_t len;
char const *format = zp->z_format;
slashp = strchr(format, '/'); slashp = strchr(format, '/');
if (slashp == NULL) if (slashp == NULL)
{ {
if (letters == NULL) char letterbuf[PERCENT_Z_LEN_BOUND + 1];
(void) strcpy(abbr, format);
else if (zp->z_format_specifier == 'z')
(void) sprintf(abbr, format, letters); letters = abbroffset(letterbuf, zp->z_gmtoff + stdoff);
else if (!letters)
letters = "%s";
sprintf(abbr, format, letters);
}
else if (stdoff != 0)
{
strcpy(abbr, slashp + 1);
} }
else if (isdst)
(void) strcpy(abbr, slashp + 1);
else else
{ {
if (slashp > format) memcpy(abbr, format, slashp - format);
(void) strncpy(abbr, format,
(unsigned) (slashp - format));
abbr[slashp - format] = '\0'; abbr[slashp - format] = '\0';
} }
if (!doquotes)
return;
for (cp = abbr; *cp != '\0'; ++cp)
if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
break;
len = strlen(abbr); len = strlen(abbr);
if (!doquotes)
return len;
for (cp = abbr; is_alpha(*cp); cp++)
continue;
if (len > 0 && *cp == '\0') if (len > 0 && *cp == '\0')
return; return len;
abbr[len + 2] = '\0'; abbr[len + 2] = '\0';
abbr[len + 1] = '>'; abbr[len + 1] = '>';
for (; len > 0; --len) memmove(abbr + 1, abbr, len);
abbr[len] = abbr[len - 1];
abbr[0] = '<'; abbr[0] = '<';
return len + 2;
} }
static void static void
updateminmax(int x) updateminmax(const zic_t x)
{ {
if (min_year > x) if (min_year > x)
min_year = x; min_year = x;
...@@ -1907,44 +2218,46 @@ updateminmax(int x) ...@@ -1907,44 +2218,46 @@ updateminmax(int x)
} }
static int static int
stringoffset(char *result, long offset) stringoffset(char *result, zic_t offset)
{ {
int hours; int hours;
int minutes; int minutes;
int seconds; int seconds;
bool negative = offset < 0;
int len = negative;
result[0] = '\0'; if (negative)
if (offset < 0)
{ {
(void) strcpy(result, "-");
offset = -offset; offset = -offset;
result[0] = '-';
} }
seconds = offset % SECSPERMIN; seconds = offset % SECSPERMIN;
offset /= SECSPERMIN; offset /= SECSPERMIN;
minutes = offset % MINSPERHOUR; minutes = offset % MINSPERHOUR;
offset /= MINSPERHOUR; offset /= MINSPERHOUR;
hours = offset; hours = offset;
if (hours >= HOURSPERDAY) if (hours >= HOURSPERDAY * DAYSPERWEEK)
{ {
result[0] = '\0'; result[0] = '\0';
return -1; return 0;
} }
(void) sprintf(end(result), "%d", hours); len += sprintf(result + len, "%d", hours);
if (minutes != 0 || seconds != 0) if (minutes != 0 || seconds != 0)
{ {
(void) sprintf(end(result), ":%02d", minutes); len += sprintf(result + len, ":%02d", minutes);
if (seconds != 0) if (seconds != 0)
(void) sprintf(end(result), ":%02d", seconds); len += sprintf(result + len, ":%02d", seconds);
} }
return 0; return len;
} }
static int static int
stringrule(char *result, const struct rule * rp, long dstoff, long gmtoff) stringrule(char *result, const struct rule * const rp, const zic_t dstoff,
const zic_t gmtoff)
{ {
long tod; zic_t tod = rp->r_tod;
int compat = 0;
result = end(result);
if (rp->r_dycode == DC_DOM) if (rp->r_dycode == DC_DOM)
{ {
int month, int month,
...@@ -1955,17 +2268,26 @@ stringrule(char *result, const struct rule * rp, long dstoff, long gmtoff) ...@@ -1955,17 +2268,26 @@ stringrule(char *result, const struct rule * rp, long dstoff, long gmtoff)
total = 0; total = 0;
for (month = 0; month < rp->r_month; ++month) for (month = 0; month < rp->r_month; ++month)
total += len_months[0][month]; total += len_months[0][month];
(void) sprintf(result, "J%d", total + rp->r_dayofmonth); /* Omit the "J" in Jan and Feb, as that's shorter. */
if (rp->r_month <= 1)
result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
else
result += sprintf(result, "J%d", total + rp->r_dayofmonth);
} }
else else
{ {
int week; int week;
int wday = rp->r_wday;
int wdayoff;
if (rp->r_dycode == DC_DOWGEQ) if (rp->r_dycode == DC_DOWGEQ)
{ {
week = 1 + rp->r_dayofmonth / DAYSPERWEEK; wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth) if (wdayoff)
return -1; compat = 2013;
wday -= wdayoff;
tod += wdayoff * SECSPERDAY;
week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
} }
else if (rp->r_dycode == DC_DOWLEQ) else if (rp->r_dycode == DC_DOWLEQ)
{ {
...@@ -1973,37 +2295,64 @@ stringrule(char *result, const struct rule * rp, long dstoff, long gmtoff) ...@@ -1973,37 +2295,64 @@ stringrule(char *result, const struct rule * rp, long dstoff, long gmtoff)
week = 5; week = 5;
else else
{ {
week = 1 + rp->r_dayofmonth / DAYSPERWEEK; wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth) if (wdayoff)
return -1; compat = 2013;
wday -= wdayoff;
tod += wdayoff * SECSPERDAY;
week = rp->r_dayofmonth / DAYSPERWEEK;
} }
} }
else else
return -1; /* "cannot happen" */ return -1; /* "cannot happen" */
(void) sprintf(result, "M%d.%d.%d", if (wday < 0)
rp->r_month + 1, week, rp->r_wday); wday += DAYSPERWEEK;
result += sprintf(result, "M%d.%d.%d",
rp->r_month + 1, week, wday);
} }
tod = rp->r_tod;
if (rp->r_todisgmt) if (rp->r_todisgmt)
tod += gmtoff; tod += gmtoff;
if (rp->r_todisstd && rp->r_stdoff == 0) if (rp->r_todisstd && rp->r_stdoff == 0)
tod += dstoff; tod += dstoff;
if (tod < 0)
{
result[0] = '\0';
return -1;
}
if (tod != 2 * SECSPERMIN * MINSPERHOUR) if (tod != 2 * SECSPERMIN * MINSPERHOUR)
{ {
(void) strcat(result, "/"); *result++ = '/';
if (stringoffset(end(result), tod) != 0) if (!stringoffset(result, tod))
return -1; return -1;
if (tod < 0)
{
if (compat < 2013)
compat = 2013;
}
else if (SECSPERDAY <= tod)
{
if (compat < 1994)
compat = 1994;
}
} }
return 0; return compat;
} }
static void static int
stringzone(char *result, const struct zone * zpfirst, int zonecount) rule_cmp(struct rule const * a, struct rule const * b)
{
if (!a)
return -!!b;
if (!b)
return 1;
if (a->r_hiyear != b->r_hiyear)
return a->r_hiyear < b->r_hiyear ? -1 : 1;
if (a->r_month - b->r_month != 0)
return a->r_month - b->r_month;
return a->r_dayofmonth - b->r_dayofmonth;
}
enum
{
YEAR_BY_YEAR_ZONE = 1};
static int
stringzone(char *result, const struct zone * const zpfirst, const int zonecount)
{ {
const struct zone *zp; const struct zone *zp;
struct rule *rp; struct rule *rp;
...@@ -2011,6 +2360,12 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount) ...@@ -2011,6 +2360,12 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount)
struct rule *dstrp; struct rule *dstrp;
int i; int i;
const char *abbrvar; const char *abbrvar;
int compat = 0;
int c;
size_t len;
int offsetlen;
struct rule stdr,
dstr;
result[0] = '\0'; result[0] = '\0';
zp = zpfirst + zonecount - 1; zp = zpfirst + zonecount - 1;
...@@ -2018,7 +2373,7 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount) ...@@ -2018,7 +2373,7 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount)
for (i = 0; i < zp->z_nrules; ++i) for (i = 0; i < zp->z_nrules; ++i)
{ {
rp = &zp->z_rules[i]; rp = &zp->z_rules[i];
if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX) if (rp->r_hiwasnum || rp->r_hiyear != ZIC_MAX)
continue; continue;
if (rp->r_yrtype != NULL) if (rp->r_yrtype != NULL)
continue; continue;
...@@ -2027,32 +2382,32 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount) ...@@ -2027,32 +2382,32 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount)
if (stdrp == NULL) if (stdrp == NULL)
stdrp = rp; stdrp = rp;
else else
return; return -1;
} }
else else
{ {
if (dstrp == NULL) if (dstrp == NULL)
dstrp = rp; dstrp = rp;
else else
return; return -1;
} }
} }
if (stdrp == NULL && dstrp == NULL) if (stdrp == NULL && dstrp == NULL)
{ {
/* /*
* There are no rules running through "max". Let's find the latest * There are no rules running through "max". Find the latest std rule
* rule. * in stdabbrrp and latest rule of any type in stdrp.
*/ */
struct rule *stdabbrrp = NULL;
for (i = 0; i < zp->z_nrules; ++i) for (i = 0; i < zp->z_nrules; ++i)
{ {
rp = &zp->z_rules[i]; rp = &zp->z_rules[i];
if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear || if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0)
(rp->r_hiyear == stdrp->r_hiyear && stdabbrrp = rp;
rp->r_month > stdrp->r_month)) if (rule_cmp(stdrp, rp) < 0)
stdrp = rp; stdrp = rp;
} }
if (stdrp != NULL && stdrp->r_stdoff != 0)
return; /* We end up in DST (a POSIX no-no). */
/* /*
* Horrid special case: if year is 2037, presume this is a zone * Horrid special case: if year is 2037, presume this is a zone
...@@ -2060,39 +2415,75 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount) ...@@ -2060,39 +2415,75 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount)
* zone. * zone.
*/ */
if (stdrp != NULL && stdrp->r_hiyear == 2037) if (stdrp != NULL && stdrp->r_hiyear == 2037)
return; return YEAR_BY_YEAR_ZONE;
if (stdrp != NULL && stdrp->r_stdoff != 0)
{
/* Perpetual DST. */
dstr.r_month = TM_JANUARY;
dstr.r_dycode = DC_DOM;
dstr.r_dayofmonth = 1;
dstr.r_tod = 0;
dstr.r_todisstd = dstr.r_todisgmt = false;
dstr.r_stdoff = stdrp->r_stdoff;
dstr.r_abbrvar = stdrp->r_abbrvar;
stdr.r_month = TM_DECEMBER;
stdr.r_dycode = DC_DOM;
stdr.r_dayofmonth = 31;
stdr.r_tod = SECSPERDAY + stdrp->r_stdoff;
stdr.r_todisstd = stdr.r_todisgmt = false;
stdr.r_stdoff = 0;
stdr.r_abbrvar
= (stdabbrrp ? stdabbrrp->r_abbrvar : "");
dstrp = &dstr;
stdrp = &stdr;
}
} }
if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0)) if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
return; return -1;
abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
doabbr(result, zp->z_format, abbrvar, FALSE, TRUE); len = doabbr(result, zp, abbrvar, 0, true);
if (stringoffset(end(result), -zp->z_gmtoff) != 0) offsetlen = stringoffset(result + len, -zp->z_gmtoff);
if (!offsetlen)
{ {
result[0] = '\0'; result[0] = '\0';
return; return -1;
} }
len += offsetlen;
if (dstrp == NULL) if (dstrp == NULL)
return; return compat;
doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE); len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true);
if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
if (stringoffset(end(result), {
-(zp->z_gmtoff + dstrp->r_stdoff)) != 0) offsetlen = stringoffset(result + len,
-(zp->z_gmtoff + dstrp->r_stdoff));
if (!offsetlen)
{ {
result[0] = '\0'; result[0] = '\0';
return; return -1;
} }
(void) strcat(result, ","); len += offsetlen;
if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) }
result[len++] = ',';
c = stringrule(result + len, dstrp, dstrp->r_stdoff, zp->z_gmtoff);
if (c < 0)
{ {
result[0] = '\0'; result[0] = '\0';
return; return -1;
} }
(void) strcat(result, ","); if (compat < c)
if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) compat = c;
len += strlen(result + len);
result[len++] = ',';
c = stringrule(result + len, stdrp, dstrp->r_stdoff, zp->z_gmtoff);
if (c < 0)
{ {
result[0] = '\0'; result[0] = '\0';
return; return -1;
} }
if (compat < c)
compat = c;
return compat;
} }
static void static void
...@@ -2102,28 +2493,34 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2102,28 +2493,34 @@ outzone(const struct zone * zpfirst, int zonecount)
struct rule *rp; struct rule *rp;
int i, int i,
j; j;
int usestart, bool usestart,
useuntil; useuntil;
zic_t starttime = 0; zic_t starttime,
zic_t untiltime = 0; untiltime;
long gmtoff; zic_t gmtoff;
long stdoff; zic_t stdoff;
int year; zic_t year;
long startoff; zic_t startoff;
int startttisstd; bool startttisstd;
int startttisgmt; bool startttisgmt;
int type; int type;
char *startbuf; char *startbuf;
char *ab; char *ab;
char *envvar; char *envvar;
int max_abbr_len; int max_abbr_len;
int max_envvar_len; int max_envvar_len;
bool prodstic; /* all rules are min to max */
int compat;
bool do_extend;
char version;
max_abbr_len = 2 + max_format_len + max_abbrvar_len; max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9; max_envvar_len = 2 * max_abbr_len + 5 * 9;
startbuf = emalloc(max_abbr_len + 1); startbuf = emalloc(max_abbr_len + 1);
ab = emalloc(max_abbr_len + 1); ab = emalloc(max_abbr_len + 1);
envvar = emalloc(max_envvar_len + 1); envvar = emalloc(max_envvar_len + 1);
INITIALIZE(untiltime);
INITIALIZE(starttime);
/* /*
* Now. . .finally. . .generate some useful data! * Now. . .finally. . .generate some useful data!
...@@ -2131,18 +2528,19 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2131,18 +2528,19 @@ outzone(const struct zone * zpfirst, int zonecount)
timecnt = 0; timecnt = 0;
typecnt = 0; typecnt = 0;
charcnt = 0; charcnt = 0;
prodstic = zonecount == 1;
/* /*
* Thanks to Earl Chew for noting the need to unconditionally initialize * Thanks to Earl Chew for noting the need to unconditionally initialize
* startttisstd. * startttisstd.
*/ */
startttisstd = FALSE; startttisstd = false;
startttisgmt = FALSE; startttisgmt = false;
min_year = max_year = EPOCH_YEAR; min_year = max_year = EPOCH_YEAR;
if (leapseen) if (leapseen)
{ {
updateminmax(leapminyear); updateminmax(leapminyear);
updateminmax(leapmaxyear + (leapmaxyear < INT_MAX)); updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
} }
for (i = 0; i < zonecount; ++i) for (i = 0; i < zonecount; ++i)
{ {
...@@ -2156,33 +2554,71 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2156,33 +2554,71 @@ outzone(const struct zone * zpfirst, int zonecount)
updateminmax(rp->r_loyear); updateminmax(rp->r_loyear);
if (rp->r_hiwasnum) if (rp->r_hiwasnum)
updateminmax(rp->r_hiyear); updateminmax(rp->r_hiyear);
if (rp->r_lowasnum || rp->r_hiwasnum)
prodstic = false;
} }
} }
/* /*
* Generate lots of data if a rule can't cover all future times. * Generate lots of data if a rule can't cover all future times.
*/ */
stringzone(envvar, zpfirst, zonecount); compat = stringzone(envvar, zpfirst, zonecount);
if (noise && envvar[0] == '\0') version = compat < 2013 ? ZIC_VERSION_PRE_2013 : ZIC_VERSION;
{ do_extend = compat < 0 || compat == YEAR_BY_YEAR_ZONE;
char *wp; if (noise)
{
wp = ecpyalloc(_("no POSIX environment variable for zone")); if (!*envvar)
wp = ecatalloc(wp, " "); warning("%s %s",
wp = ecatalloc(wp, zpfirst->z_name); _("no POSIX environment variable for zone"),
warning(wp); zpfirst->z_name);
ifree(wp); else if (compat != 0 && compat != YEAR_BY_YEAR_ZONE)
{
/*
* Circa-COMPAT clients, and earlier clients, might not work for
* this zone when given dates before 1970 or after 2038.
*/
warning(_("%s: pre-%d clients may mishandle"
" distant timestamps"),
zpfirst->z_name, compat);
}
} }
if (envvar[0] == '\0') if (do_extend)
{ {
if (min_year >= INT_MIN + YEARSPERREPEAT) /*
min_year -= YEARSPERREPEAT; * Search through a couple of extra years past the obvious 400, to
* avoid edge cases. For example, suppose a non-POSIX rule applies
* from 2012 onwards and has transitions in March and September, plus
* some one-off transitions in November 2013. If zic looked only at
* the last 400 years, it would set max_year=2413, with the intent
* that the 400 years 2014 through 2413 will be repeated. The last
* transition listed in the tzfile would be in 2413-09, less than 400
* years after the last one-off transition in 2013-11. Two years
* might be overkill, but with the kind of edge cases available we're
* not sure that one year would suffice.
*/
enum
{
years_of_observations = YEARSPERREPEAT + 2};
if (min_year >= ZIC_MIN + years_of_observations)
min_year -= years_of_observations;
else else
min_year = INT_MIN; min_year = ZIC_MIN;
if (max_year <= INT_MAX - YEARSPERREPEAT) if (max_year <= ZIC_MAX - years_of_observations)
max_year += YEARSPERREPEAT; max_year += years_of_observations;
else else
max_year = INT_MAX; max_year = ZIC_MAX;
/*
* Regardless of any of the above, for a "proDSTic" zone which
* specifies that its rules always have and always will be in effect,
* we only need one cycle to define the zone.
*/
if (prodstic)
{
min_year = 1900;
max_year = min_year + years_of_observations;
}
} }
/* /*
...@@ -2199,9 +2635,9 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2199,9 +2635,9 @@ outzone(const struct zone * zpfirst, int zonecount)
*/ */
stdoff = 0; stdoff = 0;
zp = &zpfirst[i]; zp = &zpfirst[i];
usestart = i > 0 && (zp - 1)->z_untiltime > min_time; usestart = i > 0 && (zp - 1)->z_untiltime > big_bang_time;
useuntil = i < (zonecount - 1); useuntil = i < (zonecount - 1);
if (useuntil && zp->z_untiltime <= min_time) if (useuntil && zp->z_untiltime <= big_bang_time)
continue; continue;
gmtoff = zp->z_gmtoff; gmtoff = zp->z_gmtoff;
eat(zp->z_filename, zp->z_linenum); eat(zp->z_filename, zp->z_linenum);
...@@ -2210,18 +2646,17 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2210,18 +2646,17 @@ outzone(const struct zone * zpfirst, int zonecount)
if (zp->z_nrules == 0) if (zp->z_nrules == 0)
{ {
stdoff = zp->z_stdoff; stdoff = zp->z_stdoff;
doabbr(startbuf, zp->z_format, doabbr(startbuf, zp, NULL, stdoff, false);
(char *) NULL, stdoff != 0, FALSE);
type = addtype(oadd(zp->z_gmtoff, stdoff), type = addtype(oadd(zp->z_gmtoff, stdoff),
startbuf, stdoff != 0, startttisstd, startbuf, stdoff != 0, startttisstd,
startttisgmt); startttisgmt);
if (usestart) if (usestart)
{ {
addtt(starttime, type); addtt(starttime, type);
usestart = FALSE; usestart = false;
} }
else if (stdoff != 0) else
addtt(min_time, type); addtt(big_bang_time, type);
} }
else else
for (year = min_year; year <= max_year; ++year) for (year = min_year; year <= max_year; ++year)
...@@ -2249,12 +2684,12 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2249,12 +2684,12 @@ outzone(const struct zone * zpfirst, int zonecount)
int k; int k;
zic_t jtime, zic_t jtime,
ktime = 0; ktime = 0;
long offset; zic_t offset;
if (useuntil) if (useuntil)
{ {
/* /*
* Turn untiltime into UTC assuming the current gmtoff * Turn untiltime into UT assuming the current gmtoff
* and stdoff values. * and stdoff values.
*/ */
untiltime = zp->z_untiltime; untiltime = zp->z_untiltime;
...@@ -2291,43 +2726,55 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2291,43 +2726,55 @@ outzone(const struct zone * zpfirst, int zonecount)
k = j; k = j;
ktime = jtime; ktime = jtime;
} }
else if (jtime == ktime)
{
char const *dup_rules_msg =
_("two rules for same instant");
eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum);
warning("%s", dup_rules_msg);
rp = &zp->z_rules[k];
eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum);
error("%s", dup_rules_msg);
}
} }
if (k < 0) if (k < 0)
break; /* go on to next year */ break; /* go on to next year */
rp = &zp->z_rules[k]; rp = &zp->z_rules[k];
rp->r_todo = FALSE; rp->r_todo = false;
if (useuntil && ktime >= untiltime) if (useuntil && ktime >= untiltime)
break; break;
stdoff = rp->r_stdoff; stdoff = rp->r_stdoff;
if (usestart && ktime == starttime) if (usestart && ktime == starttime)
usestart = FALSE; usestart = false;
if (usestart) if (usestart)
{ {
if (ktime < starttime) if (ktime < starttime)
{ {
startoff = oadd(zp->z_gmtoff, startoff = oadd(zp->z_gmtoff,
stdoff); stdoff);
doabbr(startbuf, zp->z_format, doabbr(startbuf, zp,
rp->r_abbrvar, rp->r_abbrvar,
rp->r_stdoff != 0, rp->r_stdoff,
FALSE); false);
continue; continue;
} }
if (*startbuf == '\0' && if (*startbuf == '\0' &&
startoff == oadd(zp->z_gmtoff, stdoff)) startoff == oadd(zp->z_gmtoff, stdoff))
{ {
doabbr(startbuf, doabbr(startbuf,
zp->z_format, zp,
rp->r_abbrvar, rp->r_abbrvar,
rp->r_stdoff != rp->r_stdoff,
0, false);
FALSE);
} }
} }
eats(zp->z_filename, zp->z_linenum, eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum); rp->r_filename, rp->r_linenum);
doabbr(ab, zp->z_format, rp->r_abbrvar, doabbr(ab, zp, rp->r_abbrvar,
rp->r_stdoff != 0, FALSE); rp->r_stdoff, false);
offset = oadd(zp->z_gmtoff, rp->r_stdoff); offset = oadd(zp->z_gmtoff, rp->r_stdoff);
type = addtype(offset, ab, rp->r_stdoff != 0, type = addtype(offset, ab, rp->r_stdoff != 0,
rp->r_todisstd, rp->r_todisgmt); rp->r_todisstd, rp->r_todisgmt);
...@@ -2340,7 +2787,7 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2340,7 +2787,7 @@ outzone(const struct zone * zpfirst, int zonecount)
zp->z_format != NULL && zp->z_format != NULL &&
strchr(zp->z_format, '%') == NULL && strchr(zp->z_format, '%') == NULL &&
strchr(zp->z_format, '/') == NULL) strchr(zp->z_format, '/') == NULL)
(void) strcpy(startbuf, zp->z_format); strcpy(startbuf, zp->z_format);
eat(zp->z_filename, zp->z_linenum); eat(zp->z_filename, zp->z_linenum);
if (*startbuf == '\0') if (*startbuf == '\0')
error(_("cannot determine time zone abbreviation to use just after until time")); error(_("cannot determine time zone abbreviation to use just after until time"));
...@@ -2366,62 +2813,82 @@ outzone(const struct zone * zpfirst, int zonecount) ...@@ -2366,62 +2813,82 @@ outzone(const struct zone * zpfirst, int zonecount)
starttime = tadd(starttime, -gmtoff); starttime = tadd(starttime, -gmtoff);
} }
} }
writezone(zpfirst->z_name, envvar); if (do_extend)
ifree(startbuf); {
ifree(ab); /*
ifree(envvar); * If we're extending the explicitly listed observations for 400 years
* because we can't fill the POSIX-TZ field, check whether we actually
* ended up explicitly listing observations through that period. If
* there aren't any near the end of the 400-year period, add a
* redundant one at the end of the final year, to make it clear that
* we are claiming to have definite knowledge of the lack of
* transitions up to that point.
*/
struct rule xr;
struct attype *lastat;
xr.r_month = TM_JANUARY;
xr.r_dycode = DC_DOM;
xr.r_dayofmonth = 1;
xr.r_tod = 0;
for (lastat = &attypes[0], i = 1; i < timecnt; i++)
if (attypes[i].at > lastat->at)
lastat = &attypes[i];
if (lastat->at < rpytime(&xr, max_year - 1))
{
/*
* Create new type code for the redundant entry, to prevent it
* being optimized away.
*/
if (typecnt >= TZ_MAX_TYPES)
{
error(_("too many local time types"));
exit(EXIT_FAILURE);
}
gmtoffs[typecnt] = gmtoffs[lastat->type];
isdsts[typecnt] = isdsts[lastat->type];
ttisstds[typecnt] = ttisstds[lastat->type];
ttisgmts[typecnt] = ttisgmts[lastat->type];
abbrinds[typecnt] = abbrinds[lastat->type];
++typecnt;
addtt(rpytime(&xr, max_year + 1), typecnt - 1);
}
}
writezone(zpfirst->z_name, envvar, version);
free(startbuf);
free(ab);
free(envvar);
} }
static void static void
addtt(const zic_t starttime, int type) addtt(zic_t starttime, int type)
{ {
if (starttime <= min_time || if (starttime <= big_bang_time ||
(timecnt == 1 && attypes[0].at < min_time)) (timecnt == 1 && attypes[0].at < big_bang_time))
{ {
gmtoffs[0] = gmtoffs[type]; gmtoffs[0] = gmtoffs[type];
isdsts[0] = isdsts[type]; isdsts[0] = isdsts[type];
ttisstds[0] = ttisstds[type]; ttisstds[0] = ttisstds[type];
ttisgmts[0] = ttisgmts[type]; ttisgmts[0] = ttisgmts[type];
if (abbrinds[type] != 0) if (abbrinds[type] != 0)
(void) strcpy(chars, &chars[abbrinds[type]]); strcpy(chars, &chars[abbrinds[type]]);
abbrinds[0] = 0; abbrinds[0] = 0;
charcnt = strlen(chars) + 1; charcnt = strlen(chars) + 1;
typecnt = 1; typecnt = 1;
timecnt = 0; timecnt = 0;
type = 0; type = 0;
} }
if (timecnt >= TZ_MAX_TIMES) attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
{
error(_("too many transitions?!"));
exit(EXIT_FAILURE);
}
attypes[timecnt].at = starttime; attypes[timecnt].at = starttime;
attypes[timecnt].type = type; attypes[timecnt].type = type;
++timecnt; ++timecnt;
} }
static int static int
addtype(long gmtoff, const char *abbr, int isdst, addtype(zic_t gmtoff, char const * abbr, bool isdst, bool ttisstd, bool ttisgmt)
int ttisstd, int ttisgmt)
{ {
int i; int i,
int j; j;
if (isdst != TRUE && isdst != FALSE)
{
error(_("internal error - addtype called with bad isdst"));
exit(EXIT_FAILURE);
}
if (ttisstd != TRUE && ttisstd != FALSE)
{
error(_("internal error - addtype called with bad ttisstd"));
exit(EXIT_FAILURE);
}
if (ttisgmt != TRUE && ttisgmt != FALSE)
{
error(_("internal error - addtype called with bad ttisgmt"));
exit(EXIT_FAILURE);
}
/* /*
* See if there's already an entry for this zone type. If so, just return * See if there's already an entry for this zone type. If so, just return
...@@ -2446,7 +2913,7 @@ addtype(long gmtoff, const char *abbr, int isdst, ...@@ -2446,7 +2913,7 @@ addtype(long gmtoff, const char *abbr, int isdst,
} }
if (!(-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) if (!(-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L))
{ {
error(_("UTC offset out of range")); error(_("UT offset out of range"));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
gmtoffs[i] = gmtoff; gmtoffs[i] = gmtoff;
...@@ -2465,10 +2932,10 @@ addtype(long gmtoff, const char *abbr, int isdst, ...@@ -2465,10 +2932,10 @@ addtype(long gmtoff, const char *abbr, int isdst,
} }
static void static void
leapadd(const zic_t t, int positive, int rolling, int count) leapadd(zic_t t, bool positive, int rolling, int count)
{ {
int i; int i,
int j; j;
if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS)
{ {
...@@ -2494,7 +2961,7 @@ leapadd(const zic_t t, int positive, int rolling, int count) ...@@ -2494,7 +2961,7 @@ leapadd(const zic_t t, int positive, int rolling, int count)
roll[j] = roll[j - 1]; roll[j] = roll[j - 1];
} }
trans[i] = t; trans[i] = t;
corr[i] = positive ? 1L : eitol(-count); corr[i] = positive ? 1 : -count;
roll[i] = rolling; roll[i] = rolling;
++leapcnt; ++leapcnt;
} while (positive && --count != 0); } while (positive && --count != 0);
...@@ -2504,7 +2971,7 @@ static void ...@@ -2504,7 +2971,7 @@ static void
adjleap(void) adjleap(void)
{ {
int i; int i;
long last = 0; zic_t last = 0;
/* /*
* propagate leap seconds forward * propagate leap seconds forward
...@@ -2516,61 +2983,201 @@ adjleap(void) ...@@ -2516,61 +2983,201 @@ adjleap(void)
} }
} }
static int static bool
yearistype(int year, const char *type) yearistype(int year, const char *type)
{ {
static char *buf; static char *buf;
int result; int result;
if (type == NULL || *type == '\0') if (type == NULL || *type == '\0')
return TRUE; return true;
buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type))); buf = erealloc(buf, 132 + strlen(yitcommand) + strlen(type));
(void) sprintf(buf, "%s %d %s", yitcommand, year, type); sprintf(buf, "%s %d %s", yitcommand, year, type);
result = system(buf); result = system(buf);
if (WIFEXITED(result)) if (WIFEXITED(result))
switch (WEXITSTATUS(result)) switch (WEXITSTATUS(result))
{ {
case 0: case 0:
return TRUE; return true;
case 1: case 1:
return FALSE; return false;
} }
error(_("Wild result from command execution")); error(_("Wild result from command execution"));
(void) fprintf(stderr, _("%s: command was '%s', result was %d\n"), fprintf(stderr, _("%s: command was '%s', result was %d\n"),
progname, buf, result); progname, buf, result);
for (;;) for (;;)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
static int /* Is A a space character in the C locale? */
lowerit(int a) static bool
is_space(char a)
{ {
a = (unsigned char) a; switch (a)
return (isascii(a) && isupper(a)) ? tolower(a) : a; {
default:
return false;
case ' ':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
return true;
}
} }
static int /* Is A an alphabetic character in the C locale? */
static bool
is_alpha(char a)
{
switch (a)
{
default:
return false;
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 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
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 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
return true;
}
}
/* If A is an uppercase character in the C locale, return its lowercase
* counterpart. Otherwise, return A. */
static char
lowerit(char a)
{
switch (a)
{
default:
return a;
case 'A':
return 'a';
case 'B':
return 'b';
case 'C':
return 'c';
case 'D':
return 'd';
case 'E':
return 'e';
case 'F':
return 'f';
case 'G':
return 'g';
case 'H':
return 'h';
case 'I':
return 'i';
case 'J':
return 'j';
case 'K':
return 'k';
case 'L':
return 'l';
case 'M':
return 'm';
case 'N':
return 'n';
case 'O':
return 'o';
case 'P':
return 'p';
case 'Q':
return 'q';
case 'R':
return 'r';
case 'S':
return 's';
case 'T':
return 't';
case 'U':
return 'u';
case 'V':
return 'v';
case 'W':
return 'w';
case 'X':
return 'x';
case 'Y':
return 'y';
case 'Z':
return 'z';
}
}
/* case-insensitive equality */
static bool
ciequal(const char *ap, const char *bp) ciequal(const char *ap, const char *bp)
{ {
while (lowerit(*ap) == lowerit(*bp++)) while (lowerit(*ap) == lowerit(*bp++))
if (*ap++ == '\0') if (*ap++ == '\0')
return TRUE; return true;
return FALSE; return false;
} }
static int static bool
itsabbr(const char *abbr, const char *word) itsabbr(const char *abbr, const char *word)
{ {
if (lowerit(*abbr) != lowerit(*word)) if (lowerit(*abbr) != lowerit(*word))
return FALSE; return false;
++word; ++word;
while (*++abbr != '\0') while (*++abbr != '\0')
do do
{ {
if (*word == '\0') if (*word == '\0')
return FALSE; return false;
} while (lowerit(*word++) != lowerit(*abbr)); } while (lowerit(*word++) != lowerit(*abbr));
return TRUE; return true;
} }
static const struct lookup * static const struct lookup *
...@@ -2613,13 +3220,11 @@ getfields(char *cp) ...@@ -2613,13 +3220,11 @@ getfields(char *cp)
if (cp == NULL) if (cp == NULL)
return NULL; return NULL;
array = (char **) (void *) array = emalloc(size_product(strlen(cp) + 1, sizeof *array));
emalloc((int) ((strlen(cp) + 1) * sizeof *array));
nsubs = 0; nsubs = 0;
for (;;) for (;;)
{ {
while (isascii((unsigned char) *cp) && while (is_space(*cp))
isspace((unsigned char) *cp))
++cp; ++cp;
if (*cp == '\0' || *cp == '#') if (*cp == '\0' || *cp == '#')
break; break;
...@@ -2637,9 +3242,8 @@ getfields(char *cp) ...@@ -2637,9 +3242,8 @@ getfields(char *cp)
error(_("Odd number of quotation marks")); error(_("Odd number of quotation marks"));
exit(1); exit(1);
} }
} while (*cp != '\0' && *cp != '#' && } while (*cp && *cp != '#' && !is_space(*cp));
(!isascii(*cp) || !isspace((unsigned char) *cp))); if (is_space(*cp))
if (isascii(*cp) && isspace((unsigned char) *cp))
++cp; ++cp;
*dp = '\0'; *dp = '\0';
} }
...@@ -2647,55 +3251,62 @@ getfields(char *cp) ...@@ -2647,55 +3251,62 @@ getfields(char *cp)
return array; return array;
} }
static long static void
oadd(long t1, long t2) time_overflow(void)
{ {
long t; error(_("time overflow"));
exit(EXIT_FAILURE);
t = t1 + t2;
if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1))
{
error(_("time overflow"));
exit(EXIT_FAILURE);
}
return t;
} }
static zic_t static zic_t
tadd(const zic_t t1, long t2) oadd(zic_t t1, zic_t t2)
{ {
zic_t t; if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2)
time_overflow();
return t1 + t2;
}
if (t1 == max_time && t2 > 0) static zic_t
return max_time; tadd(zic_t t1, zic_t t2)
if (t1 == min_time && t2 < 0) {
return min_time; if (t1 < 0)
t = t1 + t2;
if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1))
{ {
error(_("time overflow")); if (t2 < min_time - t1)
exit(EXIT_FAILURE); {
if (t1 != min_time)
time_overflow();
return min_time;
}
}
else
{
if (max_time - t1 < t2)
{
if (t1 != max_time)
time_overflow();
return max_time;
}
} }
return t; return t1 + t2;
} }
/* /*
* Given a rule, and a year, compute the date - in seconds since January 1, * 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. * 1970, 00:00 LOCAL time) in that year that the rule refers to.
*/ */
static zic_t static zic_t
rpytime(const struct rule * rp, int wantedy) rpytime(const struct rule * rp, zic_t wantedy)
{ {
int y, int m,
m,
i; i;
long dayoff; /* with a nod to Margaret O. */ zic_t dayoff; /* with a nod to Margaret O. */
zic_t t; zic_t t,
y;
if (wantedy == INT_MIN) if (wantedy == ZIC_MIN)
return min_time; return min_time;
if (wantedy == INT_MAX) if (wantedy == ZIC_MAX)
return max_time; return max_time;
dayoff = 0; dayoff = 0;
m = TM_JANUARY; m = TM_JANUARY;
...@@ -2712,12 +3323,12 @@ rpytime(const struct rule * rp, int wantedy) ...@@ -2712,12 +3323,12 @@ rpytime(const struct rule * rp, int wantedy)
--y; --y;
i = -len_years[isleap(y)]; i = -len_years[isleap(y)];
} }
dayoff = oadd(dayoff, eitol(i)); dayoff = oadd(dayoff, i);
} }
while (m != rp->r_month) while (m != rp->r_month)
{ {
i = len_months[isleap(y)][m]; i = len_months[isleap(y)][m];
dayoff = oadd(dayoff, eitol(i)); dayoff = oadd(dayoff, i);
++m; ++m;
} }
i = rp->r_dayofmonth; i = rp->r_dayofmonth;
...@@ -2732,13 +3343,13 @@ rpytime(const struct rule * rp, int wantedy) ...@@ -2732,13 +3343,13 @@ rpytime(const struct rule * rp, int wantedy)
} }
} }
--i; --i;
dayoff = oadd(dayoff, eitol(i)); dayoff = oadd(dayoff, i);
if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ)
{ {
long wday; zic_t wday;
#define LDAYSPERWEEK ((long) DAYSPERWEEK) #define LDAYSPERWEEK ((zic_t) DAYSPERWEEK)
wday = eitol(EPOCH_WDAY); wday = EPOCH_WDAY;
/* /*
* Don't trust mod of negative numbers. * Don't trust mod of negative numbers.
...@@ -2751,17 +3362,17 @@ rpytime(const struct rule * rp, int wantedy) ...@@ -2751,17 +3362,17 @@ rpytime(const struct rule * rp, int wantedy)
if (wday < 0) if (wday < 0)
wday += LDAYSPERWEEK; wday += LDAYSPERWEEK;
} }
while (wday != eitol(rp->r_wday)) while (wday != rp->r_wday)
if (rp->r_dycode == DC_DOWGEQ) if (rp->r_dycode == DC_DOWGEQ)
{ {
dayoff = oadd(dayoff, (long) 1); dayoff = oadd(dayoff, 1);
if (++wday >= LDAYSPERWEEK) if (++wday >= LDAYSPERWEEK)
wday = 0; wday = 0;
++i; ++i;
} }
else else
{ {
dayoff = oadd(dayoff, (long) -1); dayoff = oadd(dayoff, -1);
if (--wday < 0) if (--wday < 0)
wday = LDAYSPERWEEK - 1; wday = LDAYSPERWEEK - 1;
--i; --i;
...@@ -2769,7 +3380,7 @@ rpytime(const struct rule * rp, int wantedy) ...@@ -2769,7 +3380,7 @@ rpytime(const struct rule * rp, int wantedy)
if (i < 0 || i >= len_months[isleap(y)][m]) if (i < 0 || i >= len_months[isleap(y)][m])
{ {
if (noise) if (noise)
warning(_("rule goes past start/end of month--\ warning(_("rule goes past start/end of month; \
will not work with pre-2004 versions of zic")); will not work with pre-2004 versions of zic"));
} }
} }
...@@ -2790,28 +3401,21 @@ newabbr(const char *string) ...@@ -2790,28 +3401,21 @@ newabbr(const char *string)
if (strcmp(string, GRANDPARENTED) != 0) if (strcmp(string, GRANDPARENTED) != 0)
{ {
const char *cp; const char *cp;
char *wp; const char *mp;
cp = string; cp = string;
wp = NULL; mp = NULL;
while (isalpha((unsigned char) *cp) || ('0' <= *cp && *cp <= '9') while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9')
|| *cp == '-' || *cp == '+') || *cp == '-' || *cp == '+')
++cp; ++cp;
if (noise && cp - string < 3) if (noise && cp - string < 3)
wp = _("time zone abbreviation has fewer than 3 characters"); mp = _("time zone abbreviation has fewer than 3 characters");
if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
wp = _("time zone abbreviation has too many characters"); mp = _("time zone abbreviation has too many characters");
if (*cp != '\0') if (*cp != '\0')
wp = _("time zone abbreviation differs from POSIX standard"); mp = _("time zone abbreviation differs from POSIX standard");
if (wp != NULL) if (mp != NULL)
{ warning("%s (%s)", mp, string);
wp = ecpyalloc(wp);
wp = ecatalloc(wp, " (");
wp = ecatalloc(wp, string);
wp = ecatalloc(wp, ")");
warning(wp);
ifree(wp);
}
} }
i = strlen(string) + 1; i = strlen(string) + 1;
if (charcnt + i > TZ_MAX_CHARS) if (charcnt + i > TZ_MAX_CHARS)
...@@ -2819,18 +3423,18 @@ newabbr(const char *string) ...@@ -2819,18 +3423,18 @@ newabbr(const char *string)
error(_("too many, or too long, time zone abbreviations")); error(_("too many, or too long, time zone abbreviations"));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
(void) strcpy(&chars[charcnt], string); strcpy(&chars[charcnt], string);
charcnt += eitol(i); charcnt += i;
} }
static int static bool
mkdirs(char *argname) mkdirs(char *argname)
{ {
char *name; char *name;
char *cp; char *cp;
if (argname == NULL || *argname == '\0') if (argname == NULL || *argname == '\0')
return 0; return true;
cp = name = ecpyalloc(argname); cp = name = ecpyalloc(argname);
while ((cp = strchr(cp + 1, '/')) != NULL) while ((cp = strchr(cp + 1, '/')) != NULL)
{ {
...@@ -2840,60 +3444,38 @@ mkdirs(char *argname) ...@@ -2840,60 +3444,38 @@ mkdirs(char *argname)
/* /*
* DOS drive specifier? * DOS drive specifier?
*/ */
if (isalpha((unsigned char) name[0]) && if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0')
name[1] == ':' && name[2] == '\0')
{ {
*cp = '/'; *cp = '/';
continue; continue;
} }
#endif /* WIN32 */ #endif /* WIN32 */
if (!itsdir(name))
/*
* Try to create it. It's OK if creation fails because the directory
* already exists, perhaps because some other process just created it.
*/
if (mkdir(name, MKDIR_UMASK) != 0)
{ {
/* int err = errno;
* It doesn't seem to exist, so we try to create it. Creation may
* fail because of the directory being created by some other if (itsdir(name) <= 0)
* multiprocessor, so we get to do extra checking.
*/
if (mkdir(name, MKDIR_UMASK) != 0)
{ {
const char *e = strerror(errno); char const *e = strerror(err);
if (errno != EEXIST || !itsdir(name)) warning(_("%s: Can't create directory"
{ " %s: %s"),
(void) fprintf(stderr, progname, name, e);
_("%s: Cannot create directory %s: %s\n"), free(name);
progname, name, e); return false;
ifree(name);
return -1;
}
} }
} }
*cp = '/'; *cp = '/';
} }
ifree(name); free(name);
return 0; return true;
} }
static long
eitol(int i)
{
long l;
l = i;
if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0))
{
(void) fprintf(stderr,
_("%s: %d did not sign extend correctly\n"),
progname, i);
exit(EXIT_FAILURE);
}
return l;
}
/*
* UNIX was a registered trademark of The Open Group in 2003.
*/
#ifdef WIN32 #ifdef WIN32
/* /*
...@@ -2902,18 +3484,8 @@ eitol(int i) ...@@ -2902,18 +3484,8 @@ eitol(int i)
int int
link(const char *oldpath, const char *newpath) link(const char *oldpath, const char *newpath)
{ {
if (!CopyFile(oldpath, newpath, FALSE)) if (!CopyFile(oldpath, newpath, false))
return -1; return -1;
return 0; return 0;
} }
#endif #endif
/*
* This allows zic to compile by just returning a dummy value.
* localtime.c references it, but no one uses it from zic.
*/
int
pg_open_tzfile(const char *name, char *canonname)
{
return -1;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment