diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2016-07-19 15:59:36 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2016-07-19 15:59:36 -0400 |
commit | 19d477aa681b4927f95824d724a4197c696f8c75 (patch) | |
tree | 4795da77200eb86060bef5e637190e03de6081a4 /src/timezone/zic.c | |
parent | bdeed0944fadff3ea394d361d0137997fb4db953 (diff) | |
download | postgresql-19d477aa681b4927f95824d724a4197c696f8c75.tar.gz postgresql-19d477aa681b4927f95824d724a4197c696f8c75.zip |
Sync back-branch copies of the timezone code with IANA release tzcode2016c.
Back-patch commit 1c1a7cbd6a1600c9, along with subsequent portability
fixes, into all active branches. Also, back-patch commits 696027727 and
596857043 (addition of zic -P option) into 9.1 and 9.2, just to reduce
differences between the branches. src/timezone/ is now largely identical
in all active branches, except that in 9.1, pgtz.c retains the
initial-timezone-selection code that was moved over to initdb in 9.2.
Ordinarily we wouldn't risk this much code churn in back branches, but it
seems necessary in this case, because among the changes are two feature
additions in the "zic" zone data file compiler (a larger limit on the
number of allowed DST transitions, and addition of a "%z" escape in zone
abbreviations). IANA have not yet started to use those features in their
tzdata files, but presumably they will before too long. If we don't update
then we'll be unable to adopt new timezone data. Also, installations built
with --with-system-tzdata (which includes most distro-supplied builds, I
believe) might fail even if we don't update our copies of the data files.
There are assorted bug fixes too, mostly affecting obscure timezones or
post-2037 dates.
Discussion: <13601.1468868947@sss.pgh.pa.us>
Diffstat (limited to 'src/timezone/zic.c')
-rw-r--r-- | src/timezone/zic.c | 2097 |
1 files changed, 1343 insertions, 754 deletions
diff --git a/src/timezone/zic.c b/src/timezone/zic.c index 9fc20c6bac7..b546a173729 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -8,28 +8,26 @@ #include "postgres_fe.h" -#include <limits.h> #include <locale.h> +#include <sys/stat.h> #include <time.h> #include "pg_getopt.h" #include "private.h" -#include "pgtz.h" #include "tzfile.h" -#define ZIC_VERSION '2' +#define ZIC_VERSION_PRE_2013 '2' +#define ZIC_VERSION '3' typedef int64 zic_t; +#define ZIC_MIN PG_INT64_MIN +#define ZIC_MAX PG_INT64_MAX #ifndef ZIC_MAX_ABBR_LEN_WO_WARN #define ZIC_MAX_ABBR_LEN_WO_WARN 6 #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif - #ifndef WIN32 #ifdef S_IRUSR #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) @@ -38,37 +36,17 @@ typedef int64 zic_t; #endif #endif -static char elsieid[] = "@(#)zic.c 8.20"; - -/* - * On some ancient hosts, predicates like `isspace(C)' are defined - * only if isascii(C) || C == EOF. Modern hosts obey the C Standard, - * which says they are defined only if C == ((unsigned char) C) || C == EOF. - * Neither the C Standard nor Posix require that `isascii' exist. - * For portability, we check both ancient and modern requirements. - * If isascii is not defined, the isascii check succeeds trivially. - */ -#include <ctype.h> -#ifndef isascii -#define isascii(x) 1 -#endif - -#define OFFSET_STRLEN_MAXIMUM (7 + INT_STRLEN_MAXIMUM(long)) -#define RULE_STRLEN_MAXIMUM 8 /* "Mdd.dd.d" */ - -#define end(cp) (strchr((cp), '\0')) - struct rule { const char *r_filename; int r_linenum; const char *r_name; - int r_loyear; /* for example, 1986 */ - int r_hiyear; /* for example, 1986 */ + zic_t r_loyear; /* for example, 1986 */ + zic_t r_hiyear; /* for example, 1986 */ const char *r_yrtype; - int r_lowasnum; - int r_hiwasnum; + bool r_lowasnum; + bool r_hiwasnum; int r_month; /* 0..11 */ @@ -76,12 +54,11 @@ struct rule int r_dayofmonth; int r_wday; - long r_tod; /* time from midnight */ - int r_todisstd; /* above is standard time if TRUE */ - /* or wall clock time if FALSE */ - int r_todisgmt; /* above is GMT if TRUE */ - /* or local time if FALSE */ - long r_stdoff; /* offset from standard time */ + zic_t r_tod; /* time from midnight */ + bool r_todisstd; /* above is standard time if 1 or wall clock + * time if 0 */ + bool r_todisgmt; /* above is GMT if 1 or local time if 0 */ + 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) */ @@ -102,11 +79,12 @@ struct zone int z_linenum; const char *z_name; - long z_gmtoff; + zic_t z_gmtoff; const char *z_rule; const char *z_format; + char z_format_specifier; - long z_stdoff; + zic_t z_stdoff; struct rule *z_rules; int z_nrules; @@ -116,74 +94,69 @@ struct zone }; extern int link(const char *fromname, const char *toname); -static void addtt(const pg_time_t starttime, int type); -static int addtype(long gmtoff, const char *abbr, int isdst, - int ttisstd, int ttisgmt); -static void leapadd(const pg_time_t t, int positive, int rolling, int count); + +static void memory_exhausted(const char *msg) pg_attribute_noreturn(); +static void verror(const char *string, va_list args) pg_attribute_printf(1, 0); +static void error(const char *string,...) pg_attribute_printf(1, 2); +static void warning(const char *string,...) pg_attribute_printf(1, 2); +static void usage(FILE *stream, int status) pg_attribute_noreturn(); +static void addtt(zic_t starttime, int type); +static int addtype(zic_t, char const *, bool, bool, bool); +static void leapadd(zic_t, bool, int, int); static void adjleap(void); static void associate(void); -static int ciequal(const char *ap, const char *bp); -static void convert(long val, char *buf); -static void dolink(const char *fromfile, const char *tofile); -static void doabbr(char *abbr, const char *format, - const char *letters, int isdst, int doquotes); -static void eat(const char *name, int num); -static void eats(const char *name, int num, - const char *rname, int rnum); -static long eitol(int i); -static void error(const char *message); +static void dolink(const char *fromfield, const char *tofield); static char **getfields(char *buf); -static long gethms(const char *string, const char *errstrng, - int signable); +static zic_t gethms(const char *string, const char *errstring, + bool); static void infile(const char *filename); static void inleap(char **fields, int nfields); static void inlink(char **fields, int nfields); static void inrule(char **fields, int nfields); -static int inzcont(char **fields, int nfields); -static int inzone(char **fields, int nfields); -static int inzsub(char **fields, int nfields, int iscont); -static int itsabbr(const char *abbr, const char *word); +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 int lowerit(int c); -static char *memcheck(char *tocheck); -static int mkdirs(char *filename); +static bool is_alpha(char a); +static char lowerit(char); +static bool mkdirs(char *); static void newabbr(const char *abbr); -static long oadd(long t1, long t2); +static zic_t oadd(zic_t t1, zic_t t2); static void outzone(const struct zone * zp, int ntzones); -static void puttzcode(long code, FILE *fp); -static int rcomp(const void *leftp, const void *rightp); -static pg_time_t rpytime(const struct rule * rp, int wantedy); +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 void setboundaries(void); -static pg_time_t tadd(const pg_time_t t1, long t2); -static void usage(FILE *stream, int status); -static void writezone(const char *name, const char *string); -static int yearistype(int year, const char *type); +static zic_t tadd(zic_t t1, zic_t t2); +static bool yearistype(int year, const char *type); + +/* Bound on length of what %z can expand to. */ +enum +{ +PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1}; static int charcnt; -static int errors; +static bool errors; +static bool warnings; static const char *filename; static int leapcnt; -static int leapseen; -static int leapminyear; -static int leapmaxyear; +static bool leapseen; +static zic_t leapminyear; +static zic_t leapmaxyear; static int linenum; -static int max_abbrvar_len; +static int max_abbrvar_len = PERCENT_Z_LEN_BOUND; static int max_format_len; -static zic_t max_time; -static int max_year; -static zic_t min_time; -static int min_year; -static int noise; -static int print_abbrevs; +static zic_t max_year; +static zic_t min_year; +static bool noise; +static bool print_abbrevs; static zic_t print_cutoff; static const char *rfilename; static int rlinenum; static const char *progname; static int timecnt; +static int timecnt_alloc; static int typecnt; /* @@ -269,9 +242,11 @@ static int typecnt; static struct rule *rules; static int nrules; /* number of rules */ +static int nrules_alloc; static struct zone *zones; static int nzones; /* number of zones */ +static int nzones_alloc; struct link { @@ -283,6 +258,7 @@ struct link static struct link *links; static int nlinks; +static int nlinks_alloc; struct lookup { @@ -353,8 +329,8 @@ static struct lookup const end_years[] = { }; static struct lookup const leap_types[] = { - {"Rolling", TRUE}, - {"Stationary", FALSE}, + {"Rolling", true}, + {"Stationary", false}, {NULL, 0} }; @@ -371,40 +347,78 @@ static struct attype { zic_t at; unsigned char type; -} attypes[TZ_MAX_TIMES]; -static long gmtoffs[TZ_MAX_TYPES]; +} *attypes; +static zic_t gmtoffs[TZ_MAX_TYPES]; static char isdsts[TZ_MAX_TYPES]; static unsigned char abbrinds[TZ_MAX_TYPES]; -static char ttisstds[TZ_MAX_TYPES]; -static char ttisgmts[TZ_MAX_TYPES]; +static bool ttisstds[TZ_MAX_TYPES]; +static bool ttisgmts[TZ_MAX_TYPES]; static char chars[TZ_MAX_CHARS]; static zic_t trans[TZ_MAX_LEAPS]; -static long corr[TZ_MAX_LEAPS]; +static zic_t corr[TZ_MAX_LEAPS]; static char roll[TZ_MAX_LEAPS]; /* * Memory allocation. */ -static char * -memcheck(char *ptr) +static void +memory_exhausted(const char *msg) +{ + fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg); + exit(EXIT_FAILURE); +} + +static size_t +size_product(size_t nitems, size_t itemsize) +{ + if (SIZE_MAX / itemsize < nitems) + memory_exhausted(_("size overflow")); + return nitems * itemsize; +} + +static void * +memcheck(void *ptr) { if (ptr == NULL) + memory_exhausted(strerror(errno)); + return ptr; +} + +static void * +emalloc(size_t size) +{ + return memcheck(malloc(size)); +} + +static void * +erealloc(void *ptr, size_t size) +{ + return memcheck(realloc(ptr, size)); +} + +static char * +ecpyalloc(char const * str) +{ + return memcheck(strdup(str)); +} + +static void * +growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc) +{ + if (nitems < *nitems_alloc) + return ptr; + else { - const char *e = strerror(errno); + int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX; - (void) fprintf(stderr, _("%s: Memory exhausted: %s\n"), - progname, e); - exit(EXIT_FAILURE); + if ((amax - 1) / 3 * 2 < *nitems_alloc) + memory_exhausted(_("int overflow")); + *nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1; + return erealloc(ptr, size_product(*nitems_alloc, itemsize)); } - return ptr; } -#define emalloc(size) memcheck(imalloc(size)) -#define erealloc(ptr, size) memcheck(irealloc((ptr), (size))) -#define ecpyalloc(ptr) memcheck(icpyalloc(ptr)) -#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp))) - /* * Error handling. */ @@ -421,46 +435,75 @@ eats(const char *name, int num, const char *rname, int rnum) static void eat(const char *name, int num) { - eats(name, num, (char *) NULL, -1); + eats(name, num, NULL, -1); } static void -error(const char *string) +verror(const char *string, va_list args) { /* * Match the format of "cc" to allow sh users to zic ... 2>&1 | error -t * "*" -v on BSD systems. */ - (void) fprintf(stderr, _("\"%s\", line %d: %s"), - filename, linenum, string); + if (filename) + fprintf(stderr, _("\"%s\", line %d: "), filename, linenum); + vfprintf(stderr, string, args); if (rfilename != NULL) - (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"), - rfilename, rlinenum); - (void) fprintf(stderr, "\n"); - ++errors; + fprintf(stderr, _(" (rule from \"%s\", line %d)"), + rfilename, rlinenum); + fprintf(stderr, "\n"); } static void -warning(const char *string) +error(const char *string,...) { - char *cp; + va_list args; + + va_start(args, string); + verror(string, args); + va_end(args); + errors = true; +} - cp = ecpyalloc(_("warning: ")); - cp = ecatalloc(cp, string); - error(cp); - ifree(cp); - --errors; +static void +warning(const char *string,...) +{ + va_list args; + + fprintf(stderr, _("warning: ")); + va_start(args, string); + verror(string, args); + va_end(args); + warnings = true; +} + +static void +close_file(FILE *stream, char const * name) +{ + char const *e = (ferror(stream) ? _("I/O error") + : fclose(stream) != 0 ? strerror(errno) : NULL); + + if (e) + { + fprintf(stderr, "%s: ", progname); + if (name) + fprintf(stderr, "%s: ", name); + fprintf(stderr, "%s\n", e); + exit(EXIT_FAILURE); + } } static void usage(FILE *stream, int status) { - (void) fprintf(stream, _("%s: usage is %s \ -[ --version ] [ --help ] [ -v ] [ -P ] [ -l localtime ] [ -p posixrules ] \\\n\ -\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\ -\n\ -Report bugs to tz@elsie.nci.nih.gov.\n"), - progname, progname); + fprintf(stream, + _("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -P ] \\\n" + "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n" + "\t[ -L leapseconds ] [ filename ... ]\n\n" + "Report bugs to %s.\n"), + progname, progname, PACKAGE_BUGREPORT); + if (status == EXIT_SUCCESS) + close_file(stream, NULL); exit(status); } @@ -478,20 +521,21 @@ main(int argc, char *argv[]) int c; #ifndef WIN32 - (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); + umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); #endif /* !WIN32 */ progname = argv[0]; if (TYPE_BIT(zic_t) <64) { - (void) fprintf(stderr, "%s: %s\n", progname, - _("wild compilation-time specification of zic_t")); - exit(EXIT_FAILURE); + fprintf(stderr, "%s: %s\n", progname, + _("wild compilation-time specification of zic_t")); + return EXIT_FAILURE; } for (i = 1; i < argc; ++i) if (strcmp(argv[i], "--version") == 0) { - (void) printf("%s\n", elsieid); - exit(EXIT_SUCCESS); + printf("zic %s\n", PG_VERSION); + close_file(stdout, NULL); + return EXIT_SUCCESS; } else if (strcmp(argv[i], "--help") == 0) { @@ -507,10 +551,10 @@ main(int argc, char *argv[]) directory = strdup(optarg); else { - (void) fprintf(stderr, - _("%s: More than one -d option specified\n"), - progname); - exit(EXIT_FAILURE); + fprintf(stderr, + _("%s: More than one -d option specified\n"), + progname); + return EXIT_FAILURE; } break; case 'l': @@ -518,10 +562,10 @@ main(int argc, char *argv[]) lcltime = strdup(optarg); else { - (void) fprintf(stderr, - _("%s: More than one -l option specified\n"), - progname); - exit(EXIT_FAILURE); + fprintf(stderr, + _("%s: More than one -l option specified\n"), + progname); + return EXIT_FAILURE; } break; case 'p': @@ -529,10 +573,10 @@ main(int argc, char *argv[]) psxrules = strdup(optarg); else { - (void) fprintf(stderr, - _("%s: More than one -p option specified\n"), - progname); - exit(EXIT_FAILURE); + fprintf(stderr, + _("%s: More than one -p option specified\n"), + progname); + return EXIT_FAILURE; } break; case 'y': @@ -540,10 +584,10 @@ main(int argc, char *argv[]) yitcommand = strdup(optarg); else { - (void) fprintf(stderr, - _("%s: More than one -y option specified\n"), - progname); - exit(EXIT_FAILURE); + fprintf(stderr, + _("%s: More than one -y option specified\n"), + progname); + return EXIT_FAILURE; } break; case 'L': @@ -551,21 +595,21 @@ main(int argc, char *argv[]) leapsec = strdup(optarg); else { - (void) fprintf(stderr, - _("%s: More than one -L option specified\n"), - progname); - exit(EXIT_FAILURE); + fprintf(stderr, + _("%s: More than one -L option specified\n"), + progname); + return EXIT_FAILURE; } break; case 'v': - noise = TRUE; + noise = true; break; case 'P': - print_abbrevs = TRUE; + print_abbrevs = true; print_cutoff = time(NULL); break; case 's': - (void) printf("%s: -s ignored\n", progname); + warning(_("-s ignored")); break; } if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) @@ -575,8 +619,6 @@ main(int argc, char *argv[]) if (yitcommand == NULL) yitcommand = "yearistype"; - setboundaries(); - if (optind < argc && leapsec != NULL) { infile(leapsec); @@ -586,7 +628,7 @@ main(int argc, char *argv[]) for (i = optind; i < argc; ++i) infile(argv[i]); if (errors) - exit(EXIT_FAILURE); + return EXIT_FAILURE; associate(); for (i = 0; i < nzones; i = j) { @@ -613,110 +655,286 @@ main(int argc, char *argv[]) } if (lcltime != NULL) { - eat("command line", 1); + eat(_("command line"), 1); dolink(lcltime, TZDEFAULT); } if (psxrules != NULL) { - eat("command line", 1); + eat(_("command line"), 1); dolink(psxrules, TZDEFRULES); } - return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + if (warnings && (ferror(stderr) || fclose(stderr) != 0)) + return EXIT_FAILURE; + return errors ? EXIT_FAILURE : EXIT_SUCCESS; } -static void -dolink(const char *fromfield, const char *tofield) +static bool +componentcheck(char const * name, char const * component, + char const * component_end) { - char *fromname; - char *toname; + enum + { + component_len_max = 14}; + size_t component_len = component_end - component; - if (fromfield[0] == '/') - fromname = ecpyalloc(fromfield); - else + if (component_len == 0) { - fromname = ecpyalloc(directory); - fromname = ecatalloc(fromname, "/"); - fromname = ecatalloc(fromname, fromfield); + if (!*name) + error(_("empty file name")); + else + error(_(component == name + ? "file name '%s' begins with '/'" + : *component_end + ? "file name '%s' contains '//'" + : "file name '%s' ends with '/'"), + name); + return false; + } + if (0 < component_len && component_len <= 2 + && component[0] == '.' && component_end[-1] == '.') + { + error(_("file name '%s' contains '%.*s' component"), + name, (int) component_len, component); + return false; + } + if (noise) + { + if (0 < component_len && component[0] == '-') + warning(_("file name '%s' component contains leading '-'"), + name); + if (component_len_max < component_len) + warning(_("file name '%s' contains overlength component" + " '%.*s...'"), + name, component_len_max, component); + } + return true; +} + +static bool +namecheck(const char *name) +{ + char const *cp; + + /* Benign characters in a portable file name. */ + static char const benign[] = + "-/_" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /* + * Non-control chars in the POSIX portable character set, excluding the + * benign characters. + */ + static char const printable_and_not_benign[] = + " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~"; + + char const *component = name; + + for (cp = name; *cp; cp++) + { + unsigned char c = *cp; + + if (noise && !strchr(benign, c)) + { + warning((strchr(printable_and_not_benign, c) + ? _("file name '%s' contains byte '%c'") + : _("file name '%s' contains byte '\\%o'")), + name, c); + } + if (c == '/') + { + if (!componentcheck(name, component, cp)) + return false; + component = cp + 1; + } } - if (tofield[0] == '/') - toname = ecpyalloc(tofield); + return componentcheck(name, component, cp); +} + +static char * +relname(char const * dir, char const * base) +{ + if (*base == '/') + return ecpyalloc(base); else { - toname = ecpyalloc(directory); - toname = ecatalloc(toname, "/"); - toname = ecatalloc(toname, tofield); + size_t dir_len = strlen(dir); + bool needs_slash = dir_len && dir[dir_len - 1] != '/'; + char *result = emalloc(dir_len + needs_slash + strlen(base) + 1); + + result[dir_len] = '/'; + strcpy(result + dir_len + needs_slash, base); + return memcpy(result, dir, dir_len); } +} + +static void +dolink(char const * fromfield, char const * tofield) +{ + char *fromname; + char *toname; + int fromisdir; + + fromname = relname(directory, fromfield); + toname = relname(directory, tofield); /* * We get to be careful here since there's a fair chance of root running * us. */ - if (!itsdir(toname)) - (void) remove(toname); - if (link(fromname, toname) != 0) + fromisdir = itsdir(fromname); + if (fromisdir) { - int result; + char const *e = strerror(fromisdir < 0 ? errno : EPERM); - if (mkdirs(toname) != 0) - exit(EXIT_FAILURE); + fprintf(stderr, _("%s: link from %s failed: %s"), + progname, fromname, e); + exit(EXIT_FAILURE); + } + if (link(fromname, toname) != 0) + { + int link_errno = errno; + bool retry_if_link_supported = false; - result = link(fromname, toname); -#ifdef HAVE_SYMLINK - if (result != 0 && - access(fromname, F_OK) == 0 && - !itsdir(fromname)) + if (link_errno == ENOENT || link_errno == ENOTSUP) { - const char *s = tofield; - char *symlinkcontents = NULL; - - while ((s = strchr(s + 1, '/')) != NULL) - symlinkcontents = ecatalloc(symlinkcontents, "../"); - symlinkcontents = ecatalloc(symlinkcontents, fromfield); - - result = symlink(symlinkcontents, toname); - if (result == 0) - warning(_("hard link failed, symbolic link used")); - ifree(symlinkcontents); + if (!mkdirs(toname)) + exit(EXIT_FAILURE); + retry_if_link_supported = true; } -#endif - if (result != 0) + if ((link_errno == EEXIST || link_errno == ENOTSUP) + && itsdir(toname) == 0 + && (remove(toname) == 0 || errno == ENOENT)) + retry_if_link_supported = true; + if (retry_if_link_supported && link_errno != ENOTSUP) + link_errno = link(fromname, toname) == 0 ? 0 : errno; + if (link_errno != 0) { - const char *e = strerror(errno); +#ifdef HAVE_SYMLINK + const char *s = fromfield; + const char *t; + char *p; + size_t dotdots = 0; + char *symlinkcontents; + int symlink_result; + + do + t = s; + while ((s = strchr(s, '/')) + && strncmp(fromfield, tofield, ++s - fromfield) == 0); + + for (s = tofield + (t - fromfield); *s; s++) + dotdots += *s == '/'; + symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1); + for (p = symlinkcontents; dotdots-- != 0; p += 3) + memcpy(p, "../", 3); + strcpy(p, t); + symlink_result = symlink(symlinkcontents, toname); + free(symlinkcontents); + if (symlink_result == 0) + { + if (link_errno != ENOTSUP) + warning(_("symbolic link used because hard link failed: %s"), + strerror(link_errno)); + } + else +#endif /* HAVE_SYMLINK */ + { + FILE *fp, + *tp; + int c; - (void) fprintf(stderr, - _("%s: Cannot link from %s to %s: %s\n"), - progname, fromname, toname, e); - exit(EXIT_FAILURE); + fp = fopen(fromname, "rb"); + if (!fp) + { + const char *e = strerror(errno); + + fprintf(stderr, + _("%s: Can't read %s: %s\n"), + progname, fromname, e); + exit(EXIT_FAILURE); + } + tp = fopen(toname, "wb"); + if (!tp) + { + const char *e = strerror(errno); + + fprintf(stderr, + _("%s: Can't create %s: %s\n"), + progname, toname, e); + exit(EXIT_FAILURE); + } + while ((c = getc(fp)) != EOF) + putc(c, tp); + close_file(fp, fromname); + close_file(tp, toname); + if (link_errno != ENOTSUP) + warning(_("copy used because hard link failed: %s"), + strerror(link_errno)); + } } } - ifree(fromname); - ifree(toname); + free(fromname); + free(toname); } -#define TIME_T_BITS_IN_FILE 64 +#define TIME_T_BITS_IN_FILE 64 -static void -setboundaries(void) -{ - int i; +static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); +static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); - min_time = -1; - for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) - min_time *= 2; - max_time = -(min_time + 1); -} +/* + * Estimated time of the Big Bang, in seconds since the POSIX epoch. + * rounded downward to the negation of a power of two that is + * comfortably outside the error bounds. + * + * zic does not output time stamps before this, partly because they + * are physically suspect, and partly because GNOME mishandles them; see + * GNOME bug 730332 <https://bugzilla.gnome.org/show_bug.cgi?id=730332>. + * + * For the time of the Big Bang, see: + * + * Ade PAR, Aghanim N, Armitage-Caplan C et al. Planck 2013 results. + * I. Overview of products and scientific results. + * arXiv:1303.5062 2013-03-20 20:10:01 UTC + * <http://arxiv.org/pdf/1303.5062v1> [PDF] + * + * Page 36, Table 9, row Age/Gyr, column Planck+WP+highL+BAO 68% limits + * gives the value 13.798 plus-or-minus 0.037 billion years. + * Multiplying this by 1000000000 and then by 31557600 (the number of + * seconds in an astronomical year) gives a value that is comfortably + * less than 2**59, so BIG_BANG is - 2**59. + * + * BIG_BANG is approximate, and may change in future versions. + * Please do not rely on its exact value. + */ + +#ifndef BIG_BANG +#define BIG_BANG (- (((zic_t) 1) << 59)) +#endif +static const zic_t big_bang_time = BIG_BANG; + +/* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble. */ static int -itsdir(const char *name) +itsdir(char const * name) { - char *myname; - int accres; + struct stat st; + int res = stat(name, &st); + +#ifdef S_ISDIR + if (res == 0) + return S_ISDIR(st.st_mode) != 0; +#endif + if (res == 0 || errno == EOVERFLOW) + { + char *nameslashdot = relname(name, "."); + bool dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW; - myname = ecpyalloc(name); - myname = ecatalloc(myname, "/."); - accres = access(myname, F_OK); - ifree(myname); - return accres == 0; + free(nameslashdot); + return dir; + } + return -1; } /* @@ -746,8 +964,7 @@ associate(void) if (nrules != 0) { - (void) qsort((void *) rules, (size_t) nrules, - (size_t) sizeof *rules, rcomp); + qsort(rules, nrules, sizeof *rules, rcomp); for (i = 0; i < nrules - 1; ++i) { if (strcmp(rules[i].r_name, @@ -807,14 +1024,14 @@ associate(void) */ eat(zp->z_filename, zp->z_linenum); zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), - TRUE); + true); /* * Note, though, that if there's no rule, a '%s' in the format is * a bad thing. */ - if (strchr(zp->z_format, '%') != NULL) - error(_("%s in ruleless zone")); + if (zp->z_format_specifier == 's') + error("%s", _("%s in ruleless zone")); } } if (errors) @@ -829,7 +1046,7 @@ infile(const char *name) char *cp; const struct lookup *lp; int nfields; - int wantcont; + bool wantcont; int num; char buf[BUFSIZ]; @@ -842,15 +1059,15 @@ infile(const char *name) { const char *e = strerror(errno); - (void) fprintf(stderr, _("%s: Cannot open %s: %s\n"), - progname, name, e); + fprintf(stderr, _("%s: Cannot open %s: %s\n"), + progname, name, e); exit(EXIT_FAILURE); } - wantcont = FALSE; + wantcont = false; for (num = 1;; ++num) { eat(name, num); - if (fgets(buf, (int) sizeof buf, fp) != buf) + if (fgets(buf, sizeof buf, fp) != buf) break; cp = strchr(buf, '\n'); if (cp == NULL) @@ -885,66 +1102,54 @@ infile(const char *name) { case LC_RULE: inrule(fields, nfields); - wantcont = FALSE; + wantcont = false; break; case LC_ZONE: wantcont = inzone(fields, nfields); break; case LC_LINK: inlink(fields, nfields); - wantcont = FALSE; + wantcont = false; break; case LC_LEAP: if (name != leapsec) - (void) fprintf(stderr, - _("%s: Leap line in non leap seconds file %s\n"), - progname, name); + warning(_("%s: Leap line in non leap" + " seconds file %s"), + progname, name); else inleap(fields, nfields); - wantcont = FALSE; + wantcont = false; break; default: /* "cannot happen" */ - (void) fprintf(stderr, - _("%s: panic: Invalid l_value %d\n"), - progname, lp->l_value); + fprintf(stderr, + _("%s: panic: Invalid l_value %d\n"), + progname, lp->l_value); exit(EXIT_FAILURE); } } - ifree((char *) fields); - } - if (ferror(fp)) - { - (void) fprintf(stderr, _("%s: Error reading %s\n"), - progname, filename); - exit(EXIT_FAILURE); - } - if (fp != stdin && fclose(fp)) - { - const char *e = strerror(errno); - - (void) fprintf(stderr, _("%s: Error closing %s: %s\n"), - progname, filename, e); - exit(EXIT_FAILURE); + free(fields); } + close_file(fp, filename); if (wantcont) error(_("expected continuation line not found")); } -/*---------- +/* * Convert a string of one of the forms * h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss * into a number of seconds. * A null string maps to zero. * Call error with errstring and return zero on errors. - *---------- */ -static long -gethms(const char *string, const char *errstring, int signable) +static zic_t +gethms(char const * string, char const * errstring, bool signable) { - long hh; + /* PG: make hh be int not zic_t to avoid sscanf portability issues */ + int hh; int mm, ss, sign; + char xs; if (string == NULL || *string == '\0') return 0; @@ -957,35 +1162,36 @@ gethms(const char *string, const char *errstring, int signable) } else sign = 1; - if (sscanf(string, scheck(string, "%ld"), &hh) == 1) + if (sscanf(string, "%d%c", &hh, &xs) == 1) mm = ss = 0; - else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2) + else if (sscanf(string, "%d:%d%c", &hh, &mm, &xs) == 2) ss = 0; - else if (sscanf(string, scheck(string, "%ld:%d:%d"), - &hh, &mm, &ss) != 3) + else if (sscanf(string, "%d:%d:%d%c", &hh, &mm, &ss, &xs) + != 3) { - error(errstring); + error("%s", errstring); return 0; } if (hh < 0 || mm < 0 || mm >= MINSPERHOUR || ss < 0 || ss > SECSPERMIN) { - error(errstring); + error("%s", errstring); return 0; } - if (LONG_MAX / SECSPERHOUR < hh) + /* Some compilers warn that this test is unsatisfiable for 32-bit ints */ +#if INT_MAX > PG_INT32_MAX + if (ZIC_MAX / SECSPERHOUR < hh) { error(_("time overflow")); return 0; } - if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0) - warning(_("24:00 not handled by pre-1998 versions of zic")); +#endif if (noise && (hh > HOURSPERDAY || (hh == HOURSPERDAY && (mm != 0 || ss != 0)))) warning(_("values over 24 hours not handled by pre-2007 versions of zic")); - return oadd(eitol(sign) * hh * eitol(SECSPERHOUR), - eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss))); + return oadd(sign * (zic_t) hh * SECSPERHOUR, + sign * (mm * SECSPERMIN + ss)); } static void @@ -1005,80 +1211,71 @@ inrule(char **fields, int nfields) } r.r_filename = filename; r.r_linenum = linenum; - r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE); + r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true); rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); r.r_name = ecpyalloc(fields[RF_NAME]); r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); if (max_abbrvar_len < strlen(r.r_abbrvar)) max_abbrvar_len = strlen(r.r_abbrvar); - rules = (struct rule *) (void *) erealloc((char *) rules, - (int) ((nrules + 1) * sizeof *rules)); + rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc); rules[nrules++] = r; } -static int +static bool inzone(char **fields, int nfields) { int i; - static char *buf; if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { error(_("wrong number of fields on Zone line")); - return FALSE; + return false; } if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) { - buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT))); - (void) sprintf(buf, - _("\"Zone %s\" line and -l option are mutually exclusive"), - TZDEFAULT); - error(buf); - return FALSE; + error( + _("\"Zone %s\" line and -l option are mutually exclusive"), + TZDEFAULT); + return false; } if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { - buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES))); - (void) sprintf(buf, - _("\"Zone %s\" line and -p option are mutually exclusive"), - TZDEFRULES); - error(buf); - return FALSE; + error( + _("\"Zone %s\" line and -p option are mutually exclusive"), + TZDEFRULES); + return false; } for (i = 0; i < nzones; ++i) if (zones[i].z_name != NULL && strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { - buf = erealloc(buf, (int) (132 + - strlen(fields[ZF_NAME]) + - strlen(zones[i].z_filename))); - (void) sprintf(buf, - _("duplicate zone name %s (file \"%s\", line %d)"), - fields[ZF_NAME], - zones[i].z_filename, - zones[i].z_linenum); - error(buf); - return FALSE; + error( + _("duplicate zone name %s (file \"%s\", line %d)"), + fields[ZF_NAME], + zones[i].z_filename, + zones[i].z_linenum); + return false; } - return inzsub(fields, nfields, FALSE); + return inzsub(fields, nfields, false); } -static int +static bool inzcont(char **fields, int nfields) { if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { error(_("wrong number of fields on Zone continuation line")); - return FALSE; + return false; } - return inzsub(fields, nfields, TRUE); + return inzsub(fields, nfields, true); } -static int -inzsub(char **fields, int nfields, int iscont) +static bool +inzsub(char **fields, int nfields, bool iscont) { char *cp; + char *cp1; static struct zone z; int i_gmtoff, i_rule, @@ -1087,7 +1284,7 @@ inzsub(char **fields, int nfields, int iscont) i_untilmonth; int i_untilday, i_untiltime; - int hasuntil; + bool hasuntil; if (iscont) { @@ -1100,6 +1297,8 @@ inzsub(char **fields, int nfields, int iscont) i_untiltime = ZFC_TILTIME; z.z_name = NULL; } + else if (!namecheck(fields[ZF_NAME])) + return false; else { i_gmtoff = ZF_GMTOFF; @@ -1113,17 +1312,26 @@ inzsub(char **fields, int nfields, int iscont) } z.z_filename = filename; z.z_linenum = linenum; - z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE); - if ((cp = strchr(fields[i_format], '%')) != NULL) + z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true); + if ((cp = strchr(fields[i_format], '%')) != 0) { - if (*++cp != 's' || strchr(cp, '%') != NULL) + if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%') + || strchr(fields[i_format], '/')) { error(_("invalid abbreviation format")); - return FALSE; + return false; } } z.z_rule = ecpyalloc(fields[i_rule]); - z.z_format = ecpyalloc(fields[i_format]); + z.z_format = cp1 = ecpyalloc(fields[i_format]); + z.z_format_specifier = cp ? *cp : '\0'; + if (z.z_format_specifier == 'z') + { + if (noise) + warning(_("format '%s' not handled by pre-2015 versions of zic"), + z.z_format); + cp1[cp - fields[i_format]] = 's'; + } if (max_format_len < strlen(z.z_format)) max_format_len = strlen(z.z_format); hasuntil = nfields > i_untilyear; @@ -1149,11 +1357,10 @@ inzsub(char **fields, int nfields, int iscont) zones[nzones - 1].z_untiltime >= z.z_untiltime) { error(_("Zone continuation line end time is not after end time of previous line")); - return FALSE; + return false; } } - zones = (struct zone *) (void *) erealloc((char *) zones, - (int) ((nzones + 1) * sizeof *zones)); + zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc); zones[nzones++] = z; /* @@ -1170,12 +1377,15 @@ inleap(char **fields, int nfields) const struct lookup *lp; int i, j; - int year, - month, + + /* PG: make year be int not zic_t to avoid sscanf portability issues */ + int year; + int month, day; - long dayoff, + zic_t dayoff, tod; zic_t t; + char xs; if (nfields != LEAP_FIELDS) { @@ -1184,7 +1394,7 @@ inleap(char **fields, int nfields) } dayoff = 0; cp = fields[LP_YEAR]; - if (sscanf(cp, scheck(cp, "%d"), &year) != 1) + if (sscanf(cp, "%d%c", &year, &xs) != 1) { /* * Leapin' Lizards! @@ -1196,7 +1406,7 @@ inleap(char **fields, int nfields) leapmaxyear = year; if (!leapseen || leapminyear > year) leapminyear = year; - leapseen = TRUE; + leapseen = true; j = EPOCH_YEAR; while (j != year) { @@ -1210,7 +1420,7 @@ inleap(char **fields, int nfields) --j; i = -len_years[isleap(j)]; } - dayoff = oadd(dayoff, eitol(i)); + dayoff = oadd(dayoff, i); } if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { @@ -1222,17 +1432,17 @@ inleap(char **fields, int nfields) while (j != month) { i = len_months[isleap(year)][j]; - dayoff = oadd(dayoff, eitol(i)); + dayoff = oadd(dayoff, i); ++j; } cp = fields[LP_DAY]; - if (sscanf(cp, scheck(cp, "%d"), &day) != 1 || + if (sscanf(cp, "%d%c", &day, &xs) != 1 || day <= 0 || day > len_months[isleap(year)][month]) { error(_("invalid day of month")); return; } - dayoff = oadd(dayoff, eitol(day - 1)); + dayoff = oadd(dayoff, day - 1); if (dayoff < min_time / SECSPERDAY) { error(_("time too small")); @@ -1243,32 +1453,31 @@ inleap(char **fields, int nfields) error(_("time too large")); return; } - t = (zic_t) dayoff *SECSPERDAY; - - tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); + t = dayoff * SECSPERDAY; + tod = gethms(fields[LP_TIME], _("invalid time of day"), false); cp = fields[LP_CORR]; { - int positive; + bool positive; int count; if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ - positive = FALSE; + positive = false; count = 1; } else if (strcmp(cp, "--") == 0) { - positive = FALSE; + positive = false; count = 2; } else if (strcmp(cp, "+") == 0) { - positive = TRUE; + positive = true; count = 1; } else if (strcmp(cp, "++") == 0) { - positive = TRUE; + positive = true; count = 2; } else @@ -1281,7 +1490,13 @@ inleap(char **fields, int nfields) error(_("illegal Rolling/Stationary field on Leap line")); return; } - leapadd(tadd(t, tod), positive, lp->l_value, count); + t = tadd(t, tod); + if (t < big_bang_time) + { + error(_("leap second precedes Big Bang")); + return; + } + leapadd(t, positive, lp->l_value, count); } } @@ -1300,17 +1515,13 @@ inlink(char **fields, int nfields) error(_("blank FROM field on Link line")); return; } - if (*fields[LF_TO] == '\0') - { - error(_("blank TO field on Link line")); + if (!namecheck(fields[LF_TO])) return; - } l.l_filename = filename; l.l_linenum = linenum; l.l_from = ecpyalloc(fields[LF_FROM]); l.l_to = ecpyalloc(fields[LF_TO]); - links = (struct link *) (void *) erealloc((char *) links, - (int) ((nlinks + 1) * sizeof *links)); + links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc); links[nlinks++] = l; } @@ -1323,6 +1534,10 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, const char *cp; char *dp; char *ep; + char xs; + + /* PG: year_tmp is to avoid sscanf portability issues */ + int year_tmp; if ((lp = byword(monthp, mon_names)) == NULL) { @@ -1330,8 +1545,8 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, return; } rp->r_month = lp->l_value; - rp->r_todisstd = FALSE; - rp->r_todisgmt = FALSE; + rp->r_todisstd = false; + rp->r_todisgmt = false; dp = ecpyalloc(timep); if (*dp != '\0') { @@ -1339,26 +1554,26 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, switch (lowerit(*ep)) { case 's': /* Standard */ - rp->r_todisstd = TRUE; - rp->r_todisgmt = FALSE; + rp->r_todisstd = true; + rp->r_todisgmt = false; *ep = '\0'; break; case 'w': /* Wall */ - rp->r_todisstd = FALSE; - rp->r_todisgmt = FALSE; + rp->r_todisstd = false; + rp->r_todisgmt = false; *ep = '\0'; break; case 'g': /* Greenwich */ case 'u': /* Universal */ case 'z': /* Zulu */ - rp->r_todisstd = TRUE; - rp->r_todisgmt = TRUE; + rp->r_todisstd = true; + rp->r_todisgmt = true; *ep = '\0'; break; } } - rp->r_tod = gethms(dp, _("invalid time of day"), FALSE); - ifree(dp); + rp->r_tod = gethms(dp, _("invalid time of day"), false); + free(dp); /* * Year work. @@ -1370,18 +1585,20 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, switch ((int) lp->l_value) { case YR_MINIMUM: - rp->r_loyear = INT_MIN; + rp->r_loyear = ZIC_MIN; break; case YR_MAXIMUM: - rp->r_loyear = INT_MAX; + rp->r_loyear = ZIC_MAX; break; default: /* "cannot happen" */ - (void) fprintf(stderr, - _("%s: panic: Invalid l_value %d\n"), - progname, lp->l_value); + fprintf(stderr, + _("%s: panic: Invalid l_value %d\n"), + progname, lp->l_value); exit(EXIT_FAILURE); } - else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) + else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1) + rp->r_loyear = year_tmp; + else { error(_("invalid starting year")); return; @@ -1393,21 +1610,23 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, switch ((int) lp->l_value) { case YR_MINIMUM: - rp->r_hiyear = INT_MIN; + rp->r_hiyear = ZIC_MIN; break; case YR_MAXIMUM: - rp->r_hiyear = INT_MAX; + rp->r_hiyear = ZIC_MAX; break; case YR_ONLY: rp->r_hiyear = rp->r_loyear; break; default: /* "cannot happen" */ - (void) fprintf(stderr, - _("%s: panic: Invalid l_value %d\n"), - progname, lp->l_value); + fprintf(stderr, + _("%s: panic: Invalid l_value %d\n"), + progname, lp->l_value); exit(EXIT_FAILURE); } - else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) + else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1) + rp->r_hiyear = year_tmp; + else { error(_("invalid ending year")); return; @@ -1456,65 +1675,67 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, if (*ep++ != '=') { error(_("invalid day of month")); - ifree(dp); + free(dp); return; } if ((lp = byword(dp, wday_names)) == NULL) { error(_("invalid weekday name")); - ifree(dp); + free(dp); return; } rp->r_wday = lp->l_value; } - if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 || + if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 || rp->r_dayofmonth <= 0 || (rp->r_dayofmonth > len_months[1][rp->r_month])) { error(_("invalid day of month")); - ifree(dp); + free(dp); return; } } - ifree(dp); + free(dp); } static void -convert(long val, char *buf) +convert(const int32 val, char *const buf) { int i; int shift; + unsigned char *const b = (unsigned char *) buf; for (i = 0, shift = 24; i < 4; ++i, shift -= 8) - buf[i] = val >> shift; + b[i] = val >> shift; } static void -convert64(zic_t val, char *buf) +convert64(const zic_t val, char *const buf) { int i; int shift; + unsigned char *const b = (unsigned char *) buf; for (i = 0, shift = 56; i < 8; ++i, shift -= 8) - buf[i] = val >> shift; + b[i] = val >> shift; } static void -puttzcode(long val, FILE *fp) +puttzcode(const int32 val, FILE *const fp) { char buf[4]; convert(val, buf); - (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); + fwrite(buf, sizeof buf, 1, fp); } static void -puttzcode64(zic_t val, FILE *fp) +puttzcode64(const zic_t val, FILE *const fp) { char buf[8]; convert64(val, buf); - (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); + fwrite(buf, sizeof buf, 1, fp); } static int @@ -1526,14 +1747,14 @@ atcomp(const void *avp, const void *bvp) return (a < b) ? -1 : (a > b); } -static int -is32(zic_t x) +static bool +is32(const zic_t x) { return x == ((zic_t) ((int32) x)); } static void -writezone(const char *name, const char *string) +writezone(const char *const name, const char *const string, char version) { FILE *fp; int i, @@ -1543,18 +1764,18 @@ writezone(const char *name, const char *string) int timecnt32, timei32; int pass; - static char *fullname; + char *fullname; static const struct tzhead tzh0; static struct tzhead tzh; - zic_t ats[TZ_MAX_TIMES]; - unsigned char types[TZ_MAX_TIMES]; + zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1)); + void *typesptr = ats + timecnt; + unsigned char *types = typesptr; /* * Sort. */ if (timecnt > 1) - (void) qsort((void *) attypes, (size_t) timecnt, - (size_t) sizeof *attypes, atcomp); + qsort(attypes, timecnt, sizeof *attypes, atcomp); /* * Optimize. @@ -1565,21 +1786,17 @@ writezone(const char *name, const char *string) toi = 0; fromi = 0; - while (fromi < timecnt && attypes[fromi].at < min_time) + while (fromi < timecnt && attypes[fromi].at < big_bang_time) ++fromi; - if (isdsts[0] == 0) - while (fromi < timecnt && attypes[fromi].type == 0) - ++fromi; /* handled by default rule */ for (; fromi < timecnt; ++fromi) { - if (toi != 0 - && ((attypes[fromi].at - + gmtoffs[attypes[toi - 1].type]) - <= (attypes[toi - 1].at - + gmtoffs[toi == 1 ? 0 - : attypes[toi - 2].type]))) + if (toi > 1 && ((attypes[fromi].at + + gmtoffs[attypes[toi - 1].type]) <= + (attypes[toi - 1].at + + gmtoffs[attypes[toi - 2].type]))) { - attypes[toi - 1].type = attypes[fromi].type; + attypes[toi - 1].type = + attypes[fromi].type; continue; } if (toi == 0 || @@ -1588,6 +1805,9 @@ writezone(const char *name, const char *string) } timecnt = toi; } + if (noise && timecnt > 1200) + warning(_("pre-2014 clients may mishandle" + " more than 1200 transition times")); /* * Transfer. @@ -1626,6 +1846,15 @@ writezone(const char *name, const char *string) --timecnt32; ++timei32; } + + /* + * Output an INT32_MIN "transition" if appropriate; see below. + */ + if (timei32 > 0 && ats[timei32] > PG_INT32_MIN) + { + --timei32; + ++timecnt32; + } while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1])) --leapcnt32; while (leapcnt32 > 0 && !is32(trans[leapi32])) @@ -1633,45 +1862,43 @@ writezone(const char *name, const char *string) --leapcnt32; ++leapi32; } - fullname = erealloc(fullname, - (int) (strlen(directory) + 1 + strlen(name) + 1)); - (void) sprintf(fullname, "%s/%s", directory, name); + fullname = relname(directory, name); /* * Remove old file, if any, to snap links. */ - if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) + if (itsdir(fullname) == 0 && remove(fullname) != 0 && errno != ENOENT) { const char *e = strerror(errno); - (void) fprintf(stderr, _("%s: Cannot remove %s: %s\n"), - progname, fullname, e); + fprintf(stderr, _("%s: Cannot remove %s: %s\n"), + progname, fullname, e); exit(EXIT_FAILURE); } if ((fp = fopen(fullname, "wb")) == NULL) { - if (mkdirs(fullname) != 0) - (void) exit(EXIT_FAILURE); + if (!mkdirs(fullname)) + exit(EXIT_FAILURE); if ((fp = fopen(fullname, "wb")) == NULL) { const char *e = strerror(errno); - (void) fprintf(stderr, _("%s: Cannot create %s: %s\n"), - progname, fullname, e); + fprintf(stderr, _("%s: Cannot create %s: %s\n"), + progname, fullname, e); exit(EXIT_FAILURE); } } for (pass = 1; pass <= 2; ++pass) { - register int thistimei, + int thistimei, thistimecnt; - register int thisleapi, + int thisleapi, thisleapcnt; - register int thistimelim, + int thistimelim, thisleaplim; - int writetype[TZ_MAX_TIMES]; + int writetype[TZ_MAX_TYPES]; int typemap[TZ_MAX_TYPES]; - register int thistypecnt; + int thistypecnt; char thischars[TZ_MAX_CHARS]; char thischarcnt; int indmap[TZ_MAX_CHARS]; @@ -1697,24 +1924,79 @@ writezone(const char *name, const char *string) if (thistimecnt == 0) { /* - * * No transition times fall in the current * (32- or 64-bit) - * window. + * No transition times fall in the current (32- or 64-bit) window. */ if (typecnt != 0) - writetype[typecnt - 1] = TRUE; + writetype[typecnt - 1] = true; } else { for (i = thistimei - 1; i < thistimelim; ++i) if (i >= 0) - writetype[types[i]] = TRUE; + writetype[types[i]] = true; /* - * * For America/Godthab and Antarctica/Palmer + * For America/Godthab and Antarctica/Palmer */ if (thistimei == 0) - writetype[0] = TRUE; + writetype[0] = true; + } +#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH + + /* + * For some pre-2011 systems: if the last-to-be-written standard (or + * daylight) type has an offset different from the most recently used + * offset, append an (unused) copy of the most recently used type (to + * help get global "altzone" and "timezone" variables set correctly). + */ + { + int mrudst, + mrustd, + hidst, + histd, + type; + + hidst = histd = mrudst = mrustd = -1; + for (i = thistimei; i < thistimelim; ++i) + if (isdsts[types[i]]) + mrudst = types[i]; + else + mrustd = types[i]; + for (i = 0; i < typecnt; ++i) + if (writetype[i]) + { + if (isdsts[i]) + hidst = i; + else + histd = i; + } + if (hidst >= 0 && mrudst >= 0 && hidst != mrudst && + gmtoffs[hidst] != gmtoffs[mrudst]) + { + isdsts[mrudst] = -1; + type = addtype(gmtoffs[mrudst], + &chars[abbrinds[mrudst]], + true, + ttisstds[mrudst], + ttisgmts[mrudst]); + isdsts[mrudst] = 1; + writetype[type] = true; + } + if (histd >= 0 && mrustd >= 0 && histd != mrustd && + gmtoffs[histd] != gmtoffs[mrustd]) + { + isdsts[mrustd] = -1; + type = addtype(gmtoffs[mrustd], + &chars[abbrinds[mrustd]], + false, + ttisstds[mrustd], + ttisgmts[mrustd]); + isdsts[mrustd] = 0; + writetype[type] = true; + } } +#endif /* !defined + * LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */ thistypecnt = 0; for (i = 0; i < typecnt; ++i) typemap[i] = writetype[i] ? thistypecnt++ : -1; @@ -1723,7 +2005,7 @@ writezone(const char *name, const char *string) thischarcnt = 0; for (i = 0; i < typecnt; ++i) { - register char *thisabbr; + char *thisabbr; if (!writetype[i]) continue; @@ -1735,23 +2017,22 @@ writezone(const char *name, const char *string) break; if (j == thischarcnt) { - (void) strcpy(&thischars[(int) thischarcnt], - thisabbr); + strcpy(&thischars[(int) thischarcnt], + thisabbr); thischarcnt += strlen(thisabbr) + 1; } indmap[abbrinds[i]] = j; } -#define DO(field) (void) fwrite((void *) tzh.field, \ - (size_t) sizeof tzh.field, (size_t) 1, fp) +#define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp) tzh = tzh0; - (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); - tzh.tzh_version[0] = ZIC_VERSION; - convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt); - convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt); - convert(eitol(thisleapcnt), tzh.tzh_leapcnt); - convert(eitol(thistimecnt), tzh.tzh_timecnt); - convert(eitol(thistypecnt), tzh.tzh_typecnt); - convert(eitol(thischarcnt), tzh.tzh_charcnt); + strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); + tzh.tzh_version[0] = version; + convert(thistypecnt, tzh.tzh_ttisgmtcnt); + convert(thistypecnt, tzh.tzh_ttisstdcnt); + convert(thisleapcnt, tzh.tzh_leapcnt); + convert(thistimecnt, tzh.tzh_timecnt); + convert(thistypecnt, tzh.tzh_typecnt); + convert(thischarcnt, tzh.tzh_charcnt); DO(tzh_magic); DO(tzh_version); DO(tzh_reserved); @@ -1764,7 +2045,12 @@ writezone(const char *name, const char *string) #undef DO for (i = thistimei; i < thistimelim; ++i) if (pass == 1) - puttzcode((long) ats[i], fp); + + /* + * Output an INT32_MIN "transition" if appropriate; see above. + */ + puttzcode(((ats[i] < PG_INT32_MIN) ? + PG_INT32_MIN : ats[i]), fp); else { puttzcode64(ats[i], fp); @@ -1779,7 +2065,7 @@ writezone(const char *name, const char *string) /* filter out assorted junk entries */ if (strcmp(thisabbrev, GRANDPARENTED) != 0 && strcmp(thisabbrev, "zzz") != 0) - fprintf(stdout, "%s\t%ld%s\n", + fprintf(stdout, "%s\t" INT64_FORMAT "%s\n", thisabbrev, gmtoffs[tm], isdsts[tm] ? "\tD" : ""); @@ -1790,25 +2076,21 @@ writezone(const char *name, const char *string) unsigned char uc; uc = typemap[types[i]]; - (void) fwrite((void *) &uc, - (size_t) sizeof uc, - (size_t) 1, - fp); + fwrite(&uc, sizeof uc, 1, fp); } for (i = 0; i < typecnt; ++i) if (writetype[i]) { puttzcode(gmtoffs[i], fp); - (void) putc(isdsts[i], fp); - (void) putc((unsigned char) indmap[abbrinds[i]], fp); + putc(isdsts[i], fp); + putc((unsigned char) indmap[abbrinds[i]], fp); } if (thischarcnt != 0) - (void) fwrite((void *) thischars, - (size_t) sizeof thischars[0], - (size_t) thischarcnt, fp); + fwrite(thischars, sizeof thischars[0], + thischarcnt, fp); for (i = thisleapi; i < thisleaplim; ++i) { - register zic_t todo; + zic_t todo; if (roll[i]) { @@ -1835,70 +2117,113 @@ writezone(const char *name, const char *string) else todo = trans[i]; if (pass == 1) - puttzcode((long) todo, fp); + puttzcode(todo, fp); else puttzcode64(todo, fp); puttzcode(corr[i], fp); } for (i = 0; i < typecnt; ++i) if (writetype[i]) - (void) putc(ttisstds[i], fp); + putc(ttisstds[i], fp); for (i = 0; i < typecnt; ++i) if (writetype[i]) - (void) putc(ttisgmts[i], fp); + putc(ttisgmts[i], fp); + } + fprintf(fp, "\n%s\n", string); + close_file(fp, fullname); + free(ats); + free(fullname); +} + +static char const * +abbroffset(char *buf, zic_t offset) +{ + char sign = '+'; + int seconds, + minutes; + + if (offset < 0) + { + offset = -offset; + sign = '-'; } - (void) fprintf(fp, "\n%s\n", string); - if (ferror(fp) || fclose(fp)) + + seconds = offset % SECSPERMIN; + offset /= SECSPERMIN; + minutes = offset % MINSPERHOUR; + offset /= MINSPERHOUR; + if (100 <= offset) { - (void) fprintf(stderr, _("%s: Error writing %s\n"), - progname, fullname); - exit(EXIT_FAILURE); + error(_("%%z UTC offset magnitude exceeds 99:59:59")); + return "%z"; + } + else + { + char *p = buf; + + *p++ = sign; + *p++ = '0' + offset / 10; + *p++ = '0' + offset % 10; + if (minutes | seconds) + { + *p++ = '0' + minutes / 10; + *p++ = '0' + minutes % 10; + if (seconds) + { + *p++ = '0' + seconds / 10; + *p++ = '0' + seconds % 10; + } + } + *p = '\0'; + return buf; } } -static void -doabbr(char *abbr, const char *format, const char *letters, int isdst, - int doquotes) +static size_t +doabbr(char *abbr, struct zone const * zp, char const * letters, + zic_t stdoff, bool doquotes) { char *cp; char *slashp; - int len; + size_t len; + char const *format = zp->z_format; slashp = strchr(format, '/'); if (slashp == NULL) { - if (letters == NULL) - (void) strcpy(abbr, format); - else - (void) sprintf(abbr, format, letters); + char letterbuf[PERCENT_Z_LEN_BOUND + 1]; + + if (zp->z_format_specifier == 'z') + letters = abbroffset(letterbuf, zp->z_gmtoff + stdoff); + else if (!letters) + letters = "%s"; + sprintf(abbr, format, letters); + } + else if (stdoff != 0) + { + strcpy(abbr, slashp + 1); } - else if (isdst) - (void) strcpy(abbr, slashp + 1); else { - if (slashp > format) - (void) strncpy(abbr, format, - (unsigned) (slashp - format)); + memcpy(abbr, format, slashp - format); abbr[slashp - format] = '\0'; } - if (!doquotes) - return; - for (cp = abbr; *cp != '\0'; ++cp) - if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL && - strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL) - break; len = strlen(abbr); + if (!doquotes) + return len; + for (cp = abbr; is_alpha(*cp); cp++) + continue; if (len > 0 && *cp == '\0') - return; + return len; abbr[len + 2] = '\0'; abbr[len + 1] = '>'; - for (; len > 0; --len) - abbr[len] = abbr[len - 1]; + memmove(abbr + 1, abbr, len); abbr[0] = '<'; + return len + 2; } static void -updateminmax(int x) +updateminmax(const zic_t x) { if (min_year > x) min_year = x; @@ -1907,44 +2232,46 @@ updateminmax(int x) } static int -stringoffset(char *result, long offset) +stringoffset(char *result, zic_t offset) { int hours; int minutes; int seconds; + bool negative = offset < 0; + int len = negative; - result[0] = '\0'; - if (offset < 0) + if (negative) { - (void) strcpy(result, "-"); offset = -offset; + result[0] = '-'; } seconds = offset % SECSPERMIN; offset /= SECSPERMIN; minutes = offset % MINSPERHOUR; offset /= MINSPERHOUR; hours = offset; - if (hours >= HOURSPERDAY) + if (hours >= HOURSPERDAY * DAYSPERWEEK) { result[0] = '\0'; - return -1; + return 0; } - (void) sprintf(end(result), "%d", hours); + len += sprintf(result + len, "%d", hours); if (minutes != 0 || seconds != 0) { - (void) sprintf(end(result), ":%02d", minutes); + len += sprintf(result + len, ":%02d", minutes); if (seconds != 0) - (void) sprintf(end(result), ":%02d", seconds); + len += sprintf(result + len, ":%02d", seconds); } - return 0; + return len; } static int -stringrule(char *result, const struct rule * rp, long dstoff, long gmtoff) +stringrule(char *result, const struct rule * const rp, const zic_t dstoff, + const zic_t gmtoff) { - long tod; + zic_t tod = rp->r_tod; + int compat = 0; - result = end(result); if (rp->r_dycode == DC_DOM) { int month, @@ -1955,17 +2282,26 @@ stringrule(char *result, const struct rule * rp, long dstoff, long gmtoff) total = 0; for (month = 0; month < rp->r_month; ++month) total += len_months[0][month]; - (void) sprintf(result, "J%d", total + rp->r_dayofmonth); + /* Omit the "J" in Jan and Feb, as that's shorter. */ + if (rp->r_month <= 1) + result += sprintf(result, "%d", total + rp->r_dayofmonth - 1); + else + result += sprintf(result, "J%d", total + rp->r_dayofmonth); } else { int week; + int wday = rp->r_wday; + int wdayoff; if (rp->r_dycode == DC_DOWGEQ) { - week = 1 + rp->r_dayofmonth / DAYSPERWEEK; - if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth) - return -1; + wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK; + if (wdayoff) + compat = 2013; + wday -= wdayoff; + tod += wdayoff * SECSPERDAY; + week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK; } else if (rp->r_dycode == DC_DOWLEQ) { @@ -1973,37 +2309,64 @@ stringrule(char *result, const struct rule * rp, long dstoff, long gmtoff) week = 5; else { - week = 1 + rp->r_dayofmonth / DAYSPERWEEK; - if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth) - return -1; + wdayoff = rp->r_dayofmonth % DAYSPERWEEK; + if (wdayoff) + compat = 2013; + wday -= wdayoff; + tod += wdayoff * SECSPERDAY; + week = rp->r_dayofmonth / DAYSPERWEEK; } } else return -1; /* "cannot happen" */ - (void) sprintf(result, "M%d.%d.%d", - rp->r_month + 1, week, rp->r_wday); + if (wday < 0) + wday += DAYSPERWEEK; + result += sprintf(result, "M%d.%d.%d", + rp->r_month + 1, week, wday); } - tod = rp->r_tod; if (rp->r_todisgmt) tod += gmtoff; if (rp->r_todisstd && rp->r_stdoff == 0) tod += dstoff; - if (tod < 0) - { - result[0] = '\0'; - return -1; - } if (tod != 2 * SECSPERMIN * MINSPERHOUR) { - (void) strcat(result, "/"); - if (stringoffset(end(result), tod) != 0) + *result++ = '/'; + if (!stringoffset(result, tod)) return -1; + if (tod < 0) + { + if (compat < 2013) + compat = 2013; + } + else if (SECSPERDAY <= tod) + { + if (compat < 1994) + compat = 1994; + } } - return 0; + return compat; } -static void -stringzone(char *result, const struct zone * zpfirst, int zonecount) +static int +rule_cmp(struct rule const * a, struct rule const * b) +{ + if (!a) + return -!!b; + if (!b) + return 1; + if (a->r_hiyear != b->r_hiyear) + return a->r_hiyear < b->r_hiyear ? -1 : 1; + if (a->r_month - b->r_month != 0) + return a->r_month - b->r_month; + return a->r_dayofmonth - b->r_dayofmonth; +} + +enum +{ +YEAR_BY_YEAR_ZONE = 1}; + +static int +stringzone(char *result, const struct zone * const zpfirst, const int zonecount) { const struct zone *zp; struct rule *rp; @@ -2011,6 +2374,12 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount) struct rule *dstrp; int i; const char *abbrvar; + int compat = 0; + int c; + size_t len; + int offsetlen; + struct rule stdr, + dstr; result[0] = '\0'; zp = zpfirst + zonecount - 1; @@ -2018,7 +2387,7 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount) for (i = 0; i < zp->z_nrules; ++i) { rp = &zp->z_rules[i]; - if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX) + if (rp->r_hiwasnum || rp->r_hiyear != ZIC_MAX) continue; if (rp->r_yrtype != NULL) continue; @@ -2027,32 +2396,32 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount) if (stdrp == NULL) stdrp = rp; else - return; + return -1; } else { if (dstrp == NULL) dstrp = rp; else - return; + return -1; } } if (stdrp == NULL && dstrp == NULL) { /* - * There are no rules running through "max". Let's find the latest - * rule. + * There are no rules running through "max". Find the latest std rule + * in stdabbrrp and latest rule of any type in stdrp. */ + struct rule *stdabbrrp = NULL; + for (i = 0; i < zp->z_nrules; ++i) { rp = &zp->z_rules[i]; - if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear || - (rp->r_hiyear == stdrp->r_hiyear && - rp->r_month > stdrp->r_month)) + if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0) + stdabbrrp = rp; + if (rule_cmp(stdrp, rp) < 0) stdrp = rp; } - if (stdrp != NULL && stdrp->r_stdoff != 0) - return; /* We end up in DST (a POSIX no-no). */ /* * Horrid special case: if year is 2037, presume this is a zone @@ -2060,39 +2429,75 @@ stringzone(char *result, const struct zone * zpfirst, int zonecount) * zone. */ if (stdrp != NULL && stdrp->r_hiyear == 2037) - return; + return YEAR_BY_YEAR_ZONE; + + if (stdrp != NULL && stdrp->r_stdoff != 0) + { + /* Perpetual DST. */ + dstr.r_month = TM_JANUARY; + dstr.r_dycode = DC_DOM; + dstr.r_dayofmonth = 1; + dstr.r_tod = 0; + dstr.r_todisstd = dstr.r_todisgmt = false; + dstr.r_stdoff = stdrp->r_stdoff; + dstr.r_abbrvar = stdrp->r_abbrvar; + stdr.r_month = TM_DECEMBER; + stdr.r_dycode = DC_DOM; + stdr.r_dayofmonth = 31; + stdr.r_tod = SECSPERDAY + stdrp->r_stdoff; + stdr.r_todisstd = stdr.r_todisgmt = false; + stdr.r_stdoff = 0; + stdr.r_abbrvar + = (stdabbrrp ? stdabbrrp->r_abbrvar : ""); + dstrp = &dstr; + stdrp = &stdr; + } } if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0)) - return; + return -1; abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; - doabbr(result, zp->z_format, abbrvar, FALSE, TRUE); - if (stringoffset(end(result), -zp->z_gmtoff) != 0) + len = doabbr(result, zp, abbrvar, 0, true); + offsetlen = stringoffset(result + len, -zp->z_gmtoff); + if (!offsetlen) { result[0] = '\0'; - return; + return -1; } + len += offsetlen; if (dstrp == NULL) - return; - doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE); + return compat; + len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true); if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) - if (stringoffset(end(result), - -(zp->z_gmtoff + dstrp->r_stdoff)) != 0) + { + offsetlen = stringoffset(result + len, + -(zp->z_gmtoff + dstrp->r_stdoff)); + if (!offsetlen) { result[0] = '\0'; - return; + return -1; } - (void) strcat(result, ","); - if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) + len += offsetlen; + } + result[len++] = ','; + c = stringrule(result + len, dstrp, dstrp->r_stdoff, zp->z_gmtoff); + if (c < 0) { result[0] = '\0'; - return; + return -1; } - (void) strcat(result, ","); - if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) + if (compat < c) + compat = c; + len += strlen(result + len); + result[len++] = ','; + c = stringrule(result + len, stdrp, dstrp->r_stdoff, zp->z_gmtoff); + if (c < 0) { result[0] = '\0'; - return; + return -1; } + if (compat < c) + compat = c; + return compat; } static void @@ -2102,28 +2507,34 @@ outzone(const struct zone * zpfirst, int zonecount) struct rule *rp; int i, j; - int usestart, + bool usestart, useuntil; - zic_t starttime = 0; - zic_t untiltime = 0; - long gmtoff; - long stdoff; - int year; - long startoff; - int startttisstd; - int startttisgmt; + zic_t starttime, + untiltime; + zic_t gmtoff; + zic_t stdoff; + zic_t year; + zic_t startoff; + bool startttisstd; + bool startttisgmt; int type; char *startbuf; char *ab; char *envvar; int max_abbr_len; int max_envvar_len; + bool prodstic; /* all rules are min to max */ + int compat; + bool do_extend; + char version; max_abbr_len = 2 + max_format_len + max_abbrvar_len; max_envvar_len = 2 * max_abbr_len + 5 * 9; startbuf = emalloc(max_abbr_len + 1); ab = emalloc(max_abbr_len + 1); envvar = emalloc(max_envvar_len + 1); + INITIALIZE(untiltime); + INITIALIZE(starttime); /* * Now. . .finally. . .generate some useful data! @@ -2131,18 +2542,19 @@ outzone(const struct zone * zpfirst, int zonecount) timecnt = 0; typecnt = 0; charcnt = 0; + prodstic = zonecount == 1; /* * Thanks to Earl Chew for noting the need to unconditionally initialize * startttisstd. */ - startttisstd = FALSE; - startttisgmt = FALSE; + startttisstd = false; + startttisgmt = false; min_year = max_year = EPOCH_YEAR; if (leapseen) { updateminmax(leapminyear); - updateminmax(leapmaxyear + (leapmaxyear < INT_MAX)); + updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX)); } for (i = 0; i < zonecount; ++i) { @@ -2156,33 +2568,71 @@ outzone(const struct zone * zpfirst, int zonecount) updateminmax(rp->r_loyear); if (rp->r_hiwasnum) updateminmax(rp->r_hiyear); + if (rp->r_lowasnum || rp->r_hiwasnum) + prodstic = false; } } /* * Generate lots of data if a rule can't cover all future times. */ - stringzone(envvar, zpfirst, zonecount); - if (noise && envvar[0] == '\0') - { - char *wp; - - wp = ecpyalloc(_("no POSIX environment variable for zone")); - wp = ecatalloc(wp, " "); - wp = ecatalloc(wp, zpfirst->z_name); - warning(wp); - ifree(wp); + compat = stringzone(envvar, zpfirst, zonecount); + version = compat < 2013 ? ZIC_VERSION_PRE_2013 : ZIC_VERSION; + do_extend = compat < 0 || compat == YEAR_BY_YEAR_ZONE; + if (noise) + { + if (!*envvar) + warning("%s %s", + _("no POSIX environment variable for zone"), + zpfirst->z_name); + else if (compat != 0 && compat != YEAR_BY_YEAR_ZONE) + { + /* + * Circa-COMPAT clients, and earlier clients, might not work for + * this zone when given dates before 1970 or after 2038. + */ + warning(_("%s: pre-%d clients may mishandle" + " distant timestamps"), + zpfirst->z_name, compat); + } } - if (envvar[0] == '\0') + if (do_extend) { - if (min_year >= INT_MIN + YEARSPERREPEAT) - min_year -= YEARSPERREPEAT; + /* + * Search through a couple of extra years past the obvious 400, to + * avoid edge cases. For example, suppose a non-POSIX rule applies + * from 2012 onwards and has transitions in March and September, plus + * some one-off transitions in November 2013. If zic looked only at + * the last 400 years, it would set max_year=2413, with the intent + * that the 400 years 2014 through 2413 will be repeated. The last + * transition listed in the tzfile would be in 2413-09, less than 400 + * years after the last one-off transition in 2013-11. Two years + * might be overkill, but with the kind of edge cases available we're + * not sure that one year would suffice. + */ + enum + { + years_of_observations = YEARSPERREPEAT + 2}; + + if (min_year >= ZIC_MIN + years_of_observations) + min_year -= years_of_observations; else - min_year = INT_MIN; - if (max_year <= INT_MAX - YEARSPERREPEAT) - max_year += YEARSPERREPEAT; + min_year = ZIC_MIN; + if (max_year <= ZIC_MAX - years_of_observations) + max_year += years_of_observations; else - max_year = INT_MAX; + max_year = ZIC_MAX; + + /* + * Regardless of any of the above, for a "proDSTic" zone which + * specifies that its rules always have and always will be in effect, + * we only need one cycle to define the zone. + */ + if (prodstic) + { + min_year = 1900; + max_year = min_year + years_of_observations; + } } /* @@ -2199,9 +2649,9 @@ outzone(const struct zone * zpfirst, int zonecount) */ stdoff = 0; zp = &zpfirst[i]; - usestart = i > 0 && (zp - 1)->z_untiltime > min_time; + usestart = i > 0 && (zp - 1)->z_untiltime > big_bang_time; useuntil = i < (zonecount - 1); - if (useuntil && zp->z_untiltime <= min_time) + if (useuntil && zp->z_untiltime <= big_bang_time) continue; gmtoff = zp->z_gmtoff; eat(zp->z_filename, zp->z_linenum); @@ -2210,18 +2660,17 @@ outzone(const struct zone * zpfirst, int zonecount) if (zp->z_nrules == 0) { stdoff = zp->z_stdoff; - doabbr(startbuf, zp->z_format, - (char *) NULL, stdoff != 0, FALSE); + doabbr(startbuf, zp, NULL, stdoff, false); type = addtype(oadd(zp->z_gmtoff, stdoff), startbuf, stdoff != 0, startttisstd, startttisgmt); if (usestart) { addtt(starttime, type); - usestart = FALSE; + usestart = false; } - else if (stdoff != 0) - addtt(min_time, type); + else + addtt(big_bang_time, type); } else for (year = min_year; year <= max_year; ++year) @@ -2249,12 +2698,12 @@ outzone(const struct zone * zpfirst, int zonecount) int k; zic_t jtime, ktime = 0; - long offset; + zic_t offset; if (useuntil) { /* - * Turn untiltime into UTC assuming the current gmtoff + * Turn untiltime into UT assuming the current gmtoff * and stdoff values. */ untiltime = zp->z_untiltime; @@ -2291,43 +2740,55 @@ outzone(const struct zone * zpfirst, int zonecount) k = j; ktime = jtime; } + else if (jtime == ktime) + { + char const *dup_rules_msg = + _("two rules for same instant"); + + eats(zp->z_filename, zp->z_linenum, + rp->r_filename, rp->r_linenum); + warning("%s", dup_rules_msg); + rp = &zp->z_rules[k]; + eats(zp->z_filename, zp->z_linenum, + rp->r_filename, rp->r_linenum); + error("%s", dup_rules_msg); + } } if (k < 0) break; /* go on to next year */ rp = &zp->z_rules[k]; - rp->r_todo = FALSE; + rp->r_todo = false; if (useuntil && ktime >= untiltime) break; stdoff = rp->r_stdoff; if (usestart && ktime == starttime) - usestart = FALSE; + usestart = false; if (usestart) { if (ktime < starttime) { startoff = oadd(zp->z_gmtoff, stdoff); - doabbr(startbuf, zp->z_format, + doabbr(startbuf, zp, rp->r_abbrvar, - rp->r_stdoff != 0, - FALSE); + rp->r_stdoff, + false); continue; } if (*startbuf == '\0' && startoff == oadd(zp->z_gmtoff, stdoff)) { doabbr(startbuf, - zp->z_format, + zp, rp->r_abbrvar, - rp->r_stdoff != - 0, - FALSE); + rp->r_stdoff, + false); } } eats(zp->z_filename, zp->z_linenum, rp->r_filename, rp->r_linenum); - doabbr(ab, zp->z_format, rp->r_abbrvar, - rp->r_stdoff != 0, FALSE); + doabbr(ab, zp, rp->r_abbrvar, + rp->r_stdoff, false); offset = oadd(zp->z_gmtoff, rp->r_stdoff); type = addtype(offset, ab, rp->r_stdoff != 0, rp->r_todisstd, rp->r_todisgmt); @@ -2340,7 +2801,7 @@ outzone(const struct zone * zpfirst, int zonecount) zp->z_format != NULL && strchr(zp->z_format, '%') == NULL && strchr(zp->z_format, '/') == NULL) - (void) strcpy(startbuf, zp->z_format); + strcpy(startbuf, zp->z_format); eat(zp->z_filename, zp->z_linenum); if (*startbuf == '\0') error(_("cannot determine time zone abbreviation to use just after until time")); @@ -2366,62 +2827,82 @@ outzone(const struct zone * zpfirst, int zonecount) starttime = tadd(starttime, -gmtoff); } } - writezone(zpfirst->z_name, envvar); - ifree(startbuf); - ifree(ab); - ifree(envvar); + if (do_extend) + { + /* + * If we're extending the explicitly listed observations for 400 years + * because we can't fill the POSIX-TZ field, check whether we actually + * ended up explicitly listing observations through that period. If + * there aren't any near the end of the 400-year period, add a + * redundant one at the end of the final year, to make it clear that + * we are claiming to have definite knowledge of the lack of + * transitions up to that point. + */ + struct rule xr; + struct attype *lastat; + + xr.r_month = TM_JANUARY; + xr.r_dycode = DC_DOM; + xr.r_dayofmonth = 1; + xr.r_tod = 0; + for (lastat = &attypes[0], i = 1; i < timecnt; i++) + if (attypes[i].at > lastat->at) + lastat = &attypes[i]; + if (lastat->at < rpytime(&xr, max_year - 1)) + { + /* + * Create new type code for the redundant entry, to prevent it + * being optimized away. + */ + if (typecnt >= TZ_MAX_TYPES) + { + error(_("too many local time types")); + exit(EXIT_FAILURE); + } + gmtoffs[typecnt] = gmtoffs[lastat->type]; + isdsts[typecnt] = isdsts[lastat->type]; + ttisstds[typecnt] = ttisstds[lastat->type]; + ttisgmts[typecnt] = ttisgmts[lastat->type]; + abbrinds[typecnt] = abbrinds[lastat->type]; + ++typecnt; + addtt(rpytime(&xr, max_year + 1), typecnt - 1); + } + } + writezone(zpfirst->z_name, envvar, version); + free(startbuf); + free(ab); + free(envvar); } static void -addtt(const zic_t starttime, int type) +addtt(zic_t starttime, int type) { - if (starttime <= min_time || - (timecnt == 1 && attypes[0].at < min_time)) + if (starttime <= big_bang_time || + (timecnt == 1 && attypes[0].at < big_bang_time)) { gmtoffs[0] = gmtoffs[type]; isdsts[0] = isdsts[type]; ttisstds[0] = ttisstds[type]; ttisgmts[0] = ttisgmts[type]; if (abbrinds[type] != 0) - (void) strcpy(chars, &chars[abbrinds[type]]); + strcpy(chars, &chars[abbrinds[type]]); abbrinds[0] = 0; charcnt = strlen(chars) + 1; typecnt = 1; timecnt = 0; type = 0; } - if (timecnt >= TZ_MAX_TIMES) - { - error(_("too many transitions?!")); - exit(EXIT_FAILURE); - } + attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc); attypes[timecnt].at = starttime; attypes[timecnt].type = type; ++timecnt; } static int -addtype(long gmtoff, const char *abbr, int isdst, - int ttisstd, int ttisgmt) +addtype(zic_t gmtoff, char const * abbr, bool isdst, bool ttisstd, bool ttisgmt) { - int i; - int j; - - if (isdst != TRUE && isdst != FALSE) - { - error(_("internal error - addtype called with bad isdst")); - exit(EXIT_FAILURE); - } - if (ttisstd != TRUE && ttisstd != FALSE) - { - error(_("internal error - addtype called with bad ttisstd")); - exit(EXIT_FAILURE); - } - if (ttisgmt != TRUE && ttisgmt != FALSE) - { - error(_("internal error - addtype called with bad ttisgmt")); - exit(EXIT_FAILURE); - } + int i, + j; /* * See if there's already an entry for this zone type. If so, just return @@ -2446,7 +2927,7 @@ addtype(long gmtoff, const char *abbr, int isdst, } if (!(-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) { - error(_("UTC offset out of range")); + error(_("UT offset out of range")); exit(EXIT_FAILURE); } gmtoffs[i] = gmtoff; @@ -2465,10 +2946,10 @@ addtype(long gmtoff, const char *abbr, int isdst, } static void -leapadd(const zic_t t, int positive, int rolling, int count) +leapadd(zic_t t, bool positive, int rolling, int count) { - int i; - int j; + int i, + j; if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) { @@ -2494,7 +2975,7 @@ leapadd(const zic_t t, int positive, int rolling, int count) roll[j] = roll[j - 1]; } trans[i] = t; - corr[i] = positive ? 1L : eitol(-count); + corr[i] = positive ? 1 : -count; roll[i] = rolling; ++leapcnt; } while (positive && --count != 0); @@ -2504,7 +2985,7 @@ static void adjleap(void) { int i; - long last = 0; + zic_t last = 0; /* * propagate leap seconds forward @@ -2516,61 +2997,201 @@ adjleap(void) } } -static int +static bool yearistype(int year, const char *type) { static char *buf; int result; if (type == NULL || *type == '\0') - return TRUE; - buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type))); - (void) sprintf(buf, "%s %d %s", yitcommand, year, type); + return true; + buf = erealloc(buf, 132 + strlen(yitcommand) + strlen(type)); + sprintf(buf, "%s %d %s", yitcommand, year, type); result = system(buf); if (WIFEXITED(result)) switch (WEXITSTATUS(result)) { case 0: - return TRUE; + return true; case 1: - return FALSE; + return false; } error(_("Wild result from command execution")); - (void) fprintf(stderr, _("%s: command was '%s', result was %d\n"), - progname, buf, result); + fprintf(stderr, _("%s: command was '%s', result was %d\n"), + progname, buf, result); for (;;) exit(EXIT_FAILURE); } -static int -lowerit(int a) +/* Is A a space character in the C locale? */ +static bool +is_space(char a) { - a = (unsigned char) a; - return (isascii(a) && isupper(a)) ? tolower(a) : a; + switch (a) + { + default: + return false; + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + return true; + } } -static int +/* Is A an alphabetic character in the C locale? */ +static bool +is_alpha(char a) +{ + switch (a) + { + default: + return false; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + return true; + } +} + +/* If A is an uppercase character in the C locale, return its lowercase + * counterpart. Otherwise, return A. */ +static char +lowerit(char a) +{ + switch (a) + { + default: + return a; + case 'A': + return 'a'; + case 'B': + return 'b'; + case 'C': + return 'c'; + case 'D': + return 'd'; + case 'E': + return 'e'; + case 'F': + return 'f'; + case 'G': + return 'g'; + case 'H': + return 'h'; + case 'I': + return 'i'; + case 'J': + return 'j'; + case 'K': + return 'k'; + case 'L': + return 'l'; + case 'M': + return 'm'; + case 'N': + return 'n'; + case 'O': + return 'o'; + case 'P': + return 'p'; + case 'Q': + return 'q'; + case 'R': + return 'r'; + case 'S': + return 's'; + case 'T': + return 't'; + case 'U': + return 'u'; + case 'V': + return 'v'; + case 'W': + return 'w'; + case 'X': + return 'x'; + case 'Y': + return 'y'; + case 'Z': + return 'z'; + } +} + +/* case-insensitive equality */ +static bool ciequal(const char *ap, const char *bp) { while (lowerit(*ap) == lowerit(*bp++)) if (*ap++ == '\0') - return TRUE; - return FALSE; + return true; + return false; } -static int +static bool itsabbr(const char *abbr, const char *word) { if (lowerit(*abbr) != lowerit(*word)) - return FALSE; + return false; ++word; while (*++abbr != '\0') do { if (*word == '\0') - return FALSE; + return false; } while (lowerit(*word++) != lowerit(*abbr)); - return TRUE; + return true; } static const struct lookup * @@ -2613,13 +3234,11 @@ getfields(char *cp) if (cp == NULL) return NULL; - array = (char **) (void *) - emalloc((int) ((strlen(cp) + 1) * sizeof *array)); + array = emalloc(size_product(strlen(cp) + 1, sizeof *array)); nsubs = 0; for (;;) { - while (isascii((unsigned char) *cp) && - isspace((unsigned char) *cp)) + while (is_space(*cp)) ++cp; if (*cp == '\0' || *cp == '#') break; @@ -2637,9 +3256,8 @@ getfields(char *cp) error(_("Odd number of quotation marks")); exit(1); } - } while (*cp != '\0' && *cp != '#' && - (!isascii(*cp) || !isspace((unsigned char) *cp))); - if (isascii(*cp) && isspace((unsigned char) *cp)) + } while (*cp && *cp != '#' && !is_space(*cp)); + if (is_space(*cp)) ++cp; *dp = '\0'; } @@ -2647,55 +3265,62 @@ getfields(char *cp) return array; } -static long -oadd(long t1, long t2) +static void +time_overflow(void) { - long t; - - t = t1 + t2; - if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) - { - error(_("time overflow")); - exit(EXIT_FAILURE); - } - return t; + error(_("time overflow")); + exit(EXIT_FAILURE); } static zic_t -tadd(const zic_t t1, long t2) +oadd(zic_t t1, zic_t t2) { - zic_t t; + if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2) + time_overflow(); + return t1 + t2; +} - if (t1 == max_time && t2 > 0) - return max_time; - if (t1 == min_time && t2 < 0) - return min_time; - t = t1 + t2; - if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) +static zic_t +tadd(zic_t t1, zic_t t2) +{ + if (t1 < 0) { - error(_("time overflow")); - exit(EXIT_FAILURE); + if (t2 < min_time - t1) + { + if (t1 != min_time) + time_overflow(); + return min_time; + } } - return t; + else + { + if (max_time - t1 < t2) + { + if (t1 != max_time) + time_overflow(); + return max_time; + } + } + return t1 + t2; } /* - * Given a rule, and a year, compute the date - in seconds since January 1, - * 1970, 00:00 LOCAL time - in that year that the rule refers to. + * Given a rule, and a year, compute the date (in seconds since January 1, + * 1970, 00:00 LOCAL time) in that year that the rule refers to. */ static zic_t -rpytime(const struct rule * rp, int wantedy) +rpytime(const struct rule * rp, zic_t wantedy) { - int y, - m, + int m, i; - long dayoff; /* with a nod to Margaret O. */ - zic_t t; + zic_t dayoff; /* with a nod to Margaret O. */ + zic_t t, + y; - if (wantedy == INT_MIN) + if (wantedy == ZIC_MIN) return min_time; - if (wantedy == INT_MAX) + if (wantedy == ZIC_MAX) return max_time; dayoff = 0; m = TM_JANUARY; @@ -2712,12 +3337,12 @@ rpytime(const struct rule * rp, int wantedy) --y; i = -len_years[isleap(y)]; } - dayoff = oadd(dayoff, eitol(i)); + dayoff = oadd(dayoff, i); } while (m != rp->r_month) { i = len_months[isleap(y)][m]; - dayoff = oadd(dayoff, eitol(i)); + dayoff = oadd(dayoff, i); ++m; } i = rp->r_dayofmonth; @@ -2732,13 +3357,13 @@ rpytime(const struct rule * rp, int wantedy) } } --i; - dayoff = oadd(dayoff, eitol(i)); + dayoff = oadd(dayoff, i); if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { - long wday; + zic_t wday; -#define LDAYSPERWEEK ((long) DAYSPERWEEK) - wday = eitol(EPOCH_WDAY); +#define LDAYSPERWEEK ((zic_t) DAYSPERWEEK) + wday = EPOCH_WDAY; /* * Don't trust mod of negative numbers. @@ -2751,17 +3376,17 @@ rpytime(const struct rule * rp, int wantedy) if (wday < 0) wday += LDAYSPERWEEK; } - while (wday != eitol(rp->r_wday)) + while (wday != rp->r_wday) if (rp->r_dycode == DC_DOWGEQ) { - dayoff = oadd(dayoff, (long) 1); + dayoff = oadd(dayoff, 1); if (++wday >= LDAYSPERWEEK) wday = 0; ++i; } else { - dayoff = oadd(dayoff, (long) -1); + dayoff = oadd(dayoff, -1); if (--wday < 0) wday = LDAYSPERWEEK - 1; --i; @@ -2769,7 +3394,7 @@ rpytime(const struct rule * rp, int wantedy) if (i < 0 || i >= len_months[isleap(y)][m]) { if (noise) - warning(_("rule goes past start/end of month--\ + warning(_("rule goes past start/end of month; \ will not work with pre-2004 versions of zic")); } } @@ -2790,28 +3415,21 @@ newabbr(const char *string) if (strcmp(string, GRANDPARENTED) != 0) { const char *cp; - char *wp; + const char *mp; cp = string; - wp = NULL; - while (isalpha((unsigned char) *cp) || ('0' <= *cp && *cp <= '9') + mp = NULL; + while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9') || *cp == '-' || *cp == '+') ++cp; if (noise && cp - string < 3) - wp = _("time zone abbreviation has fewer than 3 characters"); + mp = _("time zone abbreviation has fewer than 3 characters"); if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) - wp = _("time zone abbreviation has too many characters"); + mp = _("time zone abbreviation has too many characters"); if (*cp != '\0') - wp = _("time zone abbreviation differs from POSIX standard"); - if (wp != NULL) - { - wp = ecpyalloc(wp); - wp = ecatalloc(wp, " ("); - wp = ecatalloc(wp, string); - wp = ecatalloc(wp, ")"); - warning(wp); - ifree(wp); - } + mp = _("time zone abbreviation differs from POSIX standard"); + if (mp != NULL) + warning("%s (%s)", mp, string); } i = strlen(string) + 1; if (charcnt + i > TZ_MAX_CHARS) @@ -2819,18 +3437,18 @@ newabbr(const char *string) error(_("too many, or too long, time zone abbreviations")); exit(EXIT_FAILURE); } - (void) strcpy(&chars[charcnt], string); - charcnt += eitol(i); + strcpy(&chars[charcnt], string); + charcnt += i; } -static int +static bool mkdirs(char *argname) { char *name; char *cp; if (argname == NULL || *argname == '\0') - return 0; + return true; cp = name = ecpyalloc(argname); while ((cp = strchr(cp + 1, '/')) != NULL) { @@ -2840,60 +3458,38 @@ mkdirs(char *argname) /* * DOS drive specifier? */ - if (isalpha((unsigned char) name[0]) && - name[1] == ':' && name[2] == '\0') + if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0') { *cp = '/'; continue; } #endif /* WIN32 */ - if (!itsdir(name)) + + /* + * Try to create it. It's OK if creation fails because the directory + * already exists, perhaps because some other process just created it. + */ + if (mkdir(name, MKDIR_UMASK) != 0) { - /* - * It doesn't seem to exist, so we try to create it. Creation may - * fail because of the directory being created by some other - * multiprocessor, so we get to do extra checking. - */ - if (mkdir(name, MKDIR_UMASK) != 0) + int err = errno; + + if (itsdir(name) <= 0) { - const char *e = strerror(errno); + char const *e = strerror(err); - if (errno != EEXIST || !itsdir(name)) - { - (void) fprintf(stderr, - _("%s: Cannot create directory %s: %s\n"), - progname, name, e); - ifree(name); - return -1; - } + warning(_("%s: Can't create directory" + " %s: %s"), + progname, name, e); + free(name); + return false; } } *cp = '/'; } - ifree(name); - return 0; -} - -static long -eitol(int i) -{ - long l; - - l = i; - if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) - { - (void) fprintf(stderr, - _("%s: %d did not sign extend correctly\n"), - progname, i); - exit(EXIT_FAILURE); - } - return l; + free(name); + return true; } -/* - * UNIX was a registered trademark of The Open Group in 2003. - */ - #ifdef WIN32 /* @@ -2902,18 +3498,11 @@ eitol(int i) int link(const char *oldpath, const char *newpath) { - if (!CopyFile(oldpath, newpath, FALSE)) + if (!CopyFile(oldpath, newpath, false)) + { + _dosmaperr(GetLastError()); return -1; + } return 0; } #endif - -/* - * This allows zic to compile by just returning a dummy value. - * localtime.c references it, but no one uses it from zic. - */ -int -pg_open_tzfile(const char *name, char *canonname) -{ - return -1; -} |