I'm guessing that a way to minimize breakage is to end the file with: the POSIX string any name/value pairs a repeat of the POSIX string. That way folks who look for the POSIX string at the very end of the file will find it, and folks who look for it after the 64-bit data will too. --ado On Mon, Sep 9, 2013 at 9:04 PM, Paul Eggert <eggert@cs.ucla.edu> wrote:
Arthur David Olson wrote:
Adding something such as "2013e" wouldn't exhaust the reserved bytes
Yes, but I anticipate that various distros will want to append their own information to the version string, and the reserved space is uncomfortably small for that. And I share Zefram's leeriness of making this version information so primary; it's really just auxiliary data that does not affect how timestamps are represented or interpreted, unlike the version byte.
So how about this idea instead? As part of tzfile.v3 we allow name-value pairs at the end of the file. One of them can be the version, another the zone name, and perhaps we'll think of others. Here's a proposed patch to do this, which I've pushed into the experimental version.
From d7680ffd3d43c4da6d9ff21ffb93b6783703a301 Mon Sep 17 00:00:00 2001 From: Paul Eggert <eggert@cs.ucla.edu> Date: Mon, 9 Sep 2013 17:16:37 -0700 Subject: [PATCH] Add optional meta-information to version-3 format.
* Makefile (ZFLAGS): Add a comment about how to enable meta-info. * tzfile.5: Describe meta-information. * zic.8: Document new options -n and -o, which cause zic to generate meta-info. * zic.c: Include <stddef.h>, for ptrdiff_t. (genoption, genoptions, genname): New static vars. (usage): Summarize new options. (addgenoption, writevalue): New function. (main, writezone): Add support for new options. --- Makefile | 3 +++ tzfile.5 | 13 ++++++++++++ zic.8 | 14 +++++++++++++ zic.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 101 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index 41b6ffc..eccb9da 100644 --- a/Makefile +++ b/Makefile @@ -250,6 +250,9 @@ LDFLAGS= $(LFLAGS) zic= ./zic ZIC= $(zic) $(ZFLAGS)
+# Uncomment this to put name and version info into zic output files. +#ZFLAGS= -n -o version='$(VERSION)' + # The name of a Posix-compliant `awk' on your system. AWK= awk
diff --git a/tzfile.5 b/tzfile.5 index ff1ec63..d609277 100644 --- a/tzfile.5 +++ b/tzfile.5 @@ -154,6 +154,19 @@ First, the hours part of its transition times may be signed and range from from 0 through 24. Second, DST is in effect all year if it starts January 1 at 00:00 and ends December 31 at 24:00 plus the difference between daylight saving and standard time. +.PP +Also, for version-3-format time zone files, the version-2 header and +data are optionally followed by a section containing auxiliary +meta-information that is not needed to process time stamps. This +section, if present, consists of the four magic bytes "=TZ\en" +followed by zero or more newline-terminated byte strings, each +containing a name-value pair separated by "=". Names consist of ASCII +letters, digits and underscores, and start with a letter; duplicate +names are not allowed. Two common names are "name", the Zone name for +the data, and "version", the version number. Values consist of any +bytes except NUL, newline, and backslash; however, newline and +backslash can represented via the two-byte strings "\en" and "\e\e" +respectively. .SH SEE ALSO newctime(3), newtzset(3) .\" This file is in the public domain, so clarified as of diff --git a/zic.8 b/zic.8 index 602c3c9..b1d3348 100644 --- a/zic.8 +++ b/zic.8 @@ -15,6 +15,11 @@ zic \- time zone compiler .B \-l .I localtime ] [ +.B \-n +] [ +.B \-o +.IB name = value +] [ .B \-p .I posixrules ] [ @@ -62,6 +67,15 @@ will act as if the input contained a link line of the form .ti +.5i Link \fItimezone\fP localtime .TP +.B "\-n" +Store each zone's name into its generated file, as meta-information +with the name "name" and value the zone's name. +.TP +.BI "\-o " name = value +Store the given name-value pair into the generated file, as +meta-information. This option can be repeated, once for each distinct +name. +.TP .BI "\-p " timezone Use the given time zone's rules when handling POSIX-format time zone environment variables. diff --git a/zic.c b/zic.c index 9939195..eefa1fb 100644 --- a/zic.c +++ b/zic.c @@ -9,6 +9,7 @@ #include "tzfile.h"
#include <stdarg.h> +#include <stddef.h>
#define ZIC_VERSION '3'
@@ -140,6 +141,9 @@ static int yearistype(int year, const char * type); static int charcnt; static int errors; static const char * filename; +static const char ** genoption; +static int genoptions; +static int genname; static int leapcnt; static int leapseen; static zic_t leapminyear; @@ -432,7 +436,8 @@ static _Noreturn void usage(FILE *stream, int status) { (void) fprintf(stream, _("%s: usage is %s \ -[ --version ] [ --help ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\ +[ --version ] [ --help ] [ -v ] [ -l localtime ]\\\n\ +\t[ -n ] [ -o name=value ]... [ -p posixrules ] \\\n\ \t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\ \n\ Report bugs to %s.\n"), @@ -446,6 +451,31 @@ static const char * directory; static const char * leapsec; static const char * yitcommand;
+static int +addgenoption(char const *option) +{ + register char const *o = option; + register ptrdiff_t namelen; + register int i; + if (! (isascii (*o) && isalpha(*o))) + return 0; + while (*++o != '=') + if (! (isascii (*o) && (isalnum(*o) || *o == '_'))) + return 0; + namelen = o - option; + if (INT_MAX < namelen) + return 0; /* fprintf won't work. */ + if (namelen == sizeof "name" - 1 + && memcmp(option, "name", namelen) == 0) + return 0; + for (i = 0; i < genoptions; i++) + if (strncmp(genoption[i], option, namelen + 1) == 0) + return 0; + genoption = erealloc(genoption, (genoptions + 1) * sizeof *genoption); + genoption[genoptions++] = option; + return 1; +} + int main(int argc, char **argv) { @@ -476,7 +506,7 @@ main(int argc, char **argv) } else if (strcmp(argv[i], "--help") == 0) { usage(stdout, EXIT_SUCCESS); } - while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1) + while ((c = getopt(argc, argv, "d:l:p:L:no:vsy:")) != EOF && c != -1) switch (c) { default: usage(stderr, EXIT_FAILURE); @@ -500,6 +530,17 @@ _("%s: More than one -l option specified\n"), exit(EXIT_FAILURE); } break; + case 'n': + genname = TRUE; + break; + case 'o': + if (!addgenoption(optarg)) { + fprintf(stderr, + _("%s: %s: invalid -o option\n"), + progname, optarg); + exit(EXIT_FAILURE); + } + break; case 'p': if (psxrules == NULL) psxrules = optarg; @@ -1386,6 +1427,22 @@ is32(const zic_t x) }
static void +writevalue(FILE *fp, char const *v) +{ + fputc('=', fp); + + for (; *v; v++) + if (*v == '\n') + fprintf(fp, "\\n"); + else if (*v == '\\') + fprintf(fp, "\\\\"); + else + fputc(*v, fp); + + fputc('\n', fp); +} + +static void writezone(const char *const name, const char *const string) { register FILE * fp; @@ -1708,6 +1765,18 @@ writezone(const char *const name, const char *const string) (void) putc(ttisgmts[i], fp); } (void) fprintf(fp, "\n%s\n", string); + if (genname || genoptions) + fprintf(fp, "=TZ\n"); + if (genname) { + fprintf(fp, "name"); + writevalue(fp, name); + } + for (i = 0; i < genoptions; i++) { + register char const *v = genoption[i]; + register int namelen = strchr(v, '=') - v; + fprintf(fp, "%.*s", namelen, v); + writevalue(fp, v + namelen + 1); + } if (ferror(fp) || fclose(fp)) { (void) fprintf(stderr, _("%s: Error writing %s\n"), progname, fullname); -- 1.8.3.1