Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 31 Aug 2009 02:22:18 +0000 (UTC)
From:      Edwin Groothuis <edwin@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org
Subject:   svn commit: r196683 - in stable/7: lib/libc lib/libc/stdtime usr.sbin/zic
Message-ID:  <200908310222.n7V2MISr013278@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: edwin
Date: Mon Aug 31 02:22:18 2009
New Revision: 196683
URL: http://svn.freebsd.org/changeset/base/196683

Log:
  MFC of r192625, r192890, r194783, r196587:
  
  r192625:
  MFC of tzcode2009e:
  
  Upgrade of the tzcode from 2004a to 2009e.
  
  Changes are numerous, but include...
  
  - New format of the output of zic, which supports both 32 and 64
    bit time_t formats.
  
  - zdump on 64 bit platforms will actually produce some output instead
    of doing nothing for a looooooooong time.
  
  - linux_base-fX, with X >= at least 8, will work without problems related
    to the local time again.
  
  The original patch, based on the 2008e, has been running for a long
  time on both my laptop and desktop machine and have been tested by
  other people.
  
  After the installation of this code and the running of zic(8), you
  need to run tzsetup(8) again to install the new datafile.
  
  r192890:
  
  MFC of tzcode2009h
  
  - Clarify the license for the tzcode: public domain
  
  r194783:
  
  Remove duplicate if-statement on gmt_is_set in gmtsub().
  
  r196587:
  
  MFC of tzcode2009k
  
  zic.c:
      Do not end a binary file with a POSIX-style time zone string
      for locations that end up in permanent DST (thanks to Andreas
      Schwab).

Deleted:
  stable/7/usr.sbin/zic/Arts.htm
  stable/7/usr.sbin/zic/tz-art.htm
  stable/7/usr.sbin/zic/tz-link.htm
Modified:
  stable/7/lib/libc/   (props changed)
  stable/7/lib/libc/stdtime/asctime.c
  stable/7/lib/libc/stdtime/difftime.c
  stable/7/lib/libc/stdtime/localtime.c
  stable/7/lib/libc/stdtime/private.h
  stable/7/lib/libc/stdtime/strftime.c
  stable/7/lib/libc/stdtime/time2posix.3
  stable/7/lib/libc/stdtime/tzfile.5
  stable/7/lib/libc/stdtime/tzfile.h
  stable/7/usr.sbin/zic/   (props changed)
  stable/7/usr.sbin/zic/README
  stable/7/usr.sbin/zic/Theory
  stable/7/usr.sbin/zic/ialloc.c
  stable/7/usr.sbin/zic/private.h
  stable/7/usr.sbin/zic/scheck.c
  stable/7/usr.sbin/zic/zdump.8
  stable/7/usr.sbin/zic/zdump.c
  stable/7/usr.sbin/zic/zic.8
  stable/7/usr.sbin/zic/zic.c

Modified: stable/7/lib/libc/stdtime/asctime.c
==============================================================================
--- stable/7/lib/libc/stdtime/asctime.c	Mon Aug 31 00:56:06 2009	(r196682)
+++ stable/7/lib/libc/stdtime/asctime.c	Mon Aug 31 02:22:18 2009	(r196683)
@@ -1,12 +1,18 @@
 /*
 ** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** Avoid the temptation to punt entirely to strftime;
+** the output of strftime is supposed to be locale specific
+** whereas the output of asctime is supposed to be constant.
 */
 
 #include <sys/cdefs.h>
 #ifndef lint
 #ifndef NOID
-static char	elsieid[] __unused = "@(#)asctime.c	7.9";
+static char	elsieid[] __unused = "@(#)asctime.c	8.2";
 #endif /* !defined NOID */
 #endif /* !defined lint */
 __FBSDID("$FreeBSD$");
@@ -19,7 +25,57 @@ __FBSDID("$FreeBSD$");
 #include "tzfile.h"
 
 /*
-** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12.
+** Some systems only handle "%.2d"; others only handle "%02d";
+** "%02.2d" makes (most) everybody happy.
+** At least some versions of gcc warn about the %02.2d;
+** we conditionalize below to avoid the warning.
+*/
+/*
+** All years associated with 32-bit time_t values are exactly four digits long;
+** some years associated with 64-bit time_t values are not.
+** Vintage programs are coded for years that are always four digits long
+** and may assume that the newline always lands in the same place.
+** For years that are less than four digits, we pad the output with
+** leading zeroes to get the newline in the traditional place.
+** The -4 ensures that we get four characters of output even if
+** we call a strftime variant that produces fewer characters for some years.
+** The ISO C 1999 and POSIX 1003.1-2004 standards prohibit padding the year,
+** but many implementations pad anyway; most likely the standards are buggy.
+*/
+#ifdef __GNUC__
+#define ASCTIME_FMT	"%.3s %.3s%3d %2.2d:%2.2d:%2.2d %-4s\n"
+#else /* !defined __GNUC__ */
+#define ASCTIME_FMT	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n"
+#endif /* !defined __GNUC__ */
+/*
+** For years that are more than four digits we put extra spaces before the year
+** so that code trying to overwrite the newline won't end up overwriting
+** a digit within a year and truncating the year (operating on the assumption
+** that no output is better than wrong output).
+*/
+#ifdef __GNUC__
+#define ASCTIME_FMT_B	"%.3s %.3s%3d %2.2d:%2.2d:%2.2d     %s\n"
+#else /* !defined __GNUC__ */
+#define ASCTIME_FMT_B	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d     %s\n"
+#endif /* !defined __GNUC__ */
+
+#define STD_ASCTIME_BUF_SIZE	26
+/*
+** Big enough for something such as
+** ??? ???-2147483648 -2147483648:-2147483648:-2147483648     -2147483648\n
+** (two three-character abbreviations, five strings denoting integers,
+** seven explicit spaces, two explicit colons, a newline,
+** and a trailing ASCII nul).
+** The values above are for systems where an int is 32 bits and are provided
+** as an example; the define below calculates the maximum for the system at
+** hand.
+*/
+#define MAX_ASCTIME_BUF_SIZE	(2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
+
+static char	buf_asctime[MAX_ASCTIME_BUF_SIZE];
+
+/*
+** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
 */
 
 char *
@@ -36,6 +92,8 @@ char *			buf;
 	};
 	const char *	wn;
 	const char *	mn;
+	char			year[INT_STRLEN_MAXIMUM(int) + 2];
+	char			result[MAX_ASCTIME_BUF_SIZE];
 
 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
 		wn = "???";
@@ -44,35 +102,41 @@ char *			buf;
 		mn = "???";
 	else	mn = mon_name[timeptr->tm_mon];
 	/*
-	** The X3J11-suggested format is
-	**	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
-	** Since the .2 in 02.2d is ignored, we drop it.
+	** Use strftime's %Y to generate the year, to avoid overflow problems
+	** when computing timeptr->tm_year + TM_YEAR_BASE.
+	** Assume that strftime is unaffected by other out-of-range members
+	** (e.g., timeptr->tm_mday) when processing "%Y".
 	*/
-	(void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
+	(void) strftime(year, sizeof year, "%Y", timeptr);
+	/*
+	** We avoid using snprintf since it's not available on all systems.
+	*/
+	(void) sprintf(result,
+		((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
 		wn, mn,
 		timeptr->tm_mday, timeptr->tm_hour,
 		timeptr->tm_min, timeptr->tm_sec,
-		TM_YEAR_BASE + timeptr->tm_year);
-	return buf;
+		year);
+	if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) {
+		(void) strcpy(buf, result);
+		return buf;
+	} else {
+#ifdef EOVERFLOW
+		errno = EOVERFLOW;
+#else /* !defined EOVERFLOW */
+		errno = EINVAL;
+#endif /* !defined EOVERFLOW */
+		return NULL;
+	}
 }
 
 /*
-** A la X3J11, with core dump avoidance.
+** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
 */
 
 char *
 asctime(timeptr)
 const struct tm *	timeptr;
 {
-	/*
-	** Big enough for something such as
-	** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
-	** (two three-character abbreviations, five strings denoting integers,
-	** three explicit spaces, two explicit colons, a newline,
-	** and a trailing ASCII nul).
-	*/
-	static char		result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
-					3 + 2 + 1 + 1];
-
-	return asctime_r(timeptr, result);
+	return asctime_r(timeptr, buf_asctime);
 }

Modified: stable/7/lib/libc/stdtime/difftime.c
==============================================================================
--- stable/7/lib/libc/stdtime/difftime.c	Mon Aug 31 00:56:06 2009	(r196682)
+++ stable/7/lib/libc/stdtime/difftime.c	Mon Aug 31 02:22:18 2009	(r196683)
@@ -1,12 +1,12 @@
 /*
 ** This file is in the public domain, so clarified as of
-** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
 */
 
 #include <sys/cdefs.h>
 #ifndef lint
 #ifndef NOID
-static char	elsieid[] __unused = "@(#)difftime.c	7.9";
+static char	elsieid[] __unused = "@(#)difftime.c	8.1";
 #endif /* !defined NOID */
 #endif /* !defined lint */
 __FBSDID("$FreeBSD$");
@@ -14,74 +14,56 @@ __FBSDID("$FreeBSD$");
 /*LINTLIBRARY*/
 
 #include "namespace.h"
-#include "private.h"
+#include "private.h"	/* for time_t, TYPE_INTEGRAL, and TYPE_SIGNED */
 #include "un-namespace.h"
 
-/*
-** Algorithm courtesy Paul Eggert (eggert@twinsun.com).
-*/
-
-#ifdef HAVE_LONG_DOUBLE
-#define long_double	long double
-#endif /* defined HAVE_LONG_DOUBLE */
-#ifndef HAVE_LONG_DOUBLE
-#define long_double	double
-#endif /* !defined HAVE_LONG_DOUBLE */
-
 double
 difftime(time1, time0)
 const time_t	time1;
 const time_t	time0;
 {
-	time_t	delta;
-	time_t	hibit;
-
-	{
-		time_t		tt;
-		double		d;
-		long_double	ld;
-
-		if (sizeof tt < sizeof d)
-			return (double) time1 - (double) time0;
-		if (sizeof tt < sizeof ld)
-			return (long_double) time1 - (long_double) time0;
+	/*
+	** If (sizeof (double) > sizeof (time_t)) simply convert and subtract
+	** (assuming that the larger type has more precision).
+	** This is the common real-world case circa 2004.
+	*/
+	if (sizeof (double) > sizeof (time_t))
+		return (double) time1 - (double) time0;
+	if (!TYPE_INTEGRAL(time_t)) {
+		/*
+		** time_t is floating.
+		*/
+		return time1 - time0;
+	}
+	if (!TYPE_SIGNED(time_t)) {
+		/*
+		** time_t is integral and unsigned.
+		** The difference of two unsigned values can't overflow
+		** if the minuend is greater than or equal to the subtrahend.
+		*/
+		if (time1 >= time0)
+			return time1 - time0;
+		else	return -((double) (time0 - time1));
 	}
-	if (time1 < time0)
-		return -difftime(time0, time1);
 	/*
-	** As much as possible, avoid loss of precision
-	** by computing the difference before converting to double.
+	** time_t is integral and signed.
+	** Handle cases where both time1 and time0 have the same sign
+	** (meaning that their difference cannot overflow).
 	*/
-	delta = time1 - time0;
-	if (delta >= 0)
-		return delta;
+	if ((time1 < 0) == (time0 < 0))
+		return time1 - time0;
 	/*
-	** Repair delta overflow.
+	** time1 and time0 have opposite signs.
+	** Punt if unsigned long is too narrow.
 	*/
-	hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1);
+	if (sizeof (unsigned long) < sizeof (time_t))
+		return (double) time1 - (double) time0;
 	/*
-	** The following expression rounds twice, which means
-	** the result may not be the closest to the true answer.
-	** For example, suppose time_t is 64-bit signed int,
-	** long_double is IEEE 754 double with default rounding,
-	** time1 = 9223372036854775807 and time0 = -1536.
-	** Then the true difference is 9223372036854777343,
-	** which rounds to 9223372036854777856
-	** with a total error of 513.
-	** But delta overflows to -9223372036854774273,
-	** which rounds to -9223372036854774784, and correcting
-	** this by subtracting 2 * (long_double) hibit
-	** (i.e. by adding 2**64 = 18446744073709551616)
-	** yields 9223372036854776832, which
-	** rounds to 9223372036854775808
-	** with a total error of 1535 instead.
-	** This problem occurs only with very large differences.
-	** It's too painful to fix this portably.
-	** We are not alone in this problem;
-	** some C compilers round twice when converting
-	** large unsigned types to small floating types,
-	** so if time_t is unsigned the "return delta" above
-	** has the same double-rounding problem with those compilers.
+	** Stay calm...decent optimizers will eliminate the complexity below.
 	*/
-	return delta - 2 * (long_double) hibit;
+	if (time1 >= 0 /* && time0 < 0 */)
+		return (unsigned long) time1 +
+			(unsigned long) (-(time0 + 1)) + 1;
+	return -(double) ((unsigned long) time0 +
+		(unsigned long) (-(time1 + 1)) + 1);
 }

Modified: stable/7/lib/libc/stdtime/localtime.c
==============================================================================
--- stable/7/lib/libc/stdtime/localtime.c	Mon Aug 31 00:56:06 2009	(r196682)
+++ stable/7/lib/libc/stdtime/localtime.c	Mon Aug 31 02:22:18 2009	(r196683)
@@ -1,20 +1,19 @@
 /*
 ** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
 */
 
 #include <sys/cdefs.h>
 #ifndef lint
 #ifndef NOID
-static char	elsieid[] __unused = "@(#)localtime.c	7.78";
+static char	elsieid[] __unused = "@(#)localtime.c	8.9";
 #endif /* !defined NOID */
 #endif /* !defined lint */
 __FBSDID("$FreeBSD$");
 
 /*
-** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
-** POSIX-style TZ environment variable handling from Guy Harris
-** (guy@auspex.com).
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
 */
 
 /*LINTLIBRARY*/
@@ -28,6 +27,20 @@ __FBSDID("$FreeBSD$");
 #include "un-namespace.h"
 
 #include "tzfile.h"
+#include "float.h"	/* for FLT_MAX and DBL_MAX */
+
+#ifndef TZ_ABBR_MAX_LEN
+#define TZ_ABBR_MAX_LEN	16
+#endif /* !defined TZ_ABBR_MAX_LEN */
+
+#ifndef TZ_ABBR_CHAR_SET
+#define TZ_ABBR_CHAR_SET \
+	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
+#endif /* !defined TZ_ABBR_CHAR_SET */
+
+#ifndef TZ_ABBR_ERR_CHAR
+#define TZ_ABBR_ERR_CHAR	'_'
+#endif /* !defined TZ_ABBR_ERR_CHAR */
 
 #include "libc_private.h"
 
@@ -74,16 +87,16 @@ __FBSDID("$FreeBSD$");
 **	5.	They might reference tm.TM_ZONE after calling offtime.
 ** What's best to do in the above cases is open to debate;
 ** for now, we just set things up so that in any of the five cases
-** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
+** WILDABBR is used. Another possibility: initialize tzname[0] to the
 ** string "tzname[0] used before set", and similarly for the other cases.
-** And another:  initialize tzname[0] to "ERA", with an explanation in the
+** And another: initialize tzname[0] to "ERA", with an explanation in the
 ** manual page of what this "time zone abbreviation" means (doing this so
 ** that tzname[0] has the "normal" length of three characters).
 */
 #define WILDABBR	"   "
 #endif /* !defined WILDABBR */
 
-static char		wildabbr[] = "WILDABBR";
+static char		wildabbr[] = WILDABBR;
 
 /*
  * In June 2004 it was decided UTC was a more appropriate default time
@@ -130,6 +143,8 @@ struct state {
 	int		timecnt;
 	int		typecnt;
 	int		charcnt;
+	int		goback;
+	int		goahead;
 	time_t		ats[TZ_MAX_TIMES];
 	unsigned char	types[TZ_MAX_TIMES];
 	struct ttinfo	ttis[TZ_MAX_TYPES];
@@ -155,40 +170,49 @@ struct rule {
 */
 
 static long		detzcode(const char * codep);
+static time_t		detzcode64(const char * codep);
+static int		differ_by_repeat(time_t t1, time_t t0);
 static const char *	getzname(const char * strp);
+static const char *	getqzname(const char * strp, const int delim);
 static const char *	getnum(const char * strp, int * nump, int min,
 				int max);
 static const char *	getsecs(const char * strp, long * secsp);
 static const char *	getoffset(const char * strp, long * offsetp);
 static const char *	getrule(const char * strp, struct rule * rulep);
 static void		gmtload(struct state * sp);
-static void		gmtsub(const time_t * timep, long offset,
+static struct tm *	gmtsub(const time_t * timep, long offset,
 				struct tm * tmp);
-static void		localsub(const time_t * timep, long offset,
+static struct tm *	localsub(const time_t * timep, long offset,
 				struct tm * tmp);
 static int		increment_overflow(int * number, int delta);
+static int		leaps_thru_end_of(int y);
+static int		long_increment_overflow(long * number, int delta);
+static int		long_normalize_overflow(long * tensptr,
+				int * unitsptr, int base);
 static int		normalize_overflow(int * tensptr, int * unitsptr,
 				int base);
 static void		settzname(void);
 static time_t		time1(struct tm * tmp,
-				void(*funcp) (const time_t *,
+				struct tm * (*funcp)(const time_t *,
 				long, struct tm *),
 				long offset);
 static time_t		time2(struct tm *tmp,
-				void(*funcp) (const time_t *,
+				struct tm * (*funcp)(const time_t *,
 				long, struct tm*),
 				long offset, int * okayp);
 static time_t		time2sub(struct tm *tmp,
-				void(*funcp) (const time_t *,
+				struct tm * (*funcp)(const time_t *,
 				long, struct tm*),
 				long offset, int * okayp, int do_norm_secs);
-static void		timesub(const time_t * timep, long offset,
+static struct tm *	timesub(const time_t * timep, long offset,
 				const struct state * sp, struct tm * tmp);
 static int		tmcomp(const struct tm * atmp,
 				const struct tm * btmp);
 static time_t		transtime(time_t janfirst, int year,
 				const struct rule * rulep, long offset);
-static int		tzload(const char * name, struct state * sp);
+static int		typesequiv(const struct state * sp, int a, int b);
+static int		tzload(const char * name, struct state * sp,
+				int doextend);
 static int		tzparse(const char * name, struct state * sp,
 				int lastditch);
 
@@ -224,7 +248,7 @@ char *			tzname[2] = {
 **	Except for the strftime function, these functions [asctime,
 **	ctime, gmtime, localtime] return values in one of two static
 **	objects: a broken-down time structure and an array of char.
-** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
+** Thanks to Paul Eggert for noting this.
 */
 
 static struct tm	tm;
@@ -245,12 +269,25 @@ const char * const	codep;
 	long	result;
 	int	i;
 
-	result = (codep[0] & 0x80) ? ~0L : 0L;
+	result = (codep[0] & 0x80) ? ~0L : 0;
 	for (i = 0; i < 4; ++i)
 		result = (result << 8) | (codep[i] & 0xff);
 	return result;
 }
 
+static time_t
+detzcode64(codep)
+const char * const	codep;
+{
+	register time_t	result;
+	register int	i;
+
+	result = (codep[0] & 0x80) ?  (~(int_fast64_t) 0) : 0;
+	for (i = 0; i < 8; ++i)
+		result = result * 256 + (codep[i] & 0xff);
+	return result;
+}
+
 static void
 settzname(void)
 {
@@ -299,16 +336,58 @@ settzname(void)
 		tzname[ttisp->tt_isdst] =
 			&sp->chars[ttisp->tt_abbrind];
 	}
+	/*
+	** Finally, scrub the abbreviations.
+	** First, replace bogus characters.
+	*/
+	for (i = 0; i < sp->charcnt; ++i)
+		if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL)
+			sp->chars[i] = TZ_ABBR_ERR_CHAR;
+	/*
+	** Second, truncate long abbreviations.
+	*/
+	for (i = 0; i < sp->typecnt; ++i) {
+		register const struct ttinfo * const	ttisp = &sp->ttis[i];
+		register char *				cp = &sp->chars[ttisp->tt_abbrind];
+
+		if (strlen(cp) > TZ_ABBR_MAX_LEN &&
+			strcmp(cp, GRANDPARENTED) != 0)
+				*(cp + TZ_ABBR_MAX_LEN) = '\0';
+	}
+}
+
+static int
+differ_by_repeat(t1, t0)
+const time_t	t1;
+const time_t	t0;
+{
+	int_fast64_t _t0 = t0;
+	int_fast64_t _t1 = t1;
+
+	if (TYPE_INTEGRAL(time_t) &&
+		TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+			return 0;
+	//turn ((int_fast64_t)(t1 - t0) == SECSPERREPEAT);
+	return _t1 - _t0 == SECSPERREPEAT;
 }
 
 static int
-tzload(name, sp)
+tzload(name, sp, doextend)
 const char *		name;
 struct state * const	sp;
+register const int	doextend;
 {
 	const char *	p;
 	int		i;
 	int		fid;
+	int		stored;
+	int		nread;
+	union {
+		struct tzhead	tzhead;
+		char		buf[2 * sizeof(struct tzhead) +
+					2 * sizeof *sp +
+					4 * TZ_MAX_TIMES];
+	} u;
 
 	/* XXX The following is from OpenBSD, and I'm not sure it is correct */
 	if (name != NULL && issetugid() != 0)
@@ -356,18 +435,13 @@ struct state * const	sp;
 			return -1;
 		}
 	}
-	{
-		struct tzhead *	tzhp;
-		union {
-			struct tzhead	tzhead;
-			char		buf[sizeof *sp + sizeof *tzhp];
-		} u;
+	nread = _read(fid, u.buf, sizeof u.buf);
+	if (_close(fid) < 0 || nread <= 0)
+		return -1;
+	for (stored = 4; stored <= 8; stored *= 2) {
 		int		ttisstdcnt;
 		int		ttisgmtcnt;
 
-		i = _read(fid, u.buf, sizeof u.buf);
-		if (_close(fid) != 0)
-			return -1;
 		ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
 		ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
 		sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
@@ -382,17 +456,19 @@ struct state * const	sp;
 			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
 			(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
 				return -1;
-		if (i - (p - u.buf) < sp->timecnt * 4 +	/* ats */
+		if (nread - (p - u.buf) <
+			sp->timecnt * stored +		/* ats */
 			sp->timecnt +			/* types */
-			sp->typecnt * (4 + 2) +		/* ttinfos */
+			sp->typecnt * 6 +		/* ttinfos */
 			sp->charcnt +			/* chars */
-			sp->leapcnt * (4 + 4) +		/* lsinfos */
+			sp->leapcnt * (stored + 4) +	/* lsinfos */
 			ttisstdcnt +			/* ttisstds */
 			ttisgmtcnt)			/* ttisgmts */
 				return -1;
 		for (i = 0; i < sp->timecnt; ++i) {
-			sp->ats[i] = detzcode(p);
-			p += 4;
+			sp->ats[i] = (stored == 4) ?
+				detzcode(p) : detzcode64(p);
+			p += stored;
 		}
 		for (i = 0; i < sp->timecnt; ++i) {
 			sp->types[i] = (unsigned char) *p++;
@@ -420,8 +496,9 @@ struct state * const	sp;
 			struct lsinfo *	lsisp;
 
 			lsisp = &sp->lsis[i];
-			lsisp->ls_trans = detzcode(p);
-			p += 4;
+			lsisp->ls_trans = (stored == 4) ?
+				detzcode(p) : detzcode64(p);
+			p += stored;
 			lsisp->ls_corr = detzcode(p);
 			p += 4;
 		}
@@ -451,10 +528,127 @@ struct state * const	sp;
 						return -1;
 			}
 		}
+		/*
+		** Out-of-sort ats should mean we're running on a
+		** signed time_t system but using a data file with
+		** unsigned values (or vice versa).
+		*/
+		for (i = 0; i < sp->timecnt - 2; ++i)
+			if (sp->ats[i] > sp->ats[i + 1]) {
+				++i;
+				if (TYPE_SIGNED(time_t)) {
+					/*
+					** Ignore the end (easy).
+					*/
+					sp->timecnt = i;
+				} else {
+					/*
+					** Ignore the beginning (harder).
+					*/
+					register int	j;
+
+					for (j = 0; j + i < sp->timecnt; ++j) {
+						sp->ats[j] = sp->ats[j + i];
+						sp->types[j] = sp->types[j + i];
+					}
+					sp->timecnt = j;
+				}
+				break;
+			}
+		/*
+		** If this is an old file, we're done.
+		*/
+		if (u.tzhead.tzh_version[0] == '\0')
+			break;
+		nread -= p - u.buf;
+		for (i = 0; i < nread; ++i)
+			u.buf[i] = p[i];
+		/*
+		** If this is a narrow integer time_t system, we're done.
+		*/
+		if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
+			break;
+	}
+	if (doextend && nread > 2 &&
+		u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+		sp->typecnt + 2 <= TZ_MAX_TYPES) {
+			struct state	ts;
+			register int	result;
+
+			u.buf[nread - 1] = '\0';
+			result = tzparse(&u.buf[1], &ts, FALSE);
+			if (result == 0 && ts.typecnt == 2 &&
+				sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
+					for (i = 0; i < 2; ++i)
+						ts.ttis[i].tt_abbrind +=
+							sp->charcnt;
+					for (i = 0; i < ts.charcnt; ++i)
+						sp->chars[sp->charcnt++] =
+							ts.chars[i];
+					i = 0;
+					while (i < ts.timecnt &&
+						ts.ats[i] <=
+						sp->ats[sp->timecnt - 1])
+							++i;
+					while (i < ts.timecnt &&
+					    sp->timecnt < TZ_MAX_TIMES) {
+						sp->ats[sp->timecnt] =
+							ts.ats[i];
+						sp->types[sp->timecnt] =
+							sp->typecnt +
+							ts.types[i];
+						++sp->timecnt;
+						++i;
+					}
+					sp->ttis[sp->typecnt++] = ts.ttis[0];
+					sp->ttis[sp->typecnt++] = ts.ttis[1];
+			}
+	}
+	sp->goback = sp->goahead = FALSE;
+	if (sp->timecnt > 1) {
+		for (i = 1; i < sp->timecnt; ++i)
+			if (typesequiv(sp, sp->types[i], sp->types[0]) &&
+				differ_by_repeat(sp->ats[i], sp->ats[0])) {
+					sp->goback = TRUE;
+					break;
+				}
+		for (i = sp->timecnt - 2; i >= 0; --i)
+			if (typesequiv(sp, sp->types[sp->timecnt - 1],
+				sp->types[i]) &&
+				differ_by_repeat(sp->ats[sp->timecnt - 1],
+				sp->ats[i])) {
+					sp->goahead = TRUE;
+					break;
+		}
 	}
 	return 0;
 }
 
+static int
+typesequiv(sp, a, b)
+const struct state * const	sp;
+const int			a;
+const int			b;
+{
+	register int	result;
+
+	if (sp == NULL ||
+		a < 0 || a >= sp->typecnt ||
+		b < 0 || b >= sp->typecnt)
+			result = FALSE;
+	else {
+		register const struct ttinfo *	ap = &sp->ttis[a];
+		register const struct ttinfo *	bp = &sp->ttis[b];
+		result = ap->tt_gmtoff == bp->tt_gmtoff &&
+			ap->tt_isdst == bp->tt_isdst &&
+			ap->tt_ttisstd == bp->tt_ttisstd &&
+			ap->tt_ttisgmt == bp->tt_ttisgmt &&
+			strcmp(&sp->chars[ap->tt_abbrind],
+			&sp->chars[bp->tt_abbrind]) == 0;
+	}
+	return result;
+}
+
 static const int	mon_lengths[2][MONSPERYEAR] = {
 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
@@ -466,7 +660,7 @@ static const int	year_lengths[2] = {
 
 /*
 ** Given a pointer into a time zone string, scan until a character that is not
-** a valid character in a zone name is found.  Return a pointer to that
+** a valid character in a zone name is found. Return a pointer to that
 ** character.
 */
 
@@ -483,6 +677,25 @@ const char *	strp;
 }
 
 /*
+** Given a pointer into an extended time zone string, scan until the ending
+** delimiter of the zone name is located. Return a pointer to the delimiter.
+**
+** As with getzname above, the legal character set is actually quite
+** restricted, with other characters producing undefined results.
+** We don't do any checking here; checking is done later in common-case code.
+*/
+
+static const char *
+getqzname(register const char *strp, const int delim)
+{
+	register int	c;
+
+	while ((c = *strp) != '\0' && c != delim)
+		++strp;
+	return strp;
+}
+
+/*
 ** Given a pointer into a time zone string, extract a number from that string.
 ** Check that the number is within a specified range; if it is not, return
 ** NULL.
@@ -547,7 +760,7 @@ long * const		secsp;
 		*secsp += num * SECSPERMIN;
 		if (*strp == ':') {
 			++strp;
-			/* `SECSPERMIN' allows for leap seconds.  */
+			/* `SECSPERMIN' allows for leap seconds. */
 			strp = getnum(strp, &num, 0, SECSPERMIN);
 			if (strp == NULL)
 				return NULL;
@@ -586,7 +799,7 @@ long * const		offsetp;
 
 /*
 ** Given a pointer into a time zone string, extract a rule in the form
-** date[/time].  See POSIX section 8 for the format of "date" and "time".
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
 ** If a valid rule is not found, return NULL.
 ** Otherwise, return a pointer to the first character not part of the rule.
 */
@@ -705,7 +918,7 @@ const long				offset;
 			dow += DAYSPERWEEK;
 
 		/*
-		** "dow" is the day-of-week of the first day of the month.  Get
+		** "dow" is the day-of-week of the first day of the month. Get
 		** the day-of-month (zero-origin) of the first "dow" day of the
 		** month.
 		*/
@@ -728,7 +941,7 @@ const long				offset;
 
 	/*
 	** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
-	** question.  To get the Epoch-relative time of the specified local
+	** question. To get the Epoch-relative time of the specified local
 	** time on that day, add the transition time and the current offset
 	** from UTC.
 	*/
@@ -766,10 +979,18 @@ const int			lastditch;
 			stdlen = (sizeof sp->chars) - 1;
 		stdoffset = 0;
 	} else {
-		name = getzname(name);
-		stdlen = name - stdname;
-		if (stdlen < 3)
-			return -1;
+		if (*name == '<') {
+			name++;
+			stdname = name;
+			name = getqzname(name, '>');
+			if (*name != '>')
+				return (-1);
+			stdlen = name - stdname;
+			name++;
+		} else {
+			name = getzname(name);
+			stdlen = name - stdname;
+		}
 		if (*name == '\0')
 			return -1;	/* was "stdoffset = 0;" */
 		else {
@@ -778,15 +999,22 @@ const int			lastditch;
 				return -1;
 		}
 	}
-	load_result = tzload(TZDEFRULES, sp);
+	load_result = tzload(TZDEFRULES, sp, FALSE);
 	if (load_result != 0)
 		sp->leapcnt = 0;		/* so, we're off a little */
 	if (*name != '\0') {
-		dstname = name;
-		name = getzname(name);
-		dstlen = name - dstname;	/* length of DST zone name */
-		if (dstlen < 3)
-			return -1;
+		if (*name == '<') {
+			dstname = ++name;
+			name = getqzname(name, '>');
+			if (*name != '>')
+				return -1;
+			dstlen = name - dstname;
+			name++;
+		} else {
+			dstname = name;
+			name = getzname(name);
+			dstlen = name - dstname; /* length of DST zone name */
+		}
 		if (*name != '\0' && *name != ',' && *name != ';') {
 			name = getoffset(name, &dstoffset);
 			if (name == NULL)
@@ -813,11 +1041,8 @@ const int			lastditch;
 				return -1;
 			sp->typecnt = 2;	/* standard time and DST */
 			/*
-			** Two transitions per year, from EPOCH_YEAR to 2037.
+			** Two transitions per year, from EPOCH_YEAR forward.
 			*/
-			sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
-			if (sp->timecnt > TZ_MAX_TIMES)
-				return -1;
 			sp->ttis[0].tt_gmtoff = -dstoffset;
 			sp->ttis[0].tt_isdst = 1;
 			sp->ttis[0].tt_abbrind = stdlen + 1;
@@ -827,7 +1052,12 @@ const int			lastditch;
 			atp = sp->ats;
 			typep = sp->types;
 			janfirst = 0;
-			for (year = EPOCH_YEAR; year <= 2037; ++year) {
+			sp->timecnt = 0;
+			for (year = EPOCH_YEAR;
+			    sp->timecnt + 2 <= TZ_MAX_TIMES;
+			    ++year) {
+			    	time_t	newfirst;
+
 				starttime = transtime(janfirst, year, &start,
 					stdoffset);
 				endtime = transtime(janfirst, year, &end,
@@ -843,8 +1073,13 @@ const int			lastditch;
 					*atp++ = endtime;
 					*typep++ = 1;	/* DST ends */
 				}
-				janfirst += year_lengths[isleap(year)] *
+				sp->timecnt += 2;
+				newfirst = janfirst;
+				newfirst += year_lengths[isleap(year)] *
 					SECSPERDAY;
+				if (newfirst <= janfirst)
+					break;
+				janfirst = newfirst;
 			}
 		} else {
 			long	theirstdoffset;
@@ -959,7 +1194,7 @@ static void
 gmtload(sp)
 struct state * const	sp;
 {
-	if (tzload(gmt, sp) != 0)
+	if (tzload(gmt, sp, TRUE) != 0)
 		(void) tzparse(gmt, sp, TRUE);
 }
 
@@ -990,7 +1225,7 @@ tzsetwall_basic(int rdlocked)
 		}
 	}
 #endif /* defined ALL_STATE */
-	if (tzload((char *) NULL, lclptr) != 0)
+	if (tzload((char *) NULL, lclptr, TRUE) != 0)
 		gmtload(lclptr);
 	settzname();
 	_RWLOCK_UNLOCK(&lcl_rwlock);
@@ -1053,7 +1288,7 @@ tzset_basic(int rdlocked)
 		lclptr->ttis[0].tt_gmtoff = 0;
 		lclptr->ttis[0].tt_abbrind = 0;
 		(void) strcpy(lclptr->chars, gmt);
-	} else if (tzload(name, lclptr) != 0)
+	} else if (tzload(name, lclptr, TRUE) != 0)
 		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
 			(void) gmtload(lclptr);
 	settzname();
@@ -1072,14 +1307,14 @@ tzset(void)
 /*
 ** The easy way to behave "as if no library function calls" localtime
 ** is to not call it--so we drop its guts into "localsub", which can be
-** freely called.  (And no, the PANS doesn't require the above behavior--
+** freely called. (And no, the PANS doesn't require the above behavior--
 ** but it *is* desirable.)
 **
 ** The unused offset argument is for the benefit of mktime variants.
 */
 
 /*ARGSUSED*/
-static void
+static struct tm *
 localsub(timep, offset, tmp)
 const time_t * const	timep;
 const long		offset;
@@ -1088,15 +1323,53 @@ struct tm * const	tmp;
 	struct state *		sp;
 	const struct ttinfo *	ttisp;
 	int			i;
-	const time_t			t = *timep;
+	struct tm *		result;
+	const time_t		t = *timep;
 
 	sp = lclptr;
 #ifdef ALL_STATE
-	if (sp == NULL) {
-		gmtsub(timep, offset, tmp);
-		return;
-	}
+	if (sp == NULL)
+		return gmtsub(timep, offset, tmp);
 #endif /* defined ALL_STATE */
+	if ((sp->goback && t < sp->ats[0]) ||
+		(sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+			time_t			newt = t;
+			register time_t		seconds;
+			register time_t		tcycles;
+			register int_fast64_t	icycles;
+
+			if (t < sp->ats[0])
+				seconds = sp->ats[0] - t;
+			else	seconds = t - sp->ats[sp->timecnt - 1];
+			--seconds;
+			tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+			++tcycles;
+			icycles = tcycles;
+			if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+				return NULL;
+			seconds = icycles;
+			seconds *= YEARSPERREPEAT;
+			seconds *= AVGSECSPERYEAR;
+			if (t < sp->ats[0])
+				newt += seconds;
+			else	newt -= seconds;
+			if (newt < sp->ats[0] ||
+				newt > sp->ats[sp->timecnt - 1])
+					return NULL;	/* "cannot happen" */
+			result = localsub(&newt, offset, tmp);
+			if (result == tmp) {
+				register time_t	newy;
+
+				newy = tmp->tm_year;
+				if (t < sp->ats[0])
+					newy -= icycles * YEARSPERREPEAT;
+				else	newy += icycles * YEARSPERREPEAT;
+				tmp->tm_year = newy;
+				if (tmp->tm_year != newy)
+					return NULL;
+			}
+			return result;
+	}
 	if (sp->timecnt == 0 || t < sp->ats[0]) {
 		i = 0;
 		while (sp->ttis[i].tt_isdst)
@@ -1105,10 +1378,17 @@ struct tm * const	tmp;
 				break;
 			}
 	} else {
-		for (i = 1; i < sp->timecnt; ++i)
-			if (t < sp->ats[i])
-				break;
-		i = sp->types[i - 1];
+		register int	lo = 1;
+		register int	hi = sp->timecnt;
+
+		while (lo < hi) {
+			register int	mid = (lo + hi) >> 1;
+
+			if (t < sp->ats[mid])
+				hi = mid;
+			else	lo = mid + 1;
+		}
+		i = (int) sp->types[lo - 1];
 	}
 	ttisp = &sp->ttis[i];
 	/*
@@ -1117,12 +1397,13 @@ struct tm * const	tmp;
 	**	t += ttisp->tt_gmtoff;
 	**	timesub(&t, 0L, sp, tmp);
 	*/
-	timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+	result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
 	tmp->tm_isdst = ttisp->tt_isdst;
 	tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
 #ifdef TM_ZONE
 	tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
 #endif /* defined TM_ZONE */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200908310222.n7V2MISr013278>