diff --git a/src/timezone/zic.c b/src/timezone/zic.c index 64adb6bd465..7da95aad53c 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -8,9 +8,11 @@ #include "postgres_fe.h" +#include #include #include #include +#include #include "pg_getopt.h" @@ -35,14 +37,24 @@ typedef int64 zic_t; #define MKDIR_UMASK 0755 #endif #endif -#ifndef S_ISLNK -#define S_ISLNK(m) 0 +#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; +#define PRIdLINENO "d" + struct rule { const char *r_filename; - int r_linenum; + lineno r_linenum; const char *r_name; zic_t r_loyear; /* for example, 1986 */ @@ -64,7 +76,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 */ }; @@ -79,7 +91,7 @@ struct rule struct zone { const char *z_filename; - int z_linenum; + lineno z_linenum; const char *z_name; zic_t z_gmtoff; @@ -90,7 +102,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; @@ -119,20 +131,21 @@ 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 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 @@ -156,7 +169,7 @@ static int leapcnt; static bool leapseen; static zic_t leapminyear; static zic_t leapmaxyear; -static int linenum; +static lineno linenum; static int max_abbrvar_len = PERCENT_Z_LEN_BOUND; static int max_format_len; static zic_t max_year; @@ -165,10 +178,10 @@ static bool noise; static bool print_abbrevs; static zic_t print_cutoff; static const char *rfilename; -static int rlinenum; +static lineno rlinenum; static const char *progname; -static int timecnt; -static int timecnt_alloc; +static ptrdiff_t timecnt; +static ptrdiff_t timecnt_alloc; static int typecnt; /* @@ -253,24 +266,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 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 { @@ -417,17 +430,18 @@ 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 nitems_max = INT_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 ((nitems_max - 1) / 3 * 2 < *nitems_alloc) - memory_exhausted(_("int overflow")); - *nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1; + if ((amax - 1) / 3 * 2 < *nitems_alloc) + memory_exhausted(_("integer overflow")); + *nitems_alloc += (*nitems_alloc >> 1) + 1; return erealloc(ptr, size_product(*nitems_alloc, itemsize)); } } @@ -437,7 +451,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 num, char const * rname, lineno rnum) { filename = name; linenum = num; @@ -446,7 +460,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 num) { eats(name, num, NULL, -1); } @@ -459,10 +473,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"); } @@ -553,9 +567,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)); @@ -567,14 +582,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, NULL); return EXIT_SUCCESS; } - else if (strcmp(argv[i], "--help") == 0) + else if (strcmp(argv[k], "--help") == 0) { usage(stdout, EXIT_SUCCESS); } @@ -662,8 +677,8 @@ 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(); @@ -713,7 +728,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) { @@ -731,8 +746,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) @@ -823,9 +840,9 @@ relname(char const * from, char const * to) for (i = 0; f[i] && f[i] == to[i]; i++) if (f[i] == '/') dir_len = i + 1; - for (; f[i]; i++) - dotdots += f[i] == '/' && f[i - 1] != '/'; - taillen = i - dir_len; + for (; to[i]; i++) + dotdots += to[i] == '/' && to[i - 1] != '/'; + taillen = strlen(f + dir_len); dotdotetcsize = 3 * dotdots + taillen + 1; if (dotdotetcsize <= linksize) { @@ -839,10 +856,19 @@ relname(char const * from, char const * to) } #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) +{ + int r = linkat(AT_FDCWD, from, AT_FDCWD, to, AT_SYMLINK_FOLLOW); + + return r == 0 ? 0 : errno; +} + static void dolink(char const * fromfield, char const * tofield, bool staysymlink) { - int fromisdir; bool todirs_made = false; int link_errno; @@ -850,17 +876,14 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink) * We get to be careful here since there's a fair chance of root running * us. */ - fromisdir = itsdir(fromfield); - 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, e); + progname, directory, fromfield, strerror(EPERM)); exit(EXIT_FAILURE); } if (staysymlink) - staysymlink = itsdir(tofield) == 2; + staysymlink = itssymlink(tofield); if (remove(tofield) == 0) todirs_made = true; else if (errno != ENOENT) @@ -871,13 +894,12 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink) progname, directory, tofield, e); exit(EXIT_FAILURE); } - link_errno = (staysymlink ? ENOTSUP - : link(fromfield, tofield) == 0 ? 0 : errno); + link_errno = staysymlink ? ENOTSUP : hardlinkerr(fromfield, tofield); if (link_errno == ENOENT && !todirs_made) { mkdirs(tofield, true); todirs_made = true; - link_errno = link(fromfield, tofield) == 0 ? 0 : errno; + link_errno = hardlinkerr(fromfield, tofield); } if (link_errno != 0) { @@ -987,31 +1009,42 @@ static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332 ? BIG_BANG : MINVAL(zic_t, TIME_T_BITS_IN_FILE)); -/* Return 1 if NAME is a directory, 2 if a symbolic link, 0 if - something else, -1 (setting errno) if trouble. */ -static int +/* Return true if NAME is a directory. */ +static bool itsdir(char const * name) { struct stat st; - int res = lstat(name, &st); - - if (res == 0) - { + int res = stat(name, &st); #ifdef S_ISDIR - return S_ISDIR(st.st_mode) ? 1 : S_ISLNK(st.st_mode) ? 2 : 0; -#else + if (res == 0) + return S_ISDIR(st.st_mode) != 0; +#endif + if (res == 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 = lstat(nameslashdot, &st) == 0; + dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW; free(nameslashdot); return dir; -#endif } - 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 } /* @@ -1034,10 +1067,10 @@ associate(void) { struct zone *zp; struct rule *rp; - int base, + ptrdiff_t i, + j, + base, out; - int i, - j; if (nrules != 0) { @@ -1124,7 +1157,7 @@ infile(const char *name) const struct lookup *lp; int nfields; bool wantcont; - int num; + lineno num; char buf[BUFSIZ]; if (strcmp(name, "-") == 0) @@ -1175,7 +1208,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); @@ -1302,7 +1335,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) { @@ -1327,8 +1360,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); @@ -1452,7 +1485,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 */ @@ -1659,7 +1692,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; @@ -1684,7 +1717,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; @@ -1834,11 +1867,11 @@ 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; static const struct tzhead tzh0; @@ -1846,7 +1879,7 @@ writezone(const char *const name, const char *const string, char version) bool dir_checked = false; zic_t one = 1; zic_t y2038_boundary = one << 31; - int nats = timecnt + WORK_AROUND_QTBUG_53071; + 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; @@ -1861,8 +1894,8 @@ writezone(const char *const name, const char *const string, char version) * Optimize. */ { - int fromi; - int toi; + ptrdiff_t fromi, + toi; toi = 0; fromi = 0; @@ -1886,9 +1919,17 @@ writezone(const char *const name, const char *const string, char version) } 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. @@ -1991,23 +2032,25 @@ writezone(const char *const name, const char *const string, char version) } 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; } @@ -2015,9 +2058,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) @@ -2118,8 +2164,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; @@ -2466,13 +2511,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; @@ -2601,11 +2646,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; @@ -2627,7 +2672,7 @@ outzone(const struct zone * zpfirst, int zonecount) int compat; bool do_extend; char version; - int lastatmax = -1; + ptrdiff_t lastatmax = -1; max_abbr_len = 2 + max_format_len + max_abbrvar_len; max_envvar_len = 2 * max_abbr_len + 5 * 9; @@ -2796,7 +2841,7 @@ outzone(const struct zone * zpfirst, int zonecount) } for (;;) { - int k; + ptrdiff_t k; zic_t jtime, ktime = 0; zic_t offset; @@ -3091,30 +3136,52 @@ adjleap(void) } } -static bool -yearistype(int year, const char *type) +static char * +shellquote(char *b, char const * s) { - static char *buf; + *b++ = '\''; + while (*s) + { + if (*s == '\'') + *b++ = '\'', *b++ = '\\', *b++ = '\''; + *b++ = *s++; + } + *b++ = '\''; + return b; +} + +static bool +yearistype(zic_t year, const char *type) +{ + 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? */ @@ -3348,7 +3415,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)) @@ -3537,7 +3604,8 @@ newabbr(const char *string) /* 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 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) { @@ -3569,7 +3637,7 @@ mkdirs(char const * argname, bool ancestors) { int err = errno; - if (err != EEXIST && itsdir(name) < 0) + if (err != EEXIST && !itsdir(name)) { error(_("%s: Cannot create directory %s: %s"), progname, name, strerror(err));