aboutsummaryrefslogtreecommitdiff
path: root/src/timezone/zic.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/timezone/zic.c')
-rw-r--r--src/timezone/zic.c142
1 files changed, 98 insertions, 44 deletions
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index 27c841be9e7..db119265c35 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -11,7 +11,6 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
-#include <unistd.h>
#include "pg_getopt.h"
@@ -46,9 +45,12 @@ typedef int64 zic_t;
static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
#endif
-/* The type and printf format for line numbers. */
+/*
+ * The type for line numbers. In Postgres, use %d to format them; upstream
+ * uses PRIdMAX but we prefer not to rely on that, not least because it
+ * results in platform-dependent strings to be translated.
+ */
typedef int lineno_t;
-#define PRIdLINENO "d"
struct rule
{
@@ -293,10 +295,13 @@ struct lookup
static struct lookup const *byword(const char *string,
const struct lookup *lp);
-static struct lookup const line_codes[] = {
+static struct lookup const zi_line_codes[] = {
{"Rule", LC_RULE},
{"Zone", LC_ZONE},
{"Link", LC_LINK},
+ {NULL, 0}
+};
+static struct lookup const leap_line_codes[] = {
{"Leap", LC_LEAP},
{NULL, 0}
};
@@ -435,7 +440,8 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
return ptr;
else
{
- ptrdiff_t amax = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
+ ptrdiff_t nitems_max = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
+ ptrdiff_t amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX;
if ((amax - 1) / 3 * 2 < *nitems_alloc)
memory_exhausted(_("integer overflow"));
@@ -471,10 +477,10 @@ verror(const char *string, va_list args)
* "*" -v on BSD systems.
*/
if (filename)
- fprintf(stderr, _("\"%s\", line %" PRIdLINENO ": "), filename, linenum);
+ fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
vfprintf(stderr, string, args);
if (rfilename != NULL)
- fprintf(stderr, _(" (rule from \"%s\", line %" PRIdLINENO ")"),
+ fprintf(stderr, _(" (rule from \"%s\", line %d)"),
rfilename, rlinenum);
fprintf(stderr, "\n");
}
@@ -563,7 +569,7 @@ static const char *leapsec;
static const char *yitcommand;
int
-main(int argc, char *argv[])
+main(int argc, char **argv)
{
int c,
k;
@@ -572,7 +578,7 @@ main(int argc, char *argv[])
#ifndef WIN32
umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
-#endif /* !WIN32 */
+#endif
progname = argv[0];
if (TYPE_BIT(zic_t) <64)
{
@@ -631,7 +637,10 @@ main(int argc, char *argv[])
break;
case 'y':
if (yitcommand == NULL)
+ {
+ warning(_("-y is obsolescent"));
yitcommand = strdup(optarg);
+ }
else
{
fprintf(stderr,
@@ -1202,6 +1211,9 @@ infile(const char *name)
wantcont = inzcont(fields, nfields);
else
{
+ struct lookup const *line_codes
+ = name == leapsec ? leap_line_codes : zi_line_codes;
+
lp = byword(fields[0], line_codes);
if (lp == NULL)
error(_("input line of unknown type"));
@@ -1220,12 +1232,7 @@ infile(const char *name)
wantcont = false;
break;
case LC_LEAP:
- if (name != leapsec)
- warning(_("%s: Leap line in non leap"
- " seconds file %s"),
- progname, name);
- else
- inleap(fields, nfields);
+ inleap(fields, nfields);
wantcont = false;
break;
default: /* "cannot happen" */
@@ -1359,7 +1366,7 @@ inzone(char **fields, int nfields)
strcmp(zones[i].z_name, fields[ZF_NAME]) == 0)
{
error(_("duplicate zone name %s"
- " (file \"%s\", line %" PRIdLINENO ")"),
+ " (file \"%s\", line %d)"),
fields[ZF_NAME],
zones[i].z_filename,
zones[i].z_linenum);
@@ -1573,21 +1580,11 @@ inleap(char **fields, int nfields)
positive = false;
count = 1;
}
- else if (strcmp(cp, "--") == 0)
- {
- positive = false;
- count = 2;
- }
else if (strcmp(cp, "+") == 0)
{
positive = true;
count = 1;
}
- else if (strcmp(cp, "++") == 0)
- {
- positive = true;
- count = 2;
- }
else
{
error(_("illegal CORRECTION field on Leap line"));
@@ -1599,9 +1596,9 @@ inleap(char **fields, int nfields)
return;
}
t = tadd(t, tod);
- if (t < early_time)
+ if (t < 0)
{
- error(_("leap second precedes Big Bang"));
+ error(_("leap second precedes Epoch"));
return;
}
leapadd(t, positive, lp->l_value, count);
@@ -1753,11 +1750,14 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
error(_("typed single year"));
return;
}
+ warning(_("year type \"%s\" is obsolete; use \"-\" instead"),
+ typep);
rp->r_yrtype = ecpyalloc(typep);
}
/*
- * Day work. Accept things such as: 1 last-Sunday Sun<=20 Sun>=7
+ * Day work. Accept things such as: 1 lastSunday last-Sunday
+ * (undocumented; warn about this) Sun<=20 Sun>=7
*/
dp = ecpyalloc(dayp);
if ((lp = byword(dp, lasts)) != NULL)
@@ -2850,9 +2850,10 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
{
ptrdiff_t k;
zic_t jtime,
- ktime = 0;
+ ktime;
zic_t offset;
+ INITIALIZE(ktime);
if (useuntil)
{
/*
@@ -2929,7 +2930,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
continue;
}
if (*startbuf == '\0' &&
- startoff == oadd(zp->z_gmtoff, stdoff))
+ startoff == oadd(zp->z_gmtoff,
+ stdoff))
{
doabbr(startbuf,
zp,
@@ -3104,14 +3106,7 @@ leapadd(zic_t t, bool positive, int rolling, int count)
}
for (i = 0; i < leapcnt; ++i)
if (t <= trans[i])
- {
- if (t == trans[i])
- {
- error(_("repeated leap second moment"));
- exit(EXIT_FAILURE);
- }
break;
- }
do
{
for (j = leapcnt; j > i; --j)
@@ -3132,12 +3127,19 @@ adjleap(void)
{
int i;
zic_t last = 0;
+ zic_t prevtrans = 0;
/*
* propagate leap seconds forward
*/
for (i = 0; i < leapcnt; ++i)
{
+ if (trans[i] - prevtrans < 28 * SECSPERDAY)
+ {
+ error(_("Leap seconds too close together"));
+ exit(EXIT_FAILURE);
+ }
+ prevtrans = trans[i];
trans[i] = tadd(trans[i], last);
last = corr[i] += last;
}
@@ -3191,7 +3193,7 @@ yearistype(zic_t year, const char *type)
exit(EXIT_FAILURE);
}
-/* Is A a space character in the C locale? */
+/* Is A a space character in the C locale? */
static bool
is_space(char a)
{
@@ -3362,6 +3364,19 @@ itsabbr(const char *abbr, const char *word)
return true;
}
+/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */
+
+static bool
+ciprefix(char const *abbr, char const *word)
+{
+ do
+ if (!*abbr)
+ return true;
+ while (lowerit(*abbr++) == lowerit(*word++));
+
+ return false;
+}
+
static const struct lookup *
byword(const char *word, const struct lookup *table)
{
@@ -3372,6 +3387,23 @@ byword(const char *word, const struct lookup *table)
return NULL;
/*
+ * If TABLE is LASTS and the word starts with "last" followed by a
+ * non-'-', skip the "last" and look in WDAY_NAMES instead. Warn about any
+ * usage of the undocumented prefix "last-".
+ */
+ if (table == lasts && ciprefix("last", word) && word[4])
+ {
+ if (word[4] == '-')
+ warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
+ word, word + 5);
+ else
+ {
+ word += 4;
+ table = wday_names;
+ }
+ }
+
+ /*
* Look for exact match.
*/
for (lp = table; lp->l_word != NULL; ++lp)
@@ -3383,13 +3415,31 @@ byword(const char *word, const struct lookup *table)
*/
foundlp = NULL;
for (lp = table; lp->l_word != NULL; ++lp)
- if (itsabbr(word, lp->l_word))
+ if (ciprefix(word, lp->l_word))
{
if (foundlp == NULL)
foundlp = lp;
else
return NULL; /* multiple inexact matches */
}
+
+ /* Warn about any backward-compatibility issue with pre-2017c zic. */
+ if (foundlp)
+ {
+ bool pre_2017c_match = false;
+
+ for (lp = table; lp->l_word; lp++)
+ if (itsabbr(word, lp->l_word))
+ {
+ if (pre_2017c_match)
+ {
+ warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
+ break;
+ }
+ pre_2017c_match = true;
+ }
+ }
+
return foundlp;
}
@@ -3621,11 +3671,15 @@ mkdirs(char const *argname, bool ancestors)
cp = name = ecpyalloc(argname);
+ /*
+ * On MS-Windows systems, do not worry about drive letters or backslashes,
+ * as this should suffice in practice. Time zone names do not use drive
+ * letters and backslashes. If the -d option of zic does not name an
+ * already-existing directory, it can use slashes to separate the
+ * already-existing ancestor prefix from the to-be-created subdirectories.
+ */
+
/* 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++;