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.c327
1 files changed, 204 insertions, 123 deletions
diff --git a/timezone/zdump.c b/timezone/zdump.c
index 9255affc16..209b79d06c 100644
--- a/timezone/zdump.c
+++ b/timezone/zdump.c
@@ -9,20 +9,72 @@
 ** 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.
+**
+** However, include private.h when debugging, so that it overrides
+** time_t consistently with the rest of the package.
 */
 
+#ifdef time_tz
+# include "private.h"
+#endif
+
 #include "stdio.h"	/* for stdout, stderr, perror */
 #include "string.h"	/* for strcpy */
 #include "sys/types.h"	/* for time_t */
 #include "time.h"	/* for struct tm */
 #include "stdlib.h"	/* for exit, malloc, atoi */
-#include "float.h"	/* for FLT_MAX and DBL_MAX */
 #include "limits.h"	/* for CHAR_BIT, LLONG_MAX */
 #include "ctype.h"	/* for isalpha et al. */
 #ifndef isascii
 #define isascii(x) 1
 #endif /* !defined isascii */
 
+/*
+** Substitutes for pre-C99 compilers.
+** Much of this section of code is stolen from private.h.
+*/
+
+#ifndef HAVE_STDINT_H
+# define HAVE_STDINT_H \
+    (199901 <= __STDC_VERSION__ || 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif
+#if HAVE_STDINT_H
+# include "stdint.h"
+#endif
+#ifndef HAVE_INTTYPES_H
+# define HAVE_INTTYPES_H HAVE_STDINT_H
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#ifndef INT_FAST32_MAX
+# if INT_MAX >> 31 == 0
+typedef long int_fast32_t;
+# else
+typedef int int_fast32_t;
+# endif
+#endif
+
+#ifndef INTMAX_MAX
+# if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long intmax_t;
+#  define strtoimax strtoll
+#  define PRIdMAX "lld"
+#  ifdef LLONG_MAX
+#   define INTMAX_MAX LLONG_MAX
+#  else
+#   define INTMAX_MAX __LONG_LONG_MAX__
+#  endif
+# else
+typedef long intmax_t;
+#  define strtoimax strtol
+#  define PRIdMAX "ld"
+#  define INTMAX_MAX LONG_MAX
+# endif
+#endif
+
+
 #ifndef ZDUMP_LO_YEAR
 #define ZDUMP_LO_YEAR	(-500)
 #endif /* !defined ZDUMP_LO_YEAR */
@@ -90,9 +142,20 @@
 #define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
 #endif /* !defined isleap_sum */
 
-#define SECSPERDAY	((long) SECSPERHOUR * HOURSPERDAY)
+#define SECSPERDAY	((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
 #define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
 #define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
+#define SECSPER400YEARS	(SECSPERNYEAR * (intmax_t) (300 + 3)	\
+			 + SECSPERLYEAR * (intmax_t) (100 - 3))
+
+/*
+** True if SECSPER400YEARS is known to be representable as an
+** intmax_t.  It's OK that SECSPER400YEARS_FITS can in theory be false
+** even if SECSPER400YEARS is representable, because when that happens
+** the code merely runs a bit more slowly, and this slowness doesn't
+** occur on any practical platform.
+*/
+enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
 
 #ifndef HAVE_GETTEXT
 #define HAVE_GETTEXT 0
@@ -112,14 +175,6 @@
 #endif /* !defined lint */
 #endif /* !defined GNUC_or_lint */
 
-#ifndef INITIALIZE
-#ifdef GNUC_or_lint
-#define INITIALIZE(x)	((x) = 0)
-#else /* !defined GNUC_or_lint */
-#define INITIALIZE(x)
-#endif /* !defined GNUC_or_lint */
-#endif /* !defined INITIALIZE */
-
 #if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
 # define ATTRIBUTE_PURE __attribute__ ((__pure__))
 #else
@@ -151,48 +206,27 @@ extern char *	optarg;
 extern int	optind;
 extern char *	tzname[2];
 
-/* The minimum and maximum finite time values.  Shift 'long long' or
-   'long' instead of 'time_t'; this avoids compile-time errors when
-   time_t is floating-point.  In practice, 'long long' is wide enough.  */
+/* The minimum and maximum finite time values.  */
 static time_t const absolute_min_time =
-  ((time_t) 0.5 == 0.5
-   ? (sizeof (time_t) == sizeof (float) ? (time_t) -FLT_MAX
-      : sizeof (time_t) == sizeof (double) ? (time_t) -DBL_MAX
-      : sizeof (time_t) == sizeof (long double) ? (time_t) -LDBL_MAX
-      : 0)
-   : (time_t) -1 < 0
-#ifdef LLONG_MAX
-   ? (time_t) ((long long) -1 << (CHAR_BIT * sizeof (time_t) - 1))
-#else
-   ? (time_t) ((long) -1 << (CHAR_BIT * sizeof (time_t) - 1))
-#endif
+  ((time_t) -1 < 0
+   ? (time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)
    : 0);
 static time_t const absolute_max_time =
-  ((time_t) 0.5 == 0.5
-   ? (sizeof (time_t) == sizeof (float) ? (time_t) FLT_MAX
-      : sizeof (time_t) == sizeof (double) ? (time_t) DBL_MAX
-      : sizeof (time_t) == sizeof (long double) ? (time_t) LDBL_MAX
-      : -1)
-   : (time_t) -1 < 0
-#ifdef LLONG_MAX
-   ? (time_t) (- (~ 0 < 0) - ((long long) -1 << (CHAR_BIT * sizeof (time_t) - 1)))
-#else
-   ? (time_t) (- (~ 0 < 0) - ((long) -1 << (CHAR_BIT * sizeof (time_t) - 1)))
-#endif
-   : (time_t) -1);
+  ((time_t) -1 < 0
+   ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1))
+   : -1);
 static size_t	longest;
 static char *	progname;
 static int	warned;
 
 static char *	abbr(struct tm * tmp);
 static void	abbrok(const char * abbrp, const char * zone);
-static long	delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE;
+static intmax_t	delta(struct tm * newp, struct tm * oldp) ATTRIBUTE_PURE;
 static void	dumptime(const struct tm * tmp);
 static time_t	hunt(char * name, time_t lot, time_t	hit);
-static void	checkabsolutes(void);
 static void	show(char * zone, time_t t, int v);
 static const char *	tformat(void);
-static time_t	yeartot(long y) ATTRIBUTE_PURE;
+static time_t	yeartot(intmax_t y) ATTRIBUTE_PURE;
 
 #ifndef TYPECHECK
 #define my_localtime	localtime
@@ -209,7 +243,7 @@ my_localtime(time_t *tp)
 
 		tm = *tmp;
 		t = mktime(&tm);
-		if (t - *tp >= 1 || *tp - t >= 1) {
+		if (t != *tp) {
 			(void) fflush(stdout);
 			(void) fprintf(stderr, "\n%s: ", progname);
 			(void) fprintf(stderr, tformat(), *tp);
@@ -270,9 +304,9 @@ static void
 usage(FILE * const stream, const int status)
 {
 	(void) fprintf(stream,
-_("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n\
-\n\
-Report bugs to %s.\n"),
+_("%s: usage: %s [--version] [--help] [-{vV}] [-{ct} [lo,]hi] zonename ...\n"
+  "\n"
+  "Report bugs to %s.\n"),
 		       progname, progname, REPORT_BUGS_TO);
 	exit(status);
 }
@@ -281,11 +315,10 @@ int
 main(int argc, char *argv[])
 {
 	register int		i;
-	register int		c;
 	register int		vflag;
+	register int		Vflag;
 	register char *		cutarg;
-	register long		cutloyear = ZDUMP_LO_YEAR;
-	register long		cuthiyear = ZDUMP_HI_YEAR;
+	register char *		cuttimes;
 	register time_t		cutlotime;
 	register time_t		cuthitime;
 	register char **	fakeenv;
@@ -297,8 +330,8 @@ main(int argc, char *argv[])
 	register struct tm *	tmp;
 	register struct tm *	newtmp;
 
-	INITIALIZE(cutlotime);
-	INITIALIZE(cuthitime);
+	cutlotime = absolute_min_time;
+	cuthitime = absolute_max_time;
 #if HAVE_GETTEXT
 	(void) setlocale(LC_ALL, "");
 #ifdef TZ_DOMAINDIR
@@ -314,37 +347,78 @@ main(int argc, char *argv[])
 		} else if (strcmp(argv[i], "--help") == 0) {
 			usage(stdout, EXIT_SUCCESS);
 		}
-	vflag = 0;
-	cutarg = NULL;
-	while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
-		if (c == 'v')
-			vflag = 1;
-		else	cutarg = optarg;
-	if ((c != EOF && c != -1) ||
-		(optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
-			usage(stderr, EXIT_FAILURE);
-	}
-	if (vflag) {
+	vflag = Vflag = 0;
+	cutarg = cuttimes = NULL;
+	for (;;)
+	  switch (getopt(argc, argv, "c:t:vV")) {
+	  case 'c': cutarg = optarg; break;
+	  case 't': cuttimes = optarg; break;
+	  case 'v': vflag = 1; break;
+	  case 'V': Vflag = 1; break;
+	  case -1:
+	    if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
+	      goto arg_processing_done;
+	    /* Fall through.  */
+	  default:
+	    usage(stderr, EXIT_FAILURE);
+	  }
+ arg_processing_done:;
+
+	if (vflag | Vflag) {
+		intmax_t	lo;
+		intmax_t	hi;
+		char *loend, *hiend;
+		register intmax_t cutloyear = ZDUMP_LO_YEAR;
+		register intmax_t cuthiyear = ZDUMP_HI_YEAR;
 		if (cutarg != NULL) {
-			long	lo;
-			long	hi;
-			char	dummy;
-
-			if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
+			lo = strtoimax(cutarg, &loend, 10);
+			if (cutarg != loend && !*loend) {
+				hi = lo;
+				cuthiyear = hi;
+			} else if (cutarg != loend && *loend == ','
+				   && (hi = strtoimax(loend + 1, &hiend, 10),
+				       loend + 1 != hiend && !*hiend)) {
+				cutloyear = lo;
 				cuthiyear = hi;
-			} else if (sscanf(cutarg, "%ld,%ld%c",
-				&lo, &hi, &dummy) == 2) {
-					cutloyear = lo;
-					cuthiyear = hi;
 			} else {
 (void) fprintf(stderr, _("%s: wild -c argument %s\n"),
 					progname, cutarg);
 				exit(EXIT_FAILURE);
 			}
 		}
-		checkabsolutes();
-		cutlotime = yeartot(cutloyear);
-		cuthitime = yeartot(cuthiyear);
+		if (cutarg != NULL || cuttimes == NULL) {
+			cutlotime = yeartot(cutloyear);
+			cuthitime = yeartot(cuthiyear);
+		}
+		if (cuttimes != NULL) {
+			lo = strtoimax(cuttimes, &loend, 10);
+			if (cuttimes != loend && !*loend) {
+				hi = lo;
+				if (hi < cuthitime) {
+					if (hi < absolute_min_time)
+						hi = absolute_min_time;
+					cuthitime = hi;
+				}
+			} else if (cuttimes != loend && *loend == ','
+				   && (hi = strtoimax(loend + 1, &hiend, 10),
+				       loend + 1 != hiend && !*hiend)) {
+				if (cutlotime < lo) {
+					if (absolute_max_time < lo)
+						lo = absolute_max_time;
+					cutlotime = lo;
+				}
+				if (hi < cuthitime) {
+					if (hi < absolute_min_time)
+						hi = absolute_min_time;
+					cuthitime = hi;
+				}
+			} else {
+				(void) fprintf(stderr,
+					_("%s: wild -t argument %s\n"),
+					progname, cuttimes);
+				exit(EXIT_FAILURE);
+			}
+		}
 	}
 	(void) time(&now);
 	longest = 0;
@@ -375,15 +449,17 @@ main(int argc, char *argv[])
 		static char	buf[MAX_STRING_LENGTH];
 
 		(void) strcpy(&fakeenv[0][3], argv[i]);
-		if (!vflag) {
+		if (! (vflag | Vflag)) {
 			show(argv[i], now, FALSE);
 			continue;
 		}
 		warned = FALSE;
 		t = absolute_min_time;
-		show(argv[i], t, TRUE);
-		t += SECSPERHOUR * HOURSPERDAY;
-		show(argv[i], t, TRUE);
+		if (!Vflag) {
+			show(argv[i], t, TRUE);
+			t += SECSPERDAY;
+			show(argv[i], t, TRUE);
+		}
 		if (t < cutlotime)
 			t = cutlotime;
 		tmp = my_localtime(&t);
@@ -392,9 +468,11 @@ main(int argc, char *argv[])
 			(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
 		}
 		for ( ; ; ) {
-			if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
+			newt = (t < absolute_max_time - SECSPERDAY / 2
+				? t + SECSPERDAY / 2
+				: absolute_max_time);
+			if (cuthitime <= newt)
 				break;
-			newt = t + SECSPERHOUR * 12;
 			newtmp = localtime(&newt);
 			if (newtmp != NULL)
 				newtm = *newtmp;
@@ -415,11 +493,13 @@ main(int argc, char *argv[])
 			tm = newtm;
 			tmp = newtmp;
 		}
-		t = absolute_max_time;
-		t -= SECSPERHOUR * HOURSPERDAY;
-		show(argv[i], t, TRUE);
-		t += SECSPERHOUR * HOURSPERDAY;
-		show(argv[i], t, TRUE);
+		if (!Vflag) {
+			t = absolute_max_time;
+			t -= SECSPERDAY;
+			show(argv[i], t, TRUE);
+			t += SECSPERDAY;
+			show(argv[i], t, TRUE);
+		}
 	}
 	if (fflush(stdout) || ferror(stdout)) {
 		(void) fprintf(stderr, "%s: ", progname);
@@ -431,44 +511,45 @@ main(int argc, char *argv[])
 	return EXIT_FAILURE;
 }
 
-static void
-checkabsolutes(void)
-{
-	if (absolute_max_time < absolute_min_time) {
-		(void) fprintf(stderr,
-_("%s: use of -v on system with floating time_t other than float or double\n"),
-			       progname);
-		exit(EXIT_FAILURE);
-	}
-}
-
 static time_t
-yeartot(const long y)
+yeartot(const intmax_t y)
 {
-	register long	myy;
-	register long	seconds;
-	register time_t	t;
+	register intmax_t	myy, seconds, years;
+	register time_t		t;
 
 	myy = EPOCH_YEAR;
 	t = 0;
-	while (myy != y) {
-		if (myy < y) {
+	while (myy < y) {
+		if (SECSPER400YEARS_FITS && 400 <= y - myy) {
+			intmax_t diff400 = (y - myy) / 400;
+			if (INTMAX_MAX / SECSPER400YEARS < diff400)
+				return absolute_max_time;
+			seconds = diff400 * SECSPER400YEARS;
+			years = diff400 * 400;
+                } else {
 			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
-			++myy;
-			if (t > absolute_max_time - seconds) {
-				t = absolute_max_time;
-				break;
-			}
-			t += seconds;
+			years = 1;
+		}
+		myy += years;
+		if (t > absolute_max_time - seconds)
+			return absolute_max_time;
+		t += seconds;
+	}
+	while (y < myy) {
+		if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
+			intmax_t diff400 = (myy - y) / 400;
+			if (INTMAX_MAX / SECSPER400YEARS < diff400)
+				return absolute_min_time;
+			seconds = diff400 * SECSPER400YEARS;
+			years = diff400 * 400;
 		} else {
-			--myy;
-			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
-			if (t < absolute_min_time + seconds) {
-				t = absolute_min_time;
-				break;
-			}
-			t -= seconds;
+			seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
+			years = 1;
 		}
+		myy -= years;
+		if (t < absolute_min_time + seconds)
+			return absolute_min_time;
+		t -= seconds;
 	}
 	return t;
 }
@@ -477,7 +558,6 @@ static time_t
 hunt(char *name, time_t lot, time_t hit)
 {
 	time_t			t;
-	long			diff;
 	struct tm		lotm;
 	register struct tm *	lotmp;
 	struct tm		tm;
@@ -490,7 +570,7 @@ hunt(char *name, time_t lot, time_t hit)
 		(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
 	}
 	for ( ; ; ) {
-		diff = (long) (hit - lot);
+		time_t diff = hit - lot;
 		if (diff < 2)
 			break;
 		t = lot;
@@ -520,11 +600,11 @@ hunt(char *name, time_t lot, time_t hit)
 ** Thanks to Paul Eggert for logic used in delta.
 */
 
-static long
+static intmax_t
 delta(struct tm * newp, struct tm *oldp)
 {
-	register long	result;
-	register int	tmy;
+	register intmax_t	result;
+	register int		tmy;
 
 	if (newp->tm_year < oldp->tm_year)
 		return -delta(oldp, newp);
@@ -553,7 +633,7 @@ show(char *zone, time_t t, int v)
 			(void) printf(tformat(), t);
 		} else {
 			dumptime(tmp);
-			(void) printf(" UTC");
+			(void) printf(" UT");
 		}
 		(void) printf(" = ");
 	}
@@ -594,18 +674,19 @@ abbr(struct tm *tmp)
 static const char *
 tformat(void)
 {
-	if (0.5 == (time_t) 0.5) {	/* floating */
-		if (sizeof (time_t) > sizeof (double))
-			return "%Lg";
-		return "%g";
-	}
 	if (0 > (time_t) -1) {		/* signed */
+		if (sizeof (time_t) == sizeof (intmax_t))
+			return "%"PRIdMAX;
 		if (sizeof (time_t) > sizeof (long))
 			return "%lld";
 		if (sizeof (time_t) > sizeof (int))
 			return "%ld";
 		return "%d";
 	}
+#ifdef PRIuMAX
+	if (sizeof (time_t) == sizeof (uintmax_t))
+		return "%"PRIuMAX;
+#endif
 	if (sizeof (time_t) > sizeof (unsigned long))
 		return "%llu";
 	if (sizeof (time_t) > sizeof (unsigned int))