[PROPOSED 1/7] Port better to C23 bool
* private.h (HAVE_STDBOOL_H): Remove. (true, false, bool): Simply rely on __STDC_VERSION__. There’s no longer need to treat pre-C99 compilers with <stdbool.h> specially, as they can be treated as if they lack <stdbool.h>. --- Makefile | 1 - NEWS | 3 +++ private.h | 10 +++------- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 156840e..afe7af7 100644 --- a/Makefile +++ b/Makefile @@ -223,7 +223,6 @@ LDLIBS= # -DHAVE_POSIX_DECLS=0 if your system's include files do not declare # functions like 'link' or variables like 'tzname' required by POSIX # -DHAVE_SNPRINTF=0 if your system lacks the snprintf function -# -DHAVE_STDBOOL_H if you have a non-C99 compiler with <stdbool.h> # -DHAVE_STDINT_H if you have a non-C99 compiler with <stdint.h> # -DHAVE_STRFTIME_L if <time.h> declares locale_t and strftime_l # -DHAVE_STRDUP=0 if your system lacks the strdup function diff --git a/NEWS b/NEWS index 164ab95..16f454c 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,9 @@ Unreleased, experimental changes number 2438 comes from the 32-bit limit in the year 2038, plus the 400-year Gregorian cycle. (Problem reported by Bradley White.) + Take advantage of the following C23 features if available: + bool/true/false keywords. + Release 2022e - 2022-10-11 11:13:02 -0700 diff --git a/private.h b/private.h index 9c31ec5..058d3c0 100644 --- a/private.h +++ b/private.h @@ -74,10 +74,6 @@ # define HAVE_POSIX_DECLS 1 #endif -#ifndef HAVE_STDBOOL_H -# define HAVE_STDBOOL_H (199901 <= __STDC_VERSION__) -#endif - #ifndef HAVE_STRDUP # define HAVE_STRDUP 1 #endif @@ -623,12 +619,12 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE; ** Finally, some convenience items. */ -#if HAVE_STDBOOL_H -# include <stdbool.h> -#else +#if __STDC_VERSION__ < 199901 # define true 1 # define false 0 # define bool int +#elif __STDC_VERSION__ < 202311 +# include <stdbool.h> #endif #define TYPE_BIT(type) (sizeof(type) * CHAR_BIT) -- 2.37.3
* private.h (HAS_INCLUDE): New macro. (HAVE_GETTEXT, HAVE_SYS_STAT_H, HAVE_UNISTD_H, HAVE_UTMPX_H) (HAVE_STDINT_H, HAVE_INTTYPES_H): Use it to simplify builder configuration. --- Makefile | 13 ++++++++----- NEWS | 2 +- private.h | 39 +++++++++++++++++++++++---------------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index afe7af7..8a2856f 100644 --- a/Makefile +++ b/Makefile @@ -210,7 +210,7 @@ LDLIBS= # -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ' # -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows) # -DHAVE_GENERIC=0 if _Generic does not work -# -DHAVE_GETTEXT if 'gettext' works (e.g., GNU/Linux, FreeBSD, Solaris) +# -DHAVE_GETTEXT if 'gettext' works (e.g., GNU/Linux, FreeBSD, Solaris)* # -DHAVE_INCOMPATIBLE_CTIME_R if your system's time.h declares # ctime_r and asctime_r incompatibly with the POSIX standard # (Solaris when _POSIX_PTHREAD_SEMANTICS is not defined). @@ -223,14 +223,15 @@ LDLIBS= # -DHAVE_POSIX_DECLS=0 if your system's include files do not declare # functions like 'link' or variables like 'tzname' required by POSIX # -DHAVE_SNPRINTF=0 if your system lacks the snprintf function -# -DHAVE_STDINT_H if you have a non-C99 compiler with <stdint.h> +# -DHAVE_STDINT_H if you have a non-C99 compiler with <stdint.h>* # -DHAVE_STRFTIME_L if <time.h> declares locale_t and strftime_l # -DHAVE_STRDUP=0 if your system lacks the strdup function # -DHAVE_STRTOLL=0 if your system lacks the strtoll function # -DHAVE_SYMLINK=0 if your system lacks the symlink function -# -DHAVE_SYS_STAT_H=0 if your compiler lacks a <sys/stat.h> +# -DHAVE_SYS_STAT_H=0 if your compiler lacks a <sys/stat.h>* # -DHAVE_TZSET=0 if your system lacks a tzset function -# -DHAVE_UNISTD_H=0 if your compiler lacks a <unistd.h> +# -DHAVE_UNISTD_H=0 if your compiler lacks a <unistd.h>* +# -DHAVE_UTMPX_H=0 if your compiler lacks a <utmpx.h>* # -Dlocale_t=XXX if your system uses XXX instead of locale_t # -DRESERVE_STD_EXT_IDS if your platform reserves standard identifiers # with external linkage, e.g., applications cannot define 'localtime'. @@ -257,11 +258,13 @@ LDLIBS= # ignore or otherwise mishandle 64-bit data in TZif files; # however, fat TZif files may trigger bugs in newer TZif readers. # Slim TZif files are more efficient, and are the default. - # -DZIC_MAX_ABBR_LEN_WO_WARN=3 # (or some other number) to set the maximum time zone abbreviation length # that zic will accept without a warning (the default is 6) # $(GCC_DEBUG_FLAGS) if you are using recent GCC and want lots of checking +# +# * Options marked "*" can be omitted if your compiler is C23 compatible. +# # Select instrumentation via "make GCC_INSTRUMENT='whatever'". GCC_INSTRUMENT = \ -fsanitize=undefined -fsanitize-address-use-after-scope \ diff --git a/NEWS b/NEWS index 16f454c..2532691 100644 --- a/NEWS +++ b/NEWS @@ -38,7 +38,7 @@ Unreleased, experimental changes 400-year Gregorian cycle. (Problem reported by Bradley White.) Take advantage of the following C23 features if available: - bool/true/false keywords. + bool/true/false keywords and __has_include. Release 2022e - 2022-10-11 11:13:02 -0700 diff --git a/private.h b/private.h index 058d3c0..6510c52 100644 --- a/private.h +++ b/private.h @@ -30,6 +30,14 @@ /* This string was in the Factory zone through version 2016f. */ #define GRANDPARENTED "Local time zone must be set--see zic manual page" +/* True if "#include INCLUDE" includes something, false otherwise. + Yield DEFAULT on pre-C23 platforms that lack __has_include. */ +#ifdef __has_include +# define HAS_INCLUDE(include, default) __has_include(include) +#else +# define HAS_INCLUDE(include, default) (default) +#endif + /* ** Defaults for preprocessor symbols. ** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'. @@ -55,8 +63,8 @@ #endif #ifndef HAVE_GETTEXT -# define HAVE_GETTEXT 0 -#endif /* !defined HAVE_GETTEXT */ +# define HAVE_GETTEXT HAS_INCLUDE(<libintl.h>, false) +#endif #ifndef HAVE_INCOMPATIBLE_CTIME_R # define HAVE_INCOMPATIBLE_CTIME_R 0 @@ -87,16 +95,16 @@ #endif /* !defined HAVE_SYMLINK */ #ifndef HAVE_SYS_STAT_H -# define HAVE_SYS_STAT_H 1 -#endif /* !defined HAVE_SYS_STAT_H */ +# define HAVE_SYS_STAT_H HAS_INCLUDE(<sys/stat.h>, true) +#endif #ifndef HAVE_UNISTD_H -# define HAVE_UNISTD_H 1 -#endif /* !defined HAVE_UNISTD_H */ +# define HAVE_UNISTD_H HAS_INCLUDE(<unistd.h>, true) +#endif #ifndef HAVE_UTMPX_H -# define HAVE_UTMPX_H 1 -#endif /* !defined HAVE_UTMPX_H */ +# define HAVE_UTMPX_H HAS_INCLUDE(<utmpx.h>, true) +#endif #ifndef NETBSD_INSPIRED # define NETBSD_INSPIRED 1 @@ -231,18 +239,17 @@ ** stdint.h, even with pre-C99 compilers. */ #ifndef HAVE_STDINT_H -# define HAVE_STDINT_H \ - (199901 <= __STDC_VERSION__ \ - || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \ - || __CYGWIN__ || INTMAX_MAX) -#endif /* !defined HAVE_STDINT_H */ - +# define HAVE_STDINT_H HAS_INCLUDE(<stdint.h>, \ + (199901 <= __STDC_VERSION__ \ + || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \ + || __CYGWIN__ || INTMAX_MAX)) +#endif #if HAVE_STDINT_H # include <stdint.h> -#endif /* !HAVE_STDINT_H */ +#endif #ifndef HAVE_INTTYPES_H -# define HAVE_INTTYPES_H HAVE_STDINT_H +# define HAVE_INTTYPES_H HAS_INCLUDE(<inttypes.h>, HAVE_STDINT_H) #endif #if HAVE_INTTYPES_H # include <inttypes.h> -- 2.37.3
* private.h: Include stddef.h here, for 'unreachable'. (unreachable): Rename from UNREACHABLE. All uses changed. This is now the C23 macro if available, a substitute otherwise. However, if DEBUG is defined, it is still 'abort'. * zic.c: Do not include stddef.h here, private.h does that now. --- NEWS | 2 +- localtime.c | 2 +- private.h | 22 +++++++++++++--------- zic.c | 7 +++---- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/NEWS b/NEWS index 2532691..a5b680f 100644 --- a/NEWS +++ b/NEWS @@ -38,7 +38,7 @@ Unreleased, experimental changes 400-year Gregorian cycle. (Problem reported by Bradley White.) Take advantage of the following C23 features if available: - bool/true/false keywords and __has_include. + bool/true/false keywords, __has_include, unreachable. Release 2022e - 2022-10-11 11:13:02 -0700 diff --git a/localtime.c b/localtime.c index 0ad28dc..c556531 100644 --- a/localtime.c +++ b/localtime.c @@ -1098,7 +1098,7 @@ transtime(const int year, register const struct rule *const rulep, value += mon_lengths[leapyear][i] * SECSPERDAY; break; - default: UNREACHABLE(); + default: unreachable(); } /* diff --git a/private.h b/private.h index 6510c52..5624abc 100644 --- a/private.h +++ b/private.h @@ -161,6 +161,7 @@ #undef tzalloc #undef tzfree +#include <stddef.h> #include <sys/types.h> /* for time_t */ #include <string.h> #include <limits.h> /* for CHAR_BIT et al. */ @@ -711,16 +712,19 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE; #endif #ifdef DEBUG -# define UNREACHABLE() abort() -#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) -# define UNREACHABLE() __builtin_unreachable() -#elif defined __has_builtin -# if __has_builtin(__builtin_unreachable) -# define UNREACHABLE() __builtin_unreachable() +# undef unreachable +# define unreachable() abort() +#elif !defined unreachable +# ifdef __has_builtin +# if __has_builtin(__builtin_unreachable) +# define unreachable() __builtin_unreachable() +# endif +# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) +# define unreachable() __builtin_unreachable() +# endif +# ifndef unreachable +# define unreachable() ((void) 0) # endif -#endif -#ifndef UNREACHABLE -# define UNREACHABLE() ((void) 0) #endif /* diff --git a/zic.c b/zic.c index ff04793..38a65eb 100644 --- a/zic.c +++ b/zic.c @@ -20,7 +20,6 @@ #include <locale.h> #include <signal.h> #include <stdarg.h> -#include <stddef.h> #include <stdio.h> typedef int_fast64_t zic_t; @@ -1457,7 +1456,7 @@ infile(const char *name) inexpires(fields, nfields); wantcont = false; break; - default: UNREACHABLE(); + default: unreachable(); } } } @@ -1903,7 +1902,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, case YR_MAXIMUM: rp->r_loyear = ZIC_MAX; break; - default: UNREACHABLE(); + default: unreachable(); } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) { error(_("invalid starting year")); return false; @@ -1921,7 +1920,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, case YR_ONLY: rp->r_hiyear = rp->r_loyear; break; - default: UNREACHABLE(); + default: unreachable(); } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) { error(_("invalid ending year")); return false; -- 2.37.3
* private.h (HAVE_GETRANDOM): New macro. * zic.c [HAVE_GETRANDOM]: Include <sys/random.h>. (get_rand_u64): New function with more randomness, by using getrandom and/or clock_gettime if available. Use simpler test for initialization. (random_dirent): Use it. Avoid slightly biasing the output of the random number generator. --- Makefile | 1 + private.h | 5 ++++ zic.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 86 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 8a2856f..6f8b2f9 100644 --- a/Makefile +++ b/Makefile @@ -210,6 +210,7 @@ LDLIBS= # -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ' # -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows) # -DHAVE_GENERIC=0 if _Generic does not work +# -DHAVE_GETRANDOM if getgrandom works (e.g., GNU/Linux)* # -DHAVE_GETTEXT if 'gettext' works (e.g., GNU/Linux, FreeBSD, Solaris)* # -DHAVE_INCOMPATIBLE_CTIME_R if your system's time.h declares # ctime_r and asctime_r incompatibly with the POSIX standard diff --git a/private.h b/private.h index 5624abc..8024df5 100644 --- a/private.h +++ b/private.h @@ -62,6 +62,11 @@ # define HAVE_GENERIC (201112 <= __STDC_VERSION__) #endif +#ifndef HAVE_GETRANDOM +# define HAVE_GETRANDOM HAS_INCLUDE(<sys/random.h>, \ + (2 < __GLIBC__ + (25 <= __GLIBC_MINOR__))) +#endif + #ifndef HAVE_GETTEXT # define HAVE_GETTEXT HAS_INCLUDE(<libintl.h>, false) #endif diff --git a/zic.c b/zic.c index 38a65eb..ddfdc79 100644 --- a/zic.c +++ b/zic.c @@ -38,6 +38,10 @@ typedef int_fast64_t zic_t; # define mkdir(name, mode) _mkdir(name) #endif +#if HAVE_GETRANDOM +# include <sys/random.h> +#endif + #if HAVE_SYS_STAT_H # include <sys/stat.h> #endif @@ -1037,6 +1041,63 @@ namecheck(const char *name) return componentcheck(name, component, cp); } +/* Return a random uint_fast64_t. */ +static uint_fast64_t +get_rand_u64(void) +{ +#if HAVE_GETRANDOM + static uint_fast64_t entropy_buffer[max(1, 256 / sizeof (uint_fast64_t))]; + static int nwords; + if (!nwords) { + ssize_t s; + do + s = getrandom(entropy_buffer, sizeof entropy_buffer, 0); + while (s < 0 && errno == EINTR); + + nwords = s < 0 ? -1 : s / sizeof *entropy_buffer; + } + if (0 < nwords) + return entropy_buffer[--nwords]; +#endif + + /* getrandom didn't work, so fall back on portable code that is + not the best because the seed doesn't necessarily have enough bits, + the seed isn't cryptographically random on platforms lacking + getrandom, and 'rand' might not be cryptographically secure. */ + { + static bool initialized; + if (!initialized) { + unsigned seed; +#ifdef CLOCK_REALTIME + struct timespec now; + clock_gettime (CLOCK_REALTIME, &now); + seed = now.tv_sec ^ now.tv_nsec; +#else + seed = time(NULL); +#endif + srand(seed); + initialized = true; + } + } + + /* Return a random number if rand() yields a random number and in + the typical case where RAND_MAX is one less than a power of two. + In other cases this code yields a sort-of-random number. */ + { + uint_fast64_t + rand_max = RAND_MAX, + multiplier = rand_max + 1, /* It's OK if this overflows to 0. */ + r = 0, rmax = 0; + do { + uint_fast64_t rmax1 = rmax * multiplier + rand_max; + r = r * multiplier + rand(); + rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX; + } while (rmax < UINT_FAST64_MAX); + + return r; + } +} + /* Generate a randomish name in the same directory as *NAME. If *NAMEALLOC, put the name into *NAMEALLOC which is assumed to be that returned by a previous call and is thus already almost set up @@ -1056,8 +1117,19 @@ random_dirent(char const **name, char **namealloc) int suffixlen = 6; char const *lastslash = strrchr(src, '/'); ptrdiff_t dirlen = lastslash ? lastslash + 1 - src : 0; - static unsigned short initialized; int i; + uint_fast64_t r; + uint_fast64_t base = alphabetlen; + + /* BASE**6 */ + uint_fast64_t base__6 = base * base * base * base * base * base; + + /* The largest uintmax_t that is a multiple of BASE**6. Any random + uintmax_t value that is this value or greater, yields a biased + remainder when divided by BASE**6. UNFAIR_MIN equals the + mathematical value of ((UINTMAX_MAX + 1) - (UINTMAX_MAX + 1) % BASE**6) + computed without overflow. */ + uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6); if (!dst) { dst = emalloc(dirlen + prefixlen + suffixlen + 1); @@ -1067,13 +1139,14 @@ random_dirent(char const **name, char **namealloc) *name = *namealloc = dst; } - /* This randomization is not the best, but is portable to C89. */ - if (!initialized++) { - unsigned now = time(NULL); - srand(rand() ^ now); + do + r = get_rand_u64(); + while (unfair_min <= r); + + for (i = 0; i < suffixlen; i++) { + dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen]; + r /= alphabetlen; } - for (i = 0; i < suffixlen; i++) - dst[dirlen + prefixlen + i] = alphabet[rand() % alphabetlen]; } /* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the -- 2.37.3
* NEWS: Mention this. * zic.c (main): When warning about ‘Zone A ... / Link A B / Link B C’, complain about the second Link line, which is the link to the link, not the first Link line, which is fine as-is. --- NEWS | 2 ++ zic.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a5b680f..64a2d0c 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ Unreleased, experimental changes Changes to code + Fix line number in zic's diagnostic for a link to a link. + Fix a bug that caused localtime to mishandle timestamps starting in the year 2438 when reading data generated by 'zic -b fat' when distant-future DST transitions occur at times given in standard diff --git a/zic.c b/zic.c index ddfdc79..f280b31 100644 --- a/zic.c +++ b/zic.c @@ -954,8 +954,11 @@ _("%s: invalid time range: %s\n"), if (noise) for (j = 0; j < nlinks; ++j) if (strcmp(links[i].l_linkname, - links[j].l_target) == 0) + links[j].l_target) == 0) { + eat(links[j].l_filename, + links[j].l_linenum); warning(_("link to link")); + } } if (lcltime != NULL) { eat(_("command line"), 1); -- 2.37.3
Keep track of which file came before which other file internally, so we always know which input line came first. This doesn’t affect behavior now, but later changes will need this. * zic.c (struct rule): Replace r_filename with r_filenum. All uses changed. (struct zone): Replace z_filename with z_filenum. All uses changed. (filenum, rfilenum): New static vars, replacing filename and rfilename. (main_argv): New static var, for translating filenums to names. (leapsec): Move decl earlier. (struct link): Replace l_filename with l_filenum. All uses changed. (LEAPSEC_FILENUM, COMMAND_LINE_FILENUM): New constants. (filename): New static function. (eats, eat, verror, inzone): Take file numbers as args instead of file names. All uses changed. Expand file numbers into their names before outputting them. (main): Initialize main_argv early. (infile): Also take file number as arg. All uses changed. No need to fiddle with name, as filename does that for us now. --- zic.c | 125 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 54 deletions(-) diff --git a/zic.c b/zic.c index f280b31..59fd4e4 100644 --- a/zic.c +++ b/zic.c @@ -72,7 +72,7 @@ static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t)); typedef intmax_t lineno; struct rule { - const char * r_filename; + int r_filenum; lineno r_linenum; const char * r_name; @@ -108,7 +108,7 @@ enum { }; struct zone { - const char * z_filename; + int z_filenum; lineno z_linenum; const char * z_name; @@ -158,7 +158,7 @@ static int getfields(char *, char **, int); static zic_t gethms(const char * string, const char * errstring); static zic_t getsave(char *, bool *); static void inexpires(char **, int); -static void infile(const char * filename); +static void infile(int, char const *); static void inleap(char ** fields, int nfields); static void inlink(char ** fields, int nfields); static void inrule(char ** fields, int nfields); @@ -194,7 +194,7 @@ enum { WORK_AROUND_QTBUG_53071 = true }; static int charcnt; static bool errors; static bool warnings; -static const char * filename; +static int filenum; static int leapcnt; static bool leapseen; static zic_t leapminyear; @@ -205,9 +205,11 @@ static int max_format_len; static zic_t max_year; static zic_t min_year; static bool noise; -static const char * rfilename; +static int rfilenum; static lineno rlinenum; static const char * progname; +static char const * leapsec; +static char *const * main_argv; static ptrdiff_t timecnt; static ptrdiff_t timecnt_alloc; static int typecnt; @@ -328,7 +330,7 @@ static ptrdiff_t nzones; /* number of zones */ static ptrdiff_t nzones_alloc; struct link { - const char * l_filename; + int l_filenum; lineno l_linenum; const char * l_target; const char * l_linkname; @@ -522,19 +524,36 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc) ** Error handling. */ +/* In most of the code, an input file name is represented by its index + into the main argument vector, except that LEAPSEC_FILENUM stands + for leapsec and COMMAND_LINE_FILENUM stands for the command line. */ +enum { LEAPSEC_FILENUM = -2, COMMAND_LINE_FILENUM = -1 }; + +/* Return the name of the Ith input file, for diagnostics. */ +static char const * +filename(int i) +{ + if (i == COMMAND_LINE_FILENUM) + return _("command line"); + else { + char const *fname = i == LEAPSEC_FILENUM ? leapsec : main_argv[i]; + return strcmp(fname, "-") == 0 ? _("standard input") : fname; + } +} + static void -eats(char const *name, lineno num, char const *rname, lineno rnum) +eats(int fnum, lineno num, int rfnum, lineno rnum) { - filename = name; + filenum = fnum; linenum = num; - rfilename = rname; + rfilenum = rfnum; rlinenum = rnum; } static void -eat(char const *name, lineno num) +eat(int fnum, lineno num) { - eats(name, num, NULL, -1); + eats(fnum, num, 0, -1); } static void ATTRIBUTE_FORMAT((printf, 1, 0)) @@ -545,12 +564,13 @@ verror(const char *const string, va_list args) ** zic ... 2>&1 | error -t "*" -v ** on BSD systems. */ - if (filename) - fprintf(stderr, _("\"%s\", line %"PRIdMAX": "), filename, linenum); + if (filenum) + fprintf(stderr, _("\"%s\", line %"PRIdMAX": "), + filename(filenum), linenum); vfprintf(stderr, string, args); - if (rfilename != NULL) + if (rfilenum) fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"), - rfilename, rlinenum); + filename(rfilenum), rlinenum); fprintf(stderr, "\n"); } @@ -757,7 +777,6 @@ redundant_time_option(char *opt) static const char * psxrules; static const char * lcltime; static const char * directory; -static const char * leapsec; static const char * tzdefault; /* -1 if the TZif output file should be slim, 0 if default, 1 if the @@ -792,6 +811,7 @@ main(int argc, char **argv) # endif /* defined TEXTDOMAINDIR */ textdomain(TZ_DOMAIN); #endif /* HAVE_GETTEXT */ + main_argv = argv; progname = argv[0]; if (TYPE_BIT(zic_t) < 64) { fprintf(stderr, "%s: %s\n", progname, @@ -926,12 +946,12 @@ _("%s: invalid time range: %s\n"), tzdefault = TZDEFAULT; if (optind < argc && leapsec != NULL) { - infile(leapsec); + infile(LEAPSEC_FILENUM, leapsec); adjleap(); } for (k = optind; k < argc; k++) - infile(argv[k]); + infile(k, argv[k]); if (errors) return EXIT_FAILURE; associate(); @@ -949,7 +969,7 @@ _("%s: invalid time range: %s\n"), ** Make links. */ for (i = 0; i < nlinks; ++i) { - eat(links[i].l_filename, links[i].l_linenum); + eat(links[i].l_filenum, links[i].l_linenum); dolink(links[i].l_target, links[i].l_linkname, false); if (noise) for (j = 0; j < nlinks; ++j) @@ -961,11 +981,11 @@ _("%s: invalid time range: %s\n"), } } if (lcltime != NULL) { - eat(_("command line"), 1); + eat(COMMAND_LINE_FILENUM, 1); dolink(lcltime, tzdefault, true); } if (psxrules != NULL) { - eat(_("command line"), 1); + eat(COMMAND_LINE_FILENUM, 1); dolink(psxrules, TZDEFRULES, true); } if (warnings && (ferror(stderr) || fclose(stderr) != 0)) @@ -1378,22 +1398,20 @@ associate(void) if (strcmp(rules[i].r_name, rules[i + 1].r_name) != 0) continue; - if (strcmp(rules[i].r_filename, - rules[i + 1].r_filename) == 0) + if (rules[i].r_filenum == rules[i + 1].r_filenum) continue; - eat(rules[i].r_filename, rules[i].r_linenum); + eat(rules[i].r_filenum, rules[i].r_linenum); warning(_("same rule name in multiple files")); - eat(rules[i + 1].r_filename, rules[i + 1].r_linenum); + eat(rules[i + 1].r_filenum, rules[i + 1].r_linenum); warning(_("same rule name in multiple files")); for (j = i + 2; j < nrules; ++j) { if (strcmp(rules[i].r_name, rules[j].r_name) != 0) break; - if (strcmp(rules[i].r_filename, - rules[j].r_filename) == 0) + if (rules[i].r_filenum == rules[j].r_filenum) continue; - if (strcmp(rules[i + 1].r_filename, - rules[j].r_filename) == 0) + if (rules[i + 1].r_filenum + == rules[j].r_filenum) continue; break; } @@ -1424,7 +1442,7 @@ associate(void) /* ** Maybe we have a local standard time offset. */ - eat(zp->z_filename, zp->z_linenum); + eat(zp->z_filenum, zp->z_linenum); zp->z_save = getsave(zp->z_rule, &zp->z_isdst); /* ** Note, though, that if there's no rule, @@ -1473,7 +1491,7 @@ inputline(FILE *fp, char *buf, ptrdiff_t bufsize) } static void -infile(const char *name) +infile(int fnum, char const *name) { register FILE * fp; register const struct lookup * lp; @@ -1481,7 +1499,6 @@ infile(const char *name) register lineno num; if (strcmp(name, "-") == 0) { - name = _("standard input"); fp = stdin; } else if ((fp = fopen(name, "r")) == NULL) { const char *e = strerror(errno); @@ -1496,7 +1513,7 @@ infile(const char *name) char buf[_POSIX2_LINE_MAX]; int nfields; char *fields[MAX_FIELDS]; - eat(name, num); + eat(fnum, num); linelen = inputline(fp, buf, sizeof buf); if (linelen < 0) break; @@ -1508,7 +1525,7 @@ infile(const char *name) wantcont = inzcont(fields, nfields); } else { struct lookup const *line_codes - = name == leapsec ? leap_line_codes : zi_line_codes; + = fnum < 0 ? leap_line_codes : zi_line_codes; lp = byword(fields[0], line_codes); if (lp == NULL) error(_("input line of unknown type")); @@ -1536,7 +1553,7 @@ infile(const char *name) } } } - close_file(fp, NULL, filename, NULL); + close_file(fp, NULL, filename(fnum), NULL); if (wantcont) error(_("expected continuation line not found")); } @@ -1639,7 +1656,7 @@ inrule(char **fields, int nfields) error(_("Invalid rule name \"%s\""), fields[RF_NAME]); return; } - r.r_filename = filename; + r.r_filenum = filenum; r.r_linenum = linenum; r.r_save = getsave(fields[RF_SAVE], &r.r_isdst); if (!rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], @@ -1680,9 +1697,9 @@ _("\"Zone %s\" line and -p option are mutually exclusive"), strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { error(_("duplicate zone name %s" " (file \"%s\", line %"PRIdMAX")"), - fields[ZF_NAME], - zones[i].z_filename, - zones[i].z_linenum); + fields[ZF_NAME], + filename(zones[i].z_filenum), + zones[i].z_linenum); return false; } return inzsub(fields, nfields, false); @@ -1729,7 +1746,7 @@ inzsub(char **fields, int nfields, bool iscont) i_untilday = ZF_TILDAY; i_untiltime = ZF_TILTIME; } - z.z_filename = filename; + z.z_filenum = filenum; z.z_linenum = linenum; z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset")); if ((cp = strchr(fields[i_format], '%')) != 0) { @@ -1745,7 +1762,7 @@ inzsub(char **fields, int nfields, bool iscont) max_format_len = format_len; hasuntil = nfields > i_untilyear; if (hasuntil) { - z.z_untilrule.r_filename = filename; + z.z_untilrule.r_filenum = filenum; z.z_untilrule.r_linenum = linenum; if (!rulesub( &z.z_untilrule, @@ -1914,7 +1931,7 @@ inlink(char **fields, int nfields) } if (! namecheck(fields[LF_LINKNAME])) return; - l.l_filename = filename; + l.l_filenum = filenum; l.l_linenum = linenum; l.l_target = ecpyalloc(fields[LF_TARGET]); l.l_linkname = ecpyalloc(fields[LF_LINKNAME]); @@ -3052,7 +3069,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) INITIALIZE(prevktime); if (useuntil && zp->z_untiltime <= min_time) continue; - eat(zp->z_filename, zp->z_linenum); + eat(zp->z_filenum, zp->z_linenum); *startbuf = '\0'; if (zp->z_nrules == 0) { int type; @@ -3080,8 +3097,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) zic_t one = 1; zic_t y2038_boundary = one << 31; struct rule *rp = &zp->z_rules[j]; - eats(zp->z_filename, zp->z_linenum, - rp->r_filename, rp->r_linenum); + eats(zp->z_filenum, zp->z_linenum, + rp->r_filenum, rp->r_linenum); rp->r_todo = year >= rp->r_loyear && year <= rp->r_hiyear; if (rp->r_todo) { @@ -3122,8 +3139,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) struct rule *r = &zp->z_rules[j]; if (!r->r_todo) continue; - eats(zp->z_filename, zp->z_linenum, - r->r_filename, r->r_linenum); + eats(zp->z_filenum, zp->z_linenum, + r->r_filenum, r->r_linenum); offset = r->r_todisut ? 0 : stdoff; if (!r->r_todisstd) offset = oadd(offset, save); @@ -3138,12 +3155,12 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) } else if (jtime == ktime) { char const *dup_rules_msg = _("two rules for same instant"); - eats(zp->z_filename, zp->z_linenum, - r->r_filename, r->r_linenum); + eats(zp->z_filenum, zp->z_linenum, + r->r_filenum, r->r_linenum); warning("%s", dup_rules_msg); r = &zp->z_rules[k]; - eats(zp->z_filename, zp->z_linenum, - r->r_filename, r->r_linenum); + eats(zp->z_filenum, zp->z_linenum, + r->r_filenum, r->r_linenum); error("%s", dup_rules_msg); } } @@ -3185,8 +3202,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) false); } } - eats(zp->z_filename, zp->z_linenum, - rp->r_filename, rp->r_linenum); + eats(zp->z_filenum, zp->z_linenum, + rp->r_filenum, rp->r_linenum); doabbr(ab, zp, rp->r_abbrvar, rp->r_isdst, rp->r_save, false); offset = oadd(zp->z_stdoff, rp->r_save); @@ -3215,7 +3232,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) if (*startbuf == '\0' && zp->z_format) doabbr(startbuf, zp, disable_percent_s, isdst, save, false); - eat(zp->z_filename, zp->z_linenum); + eat(zp->z_filenum, zp->z_linenum); if (*startbuf == '\0') error(_("can't determine time zone abbreviation to use just after until time")); else { -- 2.37.3
* zic.c (qsort_linkcmp, bsearch_linkcmp): New functions. (main): Use these functions so that the cost of checking for links to links is O(N log N), not O(N**2). --- zic.c | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/zic.c b/zic.c index 59fd4e4..82e1a4d 100644 --- a/zic.c +++ b/zic.c @@ -651,6 +651,33 @@ change_directory(char const *dir) } } +/* Compare the two links A and B, for a stable sort by link name. */ +static int +qsort_linkcmp(void const *a, void const *b) +{ + struct link const *l = a; + struct link const *m = b; + int cmp = strcmp(l->l_linkname, m->l_linkname); + if (cmp) + return cmp; + + /* The link names are the same. Make the sort stable by comparing + file numbers (where subtraction cannot overflow) and possibly + line numbers (where it can). */ + cmp = l->l_filenum - m->l_filenum; + if (cmp) + return cmp; + return (l->l_linenum > m->l_linenum) - (l->l_linenum < m->l_linenum); +} + +/* Compare the string KEY to the link B, for bsearch. */ +static int +bsearch_linkcmp(void const *key, void const *b) +{ + struct link const *m = b; + return strcmp(key, m->l_linkname); +} + /* Simple signal handling: just set a flag that is checked periodically outside critical sections. To set up the handler, prefer sigaction if available to close a signal race. */ @@ -968,17 +995,15 @@ _("%s: invalid time range: %s\n"), /* ** Make links. */ + if (noise) + qsort(links, nlinks, sizeof *links, qsort_linkcmp); for (i = 0; i < nlinks; ++i) { eat(links[i].l_filenum, links[i].l_linenum); dolink(links[i].l_target, links[i].l_linkname, false); - if (noise) - for (j = 0; j < nlinks; ++j) - if (strcmp(links[i].l_linkname, - links[j].l_target) == 0) { - eat(links[j].l_filename, - links[j].l_linenum); - warning(_("link to link")); - } + if (noise + && bsearch(links[i].l_target, links, nlinks, sizeof *links, + bsearch_linkcmp)) + warning(_("link to link")); } if (lcltime != NULL) { eat(COMMAND_LINE_FILENUM, 1); -- 2.37.3
participants (1)
-
Paul Eggert