Module Name:    src
Committed By:   snj
Date:           Tue Dec 13 06:41:29 UTC 2016

Modified Files:
        src/lib/libc/time [netbsd-7-0]: private.h zic.c

Log Message:
Pull up following revision(s) (requested by kre in ticket #1323):
        lib/libc/time/private.h: patch
        lib/libc/time/zic.c: patch
Make zic properly parse newer tzdata files.


To generate a diff of this commit:
cvs rdiff -u -r1.33.2.1 -r1.33.2.1.2.1 src/lib/libc/time/private.h
cvs rdiff -u -r1.46.2.1 -r1.46.2.1.2.1 src/lib/libc/time/zic.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libc/time/private.h
diff -u src/lib/libc/time/private.h:1.33.2.1 src/lib/libc/time/private.h:1.33.2.1.2.1
--- src/lib/libc/time/private.h:1.33.2.1	Sun Jan 25 09:11:03 2015
+++ src/lib/libc/time/private.h	Tue Dec 13 06:41:29 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: private.h,v 1.33.2.1 2015/01/25 09:11:03 martin Exp $	*/
+/*	$NetBSD: private.h,v 1.33.2.1.2.1 2016/12/13 06:41:29 snj Exp $	*/
 
 #ifndef PRIVATE_H
 #define PRIVATE_H
@@ -477,6 +477,17 @@ const char *	scheck(const char * string,
 #define TYPE_SIGNED(type) (/*CONSTCOND*/((type) -1) < 0)
 #endif /* !defined TYPE_SIGNED */
 
+#define TWOS_COMPLEMENT(t) (/*CONSTCOND*/(t) ~ (t) 0 < 0)
+
+/* Max and min values of the integer type T, of which only the bottom
+   B bits are used, and where the highest-order used bit is considered
+   to be a sign bit if T is signed.  */
+#define MAXVAL(t, b) /*LINTED*/					\
+  ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))			\
+	- 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
+#define MINVAL(t, b)						\
+  ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
+
 #ifdef LOCALTIME_IMPLEMENTATION
 /* The minimum and maximum finite time values.  */
 static time_t const time_t_min =

Index: src/lib/libc/time/zic.c
diff -u src/lib/libc/time/zic.c:1.46.2.1 src/lib/libc/time/zic.c:1.46.2.1.2.1
--- src/lib/libc/time/zic.c:1.46.2.1	Sun Jan 25 09:11:03 2015
+++ src/lib/libc/time/zic.c	Tue Dec 13 06:41:29 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: zic.c,v 1.46.2.1 2015/01/25 09:11:03 martin Exp $	*/
+/*	$NetBSD: zic.c,v 1.46.2.1.2.1 2016/12/13 06:41:29 snj Exp $	*/
 /*
 ** This file is in the public domain, so clarified as of
 ** 2006-07-17 by Arthur David Olson.
@@ -10,7 +10,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: zic.c,v 1.46.2.1 2015/01/25 09:11:03 martin Exp $");
+__RCSID("$NetBSD: zic.c,v 1.46.2.1.2.1 2016/12/13 06:41:29 snj Exp $");
 #endif /* !defined lint */
 
 #include "private.h"
@@ -88,6 +88,7 @@ struct zone {
 	zic_t		z_gmtoff;
 	const char *	z_rule;
 	const char *	z_format;
+	char		z_format_specifier;
 
 	zic_t		z_stdoff;
 
@@ -145,6 +146,16 @@ static bool	yearistype(int year, const c
 static int	atcomp(const void *avp, const void *bvp);
 static void	updateminmax(zic_t x);
 
+/* Bound on length of what %z can expand to.  */
+enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
+
+/* If true, work around a bug in Qt 5.6.1 and earlier, which mishandles
+   tz binary files whose POSIX-TZ-style strings contain '<'; see
+   QTBUG-53071 <https://bugreports.qt.io/browse/QTBUG-53071>.  This
+   workaround will no longer be needed when Qt 5.6.1 and earlier are
+   obsolete, say in the year 2021.  */
+enum { WORK_AROUND_QTBUG_53071 = 1 };
+
 static int		charcnt;
 static bool		errors;
 static bool		warnings;
@@ -154,7 +165,7 @@ static bool		leapseen;
 static zic_t		leapminyear;
 static zic_t		leapmaxyear;
 static int		linenum;
-static size_t		max_abbrvar_len;
+static size_t		max_abbrvar_len = PERCENT_Z_LEN_BOUND;
 static size_t		max_format_len;
 static zic_t		max_year;
 static zic_t		min_year;
@@ -350,6 +361,7 @@ static const int	len_years[2] = {
 
 static struct attype {
 	zic_t		at;
+	bool		dontmerge;
 	unsigned char	type;
 } *			attypes;
 static zic_t		gmtoffs[TZ_MAX_TYPES];
@@ -588,7 +600,7 @@ _("%s: More than one -L option specified
 				noise = true;
 				break;
 			case 's':
-				warning(_("-s ignored\n"));
+				warning(_("-s ignored"));
 				break;
 		}
 	if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
@@ -641,31 +653,44 @@ _("%s: More than one -L option specified
 	return errors ? EXIT_FAILURE : EXIT_SUCCESS;
 }
 
-static void
+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;
+	ptrdiff_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] == '.') {
-		fprintf(stderr, _("%s: file name '%s' contains"
-				  " '%.*s' component"),
-			progname, name, (int) component_len, component);
-		exit(EXIT_FAILURE);
+	  int len = component_len;
+	  error(_("file name '%s' contains '%.*s' component"),
+		name, len, component);
+	  return false;
 	}
-	if (!noise)
-		return;
-	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);
+	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 void
+static bool
 namecheck(const char *name)
 {
 	char const *cp;
@@ -689,14 +714,14 @@ namecheck(const char *name)
 				 ? _("file name '%s' contains byte '%c'")
 				 : _("file name '%s' contains byte '\\%o'")),
 				name, c);
-			return;
 		}
 		if (c == '/') {
-			componentcheck(name, component, cp);
+			if(!componentcheck(name, component, cp))
+			  return false;
 			component = cp + 1;
 		}
 	}
-	componentcheck(name, component, cp);
+	return componentcheck(name, component, cp);
 }
 
 static void
@@ -827,7 +852,19 @@ static const zic_t max_time = -1 - ((zic
 #define BIG_BANG (- (1LL << 59))
 #endif
 
-static const zic_t big_bang_time = BIG_BANG;
+/* If true, work around GNOME bug 730332
+   <https://bugzilla.gnome.org/show_bug.cgi?id=730332>
+   by refusing to output time stamps before BIG_BANG.
+   Such time stamps are physically suspect anyway.
+
+   The GNOME bug is scheduled to be fixed in GNOME 3.22, and if so
+   this workaround will no longer be needed when GNOME 3.21 and
+   earlier are obsolete, say in the year 2021.  */
+enum { WORK_AROUND_GNOME_BUG_730332 = true };
+
+static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332
+				 ? BIG_BANG
+				 : MINVAL(zic_t, TIME_T_BITS_IN_FILE));
 
 /* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble.  */
 static int
@@ -931,7 +968,7 @@ associate(void)
 			** 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"));
 		}
 	}
@@ -1004,7 +1041,7 @@ infile(const char *name)
 				case LC_LEAP:
 					if (name != leapsec)
 						warning(
-_("%s: Leap line in non leap seconds file %s\n"),
+_("%s: Leap line in non leap seconds file %s"),
 							progname, name);
 					else	inleap(fields, nfields);
 					wantcont = false;
@@ -1145,6 +1182,7 @@ static bool
 inzsub(char **const fields, const int nfields, const int iscont)
 {
 	char *		cp;
+	char *		cp1;
 	static struct zone	z;
 	int		i_gmtoff, i_rule, i_format;
 	int		i_untilyear, i_untilmonth;
@@ -1160,7 +1198,9 @@ inzsub(char **const fields, const int nf
 		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;
@@ -1174,13 +1214,21 @@ inzsub(char **const fields, const int nf
 	z.z_linenum = linenum;
 	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;
 		}
 	}
 	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;
@@ -1314,7 +1362,7 @@ inleap(char **const fields, const int nf
 			return;
 		}
 		t = tadd(t, tod);
-		if (t < big_bang_time) {
+		if (t < early_time) {
 			error(_("leap second precedes Big Bang"));
 			return;
 		}
@@ -1335,10 +1383,8 @@ inlink(char **const fields, const int nf
 		error(_("blank FROM field on Link line"));
 		return;
 	}
-	if (*fields[LF_TO] == '\0') {
-		error(_("blank TO field on Link line"));
+	if (! namecheck(fields[LF_TO]))
 		return;
-	}
 	l.l_filename = filename;
 	l.l_linenum = linenum;
 	l.l_from = ecpyalloc(fields[LF_FROM]);
@@ -1559,11 +1605,13 @@ writezone(const char *const name, const 
 	static char *			fullname;
 	static const struct tzhead	tzh0;
 	static struct tzhead		tzh;
-	zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1));
-	void *typesptr = ats + timecnt;
+	zic_t one = 1;
+	zic_t y2038_boundary = one << 31;
+	ptrdiff_t nats = timecnt + WORK_AROUND_QTBUG_53071;
+	zic_t *ats = emalloc(size_product(nats, sizeof *ats + 1));
+	void *typesptr = ats + nats;
 	unsigned char *types = typesptr;
 
-	namecheck(name);
 	/*
 	** Sort.
 	*/
@@ -1579,7 +1627,7 @@ writezone(const char *const name, const 
 
 		toi = 0;
 		fromi = 0;
-		while (fromi < timecnt && attypes[fromi].at < big_bang_time)
+		while (fromi < timecnt && attypes[fromi].at < early_time)
 			++fromi;
 		for ( ; fromi < timecnt; ++fromi) {
 			if (toi > 1 && ((attypes[fromi].at +
@@ -1590,8 +1638,9 @@ writezone(const char *const name, const 
 						attypes[fromi].type;
 					continue;
 			}
-			if (toi == 0 ||
-				attypes[toi - 1].type != attypes[fromi].type)
+			if (toi == 0
+			    || attypes[fromi].dontmerge
+			    || attypes[toi - 1].type != attypes[fromi].type)
 					attypes[toi++] = attypes[fromi];
 		}
 		timecnt = toi;
@@ -1606,6 +1655,19 @@ writezone(const char *const name, const 
 		ats[i] = attypes[i].at;
 		types[i] = attypes[i].type;
 	}
+
+	/* Work around QTBUG-53071 for time stamps less than y2038_boundary - 1,
+	   by inserting a no-op transition at time y2038_boundary - 1.
+	   This works only for timestamps before the boundary, which
+	   should be good enough in practice as QTBUG-53071 should be
+	   long-dead by 2038.  */
+	if (WORK_AROUND_QTBUG_53071 && timecnt != 0
+	    && ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<')) {
+	  ats[timecnt] = y2038_boundary - 1;
+	  types[timecnt] = types[timecnt - 1];
+	  timecnt++;
+	}
+
 	/*
 	** Correct for leap seconds.
 	*/
@@ -1676,19 +1738,24 @@ writezone(const char *const name, const 
 		int	thistypecnt;
 		char		thischars[TZ_MAX_CHARS];
 		char		thischarcnt;
+		bool		toomanytimes;
 		int		indmap[TZ_MAX_CHARS];
 
 		if (pass == 1) {
 			thistimei = timei32;
 			thistimecnt = timecnt32;
+			toomanytimes = thistimecnt >> 31 >> 1 != 0;
 			thisleapi = leapi32;
 			thisleapcnt = leapcnt32;
 		} else {
 			thistimei = 0;
 			thistimecnt = timecnt;
+			toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
 			thisleapi = 0;
 			thisleapcnt = leapcnt;
 		}
+		if (toomanytimes)
+		  error(_("too many transition times"));
 		thistimelim = thistimei + thistimecnt;
 		thisleaplim = thisleapi + thisleapcnt;
 		for (i = 0; i < typecnt; ++i)
@@ -1866,37 +1933,78 @@ writezone(const char *const name, const 
 	free(ats);
 }
 
-static void
-doabbr(char *const abbr, const int abbrlen, const char *const format,
-    const char *const letters, bool isdst, bool 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 *const abbr, const int abbrlen, struct zone const *zp,
+    const char *letters, zic_t stdoff, bool doquotes)
 {
 	char *	cp;
 	char *	slashp;
-	int	len;
+	size_t	len;
+	char const *format = zp->z_format;
 
 	slashp = strchr(format, '/');
 	if (slashp == NULL) {
-		if (letters == NULL)
-			(void) strlcpy(abbr, format, abbrlen);
-		else	(void) snprintf(abbr, abbrlen, format, letters);
-	} else if (isdst) {
+		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";
+		snprintf(abbr, abbrlen, format, letters);
+	} else if (stdoff != 0) {
 		(void) strlcpy(abbr, slashp + 1, abbrlen);
 	} else {
 		(void) memcpy(abbr, format, slashp - format);
 		abbr[slashp - format] = '\0';
 	}
+	len = strlen(abbr);
 	if (!doquotes)
-		return;
+		return len;
 	for (cp = abbr; is_alpha(*cp); cp++)
 		continue;
-	len = strlen(abbr);
 	if (len > 0 && *cp == '\0')
-		return;
+		return len;
 	abbr[len + 2] = '\0';
 	abbr[len + 1] = '>';
 	for ( ; len > 0; --len)
 		abbr[len] = abbr[len - 1];
 	abbr[0] = '<';
+	return len + 2;
 }
 
 static void
@@ -1908,17 +2016,19 @@ updateminmax(const zic_t x)
 		max_year = x;
 }
 
-static bool
+static int
 stringoffset(char *result, zic_t offset)
 {
 	int	hours;
 	int	minutes;
 	int	seconds;
+	bool negative = offset < 0;
+	int len = negative;
 
 	result[0] = '\0';
-	if (offset < 0) {
-		strcpy(result, "-");
+	if (negative) {
 		offset = -offset;
+		result[0] = '-';
 	}
 	seconds = offset % SECSPERMIN;
 	offset /= SECSPERMIN;
@@ -1927,15 +2037,15 @@ stringoffset(char *result, zic_t offset)
 	hours = offset;
 	if (hours >= HOURSPERDAY * DAYSPERWEEK) {
 		result[0] = '\0';
-		return false;
+		return 0;
 	}
-	sprintf(end(result), "%d", hours);
+	len += sprintf(result + len, "%d", hours);
 	if (minutes != 0 || seconds != 0) {
-		sprintf(end(result), ":%02d", minutes);
+		len += sprintf(result + len, ":%02d", minutes);
 		if (seconds != 0)
-			sprintf(end(result), ":%02d", seconds);
+			len += sprintf(result + len, ":%02d", seconds);
 	}
-	return true;
+	return len;
 }
 
 static int
@@ -1945,7 +2055,6 @@ stringrule(char *result, const struct ru
 	zic_t	tod = rp->r_tod;
 	int	compat = 0;
 
-	result = end(result);
 	if (rp->r_dycode == DC_DOM) {
 		int	month, total;
 
@@ -1956,9 +2065,9 @@ stringrule(char *result, const struct ru
 			total += len_months[0][month];
 		/* Omit the "J" in Jan and Feb, as that's shorter.  */
 		if (rp->r_month <= 1)
-		  sprintf(result, "%d", total + rp->r_dayofmonth - 1);
+		  result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
 		else
-		  sprintf(result, "J%d", total + rp->r_dayofmonth);
+		  result += sprintf(result, "J%d", total + rp->r_dayofmonth);
 	} else {
 		int	week;
 		int	wday = rp->r_wday;
@@ -1985,7 +2094,7 @@ stringrule(char *result, const struct ru
 		} else	return -1;	/* "cannot happen" */
 		if (wday < 0)
 			wday += DAYSPERWEEK;
-		sprintf(result, "M%d.%d.%d",
+		result += sprintf(result, "M%d.%d.%d",
 			rp->r_month + 1, week, wday);
 	}
 	if (rp->r_todisgmt)
@@ -1993,8 +2102,8 @@ stringrule(char *result, const struct ru
 	if (rp->r_todisstd && rp->r_stdoff == 0)
 		tod += dstoff;
 	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
-		strcat(result, "/");
-		if (! stringoffset(end(result), tod))
+		*result++ = '/';
+		if (! stringoffset(result, tod))
 			return -1;
 		if (tod < 0) {
 			if (compat < 2013)
@@ -2035,6 +2144,8 @@ stringzone(char *result, const int resul
 	const char *		abbrvar;
 	int			compat = 0;
 	int			c;
+	size_t			len;
+	int			offsetlen;
 	struct rule		stdr, dstr;
 
 	result[0] = '\0';
@@ -2102,31 +2213,37 @@ stringzone(char *result, const int resul
 	if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
 		return -1;
 	abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
-	doabbr(result, resultlen, zp->z_format, abbrvar, false, true);
-	if (! stringoffset(end(result), -zp->z_gmtoff)) {
+	len = doabbr(result, resultlen, 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), resultlen - strlen(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))) {
+	len += doabbr(result + len, resultlen - 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;
 		}
-	(void) strcat(result, ",");
-	c = stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff);
+		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;
-	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;
@@ -2160,6 +2277,7 @@ outzone(const struct zone *const zpfirst
 	int			compat;
 	bool			do_extend;
 	int			version;
+	ptrdiff_t lastatmax = -1;
 
 	max_abbr_len = 2 + max_format_len + max_abbrvar_len;
 	max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -2267,9 +2385,9 @@ outzone(const struct zone *const zpfirst
 		*/
 		stdoff = 0;
 		zp = &zpfirst[i];
-		usestart = i > 0 && (zp - 1)->z_untiltime > big_bang_time;
+		usestart = i > 0 && (zp - 1)->z_untiltime > early_time;
 		useuntil = i < (zonecount - 1);
-		if (useuntil && zp->z_untiltime <= big_bang_time)
+		if (useuntil && zp->z_untiltime <= early_time)
 			continue;
 		gmtoff = zp->z_gmtoff;
 		eat(zp->z_filename, zp->z_linenum);
@@ -2277,15 +2395,15 @@ outzone(const struct zone *const zpfirst
 		startoff = zp->z_gmtoff;
 		if (zp->z_nrules == 0) {
 			stdoff = zp->z_stdoff;
-			doabbr(startbuf, max_abbr_len + 1, zp->z_format,
-			        NULL, stdoff != 0, false);
+			doabbr(startbuf, max_abbr_len + 1, zp,
+			        NULL, stdoff, false);
 			type = addtype(oadd(zp->z_gmtoff, stdoff),
 				startbuf, stdoff != 0, startttisstd,
 				startttisgmt);
 			if (usestart) {
 				addtt(starttime, type);
 				usestart = false;
-			} else	addtt(big_bang_time, type);
+			} else	addtt(early_time, type);
 		} else for (year = min_year; year <= max_year; ++year) {
 			if (useuntil && year > zp->z_untilrule.r_hiyear)
 				break;
@@ -2345,7 +2463,17 @@ outzone(const struct zone *const zpfirst
 					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 */
@@ -2362,9 +2490,9 @@ outzone(const struct zone *const zpfirst
 							stdoff);
 						doabbr(startbuf,
 							max_abbr_len + 1,
-							zp->z_format,
+							zp,
 							rp->r_abbrvar,
-							rp->r_stdoff != 0,
+							rp->r_stdoff,
 							false);
 						continue;
 					}
@@ -2373,17 +2501,16 @@ outzone(const struct zone *const zpfirst
 						stdoff)) {
 							doabbr(startbuf,
 								max_abbr_len + 1,
-								zp->z_format,
+								zp,
 								rp->r_abbrvar,
-								rp->r_stdoff !=
-								0,
+								rp->r_stdoff,
 								false);
 					}
 				}
 				eats(zp->z_filename, zp->z_linenum,
 					rp->r_filename, rp->r_linenum);
-				doabbr(ab, max_abbr_len+1, zp->z_format, rp->r_abbrvar,
-					rp->r_stdoff != 0, false);
+				doabbr(ab, max_abbr_len + 1, 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);
@@ -2419,6 +2546,8 @@ error(_("can't determine time zone abbre
 				starttime = tadd(starttime, -gmtoff);
 		}
 	}
+	if (0 <= lastatmax)
+	  attypes[lastatmax].dontmerge = true;
 	if (do_extend) {
 		/*
 		** If we're extending the explicitly listed observations
@@ -2432,6 +2561,7 @@ error(_("can't determine time zone abbre
 		*/
 		struct rule xr;
 		struct attype *lastat;
+		memset(&xr, 0, sizeof(xr));
 		xr.r_month = TM_JANUARY;
 		xr.r_dycode = DC_DOM;
 		xr.r_dayofmonth = 1;
@@ -2440,21 +2570,8 @@ error(_("can't determine time zone abbre
 			if (attypes[i].at > lastat->at)
 				lastat = &attypes[i];
 		if (lastat->at < rpytime(&xr, max_year - 1)) {
-			/*
-			** Create new type code for the redundant entry,
-			** to prevent it being optimized away.
-			*/
-			if (typecnt >= TZ_MAX_TYPES) {
-				error(_("too many local time types"));
-				exit(EXIT_FAILURE);
-			}
-			gmtoffs[typecnt] = gmtoffs[lastat->type];
-			isdsts[typecnt] = isdsts[lastat->type];
-			ttisstds[typecnt] = ttisstds[lastat->type];
-			ttisgmts[typecnt] = ttisgmts[lastat->type];
-			abbrinds[typecnt] = abbrinds[lastat->type];
-			++typecnt;
 			addtt(rpytime(&xr, max_year + 1), typecnt-1);
+			attypes[timecnt - 1].dontmerge = true;
 		}
 	}
 	writezone(zpfirst->z_name, envvar, version);
@@ -2466,8 +2583,8 @@ error(_("can't determine time zone abbre
 static void
 addtt(const zic_t starttime, int type)
 {
-	if (starttime <= big_bang_time ||
-		(timecnt == 1 && attypes[0].at < big_bang_time)) {
+	if (starttime <= early_time ||
+		(timecnt == 1 && attypes[0].at < early_time)) {
 		gmtoffs[0] = gmtoffs[type];
 		isdsts[0] = isdsts[type];
 		ttisstds[0] = ttisstds[type];
@@ -2482,6 +2599,7 @@ addtt(const zic_t starttime, int type)
 	}
 	attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
 	attypes[timecnt].at = starttime;
+	attypes[timecnt].dontmerge = false;
 	attypes[timecnt].type = type;
 	++timecnt;
 }
@@ -2862,21 +2980,13 @@ newabbr(const char *const string)
 		*/
 		cp = string;
 		mp = NULL;
-		while (is_alpha(*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 (is_digit(*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)

Reply via email to