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

This is mostly to absorb some corner-case fixes in zic for year-2037
timestamps.  The other changes that have been made are unlikely to affect
our usage, but nonetheless we may as well take 'em.
This commit is contained in:
Tom Lane 2016-10-19 18:55:52 -04:00
parent a3215431ab
commit f3094920a5
4 changed files with 252 additions and 200 deletions

View File

@ -1280,9 +1280,8 @@ gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp)
result = timesub(timep, offset, gmtptr, tmp);
/*
* Could get fancy here and deliver something such as "UT+xxxx" or
* "UT-xxxx" if offset is non-zero, but this is no time for a treasure
* hunt.
* Could get fancy here and deliver something such as "+xx" or "-xx" if
* offset is non-zero, but this is no time for a treasure hunt.
*/
if (offset != 0)
tmp->tm_zone = wildabbr;

View File

@ -23,6 +23,7 @@
#include "pgtime.h"
/* This string was in the Factory zone through version 2016f. */
#define GRANDPARENTED "Local time zone must be set--see zic manual page"
/*

View File

@ -128,7 +128,7 @@ pg_strftime(char *s, size_t maxsize, const char *format,
int warn;
warn = IN_NONE;
p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
p = _fmt(format, t, s, s + maxsize, &warn);
if (p == s + maxsize)
return 0;
*p = '\0';

View File

@ -105,7 +105,7 @@ static int addtype(zic_t, char const *, bool, bool, bool);
static void leapadd(zic_t, bool, int, int);
static void adjleap(void);
static void associate(void);
static void dolink(const char *fromfield, const char *tofield);
static void dolink(const char *, const char *, bool);
static char **getfields(char *buf);
static zic_t gethms(const char *string, const char *errstring,
bool);
@ -119,7 +119,7 @@ static bool inzsub(char **, int, bool);
static int itsdir(const char *name);
static bool is_alpha(char a);
static char lowerit(char);
static bool mkdirs(char *);
static void mkdirs(char const *, bool);
static void newabbr(const char *abbr);
static zic_t oadd(zic_t t1, zic_t t2);
static void outzone(const struct zone * zp, int ntzones);
@ -136,6 +136,15 @@ enum
{
PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1};
/* If true, work around a bug in Qt 5.6.1 and earlier, which mishandles
tz binary files whose POSIX-TZ-style strings contain '<'; see
QTBUG-53071 <https://bugreports.qt.io/browse/QTBUG-53071>. This
workaround will no longer be needed when Qt 5.6.1 and earlier are
obsolete, say in the year 2021. */
enum
{
WORK_AROUND_QTBUG_53071 = true};
static int charcnt;
static bool errors;
static bool warnings;
@ -346,6 +355,7 @@ static const int len_years[2] = {
static struct attype
{
zic_t at;
bool dontmerge;
unsigned char type;
} *attypes;
static zic_t gmtoffs[TZ_MAX_TYPES];
@ -410,7 +420,8 @@ growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
return ptr;
else
{
int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX;
int nitems_max = INT_MAX - WORK_AROUND_QTBUG_53071;
int amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX;
if ((amax - 1) / 3 * 2 < *nitems_alloc)
memory_exhausted(_("int overflow"));
@ -478,17 +489,17 @@ warning(const char *string,...)
}
static void
close_file(FILE *stream, char const * name)
close_file(FILE *stream, char const * dir, char const * name)
{
char const *e = (ferror(stream) ? _("I/O error")
: fclose(stream) != 0 ? strerror(errno) : NULL);
if (e)
{
fprintf(stderr, "%s: ", progname);
if (name)
fprintf(stderr, "%s: ", name);
fprintf(stderr, "%s\n", e);
fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
dir ? dir : "", dir ? "/" : "",
name ? name : "", name ? ": " : "",
e);
exit(EXIT_FAILURE);
}
}
@ -503,10 +514,34 @@ usage(FILE *stream, int status)
"Report bugs to %s.\n"),
progname, progname, PACKAGE_BUGREPORT);
if (status == EXIT_SUCCESS)
close_file(stream, NULL);
close_file(stream, NULL, NULL);
exit(status);
}
/* Change the working directory to DIR, possibly creating DIR and its
ancestors. After this is done, all files are accessed with names
relative to DIR. */
static void
change_directory(char const * dir)
{
if (chdir(dir) != 0)
{
int chdir_errno = errno;
if (chdir_errno == ENOENT)
{
mkdirs(dir, false);
chdir_errno = chdir(dir) == 0 ? 0 : errno;
}
if (chdir_errno != 0)
{
fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
progname, dir, strerror(chdir_errno));
exit(EXIT_FAILURE);
}
}
}
static const char *psxrules;
static const char *lcltime;
static const char *directory;
@ -534,7 +569,7 @@ main(int argc, char *argv[])
if (strcmp(argv[i], "--version") == 0)
{
printf("zic %s\n", PG_VERSION);
close_file(stdout, NULL);
close_file(stdout, NULL, NULL);
return EXIT_SUCCESS;
}
else if (strcmp(argv[i], "--help") == 0)
@ -630,6 +665,7 @@ main(int argc, char *argv[])
if (errors)
return EXIT_FAILURE;
associate();
change_directory(directory);
for (i = 0; i < nzones; i = j)
{
/*
@ -646,7 +682,7 @@ main(int argc, char *argv[])
for (i = 0; i < nlinks; ++i)
{
eat(links[i].l_filename, links[i].l_linenum);
dolink(links[i].l_from, links[i].l_to);
dolink(links[i].l_from, links[i].l_to, false);
if (noise)
for (j = 0; j < nlinks; ++j)
if (strcmp(links[i].l_to,
@ -656,12 +692,12 @@ main(int argc, char *argv[])
if (lcltime != NULL)
{
eat(_("command line"), 1);
dolink(lcltime, TZDEFAULT);
dolink(lcltime, TZDEFAULT, true);
}
if (psxrules != NULL)
{
eat(_("command line"), 1);
dolink(psxrules, TZDEFRULES);
dolink(psxrules, TZDEFRULES, true);
}
if (warnings && (ferror(stderr) || fclose(stderr) != 0))
return EXIT_FAILURE;
@ -751,63 +787,46 @@ namecheck(const char *name)
return componentcheck(name, component, cp);
}
static char *
relname(char const * dir, char const * base)
{
if (*base == '/')
return ecpyalloc(base);
else
{
size_t dir_len = strlen(dir);
bool needs_slash = dir_len && dir[dir_len - 1] != '/';
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)
dolink(char const * fromfield, char const * tofield, bool staysymlink)
{
char *fromname;
char *toname;
int fromisdir;
fromname = relname(directory, fromfield);
toname = relname(directory, tofield);
bool todirs_made = false;
int link_errno;
/*
* We get to be careful here since there's a fair chance of root running
* us.
*/
fromisdir = itsdir(fromname);
fromisdir = itsdir(fromfield);
if (fromisdir)
{
char const *e = strerror(fromisdir < 0 ? errno : EPERM);
fprintf(stderr, _("%s: link from %s failed: %s"),
progname, fromname, e);
fprintf(stderr, _("%s: link from %s/%s failed: %s\n"),
progname, directory, fromfield, e);
exit(EXIT_FAILURE);
}
if (link(fromname, toname) != 0)
if (staysymlink)
staysymlink = itsdir(tofield) == 2;
if (remove(tofield) == 0)
todirs_made = true;
else if (errno != ENOENT)
{
int link_errno = errno;
bool retry_if_link_supported = false;
char const *e = strerror(errno);
if (link_errno == ENOENT || link_errno == ENOTSUP)
{
if (!mkdirs(toname))
fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
progname, directory, tofield, e);
exit(EXIT_FAILURE);
retry_if_link_supported = true;
}
if ((link_errno == EEXIST || link_errno == ENOTSUP)
&& 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;
link_errno = (staysymlink ? ENOTSUP
: link(fromfield, tofield) == 0 ? 0 : errno);
if (link_errno == ENOENT && !todirs_made)
{
mkdirs(tofield, true);
todirs_made = true;
link_errno = link(fromfield, tofield) == 0 ? 0 : errno;
}
if (link_errno != 0)
{
#ifdef HAVE_SYMLINK
@ -816,7 +835,7 @@ dolink(char const * fromfield, char const * tofield)
char *p;
size_t dotdots = 0;
char *symlinkcontents;
int symlink_result;
int symlink_errno;
do
t = s;
@ -829,9 +848,14 @@ dolink(char const * fromfield, char const * tofield)
for (p = symlinkcontents; dotdots-- != 0; p += 3)
memcpy(p, "../", 3);
strcpy(p, t);
symlink_result = symlink(symlinkcontents, toname);
symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
if (symlink_errno == ENOENT && !todirs_made)
{
mkdirs(tofield, true);
symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
}
free(symlinkcontents);
if (symlink_result == 0)
if (symlink_errno == 0)
{
if (link_errno != ENOTSUP)
warning(_("symbolic link used because hard link failed: %s"),
@ -844,38 +868,36 @@ dolink(char const * fromfield, char const * tofield)
*tp;
int c;
fp = fopen(fromname, "rb");
fp = fopen(fromfield, "rb");
if (!fp)
{
const char *e = strerror(errno);
char const *e = strerror(errno);
fprintf(stderr,
_("%s: Can't read %s: %s\n"),
progname, fromname, e);
fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
progname, directory, fromfield, e);
exit(EXIT_FAILURE);
}
tp = fopen(toname, "wb");
tp = fopen(tofield, "wb");
if (!tp)
{
const char *e = strerror(errno);
char const *e = strerror(errno);
fprintf(stderr,
_("%s: Can't create %s: %s\n"),
progname, toname, e);
fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
progname, directory, tofield, e);
exit(EXIT_FAILURE);
}
while ((c = getc(fp)) != EOF)
putc(c, tp);
close_file(fp, fromname);
close_file(tp, toname);
close_file(fp, directory, fromfield);
close_file(tp, directory, tofield);
if (link_errno != ENOTSUP)
warning(_("copy used because hard link failed: %s"),
strerror(link_errno));
else if (symlink_errno != ENOTSUP)
warning(_("copy used because symbolic link failed: %s"),
strerror(symlink_errno));
}
}
}
free(fromname);
free(toname);
}
#define TIME_T_BITS_IN_FILE 64
@ -888,10 +910,6 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
* rounded downward to the negation of a power of two that is
* 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.
@ -913,26 +931,45 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
#define BIG_BANG (- (((zic_t) 1) << 59))
#endif
static const zic_t big_bang_time = BIG_BANG;
/* If true, work around GNOME bug 730332
<https://bugzilla.gnome.org/show_bug.cgi?id=730332>
by refusing to output time stamps before BIG_BANG.
Such time stamps are physically suspect anyway.
/* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble. */
The GNOME bug is scheduled to be fixed in GNOME 3.22, and if so
this workaround will no longer be needed when GNOME 3.21 and
earlier are obsolete, say in the year 2021. */
enum
{
WORK_AROUND_GNOME_BUG_730332 = true};
static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332
? BIG_BANG
: MINVAL(zic_t, TIME_T_BITS_IN_FILE));
/* Return 1 if NAME is a directory, 2 if a symbolic link, 0 if
something else, -1 (setting errno) if trouble. */
static int
itsdir(char const * name)
{
struct stat st;
int res = stat(name, &st);
int res = lstat(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;
#ifdef S_ISDIR
return S_ISDIR(st.st_mode) ? 1 : S_ISLNK(st.st_mode) ? 2 : 0;
#else
size_t n = strlen(name);
char *nameslashdot = emalloc(n + 3);
bool dir;
memcpy(nameslashdot, name, n);
strcpy(&nameslashdot[n], &"/."[!(n && name[n - 1] != '/')]);
dir = lstat(nameslashdot, &st) == 0;
free(nameslashdot);
return dir;
#endif
}
return -1;
}
@ -1129,7 +1166,7 @@ infile(const char *name)
}
free(fields);
}
close_file(fp, filename);
close_file(fp, NULL, filename);
if (wantcont)
error(_("expected continuation line not found"));
}
@ -1313,7 +1350,7 @@ inzsub(char **fields, int nfields, bool iscont)
z.z_filename = filename;
z.z_linenum = linenum;
z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true);
if ((cp = strchr(fields[i_format], '%')) != 0)
if ((cp = strchr(fields[i_format], '%')) != NULL)
{
if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
|| strchr(fields[i_format], '/'))
@ -1491,7 +1528,7 @@ inleap(char **fields, int nfields)
return;
}
t = tadd(t, tod);
if (t < big_bang_time)
if (t < early_time)
{
error(_("leap second precedes Big Bang"));
return;
@ -1764,11 +1801,14 @@ writezone(const char *const name, const char *const string, char version)
int timecnt32,
timei32;
int pass;
char *fullname;
static const struct tzhead tzh0;
static struct tzhead tzh;
zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1));
void *typesptr = ats + timecnt;
bool dir_checked = false;
zic_t one = 1;
zic_t y2038_boundary = one << 31;
int nats = timecnt + WORK_AROUND_QTBUG_53071;
zic_t *ats = emalloc(size_product(nats, sizeof *ats + 1));
void *typesptr = ats + nats;
unsigned char *types = typesptr;
/*
@ -1786,7 +1826,7 @@ writezone(const char *const name, const char *const string, char version)
toi = 0;
fromi = 0;
while (fromi < timecnt && attypes[fromi].at < big_bang_time)
while (fromi < timecnt && attypes[fromi].at < early_time)
++fromi;
for (; fromi < timecnt; ++fromi)
{
@ -1799,8 +1839,9 @@ writezone(const char *const name, const char *const string, char version)
attypes[fromi].type;
continue;
}
if (toi == 0 ||
attypes[toi - 1].type != attypes[fromi].type)
if (toi == 0
|| attypes[fromi].dontmerge
|| attypes[toi - 1].type != attypes[fromi].type)
attypes[toi++] = attypes[fromi];
}
timecnt = toi;
@ -1818,6 +1859,20 @@ writezone(const char *const name, const char *const string, char version)
types[i] = attypes[i].type;
}
/*
* Work around QTBUG-53071 for time stamps less than y2038_boundary - 1,
* by inserting a no-op transition at time y2038_boundary - 1. This works
* only for timestamps before the boundary, which should be good enough in
* practice as QTBUG-53071 should be long-dead by 2038.
*/
if (WORK_AROUND_QTBUG_53071 && timecnt != 0
&& ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<'))
{
ats[timecnt] = y2038_boundary - 1;
types[timecnt] = types[timecnt - 1];
timecnt++;
}
/*
* Correct for leap seconds.
*/
@ -1862,29 +1917,35 @@ writezone(const char *const name, const char *const string, char version)
--leapcnt32;
++leapi32;
}
fullname = relname(directory, name);
/*
* Remove old file, if any, to snap links.
*/
if (itsdir(fullname) == 0 && remove(fullname) != 0 && errno != ENOENT)
if (remove(name) == 0)
dir_checked = true;
else if (errno != ENOENT)
{
const char *e = strerror(errno);
fprintf(stderr, _("%s: Cannot remove %s: %s\n"),
progname, fullname, e);
fprintf(stderr, _("%s: Cannot remove %s/%s: %s\n"),
progname, directory, name, e);
exit(EXIT_FAILURE);
}
if ((fp = fopen(fullname, "wb")) == NULL)
fp = fopen(name, "wb");
if (!fp)
{
if (!mkdirs(fullname))
exit(EXIT_FAILURE);
if ((fp = fopen(fullname, "wb")) == NULL)
{
const char *e = strerror(errno);
int fopen_errno = errno;
fprintf(stderr, _("%s: Cannot create %s: %s\n"),
progname, fullname, e);
if (fopen_errno == ENOENT && !dir_checked)
{
mkdirs(name, true);
fp = fopen(name, "wb");
fopen_errno = errno;
}
if (!fp)
{
fprintf(stderr, _("%s: Cannot create %s/%s: %s\n"),
progname, directory, name, strerror(fopen_errno));
exit(EXIT_FAILURE);
}
}
@ -2130,9 +2191,8 @@ writezone(const char *const name, const char *const string, char version)
putc(ttisgmts[i], fp);
}
fprintf(fp, "\n%s\n", string);
close_file(fp, fullname);
close_file(fp, directory, name);
free(ats);
free(fullname);
}
static char const *
@ -2527,6 +2587,7 @@ outzone(const struct zone * zpfirst, int zonecount)
int compat;
bool do_extend;
char version;
int lastatmax = -1;
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
@ -2649,9 +2710,9 @@ outzone(const struct zone * zpfirst, int zonecount)
*/
stdoff = 0;
zp = &zpfirst[i];
usestart = i > 0 && (zp - 1)->z_untiltime > big_bang_time;
usestart = i > 0 && (zp - 1)->z_untiltime > early_time;
useuntil = i < (zonecount - 1);
if (useuntil && zp->z_untiltime <= big_bang_time)
if (useuntil && zp->z_untiltime <= early_time)
continue;
gmtoff = zp->z_gmtoff;
eat(zp->z_filename, zp->z_linenum);
@ -2670,7 +2731,7 @@ outzone(const struct zone * zpfirst, int zonecount)
usestart = false;
}
else
addtt(big_bang_time, type);
addtt(early_time, type);
}
else
for (year = min_year; year <= max_year; ++year)
@ -2792,6 +2853,10 @@ outzone(const struct zone * zpfirst, int zonecount)
offset = oadd(zp->z_gmtoff, rp->r_stdoff);
type = addtype(offset, ab, rp->r_stdoff != 0,
rp->r_todisstd, rp->r_todisgmt);
if (rp->r_hiyear == ZIC_MAX
&& !(0 <= lastatmax
&& ktime < attypes[lastatmax].at))
lastatmax = timecnt;
addtt(ktime, type);
}
}
@ -2827,6 +2892,8 @@ outzone(const struct zone * zpfirst, int zonecount)
starttime = tadd(starttime, -gmtoff);
}
}
if (0 <= lastatmax)
attypes[lastatmax].dontmerge = true;
if (do_extend)
{
/*
@ -2850,22 +2917,8 @@ outzone(const struct zone * zpfirst, int zonecount)
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);
attypes[timecnt - 1].dontmerge = true;
}
}
writezone(zpfirst->z_name, envvar, version);
@ -2877,8 +2930,8 @@ outzone(const struct zone * zpfirst, int zonecount)
static void
addtt(zic_t starttime, int type)
{
if (starttime <= big_bang_time ||
(timecnt == 1 && attypes[0].at < big_bang_time))
if (starttime <= early_time
|| (timecnt == 1 && attypes[0].at < early_time))
{
gmtoffs[0] = gmtoffs[type];
isdsts[0] = isdsts[type];
@ -2894,6 +2947,7 @@ addtt(zic_t starttime, int type)
}
attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
attypes[timecnt].at = starttime;
attypes[timecnt].dontmerge = false;
attypes[timecnt].type = type;
++timecnt;
}
@ -3441,53 +3495,51 @@ newabbr(const char *string)
charcnt += i;
}
static bool
mkdirs(char *argname)
/* Ensure that the directories of ARGNAME exist, by making any missing
ones. If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
do it for ARGNAME too. Exit with failure if there is trouble. */
static void
mkdirs(char const * argname, bool ancestors)
{
char *name;
char *cp;
if (argname == NULL || *argname == '\0')
return true;
cp = name = ecpyalloc(argname);
while ((cp = strchr(cp + 1, '/')) != NULL)
{
*cp = '\0';
#ifdef WIN32
/*
* DOS drive specifier?
*/
if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0')
/* Do not mkdir a root directory, as it must exist. */
#ifdef WIN32
if (is_alpha(name[0]) && name[1] == ':')
cp += 2;
#endif
while (*cp == '/')
cp++;
while (cp && ((cp = strchr(cp, '/')) || !ancestors))
{
*cp = '/';
continue;
}
#endif /* WIN32 */
if (cp)
*cp = '\0';
/*
* Try to create it. It's OK if creation fails because the directory
* already exists, perhaps because some other process just created it.
* For simplicity do not check first whether it already exists, as
* that is checked anyway if the mkdir fails.
*/
if (mkdir(name, MKDIR_UMASK) != 0)
{
int err = errno;
if (itsdir(name) <= 0)
if (err != EEXIST && itsdir(name) < 0)
{
char const *e = strerror(err);
warning(_("%s: Can't create directory"
" %s: %s"),
progname, name, e);
free(name);
return false;
error(_("%s: Cannot create directory %s: %s"),
progname, name, strerror(err));
exit(EXIT_FAILURE);
}
}
*cp = '/';
if (cp)
*cp++ = '/';
}
free(name);
return true;
}