about summary refs log tree commit diff
path: root/timezone/zdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'timezone/zdump.c')
-rw-r--r--timezone/zdump.c131
1 files changed, 84 insertions, 47 deletions
diff --git a/timezone/zdump.c b/timezone/zdump.c
index bf75800101..608f288691 100644
--- a/timezone/zdump.c
+++ b/timezone/zdump.c
@@ -1,3 +1,5 @@
+/* Dump time zone data in a textual format.  */
+
 /*
 ** This file is in the public domain, so clarified as of
 ** 2009-05-17 by Arthur David Olson.
@@ -5,21 +7,16 @@
 
 #include "version.h"
 
-/*
-** This code has been made independent of the rest of the time
-** conversion package to increase confidence in the verification it provides.
-** You can use this code to help in verifying other implementations.
-** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
-*/
-
 #ifndef NETBSD_INSPIRED
 # define NETBSD_INSPIRED 1
 #endif
-#ifndef USE_LTZ
-# define USE_LTZ 1
-#endif
 
 #include "private.h"
+#include <stdio.h>
+
+#ifndef HAVE_SNPRINTF
+# define HAVE_SNPRINTF (199901 <= __STDC_VERSION__)
+#endif
 
 #ifndef HAVE_LOCALTIME_R
 # define HAVE_LOCALTIME_R 1
@@ -72,14 +69,11 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
 # define timezone_t char **
 #endif
 
-extern char **	environ;
-
 #if !HAVE_POSIX_DECLS
 extern int	getopt(int argc, char * const argv[],
 			const char * options);
 extern char *	optarg;
 extern int	optind;
-extern char *	tzname[];
 #endif
 
 /* The minimum and maximum finite time values.  */
@@ -144,7 +138,7 @@ sumsize(size_t a, size_t b)
 
 /* Return a pointer to a newly allocated buffer of size SIZE, exiting
    on failure.  SIZE should be nonzero.  */
-static void *
+static void * ATTRIBUTE_MALLOC
 xmalloc(size_t size)
 {
   void *p = malloc(size);
@@ -263,7 +257,7 @@ tzfree(timezone_t env)
 }
 #endif /* ! USE_LOCALTIME_RZ */
 
-/* A UTC time zone, and its initializer.  */
+/* A UT time zone, and its initializer.  */
 static timezone_t gmtz;
 static void
 gmtzinit(void)
@@ -278,7 +272,7 @@ gmtzinit(void)
   }
 }
 
-/* Convert *TP to UTC, storing the broken-down time into *TMP.
+/* Convert *TP to UT, storing the broken-down time into *TMP.
    Return TMP if successful, NULL otherwise.  This is like gmtime_r(TP, TMP),
    except typically faster if USE_LOCALTIME_RZ.  */
 static struct tm *
@@ -390,7 +384,7 @@ static void
 usage(FILE * const stream, const int status)
 {
 	fprintf(stream,
-_("%s: usage: %s OPTIONS ZONENAME ...\n"
+_("%s: usage: %s OPTIONS TIMEZONE ...\n"
   "Options include:\n"
   "  -c [L,]U   Start at year L (default -500), end before year U (default 2500)\n"
   "  -t [L,]U   Start at time L, end before time U (in seconds since 1970)\n"
@@ -550,6 +544,7 @@ main(int argc, char *argv[])
 		}
 		if (t < cutlotime)
 			t = cutlotime;
+		INITIALIZE (ab);
 		tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
 		if (tm_ok) {
 		  ab = saveabbr(&abbrev, &abbrevsize, &tm);
@@ -565,11 +560,10 @@ main(int argc, char *argv[])
 				 : cuthitime);
 		  struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
 		  bool newtm_ok = newtmp != NULL;
-		  if (! (tm_ok & newtm_ok
-			 ? (delta(&newtm, &tm) == newt - t
-			    && newtm.tm_isdst == tm.tm_isdst
-			    && strcmp(abbr(&newtm), ab) == 0)
-			 : tm_ok == newtm_ok)) {
+		  if (tm_ok != newtm_ok
+		      || (tm_ok && (delta(&newtm, &tm) != newt - t
+				    || newtm.tm_isdst != tm.tm_isdst
+				    || strcmp(abbr(&newtm), ab) != 0))) {
 		    newt = hunt(tz, argv[i], t, newt);
 		    newtmp = localtime_rz(tz, &newt, &newtm);
 		    newtm_ok = newtmp != NULL;
@@ -685,17 +679,15 @@ hunt(timezone_t tz, char *name, time_t lot, time_t hit)
 }
 
 /*
-** Thanks to Paul Eggert for logic used in delta.
+** Thanks to Paul Eggert for logic used in delta_nonneg.
 */
 
 static intmax_t
-delta(struct tm * newp, struct tm *oldp)
+delta_nonneg(struct tm *newp, struct tm *oldp)
 {
 	register intmax_t	result;
 	register int		tmy;
 
-	if (newp->tm_year < oldp->tm_year)
-		return -delta(oldp, newp);
 	result = 0;
 	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
 		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
@@ -709,6 +701,14 @@ delta(struct tm * newp, struct tm *oldp)
 	return result;
 }
 
+static intmax_t
+delta(struct tm *newp, struct tm *oldp)
+{
+  return (newp->tm_year < oldp->tm_year
+	  ? -delta_nonneg(oldp, newp)
+	  : delta_nonneg(newp, oldp));
+}
+
 #ifndef TM_GMTOFF
 /* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
    Assume A and B differ by at most one year.  */
@@ -722,8 +722,8 @@ adjusted_yday(struct tm const *a, struct tm const *b)
 }
 #endif
 
-/* If A is the broken-down local time and B the broken-down UTC for
-   the same instant, return A's UTC offset in seconds, where positive
+/* If A is the broken-down local time and B the broken-down UT for
+   the same instant, return A's UT offset in seconds, where positive
    offsets are east of Greenwich.  On failure, return LONG_MIN.
 
    If T is nonnull, *T is the timestamp that corresponds to A; call
@@ -787,6 +787,40 @@ show(timezone_t tz, char *zone, time_t t, bool v)
 		abbrok(abbr(tmp), zone);
 }
 
+#if HAVE_SNPRINTF
+# define my_snprintf snprintf
+#else
+# include <stdarg.h>
+
+/* A substitute for snprintf that is good enough for zdump.  */
+static int ATTRIBUTE_FORMAT((printf, 3, 4))
+my_snprintf(char *s, size_t size, char const *format, ...)
+{
+  int n;
+  va_list args;
+  char const *arg;
+  size_t arglen, slen;
+  char buf[1024];
+  va_start(args, format);
+  if (strcmp(format, "%s") == 0) {
+    arg = va_arg(args, char const *);
+    arglen = strlen(arg);
+  } else {
+    n = vsprintf(buf, format, args);
+    if (n < 0)
+      return n;
+    arg = buf;
+    arglen = n;
+  }
+  slen = arglen < size ? arglen : size - 1;
+  memcpy(s, arg, slen);
+  s[slen] = '\0';
+  n = arglen <= INT_MAX ? arglen : -1;
+  va_end(args);
+  return n;
+}
+#endif
+
 /* Store into BUF, of size SIZE, a formatted local time taken from *TM.
    Use ISO 8601 format +HH:MM:SS.  Omit :SS if SS is zero, and omit
    :MM too if MM is also zero.
@@ -799,16 +833,16 @@ format_local_time(char *buf, size_t size, struct tm const *tm)
 {
   int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
   return (ss
-	  ? snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
+	  ? my_snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
 	  : mm
-	  ? snprintf(buf, size, "%02d:%02d", hh, mm)
-	  : snprintf(buf, size, "%02d", hh));
+	  ? my_snprintf(buf, size, "%02d:%02d", hh, mm)
+	  : my_snprintf(buf, size, "%02d", hh));
 }
 
-/* Store into BUF, of size SIZE, a formatted UTC offset for the
+/* Store into BUF, of size SIZE, a formatted UT offset for the
    localtime *TM corresponding to time T.  Use ISO 8601 format
    +HHMMSS, or -HHMMSS for timestamps west of Greenwich; use the
-   format -00 for unknown UTC offsets.  If the hour needs more than
+   format -00 for unknown UT offsets.  If the hour needs more than
    two digits to represent, extend the length of HH as needed.
    Otherwise, omit SS if SS is zero, and omit MM too if MM is also
    zero.
@@ -837,10 +871,10 @@ format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t)
   mm = off / 60 % 60;
   hh = off / 60 / 60;
   return (ss || 100 <= hh
-	  ? snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
+	  ? my_snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
 	  : mm
-	  ? snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
-	  : snprintf(buf, size, "%c%02ld", sign, hh));
+	  ? my_snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
+	  : my_snprintf(buf, size, "%c%02ld", sign, hh));
 }
 
 /* Store into BUF (of size SIZE) a quoted string representation of P.
@@ -883,7 +917,7 @@ format_quoted_string(char *buf, size_t size, char const *p)
 
    %f zone name
    %L local time as per format_local_time
-   %Q like "U\t%Z\tD" where U is the UTC offset as for format_utc_offset
+   %Q like "U\t%Z\tD" where U is the UT offset as for format_utc_offset
       and D is the isdst flag; except omit D if it is zero, omit %Z if
       it equals U, quote and escape %Z if it contains nonalphabetics,
       and omit any trailing tabs.  */
@@ -943,15 +977,16 @@ istrftime(char *buf, size_t size, char const *time_fmt,
 	    for (abp = ab; is_alpha(*abp); abp++)
 	      continue;
 	    len = (!*abp && *ab
-		   ? snprintf(b, s, "%s", ab)
+		   ? my_snprintf(b, s, "%s", ab)
 		   : format_quoted_string(b, s, ab));
 	    if (s <= len)
 	      return false;
 	    b += len, s -= len;
 	  }
-	  formatted_len = (tm->tm_isdst
-			   ? snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
-			   : 0);
+	  formatted_len
+	    = (tm->tm_isdst
+	       ? my_snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
+	       : 0);
 	}
 	break;
       }
@@ -993,9 +1028,11 @@ abbr(struct tm const *tmp)
 #ifdef TM_ZONE
 	return tmp->TM_ZONE;
 #else
-	return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst]
-		? tzname[0 < tmp->tm_isdst]
-		: "");
+# if HAVE_TZNAME
+	if (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst])
+	  return tzname[0 < tmp->tm_isdst];
+# endif
+	return "";
 #endif
 }
 
@@ -1030,10 +1067,10 @@ tformat(void)
 static void
 dumptime(register const struct tm *timeptr)
 {
-	static const char	wday_name[][3] = {
+	static const char	wday_name[][4] = {
 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 	};
-	static const char	mon_name[][3] = {
+	static const char	mon_name[][4] = {
 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 	};
@@ -1059,7 +1096,7 @@ dumptime(register const struct tm *timeptr)
 		(int) (sizeof mon_name / sizeof mon_name[0]))
 			mn = "???";
 	else		mn = mon_name[timeptr->tm_mon];
-	printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
+	printf("%s %s%3d %.2d:%.2d:%.2d ",
 		wn, mn,
 		timeptr->tm_mday, timeptr->tm_hour,
 		timeptr->tm_min, timeptr->tm_sec);