From 670a687dea6773147a227bebfa9d801dae739ee0 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 7 Jan 2016 11:45:07 +0000 Subject: Update timezone code from tzcode 2015g. This patch updates the timezone code from tzcode 2015g. The Makefile and README changes are based on those in Paul's patch . Tested for x86_64 and x86. 2016-01-06 Paul Eggert Joseph Myers * timezone/private.h: Update from tzcode 2015g. * timezone/tzfile.h: Likewise. * timezone/tzselect.ksh: Likewise. * timezone/zdump.c: Likewise. * timezone/zic.c: Likewise. * timezone/ialloc.c: Remove file. * timezone/scheck.c: Likewise. * timezone/Makefile (extra-objs): Remove variable. ($(objpfx)zic): Do not depend on scheck.o and ialloc.o. (tz-cflags): Add -DHAVE_GETTEXT -DUSE_LTZ=0 -Wno-maybe-uninitialized. (CFLAGS-zdump.c): Remove -fwrapv -DNOID -DHAVE_GETTEXT. (CFLAGS-zic.c): Remove -DNOID -DHAVE_GETTEXT. (CFLAGS-ialloc.c): Remove variable. (CFLAGS-scheck.c): Likewise. * timezone/README: Update list of files from tzcode. --- timezone/zic.c | 1061 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 616 insertions(+), 445 deletions(-) (limited to 'timezone/zic.c') diff --git a/timezone/zic.c b/timezone/zic.c index 07d6c30..78ab870 100644 --- a/timezone/zic.c +++ b/timezone/zic.c @@ -23,7 +23,7 @@ typedef int_fast64_t zic_t; #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ #if HAVE_SYS_STAT_H -#include "sys/stat.h" +#include #endif #ifdef S_IRUSR #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) @@ -31,21 +31,6 @@ typedef int_fast64_t zic_t; #define MKDIR_UMASK 0755 #endif -/* -** 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 end(cp) (strchr((cp), '\0')) - struct rule { const char * r_filename; int r_linenum; @@ -54,8 +39,8 @@ struct rule { 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 */ @@ -64,10 +49,10 @@ struct rule { int r_wday; zic_t 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 */ + 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 */ @@ -91,6 +76,7 @@ struct zone { zic_t z_gmtoff; const char * z_rule; const char * z_format; + char z_format_specifier; zic_t z_stdoff; @@ -115,25 +101,25 @@ extern int optind; #endif static void addtt(zic_t starttime, int type); -static int addtype(zic_t gmtoff, const char * abbr, int isdst, - int ttisstd, int ttisgmt); -static void leapadd(zic_t t, int positive, int rolling, int count); +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 void dolink(const char * fromfield, const char * tofield); static char ** getfields(char * buf); -static zic_t 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 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 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 zic_t oadd(zic_t t1, zic_t t2); static void outzone(const struct zone * zp, int ntzones); @@ -143,21 +129,25 @@ static void rulesub(struct rule * rp, const char * typep, const char * monthp, const char * dayp, const char * timep); static zic_t tadd(zic_t t1, zic_t t2); -static int yearistype(int year, const char * type); +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 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_year; static zic_t min_year; -static int noise; +static bool noise; static const char * rfilename; static int rlinenum; static const char * progname; @@ -333,8 +323,8 @@ static struct lookup const end_years[] = { }; static struct lookup const leap_types[] = { - { "Rolling", TRUE }, - { "Stationary", FALSE }, + { "Rolling", true }, + { "Stationary", false }, { NULL, 0 } }; @@ -354,8 +344,8 @@ static struct attype { 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 zic_t corr[TZ_MAX_LEAPS]; @@ -376,22 +366,44 @@ static ATTRIBUTE_PURE size_t size_product(size_t nitems, size_t itemsize) { if (SIZE_MAX / itemsize < nitems) - memory_exhausted("size overflow"); + memory_exhausted(_("size overflow")); return nitems * itemsize; } +#if !HAVE_STRDUP +static char * +strdup(char const *str) +{ + char *result = malloc(strlen(str) + 1); + return result ? strcpy(result, str) : result; +} +#endif + static ATTRIBUTE_PURE void * -memcheck(void *const ptr) +memcheck(void *ptr) { if (ptr == NULL) memory_exhausted(strerror(errno)); return ptr; } -#define emalloc(size) memcheck(malloc(size)) -#define erealloc(ptr, size) memcheck(realloc(ptr, size)) -#define ecpyalloc(ptr) memcheck(icpyalloc(ptr)) -#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp))) +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) @@ -401,7 +413,7 @@ growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc) else { int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX; if ((amax - 1) / 3 * 2 < *nitems_alloc) - memory_exhausted("int overflow"); + memory_exhausted(_("int overflow")); *nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1; return erealloc(ptr, size_product(*nitems_alloc, itemsize)); } @@ -435,13 +447,13 @@ verror(const char *const string, va_list args) ** zic ... 2>&1 | error -t "*" -v ** on BSD systems. */ - fprintf(stderr, _("\"%s\", line %d: "), filename, linenum); + if (filename) + fprintf(stderr, _("\"%s\", line %d: "), filename, linenum); vfprintf(stderr, string, args); if (rfilename != NULL) - (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"), + fprintf(stderr, _(" (rule from \"%s\", line %d)"), rfilename, rlinenum); - (void) fprintf(stderr, "\n"); - ++errors; + fprintf(stderr, "\n"); } static void ATTRIBUTE_FORMAT((printf, 1, 2)) @@ -451,6 +463,7 @@ error(const char *const string, ...) va_start(args, string); verror(string, args); va_end(args); + errors = true; } static void ATTRIBUTE_FORMAT((printf, 1, 2)) @@ -461,19 +474,35 @@ warning(const char *const string, ...) va_start(args, string); verror(string, args); va_end(args); - --errors; + 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 _Noreturn void usage(FILE *stream, int status) { - (void) fprintf(stream, _("%s: usage is %s \ -[ --version ] [ --help ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\ -\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\ -\n\ -Report bugs to %s.\n"), - progname, progname, REPORT_BUGS_TO); - exit(status); + fprintf(stream, + _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n" + "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n" + "\t[ -L leapseconds ] [ filename ... ]\n\n" + "Report bugs to %s.\n"), + progname, progname, REPORT_BUGS_TO); + if (status == EXIT_SUCCESS) + close_file(stream, NULL); + exit(status); } static const char * psxrules; @@ -490,25 +519,26 @@ main(int argc, char **argv) register int c; #ifdef S_IWGRP - (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); + umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); #endif #if HAVE_GETTEXT - (void) setlocale(LC_ALL, ""); + setlocale(LC_ALL, ""); #ifdef TZ_DOMAINDIR - (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); + bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); #endif /* defined TEXTDOMAINDIR */ - (void) textdomain(TZ_DOMAIN); + textdomain(TZ_DOMAIN); #endif /* HAVE_GETTEXT */ progname = argv[0]; if (TYPE_BIT(zic_t) < 64) { - (void) fprintf(stderr, "%s: %s\n", progname, + fprintf(stderr, "%s: %s\n", progname, _("wild compilation-time specification of zic_t")); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } for (i = 1; i < argc; ++i) if (strcmp(argv[i], "--version") == 0) { - (void) printf("zic %s%s\n", PKGVERSION, TZVERSION); - exit(EXIT_SUCCESS); + printf("zic %s%s\n", PKGVERSION, TZVERSION); + close_file(stdout, NULL); + return EXIT_SUCCESS; } else if (strcmp(argv[i], "--help") == 0) { usage(stdout, EXIT_SUCCESS); } @@ -520,57 +550,57 @@ main(int argc, char **argv) if (directory == NULL) directory = optarg; else { - (void) fprintf(stderr, + fprintf(stderr, _("%s: More than one -d option specified\n"), progname); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } break; case 'l': if (lcltime == NULL) lcltime = optarg; else { - (void) fprintf(stderr, + fprintf(stderr, _("%s: More than one -l option specified\n"), progname); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } break; case 'p': if (psxrules == NULL) psxrules = optarg; else { - (void) fprintf(stderr, + fprintf(stderr, _("%s: More than one -p option specified\n"), progname); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } break; case 'y': if (yitcommand == NULL) yitcommand = optarg; else { - (void) fprintf(stderr, + fprintf(stderr, _("%s: More than one -y option specified\n"), progname); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } break; case 'L': if (leapsec == NULL) leapsec = optarg; else { - (void) fprintf(stderr, + fprintf(stderr, _("%s: More than one -L option specified\n"), progname); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } break; case 'v': - noise = TRUE; + noise = true; break; case 's': - (void) printf("%s: -s ignored\n", progname); + warning(_("-s ignored")); break; } if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) @@ -588,7 +618,7 @@ _("%s: More than one -L option specified\n"), 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) { /* @@ -611,53 +641,137 @@ _("%s: More than one -L option specified\n"), warning(_("link to link")); } 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 bool +componentcheck(char const *name, char const *component, + char const *component_end) +{ + enum { component_len_max = 14 }; + size_t component_len = component_end - component; + if (component_len == 0) { + 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) +{ + register 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:;<=>?@[\\]^`{|}~"; + + register 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; + } + } + return componentcheck(name, component, cp); +} + +static char * +relname(char const *dir, char const *base) +{ + if (*base == '/') + return ecpyalloc(base); + else { + 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(const char *const fromfield, const char *const tofield) +dolink(char const *fromfield, char const *tofield) { register char * fromname; register char * toname; + register int fromisdir; - if (fromfield[0] == '/') - fromname = ecpyalloc(fromfield); - else { - fromname = ecpyalloc(directory); - fromname = ecatalloc(fromname, "/"); - fromname = ecatalloc(fromname, fromfield); - } - if (tofield[0] == '/') - toname = ecpyalloc(tofield); - else { - toname = ecpyalloc(directory); - toname = ecatalloc(toname, "/"); - toname = ecatalloc(toname, tofield); - } + 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 - && access(fromname, F_OK) == 0 && !itsdir(fromname)) { + fromisdir = itsdir(fromname); + if (fromisdir) { + char const *e = strerror(fromisdir < 0 ? errno : EPERM); + fprintf(stderr, _("%s: link from %s failed: %s"), + progname, fromname, e); + exit(EXIT_FAILURE); + } + if (itsdir(toname) <= 0) + remove(toname); + if (link(fromname, toname) != 0) { int result; - if (mkdirs(toname) != 0) + if (! mkdirs(toname)) exit(EXIT_FAILURE); result = link(fromname, toname); if (result != 0) { const char *s = fromfield; const char *t; + char *p; + size_t dotdots = 0; register char * symlinkcontents = NULL; do @@ -666,13 +780,13 @@ dolink(const char *const fromfield, const char *const tofield) && ! strncmp (fromfield, tofield, ++s - fromfield)); - for (s = tofield + (t - fromfield); - (s = strchr(s, '/')); - s++) - symlinkcontents = - ecatalloc(symlinkcontents, - "../"); - symlinkcontents = ecatalloc(symlinkcontents, t); + 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); result = symlink(symlinkcontents, toname); if (result == 0) warning(_("hard link failed, symbolic link used")); @@ -684,7 +798,7 @@ warning(_("hard link failed, symbolic link used")); fp = fopen(fromname, "rb"); if (!fp) { const char *e = strerror(errno); - (void) fprintf(stderr, + fprintf(stderr, _("%s: Can't read %s: %s\n"), progname, fromname, e); exit(EXIT_FAILURE); @@ -692,25 +806,15 @@ warning(_("hard link failed, symbolic link used")); tp = fopen(toname, "wb"); if (!tp) { const char *e = strerror(errno); - (void) fprintf(stderr, + fprintf(stderr, _("%s: Can't create %s: %s\n"), progname, toname, e); exit(EXIT_FAILURE); } while ((c = getc(fp)) != EOF) putc(c, tp); - if (ferror(fp) || fclose(fp)) { - (void) fprintf(stderr, - _("%s: Error reading %s\n"), - progname, fromname); - exit(EXIT_FAILURE); - } - if (ferror(tp) || fclose(tp)) { - (void) fprintf(stderr, - _("%s: Error writing %s\n"), - progname, toname); - exit(EXIT_FAILURE); - } + close_file(fp, fromname); + close_file(tp, toname); warning(_("link failed, copy used")); } } @@ -720,8 +824,8 @@ warning(_("hard link failed, symbolic link used")); #define TIME_T_BITS_IN_FILE 64 -static const zic_t min_time = (zic_t) -1 << (TIME_T_BITS_IN_FILE - 1); -static const zic_t max_time = -1 - ((zic_t) -1 << (TIME_T_BITS_IN_FILE - 1)); +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); /* Estimated time of the Big Bang, in seconds since the POSIX epoch. rounded downward to the negation of a power of two that is @@ -753,17 +857,24 @@ static const zic_t max_time = -1 - ((zic_t) -1 << (TIME_T_BITS_IN_FILE - 1)); 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 *const name) +itsdir(char const *name) { - register char * myname; - register int accres; - - myname = ecpyalloc(name); - myname = ecatalloc(myname, "/."); - accres = access(myname, F_OK); - free(myname); - return accres == 0; + struct stat st; + int res = stat(name, &st); + if (res != 0) + return res; +#ifdef S_ISDIR + return S_ISDIR(st.st_mode) != 0; +#else + { + char *nameslashdot = relname(name, "."); + res = stat(nameslashdot, &st); + free(nameslashdot); + return res == 0; + } +#endif } /* @@ -790,7 +901,7 @@ associate(void) register int i, j; if (nrules != 0) { - (void) qsort(rules, nrules, sizeof *rules, rcomp); + qsort(rules, nrules, sizeof *rules, rcomp); for (i = 0; i < nrules - 1; ++i) { if (strcmp(rules[i].r_name, rules[i + 1].r_name) != 0) @@ -843,12 +954,12 @@ 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, '%') != 0) + if (zp->z_format_specifier == 's') error("%s", _("%s in ruleless zone")); } } @@ -864,7 +975,7 @@ infile(const char *name) register char * cp; register const struct lookup * lp; register int nfields; - register int wantcont; + register bool wantcont; register int num; char buf[BUFSIZ]; @@ -874,11 +985,11 @@ infile(const char *name) } else if ((fp = fopen(name, "r")) == NULL) { const char *e = strerror(errno); - (void) fprintf(stderr, _("%s: Can't open %s: %s\n"), + fprintf(stderr, _("%s: Can't open %s: %s\n"), progname, name, e); exit(EXIT_FAILURE); } - wantcont = FALSE; + wantcont = false; for (num = 1; ; ++num) { eat(name, num); if (fgets(buf, sizeof buf, fp) != buf) @@ -909,25 +1020,25 @@ infile(const char *name) else switch ((int) (lp->l_value)) { 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, + fprintf(stderr, _("%s: panic: Invalid l_value %d\n"), progname, lp->l_value); exit(EXIT_FAILURE); @@ -935,18 +1046,7 @@ _("%s: panic: Invalid l_value %d\n"), } free(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); - } + close_file(fp, filename); if (wantcont) error(_("expected continuation line not found")); } @@ -960,10 +1060,11 @@ _("%s: panic: Invalid l_value %d\n"), */ static zic_t -gethms(const char *string, const char *const errstring, const int signable) +gethms(char const *string, char const *errstring, bool signable) { zic_t hh; int mm, ss, sign; + char xs; if (string == NULL || *string == '\0') return 0; @@ -973,12 +1074,12 @@ gethms(const char *string, const char *const errstring, const int signable) sign = -1; ++string; } else sign = 1; - if (sscanf(string, scheck(string, "%"SCNdZIC), &hh) == 1) + if (sscanf(string, "%"SCNdZIC"%c", &hh, &xs) == 1) mm = ss = 0; - else if (sscanf(string, scheck(string, "%"SCNdZIC":%d"), &hh, &mm) == 2) + else if (sscanf(string, "%"SCNdZIC":%d%c", &hh, &mm, &xs) == 2) ss = 0; - else if (sscanf(string, scheck(string, "%"SCNdZIC":%d:%d"), - &hh, &mm, &ss) != 3) { + else if (sscanf(string, "%"SCNdZIC":%d:%d%c", &hh, &mm, &ss, &xs) + != 3) { error("%s", errstring); return 0; } @@ -1000,7 +1101,7 @@ warning(_("values over 24 hours not handled by pre-2007 versions of zic")); } static void -inrule(register char **const fields, const int nfields) +inrule(char **fields, int nfields) { static struct rule r; @@ -1014,7 +1115,7 @@ inrule(register char **const fields, const 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]); @@ -1025,26 +1126,26 @@ inrule(register char **const fields, const int nfields) rules[nrules++] = r; } -static int -inzone(register char **const fields, const int nfields) +static bool +inzone(char **fields, int nfields) { register int i; 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) { error( _("\"Zone %s\" line and -l option are mutually exclusive"), TZDEFAULT); - return FALSE; + return false; } if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { error( _("\"Zone %s\" line and -p option are mutually exclusive"), TZDEFRULES); - return FALSE; + return false; } for (i = 0; i < nzones; ++i) if (zones[i].z_name != NULL && @@ -1054,30 +1155,31 @@ _("duplicate zone name %s (file \"%s\", line %d)"), fields[ZF_NAME], zones[i].z_filename, zones[i].z_linenum); - return FALSE; + return false; } - return inzsub(fields, nfields, FALSE); + return inzsub(fields, nfields, false); } -static int -inzcont(register char **const fields, const int nfields) +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(register char **const fields, const int nfields, const int iscont) +static bool +inzsub(char **fields, int nfields, bool iscont) { register char * cp; + char * cp1; static struct zone z; register int i_gmtoff, i_rule, i_format; register int i_untilyear, i_untilmonth; register int i_untilday, i_untiltime; - register int hasuntil; + register bool hasuntil; if (iscont) { i_gmtoff = ZFC_GMTOFF; @@ -1088,7 +1190,9 @@ inzsub(register char **const fields, const int nfields, const int iscont) i_untilday = ZFC_TILDAY; i_untiltime = ZFC_TILTIME; z.z_name = NULL; - } else { + } else if (!namecheck(fields[ZF_NAME])) + return false; + else { i_gmtoff = ZF_GMTOFF; i_rule = ZF_RULE; i_format = ZF_FORMAT; @@ -1100,15 +1204,23 @@ inzsub(register char **const fields, const int nfields, const int iscont) } z.z_filename = filename; z.z_linenum = linenum; - z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), TRUE); + z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true); if ((cp = strchr(fields[i_format], '%')) != 0) { - if (*++cp != 's' || strchr(cp, '%') != 0) { + 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; @@ -1134,7 +1246,7 @@ inzsub(register char **const fields, const int nfields, const int iscont) error(_( "Zone continuation line end time is not after end time of previous line" )); - return FALSE; + return false; } } zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc); @@ -1147,7 +1259,7 @@ inzsub(register char **const fields, const int nfields, const int iscont) } static void -inleap(register char ** const fields, const int nfields) +inleap(char **fields, int nfields) { register const char * cp; register const struct lookup * lp; @@ -1156,6 +1268,7 @@ inleap(register char ** const fields, const int nfields) int month, day; zic_t dayoff, tod; zic_t t; + char xs; if (nfields != LEAP_FIELDS) { error(_("wrong number of fields on Leap line")); @@ -1163,7 +1276,7 @@ inleap(register char ** const fields, const int nfields) } dayoff = 0; cp = fields[LP_YEAR]; - if (sscanf(cp, scheck(cp, "%"SCNdZIC), &year) != 1) { + if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) { /* ** Leapin' Lizards! */ @@ -1174,7 +1287,7 @@ inleap(register char ** const fields, const int nfields) leapmaxyear = year; if (!leapseen || leapminyear > year) leapminyear = year; - leapseen = TRUE; + leapseen = true; j = EPOCH_YEAR; while (j != year) { if (year > j) { @@ -1198,7 +1311,7 @@ inleap(register char ** const fields, const int nfields) ++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; @@ -1213,23 +1326,23 @@ inleap(register char ** const fields, const int nfields) return; } t = dayoff * SECSPERDAY; - tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); + tod = gethms(fields[LP_TIME], _("invalid time of day"), false); cp = fields[LP_CORR]; { - register int positive; + register 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 { error(_("illegal CORRECTION field on Leap line")); @@ -1251,7 +1364,7 @@ inleap(register char ** const fields, const int nfields) } static void -inlink(register char **const fields, const int nfields) +inlink(char **fields, int nfields) { struct link l; @@ -1263,10 +1376,8 @@ inlink(register char **const fields, const int nfields) error(_("blank FROM field on Link line")); return; } - if (*fields[LF_TO] == '\0') { - error(_("blank TO field on Link line")); - return; - } + if (! namecheck(fields[LF_TO])) + return; l.l_filename = filename; l.l_linenum = linenum; l.l_from = ecpyalloc(fields[LF_FROM]); @@ -1276,50 +1387,47 @@ inlink(register char **const fields, const int nfields) } static void -rulesub(register struct rule *const rp, - const char *const loyearp, - const char *const hiyearp, - const char *const typep, - const char *const monthp, - const char *const dayp, - const char *const timep) +rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, + const char *typep, const char *monthp, const char *dayp, + const char *timep) { register const struct lookup * lp; register const char * cp; register char * dp; register char * ep; + char xs; if ((lp = byword(monthp, mon_names)) == NULL) { error(_("invalid month name")); 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') { ep = dp + strlen(dp) - 1; 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); + rp->r_tod = gethms(dp, _("invalid time of day"), false); free(dp); /* ** Year work. @@ -1335,11 +1443,11 @@ rulesub(register struct rule *const rp, rp->r_loyear = ZIC_MAX; break; default: /* "cannot happen" */ - (void) fprintf(stderr, + fprintf(stderr, _("%s: panic: Invalid l_value %d\n"), progname, lp->l_value); exit(EXIT_FAILURE); - } else if (sscanf(cp, scheck(cp, "%"SCNdZIC), &rp->r_loyear) != 1) { + } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) { error(_("invalid starting year")); return; } @@ -1357,11 +1465,11 @@ rulesub(register struct rule *const rp, rp->r_hiyear = rp->r_loyear; break; default: /* "cannot happen" */ - (void) fprintf(stderr, + fprintf(stderr, _("%s: panic: Invalid l_value %d\n"), progname, lp->l_value); exit(EXIT_FAILURE); - } else if (sscanf(cp, scheck(cp, "%"SCNdZIC), &rp->r_hiyear) != 1) { + } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) { error(_("invalid ending year")); return; } @@ -1414,7 +1522,7 @@ rulesub(register struct rule *const rp, } 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")); @@ -1453,7 +1561,7 @@ puttzcode(const int_fast32_t val, FILE *const fp) char buf[4]; convert(val, buf); - (void) fwrite(buf, sizeof buf, 1, fp); + fwrite(buf, sizeof buf, 1, fp); } static void @@ -1462,7 +1570,7 @@ puttzcode64(const zic_t val, FILE *const fp) char buf[8]; convert64(val, buf); - (void) fwrite(buf, sizeof buf, 1, fp); + fwrite(buf, sizeof buf, 1, fp); } static int @@ -1474,7 +1582,7 @@ atcomp(const void *avp, const void *bvp) return (a < b) ? -1 : (a > b); } -static int +static bool is32(const zic_t x) { return INT32_MIN <= x && x <= INT32_MAX; @@ -1488,7 +1596,7 @@ writezone(const char *const name, const char *const string, char version) register int leapcnt32, leapi32; register int timecnt32, timei32; register int pass; - static char * fullname; + char * fullname; static const struct tzhead tzh0; static struct tzhead tzh; zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1)); @@ -1499,7 +1607,7 @@ writezone(const char *const name, const char *const string, char version) ** Sort. */ if (timecnt > 1) - (void) qsort(attypes, timecnt, sizeof *attypes, atcomp); + qsort(attypes, timecnt, sizeof *attypes, atcomp); /* ** Optimize. */ @@ -1561,7 +1669,7 @@ writezone(const char *const name, const char *const string, char version) ++timei32; } /* - ** Output an INT32_MIN "transition" if appropriate--see below. + ** Output an INT32_MIN "transition" if appropriate; see below. */ if (timei32 > 0 && ats[timei32] > INT32_MIN) { --timei32; @@ -1573,26 +1681,24 @@ writezone(const char *const name, const char *const string, char version) --leapcnt32; ++leapi32; } - fullname = erealloc(fullname, - 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: Can't remove %s: %s\n"), + fprintf(stderr, _("%s: Can't remove %s: %s\n"), progname, fullname, e); exit(EXIT_FAILURE); } if ((fp = fopen(fullname, "wb")) == NULL) { - if (mkdirs(fullname) != 0) + if (! mkdirs(fullname)) exit(EXIT_FAILURE); if ((fp = fopen(fullname, "wb")) == NULL) { const char *e = strerror(errno); - (void) fprintf(stderr, _("%s: Can't create %s: %s\n"), + fprintf(stderr, _("%s: Can't create %s: %s\n"), progname, fullname, e); exit(EXIT_FAILURE); } @@ -1606,7 +1712,7 @@ writezone(const char *const name, const char *const string, char version) register int thistypecnt; char thischars[TZ_MAX_CHARS]; char thischarcnt; - int indmap[TZ_MAX_CHARS]; + int indmap[TZ_MAX_CHARS]; if (pass == 1) { thistimei = timei32; @@ -1629,16 +1735,16 @@ writezone(const char *const name, const char *const string, char version) ** (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 */ if (thistimei == 0) - writetype[0] = TRUE; + writetype[0] = true; } #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH /* @@ -1668,22 +1774,22 @@ writezone(const char *const name, const char *const string, char version) isdsts[mrudst] = -1; type = addtype(gmtoffs[mrudst], &chars[abbrinds[mrudst]], - TRUE, + true, ttisstds[mrudst], ttisgmts[mrudst]); - isdsts[mrudst] = TRUE; - writetype[type] = TRUE; + 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, + false, ttisstds[mrustd], ttisgmts[mrustd]); - isdsts[mrustd] = FALSE; - writetype[type] = TRUE; + isdsts[mrustd] = 0; + writetype[type] = true; } } #endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */ @@ -1705,15 +1811,15 @@ writezone(const char *const name, const char *const string, char version) if (strcmp(&thischars[j], thisabbr) == 0) break; if (j == thischarcnt) { - (void) strcpy(&thischars[(int) thischarcnt], + strcpy(&thischars[(int) thischarcnt], thisabbr); thischarcnt += strlen(thisabbr) + 1; } indmap[abbrinds[i]] = j; } -#define DO(field) ((void) fwrite(tzh.field, sizeof tzh.field, 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); + 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); @@ -1735,7 +1841,7 @@ writezone(const char *const name, const char *const string, char version) if (pass == 1) /* ** Output an INT32_MIN "transition" - ** if appropriate--see above. + ** if appropriate; see above. */ puttzcode(((ats[i] < INT32_MIN) ? INT32_MIN : ats[i]), fp); @@ -1744,16 +1850,16 @@ writezone(const char *const name, const char *const string, char version) unsigned char uc; uc = typemap[types[i]]; - (void) fwrite(&uc, sizeof uc, 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(thischars, sizeof thischars[0], + fwrite(thischars, sizeof thischars[0], thischarcnt, fp); for (i = thisleapi; i < thisleaplim; ++i) { register zic_t todo; @@ -1782,54 +1888,88 @@ writezone(const char *const name, const char *const string, char version) } 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); - } - (void) fprintf(fp, "\n%s\n", string); - if (ferror(fp) || fclose(fp)) { - (void) fprintf(stderr, _("%s: Error writing %s\n"), - progname, fullname); - exit(EXIT_FAILURE); + putc(ttisgmts[i], fp); } + fprintf(fp, "\n%s\n", string); + close_file(fp, fullname); free(ats); + free(fullname); } -static void -doabbr(char *const abbr, const char *const format, const char *const letters, - const int isdst, const int doquotes) +static char const * +abbroffset(char *buf, zic_t offset) +{ + char sign = '+'; + int seconds, minutes; + + if (offset < 0) { + offset = -offset; + sign = '-'; + } + + seconds = offset % SECSPERMIN; + offset /= SECSPERMIN; + minutes = offset % MINSPERHOUR; + offset /= MINSPERHOUR; + if (100 <= offset) { + 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 size_t +doabbr(char *abbr, struct zone const *zp, char const *letters, + zic_t stdoff, bool doquotes) { register char * cp; register char * slashp; - register int len; + register 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); - } else if (isdst) { - (void) strcpy(abbr, slashp + 1); + 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 (slashp > format) - (void) strncpy(abbr, format, 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 @@ -1847,11 +1987,12 @@ stringoffset(char *result, zic_t offset) register int hours; register int minutes; register int seconds; + bool negative = offset < 0; + int len = negative; - result[0] = '\0'; - if (offset < 0) { - (void) strcpy(result, "-"); + if (negative) { offset = -offset; + result[0] = '-'; } seconds = offset % SECSPERMIN; offset /= SECSPERMIN; @@ -1860,15 +2001,15 @@ stringoffset(char *result, zic_t offset) hours = offset; 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 @@ -1878,7 +2019,6 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff, register zic_t tod = rp->r_tod; register int compat = 0; - result = end(result); if (rp->r_dycode == DC_DOM) { register int month, total; @@ -1889,9 +2029,9 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff, total += len_months[0][month]; /* Omit the "J" in Jan and Feb, as that's shorter. */ if (rp->r_month <= 1) - (void) sprintf(result, "%d", total + rp->r_dayofmonth - 1); + result += sprintf(result, "%d", total + rp->r_dayofmonth - 1); else - (void) sprintf(result, "J%d", total + rp->r_dayofmonth); + result += sprintf(result, "J%d", total + rp->r_dayofmonth); } else { register int week; register int wday = rp->r_wday; @@ -1918,16 +2058,16 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff, } else return -1; /* "cannot happen" */ if (wday < 0) wday += DAYSPERWEEK; - (void) sprintf(result, "M%d.%d.%d", - rp->r_month + 1, week, wday); + result += sprintf(result, "M%d.%d.%d", + rp->r_month + 1, week, wday); } if (rp->r_todisgmt) tod += gmtoff; if (rp->r_todisstd && rp->r_stdoff == 0) tod += dstoff; 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) @@ -1967,6 +2107,8 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount) register const char * abbrvar; register int compat = 0; register int c; + size_t len; + int offsetlen; struct rule stdr, dstr; result[0] = '\0'; @@ -2016,14 +2158,14 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount) dstr.r_dycode = DC_DOM; dstr.r_dayofmonth = 1; dstr.r_tod = 0; - dstr.r_todisstd = dstr.r_todisgmt = FALSE; + 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_todisstd = stdr.r_todisgmt = false; stdr.r_stdoff = 0; stdr.r_abbrvar = (stdabbrrp ? stdabbrrp->r_abbrvar : ""); @@ -2034,30 +2176,36 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount) if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0)) 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 -1; } + len += offsetlen; if (dstrp == NULL) return compat; - doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE); - if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) - if (stringoffset(end(result), - -(zp->z_gmtoff + dstrp->r_stdoff)) != 0) { - result[0] = '\0'; - return -1; - } - (void) strcat(result, ","); - c = stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff); + len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true); + if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) { + offsetlen = stringoffset(result + len, + -(zp->z_gmtoff + dstrp->r_stdoff)); + if (! offsetlen) { + result[0] = '\0'; + return -1; + } + len += offsetlen; + } + result[len++] = ','; + c = stringrule(result + len, dstrp, dstrp->r_stdoff, zp->z_gmtoff); if (c < 0) { result[0] = '\0'; return -1; } if (compat < c) compat = c; - (void) strcat(result, ","); - c = stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff); + len += strlen(result + len); + result[len++] = ','; + c = stringrule(result + len, stdrp, dstrp->r_stdoff, zp->z_gmtoff); if (c < 0) { result[0] = '\0'; return -1; @@ -2068,28 +2216,28 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount) } static void -outzone(const struct zone * const zpfirst, const int zonecount) +outzone(const struct zone *zpfirst, int zonecount) { register const struct zone * zp; register struct rule * rp; register int i, j; - register int usestart, useuntil; + register bool usestart, useuntil; register zic_t starttime, untiltime; register zic_t gmtoff; register zic_t stdoff; register zic_t year; register zic_t startoff; - register int startttisstd; - register int startttisgmt; + register bool startttisstd; + register bool startttisgmt; register int type; register char * startbuf; register char * ab; register char * envvar; register int max_abbr_len; register int max_envvar_len; - register int prodstic; /* all rules are min to max */ + register bool prodstic; /* all rules are min to max */ register int compat; - register int do_extend; + register bool do_extend; register char version; max_abbr_len = 2 + max_format_len + max_abbrvar_len; @@ -2110,8 +2258,8 @@ outzone(const struct zone * const zpfirst, const int zonecount) ** 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); @@ -2128,7 +2276,7 @@ outzone(const struct zone * const zpfirst, const int zonecount) if (rp->r_hiwasnum) updateminmax(rp->r_hiyear); if (rp->r_lowasnum || rp->r_hiwasnum) - prodstic = FALSE; + prodstic = false; } } /* @@ -2208,14 +2356,13 @@ outzone(const struct zone * const zpfirst, const int zonecount) startoff = zp->z_gmtoff; if (zp->z_nrules == 0) { stdoff = zp->z_stdoff; - doabbr(startbuf, zp->z_format, - 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 addtt(big_bang_time, type); } else for (year = min_year; year <= max_year; ++year) { if (useuntil && year > zp->z_untilrule.r_hiyear) @@ -2276,42 +2423,51 @@ outzone(const struct zone * const zpfirst, const int zonecount) if (k < 0 || jtime < ktime) { 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); @@ -2323,7 +2479,7 @@ outzone(const struct zone * const zpfirst, const 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(_("can't determine time zone abbreviation to use just after until time")); @@ -2369,7 +2525,7 @@ error(_("can't determine time zone abbreviation to use just after until time")); if (lastat->at < rpytime(&xr, max_year - 1)) { /* ** Create new type code for the redundant entry, - ** to prevent it being optimised away. + ** to prevent it being optimized away. */ if (typecnt >= TZ_MAX_TYPES) { error(_("too many local time types")); @@ -2391,7 +2547,7 @@ error(_("can't determine time zone abbreviation to use just after until time")); } static void -addtt(const zic_t starttime, int type) +addtt(zic_t starttime, int type) { if (starttime <= big_bang_time || (timecnt == 1 && attypes[0].at < big_bang_time)) { @@ -2400,7 +2556,7 @@ addtt(const zic_t starttime, int 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; @@ -2414,23 +2570,10 @@ addtt(const zic_t starttime, int type) } static int -addtype(const zic_t gmtoff, const char *const abbr, const int isdst, - const int ttisstd, const int ttisgmt) +addtype(zic_t gmtoff, char const *abbr, bool isdst, bool ttisstd, bool ttisgmt) { register int i, 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); - } /* ** See if there's already an entry for this zone type. ** If so, just return its index. @@ -2470,7 +2613,7 @@ addtype(const zic_t gmtoff, const char *const abbr, const int isdst, } static void -leapadd(const zic_t t, const int positive, const int rolling, int count) +leapadd(zic_t t, bool positive, int rolling, int count) { register int i, j; @@ -2514,64 +2657,106 @@ adjleap(void) } } -static int -yearistype(const int year, const char *const type) +static bool +yearistype(int year, const char *type) { static char * buf; int result; if (type == NULL || *type == '\0') - return TRUE; + return true; buf = erealloc(buf, 132 + strlen(yitcommand) + strlen(type)); - (void) sprintf(buf, "%s %d %s", yitcommand, year, 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"), + 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) +{ + switch (a) { + default: + return false; + case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': + return true; + } +} + +/* Is A an alphabetic character in the C locale? */ +static bool +is_alpha(char a) { - a = (unsigned char) a; - return (isascii(a) && isupper(a)) ? tolower(a) : 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 ATTRIBUTE_PURE int +static ATTRIBUTE_PURE bool ciequal(register const char *ap, register const char *bp) { while (lowerit(*ap) == lowerit(*bp++)) if (*ap++ == '\0') - return TRUE; - return FALSE; + return true; + return false; } -static ATTRIBUTE_PURE int +static ATTRIBUTE_PURE bool itsabbr(register const char *abbr, register 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 ATTRIBUTE_PURE const struct lookup * -byword(register const char *const word, - register const struct lookup *const table) +byword(const char *word, const struct lookup *table) { register const struct lookup * foundlp; register const struct lookup * lp; @@ -2609,8 +2794,7 @@ getfields(register char *cp) 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; @@ -2627,9 +2811,8 @@ getfields(register char *cp) )); 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'; } @@ -2637,37 +2820,47 @@ getfields(register char *cp) return array; } +static _Noreturn void +time_overflow(void) +{ + error(_("time overflow")); + exit(EXIT_FAILURE); +} + static ATTRIBUTE_PURE zic_t -oadd(const zic_t t1, const zic_t t2) +oadd(zic_t t1, zic_t t2) { - if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2) { - error(_("time overflow")); - exit(EXIT_FAILURE); - } + if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2) + time_overflow(); return t1 + t2; } static ATTRIBUTE_PURE zic_t -tadd(const zic_t t1, const zic_t t2) +tadd(zic_t t1, zic_t t2) { - if (t1 == max_time && t2 > 0) - return max_time; - if (t1 == min_time && t2 < 0) - return min_time; - if (t1 < 0 ? t2 < min_time - t1 : max_time - t1 < t2) { - error(_("time overflow")); - exit(EXIT_FAILURE); - } - return t1 + t2; + if (t1 < 0) { + if (t2 < min_time - t1) { + if (t1 != min_time) + time_overflow(); + return min_time; + } + } 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(register const struct rule *const rp, register const zic_t wantedy) +rpytime(const struct rule *rp, zic_t wantedy) { register int m, i; register zic_t dayoff; /* with a nod to Margaret O. */ @@ -2735,7 +2928,7 @@ rpytime(register const struct rule *const rp, register const zic_t 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")); } } @@ -2748,7 +2941,7 @@ will not work with pre-2004 versions of zic")); } static void -newabbr(const char *const string) +newabbr(const char *string) { register int i; @@ -2756,29 +2949,15 @@ newabbr(const char *const string) register const char * cp; const char * mp; - /* - ** Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics - ** optionally followed by a + or - and a number from 1 to 14. - */ cp = string; mp = NULL; - while (isascii((unsigned char) *cp) && - isalpha((unsigned char) *cp)) + while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9') + || *cp == '-' || *cp == '+') ++cp; - if (cp - string == 0) -mp = _("time zone abbreviation lacks alphabetic at start"); if (noise && cp - string < 3) -mp = _("time zone abbreviation has fewer than 3 alphabetics"); + mp = _("time zone abbreviation has fewer than 3 characters"); if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) -mp = _("time zone abbreviation has too many alphabetics"); - if (mp == NULL && (*cp == '+' || *cp == '-')) { - ++cp; - if (isascii((unsigned char) *cp) && - isdigit((unsigned char) *cp)) - if (*cp++ == '1' && - *cp >= '0' && *cp <= '4') - ++cp; - } + mp = _("time zone abbreviation has too many characters"); if (*cp != '\0') mp = _("time zone abbreviation differs from POSIX standard"); if (mp != NULL) @@ -2789,18 +2968,18 @@ mp = _("time zone abbreviation differs from POSIX standard"); error(_("too many, or too long, time zone abbreviations")); exit(EXIT_FAILURE); } - (void) strcpy(&chars[charcnt], string); + strcpy(&chars[charcnt], string); charcnt += i; } -static int +static bool mkdirs(char *argname) { register char * name; register char * cp; if (argname == NULL || *argname == '\0') - return 0; + return true; cp = name = ecpyalloc(argname); while ((cp = strchr(cp + 1, '/')) != 0) { *cp = '\0'; @@ -2808,37 +2987,29 @@ 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 - if (!itsdir(name)) { - /* - ** 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) { - const char *e = strerror(errno); - - if (errno != EEXIST || !itsdir(name)) { - (void) fprintf(stderr, -_("%s: Can't create directory %s: %s\n"), - progname, name, e); - free(name); - return -1; - } + /* + ** 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) { + int err = errno; + if (itsdir(name) <= 0) { + char const *e = strerror(err); + warning(_("%s: Can't create directory" + " %s: %s"), + progname, name, e); + free(name); + return false; } } *cp = '/'; } free(name); - return 0; + return true; } - -/* -** UNIX was a registered trademark of The Open Group in 2003. -*/ -- cgit v1.1