>From 9b900f41f4fe7f29281f3f15ad1dd69784e91023 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 30 Sep 2021 01:12:44 -0700
Subject: [PATCH] Fix 'zic -b fat' bug with Port Moresby 32-bit data
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem reported by Daniel Fischer in:
https://mm.icann.org/pipermail/tz/2021-September/030849.html
* NEWS: Mention this.
* zic.c (limitrange, writezone): Don’t add a pre-transition unless
there’s a preceding transition to base it on.
(writezone): With fat output, arrange for a transition at the
start of the 32-bit data if needed.
---
 NEWS  |  7 +++++++
 zic.c | 12 +++++-------
 2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/NEWS b/NEWS
index 7fd6ab3..5fd032e 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ Unreleased, experimental changes
 
   Briefly:
     Revert most 2021b changes to 'backward'
+    Fix zic -b fat bug in pre-1970 32-bit data
 
   Changes to Link directives' source files
 
@@ -13,6 +14,12 @@ Unreleased, experimental changes
     directive, some downstream uses ran into trouble with the move.
     (Problem reported by Stephen Colebourne for Joda-Time.)
 
+  Changes to code
+
+    Fix a bug in 'zic -b fat' that caused old timestamps to be
+    mishandled in 32-bit-only readers.  (Problem reported by Daniel
+    Fischer.)
+
 
 Release 2021b - 2021-09-24 16:23:00 -0700
 
diff --git a/zic.c b/zic.c
index 224cc22..0101351 100644
--- a/zic.c
+++ b/zic.c
@@ -2003,7 +2003,7 @@ limitrange(struct timerange r, bool locut, zic_t lo, zic_t hi,
      This is needed when the output is truncated at the start, and is
      also useful when catering to buggy 32-bit clients that do not use
      time type 0 for timestamps before the first transition.  */
-  r.pretrans = locut && ! (r.count && ats[r.base] == lo);
+  r.pretrans = locut && r.base && ! (r.count && ats[r.base] == lo);
 
   /* Determine whether to append an expiration to the leap second table.  */
   r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
@@ -2120,7 +2120,7 @@ writezone(const char *const name, const char *const string, char version,
 	rangeall.pretrans = rangeall.leapexpiry = false;
 	range64 = limitrange(rangeall, min_time < lo_time,
 			     lo_time, hi_time, ats, types);
-	range32 = limitrange(range64, INT32_MIN < lo_time,
+	range32 = limitrange(range64, INT32_MIN < lo_time || want_bloat(),
 			     INT32_MIN, INT32_MAX, ats, types);
 
 	/* TZif version 4 is needed if a no-op transition is appended to
@@ -2210,7 +2210,7 @@ writezone(const char *const name, const char *const string, char version,
 		  hicut = false;
 		memset(omittype, true, typecnt);
 		omittype[thisdefaulttype] = false;
-		for (i = thistimei; i < thistimelim; i++)
+		for (i = thistimei - pretrans; i < thistimelim; i++)
 		  omittype[types[i]] = false;
 
 		/* Reorder types to make THISDEFAULTTYPE type 0.
@@ -2232,7 +2232,7 @@ writezone(const char *const name, const char *const string, char version,
 			register int	mrudst, mrustd, hidst, histd, type;
 
 			hidst = histd = mrudst = mrustd = -1;
-			for (i = thistimei; i < thistimelim; ++i)
+			for (i = thistimei - pretrans; i < thistimelim; ++i)
 				if (isdsts[types[i]])
 					mrudst = types[i];
 				else	mrustd = types[i];
@@ -2348,9 +2348,7 @@ writezone(const char *const name, const char *const string, char version,
 		if (hicut)
 		  puttzcodepass(hi_time + 1, fp, pass);
 		currenttype = 0;
-		if (pretrans)
-		  putc(currenttype, fp);
-		for (i = thistimei; i < thistimelim; ++i) {
+		for (i = thistimei - pretrans; i < thistimelim; ++i) {
 		  currenttype = typemap[types[i]];
 		  putc(currenttype, fp);
 		}
-- 
2.30.2

