summaryrefslogtreecommitdiff
path: root/src/timezone/zic.c
diff options
context:
space:
mode:
authorPavan Deolasee2017-06-14 05:42:18 +0000
committerPavan Deolasee2017-06-14 05:42:18 +0000
commit15dd5274c323fb93e4e3ea9ad2185aaaec10f79c (patch)
tree9dafb4c7f735d9429ea461dc792933af87493c33 /src/timezone/zic.c
parentdfbb88e3bbb526dcb204b456b9e5cfd9d10d0d0a (diff)
parentd5cb3bab564e0927ffac7c8729eacf181a12dd40 (diff)
Merge from PG master upto d5cb3bab564e0927ffac7c8729eacf181a12dd40
This is the result of the "git merge remotes/PGSQL/master" upto the said commit point. We have done some basic analysis, fixed compilation problems etc, but bulk of the logical problems in conflict resolution etc will be handled by subsequent commits.
Diffstat (limited to 'src/timezone/zic.c')
-rw-r--r--src/timezone/zic.c712
1 files changed, 442 insertions, 270 deletions
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index b546a17372..5fa0a81262 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -8,9 +8,10 @@
#include "postgres_fe.h"
-#include <locale.h>
+#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
+#include <unistd.h>
#include "pg_getopt.h"
@@ -35,11 +36,24 @@ typedef int64 zic_t;
#define MKDIR_UMASK 0755
#endif
#endif
+#ifndef AT_SYMLINK_FOLLOW
+#define linkat(fromdir, from, todir, to, flag) \
+ (itssymlink(from) ? (errno = ENOTSUP, -1) : link(from, to))
+#endif
+
+/* The maximum ptrdiff_t value, for pre-C99 platforms. */
+#ifndef PTRDIFF_MAX
+static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
+#endif
+
+/* The type and printf format for line numbers. */
+typedef int lineno_t;
+#define PRIdLINENO "d"
struct rule
{
const char *r_filename;
- int r_linenum;
+ lineno_t r_linenum;
const char *r_name;
zic_t r_loyear; /* for example, 1986 */
@@ -61,7 +75,7 @@ struct rule
zic_t r_stdoff; /* offset from standard time */
const char *r_abbrvar; /* variable part of abbreviation */
- int r_todo; /* a rule to do (used in outzone) */
+ bool r_todo; /* a rule to do (used in outzone) */
zic_t r_temp; /* used in outzone */
};
@@ -76,7 +90,7 @@ struct rule
struct zone
{
const char *z_filename;
- int z_linenum;
+ lineno_t z_linenum;
const char *z_name;
zic_t z_gmtoff;
@@ -87,7 +101,7 @@ struct zone
zic_t z_stdoff;
struct rule *z_rules;
- int z_nrules;
+ ptrdiff_t z_nrules;
struct rule z_untilrule;
zic_t z_untiltime;
@@ -105,7 +119,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);
@@ -116,26 +130,36 @@ static void inrule(char **fields, int nfields);
static bool inzcont(char **fields, int nfields);
static bool inzone(char **fields, int nfields);
static bool inzsub(char **, int, bool);
-static int itsdir(const char *name);
+static bool itsdir(char const *);
+static bool itssymlink(char const *);
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);
+static void outzone(const struct zone * zp, ptrdiff_t ntzones);
static zic_t rpytime(const struct rule * rp, zic_t wantedy);
static void rulesub(struct rule * rp,
const char *loyearp, const char *hiyearp,
const char *typep, const char *monthp,
const char *dayp, const char *timep);
static zic_t tadd(zic_t t1, zic_t t2);
-static bool yearistype(int year, const char *type);
+static bool yearistype(zic_t year, const char *type);
/* Bound on length of what %z can expand to. */
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;
@@ -144,7 +168,7 @@ static int leapcnt;
static bool leapseen;
static zic_t leapminyear;
static zic_t leapmaxyear;
-static int linenum;
+static lineno_t linenum;
static int max_abbrvar_len = PERCENT_Z_LEN_BOUND;
static int max_format_len;
static zic_t max_year;
@@ -153,10 +177,10 @@ static bool noise;
static bool print_abbrevs;
static zic_t print_cutoff;
static const char *rfilename;
-static int rlinenum;
+static lineno_t rlinenum;
static const char *progname;
-static int timecnt;
-static int timecnt_alloc;
+static ptrdiff_t timecnt;
+static ptrdiff_t timecnt_alloc;
static int typecnt;
/*
@@ -241,24 +265,24 @@ static int typecnt;
#define YR_ONLY 2
static struct rule *rules;
-static int nrules; /* number of rules */
-static int nrules_alloc;
+static ptrdiff_t nrules; /* number of rules */
+static ptrdiff_t nrules_alloc;
static struct zone *zones;
-static int nzones; /* number of zones */
-static int nzones_alloc;
+static ptrdiff_t nzones; /* number of zones */
+static ptrdiff_t nzones_alloc;
struct link
{
const char *l_filename;
- int l_linenum;
+ lineno_t l_linenum;
const char *l_from;
const char *l_to;
};
static struct link *links;
-static int nlinks;
-static int nlinks_alloc;
+static ptrdiff_t nlinks;
+static ptrdiff_t nlinks_alloc;
struct lookup
{
@@ -346,6 +370,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];
@@ -404,17 +429,17 @@ ecpyalloc(char const * str)
}
static void *
-growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
+growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
{
if (nitems < *nitems_alloc)
return ptr;
else
{
- int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX;
+ ptrdiff_t amax = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
if ((amax - 1) / 3 * 2 < *nitems_alloc)
- memory_exhausted(_("int overflow"));
- *nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1;
+ memory_exhausted(_("integer overflow"));
+ *nitems_alloc += (*nitems_alloc >> 1) + 1;
return erealloc(ptr, size_product(*nitems_alloc, itemsize));
}
}
@@ -424,7 +449,7 @@ growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
*/
static void
-eats(const char *name, int num, const char *rname, int rnum)
+eats(char const * name, lineno_t num, char const * rname, lineno_t rnum)
{
filename = name;
linenum = num;
@@ -433,7 +458,7 @@ eats(const char *name, int num, const char *rname, int rnum)
}
static void
-eat(const char *name, int num)
+eat(char const * name, lineno_t num)
{
eats(name, num, NULL, -1);
}
@@ -446,10 +471,10 @@ verror(const char *string, va_list args)
* "*" -v on BSD systems.
*/
if (filename)
- fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
+ fprintf(stderr, _("\"%s\", line %" PRIdLINENO ": "), filename, linenum);
vfprintf(stderr, string, args);
if (rfilename != NULL)
- fprintf(stderr, _(" (rule from \"%s\", line %d)"),
+ fprintf(stderr, _(" (rule from \"%s\", line %" PRIdLINENO ")"),
rfilename, rlinenum);
fprintf(stderr, "\n");
}
@@ -478,17 +503,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 +528,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;
@@ -516,9 +565,10 @@ static const char *yitcommand;
int
main(int argc, char *argv[])
{
- int i;
- int j;
- int c;
+ int c,
+ k;
+ ptrdiff_t i,
+ j;
#ifndef WIN32
umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
@@ -530,14 +580,14 @@ main(int argc, char *argv[])
_("wild compilation-time specification of zic_t"));
return EXIT_FAILURE;
}
- for (i = 1; i < argc; ++i)
- if (strcmp(argv[i], "--version") == 0)
+ for (k = 1; k < argc; k++)
+ if (strcmp(argv[k], "--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)
+ else if (strcmp(argv[k], "--help") == 0)
{
usage(stdout, EXIT_SUCCESS);
}
@@ -625,11 +675,12 @@ main(int argc, char *argv[])
adjleap();
}
- for (i = optind; i < argc; ++i)
- infile(argv[i]);
+ for (k = optind; k < argc; k++)
+ infile(argv[k]);
if (errors)
return EXIT_FAILURE;
associate();
+ change_directory(directory);
for (i = 0; i < nzones; i = j)
{
/*
@@ -646,7 +697,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 +707,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;
@@ -675,7 +726,7 @@ componentcheck(char const * name, char const * component,
enum
{
component_len_max = 14};
- size_t component_len = component_end - component;
+ ptrdiff_t component_len = component_end - component;
if (component_len == 0)
{
@@ -693,8 +744,10 @@ componentcheck(char const * name, char const * component,
if (0 < component_len && component_len <= 2
&& component[0] == '.' && component_end[-1] == '.')
{
+ int len = component_len;
+
error(_("file name '%s' contains '%.*s' component"),
- name, (int) component_len, component);
+ name, len, component);
return false;
}
if (noise)
@@ -751,131 +804,160 @@ namecheck(const char *name)
return componentcheck(name, component, cp);
}
+/*
+ * Create symlink contents suitable for symlinking FROM to TO, as a
+ * freshly allocated string. FROM should be a relative file name, and
+ * is relative to the global variable DIRECTORY. TO can be either
+ * relative or absolute.
+ */
+#ifdef HAVE_SYMLINK
static char *
-relname(char const * dir, char const * base)
+relname(char const * from, char const * to)
+{
+ size_t i,
+ taillen,
+ dotdotetcsize;
+ size_t dir_len = 0,
+ dotdots = 0,
+ linksize = SIZE_MAX;
+ char const *f = from;
+ char *result = NULL;
+
+ if (*to == '/')
+ {
+ /* Make F absolute too. */
+ size_t len = strlen(directory);
+ bool needslash = len && directory[len - 1] != '/';
+
+ linksize = len + needslash + strlen(from) + 1;
+ f = result = emalloc(linksize);
+ strcpy(result, directory);
+ result[len] = '/';
+ strcpy(result + len + needslash, from);
+ }
+ for (i = 0; f[i] && f[i] == to[i]; i++)
+ if (f[i] == '/')
+ dir_len = i + 1;
+ for (; to[i]; i++)
+ dotdots += to[i] == '/' && to[i - 1] != '/';
+ taillen = strlen(f + dir_len);
+ dotdotetcsize = 3 * dotdots + taillen + 1;
+ if (dotdotetcsize <= linksize)
+ {
+ if (!result)
+ result = emalloc(dotdotetcsize);
+ for (i = 0; i < dotdots; i++)
+ memcpy(result + 3 * i, "../", 3);
+ memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
+ }
+ return result;
+}
+#endif /* HAVE_SYMLINK */
+
+/* Hard link FROM to TO, following any symbolic links.
+ Return 0 if successful, an error number otherwise. */
+static int
+hardlinkerr(char const * from, char const * to)
{
- 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);
+ int r = linkat(AT_FDCWD, from, AT_FDCWD, to, AT_SYMLINK_FOLLOW);
- result[dir_len] = '/';
- strcpy(result + dir_len + needs_slash, base);
- return memcpy(result, dir, dir_len);
- }
+ return r == 0 ? 0 : errno;
}
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);
- if (fromisdir)
+ if (itsdir(fromfield))
{
- char const *e = strerror(fromisdir < 0 ? errno : EPERM);
+ fprintf(stderr, _("%s: link from %s/%s failed: %s\n"),
+ progname, directory, fromfield, strerror(EPERM));
+ exit(EXIT_FAILURE);
+ }
+ if (staysymlink)
+ staysymlink = itssymlink(tofield);
+ if (remove(tofield) == 0)
+ todirs_made = true;
+ else if (errno != ENOENT)
+ {
+ char const *e = strerror(errno);
- fprintf(stderr, _("%s: link from %s failed: %s"),
- progname, fromname, e);
+ fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
+ progname, directory, tofield, e);
exit(EXIT_FAILURE);
}
- if (link(fromname, toname) != 0)
+ link_errno = staysymlink ? ENOTSUP : hardlinkerr(fromfield, tofield);
+ if (link_errno == ENOENT && !todirs_made)
+ {
+ mkdirs(tofield, true);
+ todirs_made = true;
+ link_errno = hardlinkerr(fromfield, tofield);
+ }
+ if (link_errno != 0)
{
- int link_errno = errno;
- bool retry_if_link_supported = false;
+#ifdef HAVE_SYMLINK
+ bool absolute = *fromfield == '/';
+ char *linkalloc = absolute ? NULL : relname(fromfield, tofield);
+ char const *contents = absolute ? fromfield : linkalloc;
+ int symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
- if (link_errno == ENOENT || link_errno == ENOTSUP)
+ if (symlink_errno == ENOENT && !todirs_made)
{
- if (!mkdirs(toname))
- exit(EXIT_FAILURE);
- retry_if_link_supported = true;
+ mkdirs(tofield, true);
+ symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
}
- 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;
- if (link_errno != 0)
+ free(linkalloc);
+ if (symlink_errno == 0)
{
-#ifdef HAVE_SYMLINK
- 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
+ if (link_errno != ENOTSUP)
+ warning(_("symbolic link used because hard link failed: %s"),
+ strerror(link_errno));
+ }
+ else
#endif /* HAVE_SYMLINK */
- {
- FILE *fp,
- *tp;
- int c;
+ {
+ FILE *fp,
+ *tp;
+ int c;
- fp = fopen(fromname, "rb");
- if (!fp)
- {
- const char *e = strerror(errno);
+ fp = fopen(fromfield, "rb");
+ if (!fp)
+ {
+ char const *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 read %s/%s: %s\n"),
+ progname, directory, fromfield, e);
+ exit(EXIT_FAILURE);
+ }
+ tp = fopen(tofield, "wb");
+ if (!tp)
+ {
+ char const *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));
+ 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));
+#ifdef HAVE_SYMLINK
+ else if (symlink_errno != ENOTSUP)
+ warning(_("copy used because symbolic link failed: %s"),
+ strerror(symlink_errno));
+#endif
}
}
- free(fromname);
- free(toname);
}
#define TIME_T_BITS_IN_FILE 64
@@ -888,10 +970,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,28 +991,58 @@ 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. */
-static int
+ 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 true if NAME is a directory. */
+static bool
itsdir(char const * name)
{
struct stat st;
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;
+ size_t n = strlen(name);
+ char *nameslashdot = emalloc(n + 3);
+ bool dir;
+ memcpy(nameslashdot, name, n);
+ strcpy(&nameslashdot[n], &"/."[!(n && name[n - 1] != '/')]);
+ dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW;
free(nameslashdot);
return dir;
}
- return -1;
+ return false;
+}
+
+/* Return true if NAME is a symbolic link. */
+static bool
+itssymlink(char const * name)
+{
+#ifdef HAVE_SYMLINK
+ char c;
+
+ return 0 <= readlink(name, &c, 1);
+#else
+ return false;
+#endif
}
/*
@@ -957,10 +1065,10 @@ associate(void)
{
struct zone *zp;
struct rule *rp;
- int base,
+ ptrdiff_t i,
+ j,
+ base,
out;
- int i,
- j;
if (nrules != 0)
{
@@ -1047,7 +1155,7 @@ infile(const char *name)
const struct lookup *lp;
int nfields;
bool wantcont;
- int num;
+ lineno_t num;
char buf[BUFSIZ];
if (strcmp(name, "-") == 0)
@@ -1098,7 +1206,7 @@ infile(const char *name)
if (lp == NULL)
error(_("input line of unknown type"));
else
- switch ((int) (lp->l_value))
+ switch (lp->l_value)
{
case LC_RULE:
inrule(fields, nfields);
@@ -1129,7 +1237,7 @@ infile(const char *name)
}
free(fields);
}
- close_file(fp, filename);
+ close_file(fp, NULL, filename);
if (wantcont)
error(_("expected continuation line not found"));
}
@@ -1225,7 +1333,7 @@ inrule(char **fields, int nfields)
static bool
inzone(char **fields, int nfields)
{
- int i;
+ ptrdiff_t i;
if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS)
{
@@ -1250,8 +1358,8 @@ inzone(char **fields, int nfields)
if (zones[i].z_name != NULL &&
strcmp(zones[i].z_name, fields[ZF_NAME]) == 0)
{
- error(
- _("duplicate zone name %s (file \"%s\", line %d)"),
+ error(_("duplicate zone name %s"
+ " (file \"%s\", line %" PRIdLINENO ")"),
fields[ZF_NAME],
zones[i].z_filename,
zones[i].z_linenum);
@@ -1313,7 +1421,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], '/'))
@@ -1375,7 +1483,7 @@ inleap(char **fields, int nfields)
{
const char *cp;
const struct lookup *lp;
- int i,
+ zic_t i,
j;
/* PG: make year be int not zic_t to avoid sscanf portability issues */
@@ -1491,7 +1599,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;
@@ -1582,7 +1690,7 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
lp = byword(cp, begin_years);
rp->r_lowasnum = lp == NULL;
if (!rp->r_lowasnum)
- switch ((int) lp->l_value)
+ switch (lp->l_value)
{
case YR_MINIMUM:
rp->r_loyear = ZIC_MIN;
@@ -1607,7 +1715,7 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
lp = byword(cp, end_years);
rp->r_hiwasnum = lp == NULL;
if (!rp->r_hiwasnum)
- switch ((int) lp->l_value)
+ switch (lp->l_value)
{
case YR_MINIMUM:
rp->r_hiyear = ZIC_MIN;
@@ -1757,18 +1865,21 @@ static void
writezone(const char *const name, const char *const string, char version)
{
FILE *fp;
- int i,
+ ptrdiff_t i,
j;
int leapcnt32,
leapi32;
- int timecnt32,
+ ptrdiff_t 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;
+ ptrdiff_t nats = timecnt + WORK_AROUND_QTBUG_53071;
+ zic_t *ats = emalloc(size_product(nats, sizeof *ats + 1));
+ void *typesptr = ats + nats;
unsigned char *types = typesptr;
/*
@@ -1781,12 +1892,12 @@ writezone(const char *const name, const char *const string, char version)
* Optimize.
*/
{
- int fromi;
- int toi;
+ ptrdiff_t fromi,
+ toi;
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,15 +1910,24 @@ 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;
}
+
if (noise && timecnt > 1200)
- warning(_("pre-2014 clients may mishandle"
- " more than 1200 transition times"));
+ {
+ if (timecnt > TZ_MAX_TIMES)
+ warning(_("reference clients mishandle"
+ " more than %d transition times"),
+ TZ_MAX_TIMES);
+ else
+ warning(_("pre-2014 clients may mishandle"
+ " more than 1200 transition times"));
+ }
/*
* Transfer.
@@ -1819,6 +1939,20 @@ writezone(const char *const name, const char *const string, char version)
}
/*
+ * 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.
*/
for (i = 0; i < timecnt; ++i)
@@ -1862,51 +1996,59 @@ 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);
}
}
for (pass = 1; pass <= 2; ++pass)
{
- int thistimei,
- thistimecnt;
+ ptrdiff_t thistimei,
+ thistimecnt,
+ thistimelim;
int thisleapi,
- thisleapcnt;
- int thistimelim,
+ thisleapcnt,
thisleaplim;
int writetype[TZ_MAX_TYPES];
int typemap[TZ_MAX_TYPES];
int thistypecnt;
char thischars[TZ_MAX_CHARS];
- char thischarcnt;
+ int thischarcnt;
+ bool toomanytimes;
int indmap[TZ_MAX_CHARS];
if (pass == 1)
{
thistimei = timei32;
thistimecnt = timecnt32;
+ toomanytimes = thistimecnt >> 31 >> 1 != 0;
thisleapi = leapi32;
thisleapcnt = leapcnt32;
}
@@ -1914,9 +2056,12 @@ writezone(const char *const name, const char *const string, char version)
{
thistimei = 0;
thistimecnt = timecnt;
+ toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
thisleapi = 0;
thisleapcnt = leapcnt;
}
+ if (toomanytimes)
+ error(_("too many transition times"));
thistimelim = thistimei + thistimecnt;
thisleaplim = thisleapi + thisleapcnt;
for (i = 0; i < typecnt; ++i)
@@ -2017,8 +2162,7 @@ writezone(const char *const name, const char *const string, char version)
break;
if (j == thischarcnt)
{
- strcpy(&thischars[(int) thischarcnt],
- thisabbr);
+ strcpy(&thischars[thischarcnt], thisabbr);
thischarcnt += strlen(thisabbr) + 1;
}
indmap[abbrinds[i]] = j;
@@ -2130,9 +2274,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 *
@@ -2366,13 +2509,13 @@ enum
YEAR_BY_YEAR_ZONE = 1};
static int
-stringzone(char *result, const struct zone * const zpfirst, const int zonecount)
+stringzone(char *result, struct zone const * zpfirst, ptrdiff_t zonecount)
{
const struct zone *zp;
struct rule *rp;
struct rule *stdrp;
struct rule *dstrp;
- int i;
+ ptrdiff_t i;
const char *abbrvar;
int compat = 0;
int c;
@@ -2501,11 +2644,11 @@ stringzone(char *result, const struct zone * const zpfirst, const int zonecount)
}
static void
-outzone(const struct zone * zpfirst, int zonecount)
+outzone(const struct zone * zpfirst, ptrdiff_t zonecount)
{
const struct zone *zp;
struct rule *rp;
- int i,
+ ptrdiff_t i,
j;
bool usestart,
useuntil;
@@ -2527,6 +2670,10 @@ outzone(const struct zone * zpfirst, int zonecount)
int compat;
bool do_extend;
char version;
+ ptrdiff_t lastatmax = -1;
+ zic_t one = 1;
+ zic_t y2038_boundary = one << 31;
+ zic_t max_year0;
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -2636,12 +2783,13 @@ outzone(const struct zone * zpfirst, int zonecount)
}
/*
- * For the benefit of older systems, generate data from 1900 through 2037.
+ * For the benefit of older systems, generate data from 1900 through 2038.
*/
if (min_year > 1900)
min_year = 1900;
- if (max_year < 2037)
- max_year = 2037;
+ max_year0 = max_year;
+ if (max_year < 2038)
+ max_year = 2038;
for (i = 0; i < zonecount; ++i)
{
/*
@@ -2649,9 +2797,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 +2818,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)
@@ -2691,11 +2839,16 @@ outzone(const struct zone * zpfirst, int zonecount)
year <= rp->r_hiyear &&
yearistype(year, rp->r_yrtype);
if (rp->r_todo)
+ {
rp->r_temp = rpytime(rp, year);
+ rp->r_todo
+ = (rp->r_temp < y2038_boundary
+ || year <= max_year0);
+ }
}
for (;;)
{
- int k;
+ ptrdiff_t k;
zic_t jtime,
ktime = 0;
zic_t offset;
@@ -2792,6 +2945,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 +2984,8 @@ outzone(const struct zone * zpfirst, int zonecount)
starttime = tadd(starttime, -gmtoff);
}
}
+ if (0 <= lastatmax)
+ attypes[lastatmax].dontmerge = true;
if (do_extend)
{
/*
@@ -2850,22 +3009,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 +3022,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 +3039,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;
}
@@ -2997,30 +3143,52 @@ adjleap(void)
}
}
+static char *
+shellquote(char *b, char const * s)
+{
+ *b++ = '\'';
+ while (*s)
+ {
+ if (*s == '\'')
+ *b++ = '\'', *b++ = '\\', *b++ = '\'';
+ *b++ = *s++;
+ }
+ *b++ = '\'';
+ return b;
+}
+
static bool
-yearistype(int year, const char *type)
+yearistype(zic_t year, const char *type)
{
- static char *buf;
+ char *buf;
+ char *b;
int result;
if (type == NULL || *type == '\0')
return true;
- buf = erealloc(buf, 132 + strlen(yitcommand) + strlen(type));
- sprintf(buf, "%s %d %s", yitcommand, year, type);
+ buf = emalloc(1 + 4 * strlen(yitcommand) + 2
+ + INT_STRLEN_MAXIMUM(zic_t) +2 + 4 * strlen(type) + 2);
+ b = shellquote(buf, yitcommand);
+ *b++ = ' ';
+ b += sprintf(b, INT64_FORMAT, year);
+ *b++ = ' ';
+ b = shellquote(b, type);
+ *b = '\0';
result = system(buf);
if (WIFEXITED(result))
- switch (WEXITSTATUS(result))
+ {
+ int status = WEXITSTATUS(result);
+
+ if (status <= 1)
{
- case 0:
- return true;
- case 1:
- return false;
+ free(buf);
+ return status == 0;
}
+ }
error(_("Wild result from command execution"));
fprintf(stderr, _("%s: command was '%s', result was %d\n"),
progname, buf, result);
- for (;;)
- exit(EXIT_FAILURE);
+ exit(EXIT_FAILURE);
}
/* Is A a space character in the C locale? */
@@ -3254,7 +3422,7 @@ getfields(char *cp)
else
{
error(_("Odd number of quotation marks"));
- exit(1);
+ exit(EXIT_FAILURE);
}
} while (*cp && *cp != '#' && !is_space(*cp));
if (is_space(*cp))
@@ -3441,53 +3609,57 @@ 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.
+ Do not consider an existing non-directory to be 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';
+
+ /* 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++;
- /*
- * DOS drive specifier?
- */
- if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0')
- {
- *cp = '/';
- continue;
- }
-#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
* 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)
{
+ /*
+ * For speed, skip itsdir if errno == EEXIST. Since mkdirs is
+ * called only after open fails with ENOENT on a subfile, EEXIST
+ * implies itsdir here.
+ */
int err = errno;
- if (itsdir(name) <= 0)
+ if (err != EEXIST && !itsdir(name))
{
- 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;
}