summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2017-09-22 00:04:21 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2017-09-22 00:04:21 -0400
commit89f02e17a6ac81bbc78ea92f2ead06d8816ee297 (patch)
tree572b182b618f62761cc5c5a9608af5a06248ca0f
parent122289a66b92799885917953907d5b3f3f9e6ac2 (diff)
downloadpostgresql-89f02e17a6ac81bbc78ea92f2ead06d8816ee297.tar.gz
Sync our copy of the timezone library with IANA tzcode master.
This patch absorbs a few unreleased fixes in the IANA code. It corresponds to commit 2d8b944c1cec0808ac4f7a9ee1a463c28f9cd00a in https://github.com/eggert/tz. Non-cosmetic changes include: TZDEFRULESTRING is updated to match current US DST practice, rather than what it was over ten years ago. This only matters for interpretation of POSIX-style zone names (e.g., "EST5EDT"), and only if the timezone database doesn't include either an exact match for the zone name or a "posixrules" entry. The latter should not be true in any current Postgres installation, but this could possibly matter when using --with-system-tzdata. Get rid of a nonportable use of "++var" on a bool var. This is part of a larger fix that eliminates some vestigial support for consecutive leap seconds, and adds checks to the "zic" compiler that the data files do not specify that. Remove a couple of ancient compatibility hacks. The IANA crew think these are obsolete, and I tend to agree. But perhaps our buildfarm will think different. Back-patch to all supported branches, in line with our policy that all branches should be using current IANA code. Before v10, this includes application of current pgindent rules, to avoid whitespace problems in future back-patches. Discussion: https://postgr.es/m/E1dsWhf-0000pT-F9@gemulon.postgresql.org
-rw-r--r--src/timezone/localtime.c138
-rw-r--r--src/timezone/private.h27
-rw-r--r--src/timezone/strftime.c50
-rw-r--r--src/timezone/tzfile.h8
-rw-r--r--src/timezone/zic.c217
5 files changed, 236 insertions, 204 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index 04105b2c96..d946e882aa 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -26,15 +26,15 @@
#ifndef WILDABBR
/*
* 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).
- * 2. They might reference tzname[1] before calling tzset (explicitly
+ * 2. They might reference tzname[1] before calling tzset (explicitly
* or implicitly).
- * 3. They might reference tzname[1] after setting to a time zone
+ * 3. They might reference tzname[1] after setting to a time zone
* in which Daylight Saving Time is never observed.
- * 4. They might reference tzname[0] after setting to a time zone
+ * 4. They might reference tzname[0] after setting to a time zone
* in which Standard Time is never observed.
- * 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;
* 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
@@ -44,18 +44,14 @@
* that tzname[0] has the "normal" length of three characters).
*/
#define WILDABBR " "
-#endif /* !defined WILDABBR */
+#endif /* !defined WILDABBR */
static const char wildabbr[] = WILDABBR;
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));
-
/*
- * We cache the result of trying to load the TZDEFRULES zone here.
+ * PG: We cache the result of trying to load the TZDEFRULES zone here.
* tzdefrules_loaded is 0 if not tried yet, +1 if good, -1 if failed.
*/
static struct state tzdefrules_s;
@@ -63,12 +59,12 @@ static int tzdefrules_loaded = 0;
/*
* 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.
+ * Default to US rules as of 2017-05-07.
* POSIX 1003.1 section 8.1.1 says that the default DST rules are
* implementation dependent; for historical reasons, US rules are a
* common default.
*/
-#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
+#define TZDEFRULESTRING ",M3.2.0,M11.1.0"
/* structs ttinfo, lsinfo, state have been moved to pgtz.h */
@@ -112,7 +108,7 @@ 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)
+init_ttinfo(struct ttinfo *s, int32 gmtoff, bool isdst, int abbrind)
{
s->tt_gmtoff = gmtoff;
s->tt_isdst = isdst;
@@ -189,16 +185,14 @@ union input_buffer
/* The entire buffer. */
char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state)
- + 4 * TZ_MAX_TIMES];
+ + 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
+ struct file_analysis
{
/* The input buffer. */
union input_buffer u;
@@ -206,6 +200,8 @@ union local_storage
/* A temporary state used for parsing a TZ string in the file. */
struct state st;
} u;
+
+ /* We don't need the "fullname" member */
};
/* Load tz data from the file named NAME into *SP. Read extended
@@ -215,8 +211,8 @@ union local_storage
* 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)
+tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
+ union local_storage *lsp)
{
int i;
int fid;
@@ -255,6 +251,8 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
{
int32 ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
int32 ttisgmtcnt = detzcode(up->tzhead.tzh_ttisgmtcnt);
+ int64 prevtr = 0;
+ int32 prevcorr = 0;
int32 leapcnt = detzcode(up->tzhead.tzh_leapcnt);
int32 timecnt = detzcode(up->tzhead.tzh_timecnt);
int32 typecnt = detzcode(up->tzhead.tzh_typecnt);
@@ -270,7 +268,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
return EINVAL;
if (nread
< (tzheadsize /* struct tzhead */
- + timecnt * stored /* ats */
+ + timecnt * stored /* ats */
+ timecnt /* types */
+ typecnt * 6 /* ttinfos */
+ charcnt /* chars */
@@ -285,8 +283,8 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
/*
* Read transitions, discarding those out of pg_time_t range. But
- * pretend the last transition before time_t_min occurred at
- * time_t_min.
+ * pretend the last transition before TIME_T_MIN occurred at
+ * TIME_T_MIN.
*/
timecnt = 0;
for (i = 0; i < sp->timecnt; ++i)
@@ -294,12 +292,12 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
int64 at
= stored == 4 ? detzcode(p) : detzcode64(p);
- sp->types[i] = at <= time_t_max;
+ 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);
+ = ((TYPE_SIGNED(pg_time_t) ? at < TIME_T_MIN : at < 0)
+ ? TIME_T_MIN : at);
if (timecnt && attime <= sp->ats[timecnt - 1])
{
@@ -354,20 +352,22 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
int32 corr = detzcode(p + stored);
p += stored + 4;
- if (tr <= time_t_max)
+ /* Leap seconds cannot occur before the Epoch. */
+ if (tr < 0)
+ return EINVAL;
+ if (tr <= TIME_T_MAX)
{
- pg_time_t trans
- = ((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;
+ /*
+ * Leap seconds cannot occur more than once per UTC month, and
+ * UTC months are at least 28 days long (minus 1 second for a
+ * negative leap second). Each leap second's correction must
+ * differ from the previous one's by 1 second.
+ */
+ if (tr - prevtr < 28 * SECSPERDAY - 1
+ || (corr != prevcorr - 1 && corr != prevcorr + 1))
+ return EINVAL;
+ sp->lsis[leapcnt].ls_trans = prevtr = tr;
+ sp->lsis[leapcnt].ls_corr = prevcorr = corr;
leapcnt++;
}
}
@@ -508,7 +508,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
}
/*
- * If type 0 is is unused in transitions, it's the type to use for early
+ * If type 0 is unused in transitions, it's the type to use for early
* times.
*/
for (i = 0; i < sp->timecnt; ++i)
@@ -553,7 +553,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend,
* given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
*/
int
-tzload(const char *name, char *canonname, struct state * sp, bool doextend)
+tzload(const char *name, char *canonname, struct state *sp, bool doextend)
{
union local_storage *lsp = malloc(sizeof *lsp);
@@ -569,7 +569,7 @@ tzload(const char *name, char *canonname, struct state * sp, bool doextend)
}
static bool
-typesequiv(const struct state * sp, int a, int b)
+typesequiv(const struct state *sp, int a, int b)
{
bool result;
@@ -737,7 +737,7 @@ getoffset(const char *strp, int32 *offsetp)
* Otherwise, return a pointer to the first character not part of the rule.
*/
static const char *
-getrule(const char *strp, struct rule * rulep)
+getrule(const char *strp, struct rule *rulep)
{
if (*strp == 'J')
{
@@ -788,7 +788,7 @@ getrule(const char *strp, struct rule * rulep)
strp = getoffset(strp, &rulep->r_time);
}
else
- rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
return strp;
}
@@ -797,7 +797,7 @@ getrule(const char *strp, struct rule * rulep)
* effect, calculate the year-relative time that rule takes effect.
*/
static int32
-transtime(int year, const struct rule * rulep,
+transtime(int year, const struct rule *rulep,
int32 offset)
{
bool leapyear;
@@ -894,7 +894,7 @@ transtime(int year, const struct rule * rulep,
* Returns true on success, false on failure.
*/
bool
-tzparse(const char *name, struct state * sp, bool lastditch)
+tzparse(const char *name, struct state *sp, bool lastditch)
{
const char *stdname;
const char *dstname = NULL;
@@ -921,7 +921,7 @@ tzparse(const char *name, struct state * sp, bool lastditch)
stdlen = (sizeof sp->chars) - 1;
charcnt = stdlen + 1;
stdoffset = 0;
- sp->goback = sp->goahead = false; /* simulate failed tzload() */
+ sp->goback = sp->goahead = false; /* simulate failed tzload() */
load_ok = false;
}
else
@@ -1217,7 +1217,7 @@ tzparse(const char *name, struct state * sp, bool lastditch)
}
static void
-gmtload(struct state * sp)
+gmtload(struct state *sp)
{
if (tzload(gmt, NULL, sp, true) != 0)
tzparse(gmt, sp, true);
@@ -1231,8 +1231,8 @@ gmtload(struct state * sp)
* but it *is* desirable.)
*/
static struct pg_tm *
-localsub(struct state const * sp, pg_time_t const * timep,
- struct pg_tm * tmp)
+localsub(struct state const *sp, pg_time_t const *timep,
+ struct pg_tm *tmp)
{
const struct ttinfo *ttisp;
int i;
@@ -1323,7 +1323,7 @@ pg_localtime(const pg_time_t *timep, const pg_tz *tz)
* Except we have a private "struct state" for GMT, so no sp is passed in.
*/
static struct pg_tm *
-gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp)
+gmtsub(pg_time_t const *timep, int32 offset, struct pg_tm *tmp)
{
struct pg_tm *result;
@@ -1362,15 +1362,22 @@ pg_gmtime(const pg_time_t *timep)
* where, to make the math easy, the answer for year zero is defined as zero.
*/
static int
+leaps_thru_end_of_nonneg(int y)
+{
+ return y / 4 - y / 100 + y / 400;
+}
+
+static int
leaps_thru_end_of(const int y)
{
- return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
- -(leaps_thru_end_of(-(y + 1)) + 1);
+ return (y < 0
+ ? -1 - leaps_thru_end_of_nonneg(-1 - y)
+ : leaps_thru_end_of_nonneg(y));
}
static struct pg_tm *
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;
pg_time_t tdays;
@@ -1390,22 +1397,9 @@ timesub(const pg_time_t *timep, int32 offset,
lp = &sp->lsis[i];
if (*timep >= lp->ls_trans)
{
- if (*timep == lp->ls_trans)
- {
- hit = ((i == 0 && lp->ls_corr > 0) ||
- lp->ls_corr > sp->lsis[i - 1].ls_corr);
- if (hit)
- while (i > 0 &&
- sp->lsis[i].ls_trans ==
- sp->lsis[i - 1].ls_trans + 1 &&
- sp->lsis[i].ls_corr ==
- sp->lsis[i - 1].ls_corr + 1)
- {
- ++hit;
- --i;
- }
- }
corr = lp->ls_corr;
+ hit = (*timep == lp->ls_trans
+ && (i == 0 ? 0 : lp[-1].ls_corr) < corr);
break;
}
}
@@ -1529,13 +1523,13 @@ increment_overflow_time(pg_time_t *tp, int32 j)
{
/*----------
* This is like
- * 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...',
+ * '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))
+ ? (TYPE_SIGNED(pg_time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp)
+ : *tp <= TIME_T_MAX - j))
return true;
*tp += j;
return false;
diff --git a/src/timezone/private.h b/src/timezone/private.h
index f78053660e..701112ec5b 100644
--- a/src/timezone/private.h
+++ b/src/timezone/private.h
@@ -38,26 +38,9 @@
#define EOVERFLOW EINVAL
#endif
-#ifndef WIFEXITED
-#define WIFEXITED(status) (((status) & 0xff) == 0)
-#endif /* !defined WIFEXITED */
-#ifndef WEXITSTATUS
-#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
-#endif /* !defined WEXITSTATUS */
-
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
#define is_digit(c) ((unsigned)(c) - '0' <= 9)
-/*
- * SunOS 4.1.1 libraries lack remove.
- */
-
-#ifndef remove
-extern int unlink(const char *filename);
-
-#define remove unlink
-#endif /* !defined remove */
-
/*
* Finally, some convenience items.
@@ -78,6 +61,10 @@ extern int unlink(const char *filename);
#define MINVAL(t, b) \
((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
+/* The extreme time values, assuming no padding. */
+#define TIME_T_MIN MINVAL(pg_time_t, TYPE_BIT(pg_time_t))
+#define TIME_T_MAX MAXVAL(pg_time_t, TYPE_BIT(pg_time_t))
+
/*
* 302 / 1000 is log10(2.0) rounded up.
* Subtract one for the sign bit if the type is signed;
@@ -91,7 +78,7 @@ extern int unlink(const char *filename);
/*
* INITIALIZE(x)
*/
-#define INITIALIZE(x) ((x) = 0)
+#define INITIALIZE(x) ((x) = 0)
#undef _
#define _(msgid) (msgid)
@@ -146,7 +133,7 @@ extern int unlink(const char *filename);
* or
* isleap(a + b) == isleap(a % 400 + b % 400)
* This is true even if % means modulo rather than Fortran remainder
- * (which is allowed by C89 but not C99).
+ * (which is allowed by C89 but not by C99 or later).
* We use this to avoid addition overflow problems.
*/
@@ -162,4 +149,4 @@ extern int unlink(const char *filename);
((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR)
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
-#endif /* !defined PRIVATE_H */
+#endif /* !defined PRIVATE_H */
diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c
index 2f32cf8bdb..e1c6483443 100644
--- a/src/timezone/strftime.c
+++ b/src/timezone/strftime.c
@@ -41,7 +41,6 @@
#include "postgres.h"
#include <fcntl.h>
-#include <locale.h>
#include "private.h"
@@ -83,17 +82,17 @@ static const struct lc_time_T C_time_locale = {
/*
* x_fmt
*
- * C99 requires this format. Using just numbers (as here) makes Quakers
- * happier; it's also compatible with SVR4.
+ * C99 and later require this format. Using just numbers (as here) makes
+ * Quakers happier; it's also compatible with SVR4.
*/
"%m/%d/%y",
/*
* c_fmt
*
- * C99 requires this format. Previously this code used "%D %X", but we now
- * conform to C99. Note that "%a %b %d %H:%M:%S %Y" is used by Solaris
- * 2.3.
+ * C99 and later require this format. Previously this code used "%D %X",
+ * but we now conform to C99. Note that "%a %b %d %H:%M:%S %Y" is used by
+ * Solaris 2.3.
*/
"%a %b %e %T %Y",
@@ -107,26 +106,25 @@ static const struct lc_time_T C_time_locale = {
"%a %b %e %H:%M:%S %Z %Y"
};
+enum warn
+{
+ IN_NONE, IN_SOME, IN_THIS, IN_ALL
+};
+
static char *_add(const char *, char *, const char *);
static char *_conv(int, const char *, char *, const char *);
-static char *_fmt(const char *, const struct pg_tm *, char *,
- const char *, int *);
+static char *_fmt(const char *, const struct pg_tm *, char *, const char *,
+ enum warn *);
static char *_yconv(int, int, bool, bool, char *, const char *);
-#define IN_NONE 0
-#define IN_SOME 1
-#define IN_THIS 2
-#define IN_ALL 3
-
size_t
pg_strftime(char *s, size_t maxsize, const char *format,
- const struct pg_tm * t)
+ const struct pg_tm *t)
{
char *p;
- int warn;
+ enum warn warn = IN_NONE;
- warn = IN_NONE;
p = _fmt(format, t, s, s + maxsize, &warn);
if (p == s + maxsize)
return 0;
@@ -135,8 +133,8 @@ pg_strftime(char *s, size_t maxsize, const char *format,
}
static char *
-_fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
- int *warnp)
+_fmt(const char *format, const struct pg_tm *t, char *pt,
+ const char *ptlim, enum warn *warnp)
{
for (; *format; ++format)
{
@@ -185,7 +183,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
continue;
case 'c':
{
- int warn2 = IN_SOME;
+ enum warn warn2 = IN_SOME;
pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
if (warn2 == IN_ALL)
@@ -204,9 +202,9 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
case 'O':
/*
- * C99 locale modifiers. The sequences %Ec %EC %Ex %EX
- * %Ey %EY %Od %oe %OH %OI %Om %OM %OS %Ou %OU %OV %Ow
- * %OW %Oy are supposed to provide alternate
+ * Locale modifiers of C99 and later. The sequences %Ec
+ * %EC %Ex %EX %Ey %EY %Od %oe %OH %OI %Om %OM %OS %Ou %OU
+ * %OV %Ow %OW %Oy are supposed to provide alternate
* representations.
*/
goto label;
@@ -246,7 +244,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
*/
pt = _add("kitchen sink", pt, ptlim);
continue;
-#endif /* defined KITCHEN_SINK */
+#endif /* defined KITCHEN_SINK */
case 'l':
/*
@@ -418,7 +416,7 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
continue;
case 'x':
{
- int warn2 = IN_SOME;
+ enum warn warn2 = IN_SOME;
pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
if (warn2 == IN_ALL)
@@ -443,8 +441,8 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim,
pt = _add(t->tm_zone, pt, ptlim);
/*
- * C99 says that %Z must be replaced by the empty string
- * if the time zone is not determinable.
+ * C99 and later say that %Z must be replaced by the empty
+ * string if the time zone is not determinable.
*/
continue;
case 'z':
diff --git a/src/timezone/tzfile.h b/src/timezone/tzfile.h
index 56a5b43472..2843833e49 100644
--- a/src/timezone/tzfile.h
+++ b/src/timezone/tzfile.h
@@ -34,9 +34,9 @@ struct tzhead
{
char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
- char tzh_reserved[15]; /* reserved; must be zero */
- char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
- char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_reserved[15]; /* reserved; must be zero */
+ char tzh_ttisgmtcnt[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_timecnt[4]; /* coded number of transition times */
char tzh_typecnt[4]; /* coded number of local time types */
@@ -100,4 +100,4 @@ struct tzhead
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
-#endif /* !defined TZFILE_H */
+#endif /* !defined TZFILE_H */
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index f6beedcab2..db119265c3 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -9,10 +9,8 @@
#include "postgres_fe.h"
#include <fcntl.h>
-#include <locale.h>
#include <sys/stat.h>
#include <time.h>
-#include <unistd.h>
#include "pg_getopt.h"
@@ -28,7 +26,7 @@ typedef int64 zic_t;
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
#define ZIC_MAX_ABBR_LEN_WO_WARN 6
-#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
+#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
#ifndef WIN32
#ifdef S_IRUSR
@@ -47,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
{
@@ -84,9 +85,9 @@ struct rule
* r_dycode r_dayofmonth r_wday
*/
-#define DC_DOM 0 /* 1..31 */ /* unused */
-#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
-#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
+#define DC_DOM 0 /* 1..31 */ /* unused */
+#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
+#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
struct zone
{
@@ -138,9 +139,9 @@ static char lowerit(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, ptrdiff_t ntzones);
-static zic_t rpytime(const struct rule * rp, zic_t wantedy);
-static void rulesub(struct rule * rp,
+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);
@@ -292,12 +293,15 @@ struct lookup
};
static struct lookup const *byword(const char *string,
- const struct lookup * lp);
+ 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}
};
@@ -373,7 +377,7 @@ static struct attype
zic_t at;
bool dontmerge;
unsigned char type;
-} *attypes;
+} *attypes;
static zic_t gmtoffs[TZ_MAX_TYPES];
static char isdsts[TZ_MAX_TYPES];
static unsigned char abbrinds[TZ_MAX_TYPES];
@@ -424,7 +428,7 @@ erealloc(void *ptr, size_t size)
}
static char *
-ecpyalloc(char const * str)
+ecpyalloc(char const *str)
{
return memcheck(strdup(str));
}
@@ -436,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"));
@@ -450,7 +455,7 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
*/
static void
-eats(char const * name, lineno_t num, char const * rname, lineno_t rnum)
+eats(char const *name, lineno_t num, char const *rname, lineno_t rnum)
{
filename = name;
linenum = num;
@@ -459,7 +464,7 @@ eats(char const * name, lineno_t num, char const * rname, lineno_t rnum)
}
static void
-eat(char const * name, lineno_t num)
+eat(char const *name, lineno_t num)
{
eats(name, num, NULL, -1);
}
@@ -472,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");
}
@@ -504,7 +509,7 @@ warning(const char *string,...)
}
static void
-close_file(FILE *stream, char const * dir, 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);
@@ -537,7 +542,7 @@ usage(FILE *stream, int status)
ancestors. After this is done, all files are accessed with names
relative to DIR. */
static void
-change_directory(char const * dir)
+change_directory(char const *dir)
{
if (chdir(dir) != 0)
{
@@ -564,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;
@@ -573,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)
{
@@ -632,7 +637,10 @@ main(int argc, char *argv[])
break;
case 'y':
if (yitcommand == NULL)
+ {
+ warning(_("-y is obsolescent"));
yitcommand = strdup(optarg);
+ }
else
{
fprintf(stderr,
@@ -721,8 +729,8 @@ main(int argc, char *argv[])
}
static bool
-componentcheck(char const * name, char const * component,
- char const * component_end)
+componentcheck(char const *name, char const *component,
+ char const *component_end)
{
enum
{
@@ -813,7 +821,7 @@ namecheck(const char *name)
*/
#ifdef HAVE_SYMLINK
static char *
-relname(char const * from, char const * to)
+relname(char const *from, char const *to)
{
size_t i,
taillen,
@@ -853,12 +861,12 @@ relname(char const * from, char const * to)
}
return result;
}
-#endif /* HAVE_SYMLINK */
+#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)
+hardlinkerr(char const *from, char const *to)
{
int r = linkat(AT_FDCWD, from, AT_FDCWD, to, AT_SYMLINK_FOLLOW);
@@ -866,7 +874,7 @@ hardlinkerr(char const * from, char const * to)
}
static void
-dolink(char const * fromfield, char const * tofield, bool staysymlink)
+dolink(char const *fromfield, char const *tofield, bool staysymlink)
{
bool todirs_made = false;
int link_errno;
@@ -921,7 +929,7 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink)
strerror(link_errno));
}
else
-#endif /* HAVE_SYMLINK */
+#endif /* HAVE_SYMLINK */
{
FILE *fp,
*tp;
@@ -1010,7 +1018,7 @@ static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332
/* Return true if NAME is a directory. */
static bool
-itsdir(char const * name)
+itsdir(char const *name)
{
struct stat st;
int res = stat(name, &st);
@@ -1035,7 +1043,7 @@ itsdir(char const * name)
/* Return true if NAME is a symbolic link. */
static bool
-itssymlink(char const * name)
+itssymlink(char const *name)
{
#ifdef HAVE_SYMLINK
char c;
@@ -1203,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"));
@@ -1221,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" */
@@ -1251,7 +1257,7 @@ infile(const char *name)
* Call error with errstring and return zero on errors.
*/
static zic_t
-gethms(char const * string, char const * errstring, bool signable)
+gethms(char const *string, char const *errstring, bool signable)
{
/* PG: make hh be int not zic_t to avoid sscanf portability issues */
int hh;
@@ -1360,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);
@@ -1574,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"));
@@ -1600,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);
@@ -1635,7 +1631,7 @@ inlink(char **fields, int nfields)
}
static void
-rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
+rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
const char *typep, const char *monthp, const char *dayp,
const char *timep)
{
@@ -1754,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)
@@ -2141,7 +2140,7 @@ writezone(const char *const name, const char *const string, char version)
writetype[type] = true;
}
}
-#endif /* !defined
+#endif /* !defined
* LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
thistypecnt = 0;
for (i = 0; i < typecnt; ++i)
@@ -2324,7 +2323,7 @@ abbroffset(char *buf, zic_t offset)
}
static size_t
-doabbr(char *abbr, struct zone const * zp, char const * letters,
+doabbr(char *abbr, struct zone const *zp, char const *letters,
zic_t stdoff, bool doquotes)
{
char *cp;
@@ -2410,7 +2409,7 @@ stringoffset(char *result, zic_t offset)
}
static int
-stringrule(char *result, const struct rule * const rp, const zic_t dstoff,
+stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
const zic_t gmtoff)
{
zic_t tod = rp->r_tod;
@@ -2492,7 +2491,7 @@ stringrule(char *result, const struct rule * const rp, const zic_t dstoff,
}
static int
-rule_cmp(struct rule const * a, struct rule const * b)
+rule_cmp(struct rule const *a, struct rule const *b)
{
if (!a)
return -!!b;
@@ -2510,7 +2509,7 @@ enum
YEAR_BY_YEAR_ZONE = 1};
static int
-stringzone(char *result, struct zone const * zpfirst, ptrdiff_t zonecount)
+stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
{
const struct zone *zp;
struct rule *rp;
@@ -2645,7 +2644,7 @@ stringzone(char *result, struct zone const * zpfirst, ptrdiff_t zonecount)
}
static void
-outzone(const struct zone * zpfirst, ptrdiff_t zonecount)
+outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
{
const struct zone *zp;
struct rule *rp;
@@ -2851,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)
{
/*
@@ -2930,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,
@@ -3046,7 +3047,7 @@ addtt(zic_t starttime, int type)
}
static int
-addtype(zic_t gmtoff, char const * abbr, bool isdst, bool ttisstd, bool ttisgmt)
+addtype(zic_t gmtoff, char const *abbr, bool isdst, bool ttisstd, bool ttisgmt)
{
int i,
j;
@@ -3105,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)
@@ -3133,19 +3127,26 @@ 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;
}
}
static char *
-shellquote(char *b, char const * s)
+shellquote(char *b, char const *s)
{
*b++ = '\'';
while (*s)
@@ -3192,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)
{
@@ -3363,8 +3364,21 @@ 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)
+byword(const char *word, const struct lookup *table)
{
const struct lookup *foundlp;
const struct lookup *lp;
@@ -3373,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)
@@ -3384,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;
}
@@ -3479,7 +3528,7 @@ tadd(zic_t t1, zic_t t2)
*/
static zic_t
-rpytime(const struct rule * rp, zic_t wantedy)
+rpytime(const struct rule *rp, zic_t wantedy)
{
int m,
i;
@@ -3571,7 +3620,7 @@ will not work with pre-2004 versions of zic"));
return min_time;
if (dayoff > max_time / SECSPERDAY)
return max_time;
- t = (zic_t) dayoff *SECSPERDAY;
+ t = (zic_t) dayoff * SECSPERDAY;
return tadd(t, rp->r_tod);
}
@@ -3615,18 +3664,22 @@ newabbr(const char *string)
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)
+mkdirs(char const *argname, bool ancestors)
{
char *name;
char *cp;
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++;