[tz] version information in tz binary file?

Paul Eggert eggert at cs.ucla.edu
Tue Sep 10 01:04:05 UTC 2013


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 at 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




More information about the tz mailing list