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); result = timesub(timep, offset, gmtptr, tmp);
/* /*
* Could get fancy here and deliver something such as "UT+xxxx" or * Could get fancy here and deliver something such as "+xx" or "-xx" if
* "UT-xxxx" if offset is non-zero, but this is no time for a treasure * offset is non-zero, but this is no time for a treasure hunt.
* hunt.
*/ */
if (offset != 0) if (offset != 0)
tmp->tm_zone = wildabbr; tmp->tm_zone = wildabbr;

View File

@ -23,6 +23,7 @@
#include "pgtime.h" #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" #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; int warn;
warn = IN_NONE; 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) if (p == s + maxsize)
return 0; return 0;
*p = '\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 leapadd(zic_t, bool, int, int);
static void adjleap(void); static void adjleap(void);
static void associate(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 char **getfields(char *buf);
static zic_t gethms(const char *string, const char *errstring, static zic_t gethms(const char *string, const char *errstring,
bool); bool);
@ -119,7 +119,7 @@ static bool inzsub(char **, int, bool);
static int itsdir(const char *name); static int itsdir(const char *name);
static bool is_alpha(char a); static bool is_alpha(char a);
static char lowerit(char); static char lowerit(char);
static bool mkdirs(char *); static void mkdirs(char const *, bool);
static void newabbr(const char *abbr); static void newabbr(const char *abbr);
static zic_t oadd(zic_t t1, zic_t 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);
@ -136,6 +136,15 @@ enum
{ {
PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1}; 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 int charcnt;
static bool errors; static bool errors;
static bool warnings; static bool warnings;
@ -346,6 +355,7 @@ static const int len_years[2] = {
static struct attype static struct attype
{ {
zic_t at; zic_t at;
bool dontmerge;
unsigned char type; unsigned char type;
} *attypes; } *attypes;
static zic_t gmtoffs[TZ_MAX_TYPES]; static zic_t gmtoffs[TZ_MAX_TYPES];
@ -410,7 +420,8 @@ growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
return ptr; return ptr;
else 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) if ((amax - 1) / 3 * 2 < *nitems_alloc)
memory_exhausted(_("int overflow")); memory_exhausted(_("int overflow"));
@ -478,17 +489,17 @@ warning(const char *string,...)
} }
static void 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") char const *e = (ferror(stream) ? _("I/O error")
: fclose(stream) != 0 ? strerror(errno) : NULL); : fclose(stream) != 0 ? strerror(errno) : NULL);
if (e) if (e)
{ {
fprintf(stderr, "%s: ", progname); fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
if (name) dir ? dir : "", dir ? "/" : "",
fprintf(stderr, "%s: ", name); name ? name : "", name ? ": " : "",
fprintf(stderr, "%s\n", e); e);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
@ -503,10 +514,34 @@ usage(FILE *stream, int status)
"Report bugs to %s.\n"), "Report bugs to %s.\n"),
progname, progname, PACKAGE_BUGREPORT); progname, progname, PACKAGE_BUGREPORT);
if (status == EXIT_SUCCESS) if (status == EXIT_SUCCESS)
close_file(stream, NULL); close_file(stream, NULL, NULL);
exit(status); 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 *psxrules;
static const char *lcltime; static const char *lcltime;
static const char *directory; static const char *directory;
@ -534,7 +569,7 @@ main(int argc, char *argv[])
if (strcmp(argv[i], "--version") == 0) if (strcmp(argv[i], "--version") == 0)
{ {
printf("zic %s\n", PG_VERSION); printf("zic %s\n", PG_VERSION);
close_file(stdout, NULL); close_file(stdout, NULL, NULL);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
else if (strcmp(argv[i], "--help") == 0) else if (strcmp(argv[i], "--help") == 0)
@ -630,6 +665,7 @@ main(int argc, char *argv[])
if (errors) if (errors)
return EXIT_FAILURE; return EXIT_FAILURE;
associate(); associate();
change_directory(directory);
for (i = 0; i < nzones; i = j) for (i = 0; i < nzones; i = j)
{ {
/* /*
@ -646,7 +682,7 @@ main(int argc, char *argv[])
for (i = 0; i < nlinks; ++i) for (i = 0; i < nlinks; ++i)
{ {
eat(links[i].l_filename, links[i].l_linenum); 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) if (noise)
for (j = 0; j < nlinks; ++j) for (j = 0; j < nlinks; ++j)
if (strcmp(links[i].l_to, if (strcmp(links[i].l_to,
@ -656,12 +692,12 @@ 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, true);
} }
if (psxrules != NULL) if (psxrules != NULL)
{ {
eat(_("command line"), 1); eat(_("command line"), 1);
dolink(psxrules, TZDEFRULES); dolink(psxrules, TZDEFRULES, true);
} }
if (warnings && (ferror(stderr) || fclose(stderr) != 0)) if (warnings && (ferror(stderr) || fclose(stderr) != 0))
return EXIT_FAILURE; return EXIT_FAILURE;
@ -751,131 +787,117 @@ namecheck(const char *name)
return componentcheck(name, component, cp); 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 static void
dolink(char const * fromfield, char const * tofield) dolink(char const * fromfield, char const * tofield, bool staysymlink)
{ {
char *fromname;
char *toname;
int fromisdir; int fromisdir;
bool todirs_made = false;
fromname = relname(directory, fromfield); int link_errno;
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.
*/ */
fromisdir = itsdir(fromname); fromisdir = itsdir(fromfield);
if (fromisdir) if (fromisdir)
{ {
char const *e = strerror(fromisdir < 0 ? errno : EPERM); char const *e = strerror(fromisdir < 0 ? errno : EPERM);
fprintf(stderr, _("%s: link from %s failed: %s"), fprintf(stderr, _("%s: link from %s/%s failed: %s\n"),
progname, fromname, e); progname, directory, fromfield, e);
exit(EXIT_FAILURE); 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; char const *e = strerror(errno);
bool retry_if_link_supported = false;
if (link_errno == ENOENT || link_errno == ENOTSUP) fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
{ progname, directory, tofield, e);
if (!mkdirs(toname)) exit(EXIT_FAILURE);
exit(EXIT_FAILURE); }
retry_if_link_supported = true; link_errno = (staysymlink ? ENOTSUP
} : link(fromfield, tofield) == 0 ? 0 : errno);
if ((link_errno == EEXIST || link_errno == ENOTSUP) if (link_errno == ENOENT && !todirs_made)
&& itsdir(toname) == 0 {
&& (remove(toname) == 0 || errno == ENOENT)) mkdirs(tofield, true);
retry_if_link_supported = true; todirs_made = true;
if (retry_if_link_supported && link_errno != ENOTSUP) link_errno = link(fromfield, tofield) == 0 ? 0 : errno;
link_errno = link(fromname, toname) == 0 ? 0 : errno; }
if (link_errno != 0) if (link_errno != 0)
{ {
#ifdef HAVE_SYMLINK #ifdef HAVE_SYMLINK
const char *s = fromfield; const char *s = fromfield;
const char *t; const char *t;
char *p; char *p;
size_t dotdots = 0; size_t dotdots = 0;
char *symlinkcontents; char *symlinkcontents;
int symlink_result; int symlink_errno;
do do
t = s; t = s;
while ((s = strchr(s, '/')) while ((s = strchr(s, '/'))
&& strncmp(fromfield, tofield, ++s - fromfield) == 0); && strncmp(fromfield, tofield, ++s - fromfield) == 0);
for (s = tofield + (t - fromfield); *s; s++) for (s = tofield + (t - fromfield); *s; s++)
dotdots += *s == '/'; dotdots += *s == '/';
symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1); symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1);
for (p = symlinkcontents; dotdots-- != 0; p += 3) for (p = symlinkcontents; dotdots-- != 0; p += 3)
memcpy(p, "../", 3); memcpy(p, "../", 3);
strcpy(p, t); strcpy(p, t);
symlink_result = symlink(symlinkcontents, toname); symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
free(symlinkcontents); if (symlink_errno == ENOENT && !todirs_made)
if (symlink_result == 0) {
{ mkdirs(tofield, true);
if (link_errno != ENOTSUP) symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
warning(_("symbolic link used because hard link failed: %s"), }
strerror(link_errno)); free(symlinkcontents);
} if (symlink_errno == 0)
else {
if (link_errno != ENOTSUP)
warning(_("symbolic link used because hard link failed: %s"),
strerror(link_errno));
}
else
#endif /* HAVE_SYMLINK */ #endif /* HAVE_SYMLINK */
{
FILE *fp,
*tp;
int c;
fp = fopen(fromfield, "rb");
if (!fp)
{ {
FILE *fp, char const *e = strerror(errno);
*tp;
int c;
fp = fopen(fromname, "rb"); fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
if (!fp) progname, directory, fromfield, 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));
} }
tp = fopen(tofield, "wb");
if (!tp)
{
char const *e = strerror(errno);
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, 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 #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 * rounded downward to the negation of a power of two that is
* comfortably outside the error bounds. * 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: * For the time of the Big Bang, see:
* *
* Ade PAR, Aghanim N, Armitage-Caplan C et al. Planck 2013 results. * 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)) #define BIG_BANG (- (((zic_t) 1) << 59))
#endif #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 static int
itsdir(char const * name) itsdir(char const * name)
{ {
struct stat st; struct stat st;
int res = stat(name, &st); int res = lstat(name, &st);
#ifdef S_ISDIR
if (res == 0) if (res == 0)
return S_ISDIR(st.st_mode) != 0;
#endif
if (res == 0 || errno == EOVERFLOW)
{ {
char *nameslashdot = relname(name, "."); #ifdef S_ISDIR
bool dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW; 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); free(nameslashdot);
return dir; return dir;
#endif
} }
return -1; return -1;
} }
@ -1129,7 +1166,7 @@ infile(const char *name)
} }
free(fields); free(fields);
} }
close_file(fp, filename); close_file(fp, NULL, filename);
if (wantcont) if (wantcont)
error(_("expected continuation line not found")); error(_("expected continuation line not found"));
} }
@ -1313,7 +1350,7 @@ inzsub(char **fields, int nfields, bool 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 UT offset"), true); 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, '%') if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
|| strchr(fields[i_format], '/')) || strchr(fields[i_format], '/'))
@ -1491,7 +1528,7 @@ inleap(char **fields, int nfields)
return; return;
} }
t = tadd(t, tod); t = tadd(t, tod);
if (t < big_bang_time) if (t < early_time)
{ {
error(_("leap second precedes Big Bang")); error(_("leap second precedes Big Bang"));
return; return;
@ -1764,11 +1801,14 @@ writezone(const char *const name, const char *const string, char version)
int timecnt32, int timecnt32,
timei32; timei32;
int pass; int pass;
char *fullname;
static const struct tzhead tzh0; static const struct tzhead tzh0;
static struct tzhead tzh; static struct tzhead tzh;
zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1)); bool dir_checked = false;
void *typesptr = ats + timecnt; 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; unsigned char *types = typesptr;
/* /*
@ -1786,7 +1826,7 @@ writezone(const char *const name, const char *const string, char version)
toi = 0; toi = 0;
fromi = 0; fromi = 0;
while (fromi < timecnt && attypes[fromi].at < big_bang_time) while (fromi < timecnt && attypes[fromi].at < early_time)
++fromi; ++fromi;
for (; fromi < timecnt; ++fromi) for (; fromi < timecnt; ++fromi)
{ {
@ -1799,8 +1839,9 @@ writezone(const char *const name, const char *const string, char version)
attypes[fromi].type; attypes[fromi].type;
continue; continue;
} }
if (toi == 0 || if (toi == 0
attypes[toi - 1].type != attypes[fromi].type) || attypes[fromi].dontmerge
|| attypes[toi - 1].type != attypes[fromi].type)
attypes[toi++] = attypes[fromi]; attypes[toi++] = attypes[fromi];
} }
timecnt = toi; timecnt = toi;
@ -1818,6 +1859,20 @@ writezone(const char *const name, const char *const string, char version)
types[i] = attypes[i].type; 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. * Correct for leap seconds.
*/ */
@ -1862,29 +1917,35 @@ writezone(const char *const name, const char *const string, char version)
--leapcnt32; --leapcnt32;
++leapi32; ++leapi32;
} }
fullname = relname(directory, name);
/* /*
* Remove old file, if any, to snap links. * 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); const char *e = strerror(errno);
fprintf(stderr, _("%s: Cannot remove %s: %s\n"), fprintf(stderr, _("%s: Cannot remove %s/%s: %s\n"),
progname, fullname, e); progname, directory, name, e);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ((fp = fopen(fullname, "wb")) == NULL) fp = fopen(name, "wb");
if (!fp)
{ {
if (!mkdirs(fullname)) int fopen_errno = errno;
exit(EXIT_FAILURE);
if ((fp = fopen(fullname, "wb")) == NULL)
{
const char *e = strerror(errno);
fprintf(stderr, _("%s: Cannot create %s: %s\n"), if (fopen_errno == ENOENT && !dir_checked)
progname, fullname, e); {
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); exit(EXIT_FAILURE);
} }
} }
@ -2130,9 +2191,8 @@ writezone(const char *const name, const char *const string, char version)
putc(ttisgmts[i], fp); putc(ttisgmts[i], fp);
} }
fprintf(fp, "\n%s\n", string); fprintf(fp, "\n%s\n", string);
close_file(fp, fullname); close_file(fp, directory, name);
free(ats); free(ats);
free(fullname);
} }
static char const * static char const *
@ -2527,6 +2587,7 @@ outzone(const struct zone * zpfirst, int zonecount)
int compat; int compat;
bool do_extend; bool do_extend;
char version; char version;
int lastatmax = -1;
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;
@ -2649,9 +2710,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 > big_bang_time; usestart = i > 0 && (zp - 1)->z_untiltime > early_time;
useuntil = i < (zonecount - 1); useuntil = i < (zonecount - 1);
if (useuntil && zp->z_untiltime <= big_bang_time) if (useuntil && zp->z_untiltime <= early_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);
@ -2670,7 +2731,7 @@ outzone(const struct zone * zpfirst, int zonecount)
usestart = false; usestart = false;
} }
else else
addtt(big_bang_time, type); addtt(early_time, type);
} }
else else
for (year = min_year; year <= max_year; ++year) 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); 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);
if (rp->r_hiyear == ZIC_MAX
&& !(0 <= lastatmax
&& ktime < attypes[lastatmax].at))
lastatmax = timecnt;
addtt(ktime, type); addtt(ktime, type);
} }
} }
@ -2827,6 +2892,8 @@ outzone(const struct zone * zpfirst, int zonecount)
starttime = tadd(starttime, -gmtoff); starttime = tadd(starttime, -gmtoff);
} }
} }
if (0 <= lastatmax)
attypes[lastatmax].dontmerge = true;
if (do_extend) if (do_extend)
{ {
/* /*
@ -2850,22 +2917,8 @@ outzone(const struct zone * zpfirst, int zonecount)
lastat = &attypes[i]; lastat = &attypes[i];
if (lastat->at < rpytime(&xr, max_year - 1)) 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); addtt(rpytime(&xr, max_year + 1), typecnt - 1);
attypes[timecnt - 1].dontmerge = true;
} }
} }
writezone(zpfirst->z_name, envvar, version); writezone(zpfirst->z_name, envvar, version);
@ -2877,8 +2930,8 @@ outzone(const struct zone * zpfirst, int zonecount)
static void static void
addtt(zic_t starttime, int type) addtt(zic_t starttime, int type)
{ {
if (starttime <= big_bang_time || if (starttime <= early_time
(timecnt == 1 && attypes[0].at < big_bang_time)) || (timecnt == 1 && attypes[0].at < early_time))
{ {
gmtoffs[0] = gmtoffs[type]; gmtoffs[0] = gmtoffs[type];
isdsts[0] = isdsts[type]; isdsts[0] = isdsts[type];
@ -2894,6 +2947,7 @@ addtt(zic_t starttime, int type)
} }
attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc); attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
attypes[timecnt].at = starttime; attypes[timecnt].at = starttime;
attypes[timecnt].dontmerge = false;
attypes[timecnt].type = type; attypes[timecnt].type = type;
++timecnt; ++timecnt;
} }
@ -3441,53 +3495,51 @@ newabbr(const char *string)
charcnt += i; charcnt += i;
} }
static bool /* Ensure that the directories of ARGNAME exist, by making any missing
mkdirs(char *argname) 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 *name;
char *cp; char *cp;
if (argname == NULL || *argname == '\0')
return true;
cp = name = ecpyalloc(argname); cp = name = ecpyalloc(argname);
while ((cp = strchr(cp + 1, '/')) != NULL)
{
*cp = '\0';
#ifdef WIN32
/* /* Do not mkdir a root directory, as it must exist. */
* DOS drive specifier? #ifdef WIN32
*/ if (is_alpha(name[0]) && name[1] == ':')
if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0') cp += 2;
{ #endif
*cp = '/'; while (*cp == '/')
continue; cp++;
}
#endif /* WIN32 */ while (cp && ((cp = strchr(cp, '/')) || !ancestors))
{
if (cp)
*cp = '\0';
/* /*
* Try to create it. It's OK if creation fails because the directory * Try to create it. It's OK if creation fails because the directory
* already exists, perhaps because some other process just created it. * 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) if (mkdir(name, MKDIR_UMASK) != 0)
{ {
int err = errno; int err = errno;
if (itsdir(name) <= 0) if (err != EEXIST && itsdir(name) < 0)
{ {
char const *e = strerror(err); error(_("%s: Cannot create directory %s: %s"),
progname, name, strerror(err));
warning(_("%s: Can't create directory" exit(EXIT_FAILURE);
" %s: %s"),
progname, name, e);
free(name);
return false;
} }
} }
*cp = '/'; if (cp)
*cp++ = '/';
} }
free(name); free(name);
return true;
} }