summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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++;