diff options
Diffstat (limited to 'winsup/cygwin/localtime.cc')
-rw-r--r-- | winsup/cygwin/localtime.cc | 1703 |
1 files changed, 1010 insertions, 693 deletions
diff --git a/winsup/cygwin/localtime.cc b/winsup/cygwin/localtime.cc index 239aaffdd..19eef6759 100644 --- a/winsup/cygwin/localtime.cc +++ b/winsup/cygwin/localtime.cc @@ -1,6 +1,14 @@ +/* $NetBSD: localtime.c,v 1.72 2012/10/28 19:02:29 christos Exp $ */ + +/* Don't reformat the code arbitrarily. + + It uses in wide parts the exact formatting as the upstream NetBSD + versions. The purpose is to simplify subsequent diffs to the NetBSD + version, should the need arise again at one point. */ + /* ** 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. */ /* Temporarily merged private.h and tzfile.h for ease of management - DJ */ @@ -15,16 +23,17 @@ #ifndef lint #ifndef NOID -static char elsieid[] = "@(#)localtime.c 7.66"; +static char elsieid[] = "@(#)localtime.c 8.17"; #endif /* !defined NOID */ #endif /* !defined lint */ /* -** 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. */ +#define NO_ERROR_IN_DST_GAP + /*LINTLIBRARY*/ #ifndef PRIVATE_H @@ -33,7 +42,7 @@ static char elsieid[] = "@(#)localtime.c 7.66"; /* ** 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 */ /* @@ -55,145 +64,33 @@ static char privatehid[] = "@(#)private.h 7.48"; #endif /* !defined lint */ /* -** Defaults for preprocessor symbols. -** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. -*/ - -#ifndef HAVE_ADJTIME -#define HAVE_ADJTIME 1 -#endif /* !defined HAVE_ADJTIME */ - -#ifndef HAVE_GETTEXT -#define HAVE_GETTEXT 0 -#endif /* !defined HAVE_GETTEXT */ - -#ifndef HAVE_SETTIMEOFDAY -#define HAVE_SETTIMEOFDAY 3 -#endif /* !defined HAVE_SETTIMEOFDAY */ - -#ifndef HAVE_STRERROR -#define HAVE_STRERROR 0 -#endif /* !defined HAVE_STRERROR */ - -#ifndef HAVE_SYMLINK -#define HAVE_SYMLINK 1 -#endif /* !defined HAVE_SYMLINK */ - -#ifndef HAVE_UNISTD_H -#define HAVE_UNISTD_H 1 -#endif /* !defined HAVE_UNISTD_H */ - -#ifndef HAVE_UTMPX_H -#define HAVE_UTMPX_H 0 -#endif /* !defined HAVE_UTMPX_H */ - -#ifndef LOCALE_HOME -#define LOCALE_HOME "/usr/lib/locale" -#endif /* !defined LOCALE_HOME */ - -/* ** Nested includes */ #include "stdio.h" #include "limits.h" /* for CHAR_BIT */ #include "stdlib.h" - -#if HAVE_GETTEXT - 0 -#endif /* HAVE_GETTEXT - 0 */ - -#if HAVE_UNISTD_H - 0 #include "unistd.h" /* for F_OK and R_OK */ -#endif /* HAVE_UNISTD_H - 0 */ - -#if !(HAVE_UNISTD_H - 0) -#ifndef F_OK -#define F_OK 0 -#endif /* !defined F_OK */ -#ifndef R_OK -#define R_OK 4 -#endif /* !defined R_OK */ -#endif /* !(HAVE_UNISTD_H - 0) */ /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ #define is_digit(c) ((unsigned)(c) - '0' <= 9) -/* -** Workarounds for compilers/systems. -*/ - -/* -** SunOS 4.1.1 cc lacks const. -*/ - -#ifndef const -#ifndef __STDC__ -#define const -#endif /* !defined __STDC__ */ -#endif /* !defined const */ - -/* -** SunOS 4.1.1 cc lacks prototypes. -*/ - -#ifndef P -#ifdef __STDC__ -#define P(x) x -#endif /* defined __STDC__ */ -#ifndef __STDC__ -#define P(x) () -#endif /* !defined __STDC__ */ -#endif /* !defined P */ - -/* -** SunOS 4.1.1 headers lack EXIT_SUCCESS. -*/ - -#ifndef EXIT_SUCCESS -#define EXIT_SUCCESS 0 -#endif /* !defined EXIT_SUCCESS */ - -/* -** SunOS 4.1.1 headers lack EXIT_FAILURE. -*/ - -#ifndef EXIT_FAILURE -#define EXIT_FAILURE 1 -#endif /* !defined EXIT_FAILURE */ - -/* -** SunOS 4.1.1 headers lack FILENAME_MAX. -*/ - -#ifndef FILENAME_MAX - -#ifndef MAXPATHLEN -#ifdef unix -#endif /* defined unix */ -#endif /* !defined MAXPATHLEN */ - -#ifdef MAXPATHLEN -#define FILENAME_MAX MAXPATHLEN -#endif /* defined MAXPATHLEN */ -#ifndef MAXPATHLEN -#define FILENAME_MAX 1024 /* Pure guesswork */ -#endif /* !defined MAXPATHLEN */ - -#endif /* !defined FILENAME_MAX */ - -/* -** SunOS 4.1.1 libraries lack remove. -*/ - -#ifndef remove -extern int unlink P((const char * filename)); -#define remove unlink -#endif /* !defined remove */ +#ifndef __pure +#if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__) +# define __pure __attribute__ ((__pure__)) +#else +# define __pure /* empty */ +#endif +#endif /* ** Finally, some convenience items. */ +#ifndef TYPE_INTEGRAL +#define TYPE_INTEGRAL(type) (/*CONSTCOND*/((type) 0.5) != 0.5) +#endif /* !defined TYPE_INTEGRAL */ + #ifndef TYPE_BIT #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) #endif /* !defined TYPE_BIT */ @@ -237,23 +134,29 @@ extern int unlink P((const char * filename)); #endif /* !defined GNUC_or_lint */ #endif /* !defined INITIALIZE */ +#ifndef TZ_DOMAIN +#define TZ_DOMAIN "tz" +#endif /* !defined TZ_DOMAIN */ + +#ifndef YEARSPERREPEAT +#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ +#endif /* !defined YEARSPERREPEAT */ + /* -** For the benefit of GNU folk... -** `_(MSGID)' uses the current locale's message library string for MSGID. -** The default is to use gettext if available, and use MSGID otherwise. +** The Gregorian year averages 365.2425 days, which is 31556952 seconds. */ -#ifndef _ -#if HAVE_GETTEXT - 0 -#define _(msgid) gettext(msgid) -#else /* !(HAVE_GETTEXT - 0) */ -#define _(msgid) msgid -#endif /* !(HAVE_GETTEXT - 0) */ -#endif /* !defined _ */ +#ifndef AVGSECSPERYEAR +#define AVGSECSPERYEAR 31556952L +#endif /* !defined AVGSECSPERYEAR */ -#ifndef TZ_DOMAIN -#define TZ_DOMAIN "tz" -#endif /* !defined TZ_DOMAIN */ +#ifndef SECSPERREPEAT +#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR) +#endif /* !defined SECSPERREPEAT */ + +#ifndef SECSPERREPEAT_BITS +#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ +#endif /* !defined SECSPERREPEAT_BITS */ /* ** UNIX was a registered trademark of UNIX System Laboratories in 1993. @@ -267,7 +170,7 @@ extern int unlink P((const char * filename)); /* ** 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. */ /* @@ -312,7 +215,8 @@ static char tzfilehid[] = "@(#)tzfile.h 7.14"; struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_reserved[16]; /* reserved for future use */ + char tzh_version[1]; /* '\0' or '2' as of 2005 */ + char tzh_reserved[15]; /* reserved for future use */ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_leapcnt[4]; /* coded number of leap seconds */ @@ -334,19 +238,29 @@ struct tzhead { ** tzh_leapcnt repetitions of ** one (char [4]) coded leap second transition times ** one (char [4]) total correction after above -** tzh_ttisstdcnt (char)s indexed by type; if true, transition -** time is standard time, if false, +** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition +** time is standard time, if FALSE, ** transition time is wall clock time ** if absent, transition times are ** assumed to be wall clock time -** tzh_ttisgmtcnt (char)s indexed by type; if true, transition -** time is UTC, if false, +** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition +** time is UTC, if FALSE, ** transition time is local time ** if absent, transition times are ** assumed to be local time */ /* +** If tzh_version is '2' or greater, the above is followed by a second instance +** of tzhead and a second instance of the data in which each coded transition +** time uses 8 rather than 4 chars, +** then a POSIX-TZ-environment-variable-style string for use in handling +** instants after the last transition time stored in the file +** (with nothing between the newlines if there is no POSIX representation for +** such instants). +*/ + +/* ** In the current implementation, "tzset()" refuses to deal with files that ** exceed any of the limits below. */ @@ -358,7 +272,7 @@ struct tzhead { ** 138 years of Pacific Presidential Election time ** (where there are three time zone transitions every fourth year). */ -#define TZ_MAX_TIMES 370 +#define TZ_MAX_TIMES 1200 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES @@ -368,7 +282,7 @@ struct tzhead { #ifdef NOSOLAR /* ** Must be at least 14 for Europe/Riga as of Jan 12 1995, -** as noted by Earl Chew <earl@hpato.aus.hp.com>. +** as noted by Earl Chew. */ #define TZ_MAX_TYPES 20 /* Maximum number of local time types */ #endif /* !defined NOSOLAR */ @@ -426,28 +340,6 @@ struct tzhead { #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) -#ifndef USG - -/* -** Use of the underscored variants may cause problems if you move your code to -** certain System-V-based systems; for maximum portability, use the -** underscore-free variants. The underscored variants are provided for -** backward compatibility only; they may disappear from future versions of -** this file. -*/ - -#define SECS_PER_MIN SECSPERMIN -#define MINS_PER_HOUR MINSPERHOUR -#define HOURS_PER_DAY HOURSPERDAY -#define DAYS_PER_WEEK DAYSPERWEEK -#define DAYS_PER_NYEAR DAYSPERNYEAR -#define DAYS_PER_LYEAR DAYSPERLYEAR -#define SECS_PER_HOUR SECSPERHOUR -#define SECS_PER_DAY SECSPERDAY -#define MONS_PER_YEAR MONSPERYEAR - -#endif /* !defined USG */ - #endif /* !defined TZFILE_H */ #include "fcntl.h" @@ -477,25 +369,36 @@ struct tzhead { ** 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[] NO_COPY = WILDABBR; +static const char wildabbr[] = WILDABBR; -static char gmt[] NO_COPY = "GMT"; +static const char gmt[] = "GMT"; + +/* +** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. +** We default to US rules as of 1999-08-17. +** POSIX 1003.1 section 8.1.1 says that the default DST rules are +** implementation dependent; for historical reasons, US rules are a +** common default. +*/ +#ifndef TZDEFRULESTRING +#define TZDEFRULESTRING ",M4.1.0,M10.5.0" +#endif /* !defined TZDEFDST */ struct ttinfo { /* time type information */ long tt_gmtoff; /* UTC offset in seconds */ int tt_isdst; /* used to set tm_isdst */ int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* true if transition is std time */ - int tt_ttisgmt; /* true if transition is UTC */ + int tt_ttisstd; /* TRUE if transition is std time */ + int tt_ttisgmt; /* TRUE if transition is UTC */ }; struct lsinfo { /* leap second information */ @@ -512,19 +415,23 @@ struct lsinfo { /* leap second information */ #define MY_TZNAME_MAX 255 #endif /* !defined TZNAME_MAX */ -struct state { +struct __state { int leapcnt; 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]; - char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), - (2 * (MY_TZNAME_MAX + 1)))]; + char chars[/*CONSTCOND*/BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, + sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))]; struct lsinfo lsis[TZ_MAX_LEAPS]; }; +typedef struct __state *timezone_t; + struct rule { int r_type; /* type of rule--see below */ int r_day; /* day number of rule */ @@ -537,59 +444,60 @@ struct rule { #define DAY_OF_YEAR 1 /* n - day of year */ #define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ +typedef struct tm *(*subfun_t)(const timezone_t sp, const time_t *timep, + long offset, struct tm *tmp); + /* ** Prototypes for static functions. */ -static long detzcode P((const char * codep)); -static const char * getzname P((const char * strp)); -static const char * getnum P((const char * strp, int * nump, int min, - int max)); -static const char * getsecs P((const char * strp, long * secsp)); -static const char * getoffset P((const char * strp, long * offsetp)); -static const char * getrule P((const char * strp, struct rule * rulep)); -static void gmtload P((struct state * sp)); -static void gmtsub P((const time_t * timep, long offset, - struct tm * tmp)); -static void localsub P((const time_t * timep, long offset, - struct tm * tmp)); -static int increment_overflow P((int * number, int delta)); -static int normalize_overflow P((int * tensptr, int * unitsptr, - int base)); -static void settzname P((void)); -static time_t time1 P((struct tm * tmp, - void(*funcp) P((const time_t *, - long, struct tm *)), - long offset)); -static time_t time2 P((struct tm *tmp, - void(*funcp) P((const time_t *, - long, struct tm*)), - long offset, int * okayp)); -static time_t time2sub P((struct tm *tmp, - void(*funcp) P((const time_t *, - long, struct tm*)), - long offset, int * okayp, int do_norm_secs)); -static void timesub P((const time_t * timep, long offset, - const struct state * sp, struct tm * tmp)); -static int tmcomp P((const struct tm * atmp, - const struct tm * btmp)); -static time_t transtime P((time_t janfirst, int year, - const struct rule * rulep, long offset)); -static int tzload P((const char * name, struct state * sp)); -static int tzparse P((const char * name, struct state * sp, - int lastditch)); - -#ifdef ALL_STATE -static struct state * lclptr; -static struct state * gmtptr; -#endif /* defined ALL_STATE */ - -#ifndef ALL_STATE -static struct state lclmem; -static struct state gmtmem; -#define lclptr (&lclmem) -#define gmtptr (&gmtmem) -#endif /* State Farm */ +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) __pure; +static const char * getqzname(const char * strp, const int delim) __pure; +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(timezone_t sp); +static struct tm * gmtsub(const timezone_t sp, const time_t *timep, + long offset, struct tm * tmp); +static struct tm * localsub(const timezone_t sp, 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) __pure; +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(const timezone_t sp, struct tm * const tmp, + subfun_t funcp, const long offset); +static time_t time2(const timezone_t sp, struct tm * const tmp, + subfun_t funcp, + const long offset, int *const okayp); +static time_t time2sub(const timezone_t sp, struct tm * const tmp, + subfun_t funcp, const long offset, + int *const okayp, const int do_norm_secs); +static struct tm * timesub(const timezone_t sp, const time_t * timep, + long offset, 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) __pure; +static int typesequiv(const timezone_t sp, int a, int b); +static int tzload(timezone_t sp, const char * name, + int doextend); +static int tzparse(timezone_t sp, const char * name, + int lastditch); +static void tzset_unlocked(void); +static long leapcorr(const timezone_t sp, time_t * timep); + +static timezone_t lclptr; +static timezone_t gmtptr; #ifndef TZ_STRLEN_MAX #define TZ_STRLEN_MAX 255 @@ -609,8 +517,8 @@ static int gmt_is_set; #undef _tzname char * tzname[2] = { - wildabbr, - wildabbr + (char *) wildabbr, + (char *) wildabbr }; /* @@ -618,12 +526,11 @@ 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; - /* These variables are initialized by tzset. The macro versions are defined in time.h, and indirect through the __imp_ pointers. */ @@ -633,34 +540,46 @@ static struct tm tm; #undef _daylight #ifdef USG_COMPAT -long timezone; /* was time_t but POSIX requires long. */ -int daylight; +long timezone = 0; +int daylight; #endif /* defined USG_COMPAT */ #ifdef ALTZONE -time_t altzone; +time_t altzone = 0; #endif /* defined ALTZONE */ static long -detzcode(const char *codep) +detzcode(const char *const codep) { - register long result; - register int i; + 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(const char *const codep) +{ + time_t result; + int i; + + result = (time_t)((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 P((void)) +settzname (void) { - register struct state * const sp = lclptr; - register int i; + timezone_t const sp = lclptr; + int i; - tzname[0] = wildabbr; - tzname[1] = wildabbr; + tzname[0] = (char *) wildabbr; + tzname[1] = (char *) wildabbr; #ifdef USG_COMPAT daylight = 0; timezone = 0; @@ -668,25 +587,23 @@ settzname P((void)) #ifdef ALTZONE altzone = 0; #endif /* defined ALTZONE */ -#ifdef ALL_STATE if (sp == NULL) { - tzname[0] = tzname[1] = gmt; + tzname[0] = tzname[1] = (char *) gmt; return; } -#endif /* defined ALL_STATE */ for (i = 0; i < sp->typecnt; ++i) { - register const struct ttinfo * const ttisp = &sp->ttis[i]; + const struct ttinfo * const ttisp = &sp->ttis[i]; tzname[ttisp->tt_isdst] = &sp->chars[ttisp->tt_abbrind]; #ifdef USG_COMPAT if (ttisp->tt_isdst) daylight = 1; - if (i == 0 || !ttisp->tt_isdst) + if (!ttisp->tt_isdst) timezone = -(ttisp->tt_gmtoff); #endif /* defined USG_COMPAT */ #ifdef ALTZONE - if (i == 0 || ttisp->tt_isdst) + if (ttisp->tt_isdst) altzone = -(ttisp->tt_gmtoff); #endif /* defined ALTZONE */ } @@ -694,9 +611,7 @@ settzname P((void)) ** And to get the latest zone names into tzname. . . */ for (i = 0; i < sp->timecnt; ++i) { - register const struct ttinfo * const ttisp = - &sp->ttis[ - sp->types[i]]; + const struct ttinfo *const ttisp = &sp->ttis[sp->types[i]]; tzname[ttisp->tt_isdst] = &sp->chars[ttisp->tt_abbrind]; @@ -706,17 +621,40 @@ settzname P((void)) #include "tz_posixrules.h" static int -tzload(const char *name, struct state *sp) +differ_by_repeat(const time_t t1, const time_t t0) { - register const char * p; - register int i; - register int fid; + if (TYPE_INTEGRAL(time_t) && + TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) + return 0; + return (int_fast64_t)t1 - (int_fast64_t)t0 == SECSPERREPEAT; +} + +static int +tzload(timezone_t sp, const char *name, const int doextend) +{ + const char * p; + int i; + int fid; + int stored; + ssize_t nread; + typedef union { + struct tzhead tzhead; + char buf[2 * sizeof(struct tzhead) + + 2 * sizeof *sp + + 4 * TZ_MAX_TIMES]; + } u_t; + u_t * up; save_errno save; - if (name == NULL && (name = TZDEFAULT) == NULL) + up = (u_t *) calloc(1, sizeof *up); + if (up == NULL) return -1; + + sp->goback = sp->goahead = FALSE; + if (name == NULL && (name = TZDEFAULT) == NULL) + goto oops; { - register int doaccess; + int doaccess; /* ** Section 4.9.1 of the C standard says that ** "FILENAME_MAX expands to an integral constant expression @@ -731,24 +669,21 @@ tzload(const char *name, struct state *sp) doaccess = name[0] == '/'; if (!doaccess) { if ((p = TZDIR) == NULL) - return -1; + goto oops; if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) - return -1; - strcpy(fullname, p); - strcat(fullname, "/"); - strcat(fullname, name); + goto oops; + (void) strcpy(fullname, p); /* XXX strcpy is safe */ + (void) strcat(fullname, "/"); /* XXX strcat is safe */ + (void) strcat(fullname, name); /* XXX strcat is safe */ /* ** Set doaccess if '.' (as in "../") shows up in name. */ if (strchr(name, '.') != NULL) - doaccess = true; + doaccess = TRUE; name = fullname; } -#if 0 - if (doaccess && access(name, R_OK) != 0) - return -1; -#endif - if ((fid = open(name, OPEN_MODE)) == -1) + if ((doaccess && access(name, R_OK) != 0) + || (fid = open(name, OPEN_MODE)) == -1) { const char *base = strrchr(name, '/'); if (base) @@ -762,110 +697,202 @@ tzload(const char *name, struct state *sp) fid = -2; } } - { - struct tzhead * tzhp; - union { - struct tzhead tzhead; - char buf[sizeof *sp + sizeof *tzhp]; - } u; + if (fid == -2) + { + memcpy(up->buf, _posixrules_data, sizeof (_posixrules_data)); + nread = sizeof (_posixrules_data); + } + else + { + nread = read(fid, up->buf, sizeof up->buf); + if (close(fid) < 0 || nread <= 0) + goto oops; + } + for (stored = 4; stored <= 8; stored *= 2) { int ttisstdcnt; int ttisgmtcnt; - if (fid == -2) - { - memcpy(u.buf, _posixrules_data, sizeof (_posixrules_data)); - i = sizeof (_posixrules_data); - } - else - { - i = read(fid, u.buf, sizeof u.buf); - if (close(fid) != 0) - return -1; - } - ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); - ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); - sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); - sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); - sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt); - sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); - p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; + ttisstdcnt = (int) detzcode(up->tzhead.tzh_ttisstdcnt); + ttisgmtcnt = (int) detzcode(up->tzhead.tzh_ttisgmtcnt); + sp->leapcnt = (int) detzcode(up->tzhead.tzh_leapcnt); + sp->timecnt = (int) detzcode(up->tzhead.tzh_timecnt); + sp->typecnt = (int) detzcode(up->tzhead.tzh_typecnt); + sp->charcnt = (int) detzcode(up->tzhead.tzh_charcnt); + p = up->tzhead.tzh_charcnt + sizeof up->tzhead.tzh_charcnt; if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) - return -1; - if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */ + goto oops; + if (nread - (p - up->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; + goto oops; for (i = 0; i < sp->timecnt; ++i) { - sp->ats[i] = detzcode(p); - p += 4; + sp->ats[i] = (time_t)((stored == 4) ? + detzcode(p) : detzcode64(p)); + p += stored; } for (i = 0; i < sp->timecnt; ++i) { sp->types[i] = (unsigned char) *p++; if (sp->types[i] >= sp->typecnt) - return -1; + goto oops; } for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; + struct ttinfo * ttisp; ttisp = &sp->ttis[i]; ttisp->tt_gmtoff = detzcode(p); p += 4; ttisp->tt_isdst = (unsigned char) *p++; if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) - return -1; + goto oops; ttisp->tt_abbrind = (unsigned char) *p++; if (ttisp->tt_abbrind < 0 || ttisp->tt_abbrind > sp->charcnt) - return -1; + goto oops; } for (i = 0; i < sp->charcnt; ++i) sp->chars[i] = *p++; sp->chars[i] = '\0'; /* ensure '\0' at end */ for (i = 0; i < sp->leapcnt; ++i) { - register struct lsinfo * lsisp; + struct lsinfo * lsisp; lsisp = &sp->lsis[i]; - lsisp->ls_trans = detzcode(p); - p += 4; + lsisp->ls_trans = (time_t)((stored == 4) ? + detzcode(p) : detzcode64(p)); + p += stored; lsisp->ls_corr = detzcode(p); p += 4; } for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; + struct ttinfo * ttisp; ttisp = &sp->ttis[i]; if (ttisstdcnt == 0) - ttisp->tt_ttisstd = false; + ttisp->tt_ttisstd = FALSE; else { ttisp->tt_ttisstd = *p++; - if (ttisp->tt_ttisstd != true && - ttisp->tt_ttisstd != false) - return -1; + if (ttisp->tt_ttisstd != TRUE && + ttisp->tt_ttisstd != FALSE) + goto oops; } } for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; + struct ttinfo * ttisp; ttisp = &sp->ttis[i]; if (ttisgmtcnt == 0) - ttisp->tt_ttisgmt = false; + ttisp->tt_ttisgmt = FALSE; else { ttisp->tt_ttisgmt = *p++; - if (ttisp->tt_ttisgmt != true && - ttisp->tt_ttisgmt != false) - return -1; + if (ttisp->tt_ttisgmt != TRUE && + ttisp->tt_ttisgmt != FALSE) + goto oops; } } + /* + ** 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). + */ + 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 (up->tzhead.tzh_version[0] == '\0') + break; + nread -= p - up->buf; + for (i = 0; i < nread; ++i) + up->buf[i] = p[i]; + /* + ** If this is a narrow integer time_t system, we're done. + */ + if (stored >= (int) sizeof(time_t) +/* CONSTCOND */ + && TYPE_INTEGRAL(time_t)) + break; + } + if (doextend && nread > 2 && + up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && + sp->typecnt + 2 <= TZ_MAX_TYPES) { + struct __state ts; + int result; + + up->buf[nread - 1] = '\0'; + result = tzparse(&ts, &up->buf[1], 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]; + } + } + 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; + } } + free(up); if (sp == lclptr) { __gettzinfo ()->__tzrule[0].offset @@ -874,6 +901,31 @@ tzload(const char *name, struct state *sp) = -sp->ttis[0].tt_gmtoff; } return 0; +oops: + free(up); + return -1; +} + +static int +typesequiv(const timezone_t sp, const int a, const int b) +{ + int result; + + if (sp == NULL || + a < 0 || a >= sp->typecnt || + b < 0 || b >= sp->typecnt) + result = FALSE; + else { + const struct ttinfo * ap = &sp->ttis[a]; + 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] = { @@ -887,14 +939,14 @@ 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. */ static const char * getzname(const char *strp) { - register char c; + char c; while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+') @@ -903,6 +955,25 @@ getzname(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(const char *strp, const int delim) +{ + 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. @@ -910,22 +981,28 @@ getzname(const char *strp) */ static const char * -getnum(const char *strp, int *nump, const int min, const int max) +getnum(const char *strp, int *const nump, const int min, const int max) { - register char c; - register int num; + char c; + int num; - if (strp == NULL || !is_digit(c = *strp)) + if (strp == NULL || !is_digit(c = *strp)) { + errno = EINVAL; return NULL; + } num = 0; do { num = num * 10 + (c - '0'); - if (num > max) + if (num > max) { + errno = EOVERFLOW; return NULL; /* illegal value */ + } c = *++strp; } while (is_digit(c)); - if (num < min) + if (num < min) { + errno = EINVAL; return NULL; /* illegal value */ + } *nump = num; return strp; } @@ -939,7 +1016,7 @@ getnum(const char *strp, int *nump, const int min, const int max) */ static const char * -getsecs(const char *strp, long *secsp) +getsecs(const char *strp, long *const secsp) { int num; @@ -961,7 +1038,7 @@ getsecs(const char *strp, long *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; @@ -979,9 +1056,9 @@ getsecs(const char *strp, long *secsp) */ static const char * -getoffset(const char *strp, long *offsetp) +getoffset(const char *strp, long *const offsetp) { - register int neg = 0; + int neg = 0; if (*strp == '-') { neg = 1; @@ -998,13 +1075,13 @@ getoffset(const char *strp, long *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. */ static const char * -getrule(const char *strp, struct rule *rulep) +getrule(const char *strp, struct rule *const rulep) { if (*strp == 'J') { /* @@ -1056,12 +1133,12 @@ getrule(const char *strp, struct rule *rulep) */ static time_t -transtime(const time_t janfirst, const int year, const struct rule *rulep, - long offset) +transtime(const time_t janfirst, const int year, const struct rule *const rulep, + const long offset) { - register int leapyear; - register time_t value; - register int i; + int leapyear; + time_t value; + int i; int d, m1, yy0, yy1, yy2, dow; INITIALIZE(value); @@ -1076,7 +1153,7 @@ transtime(const time_t janfirst, const int year, const struct rule *rulep, ** add SECSPERDAY times the day number-1 to the time of ** January 1, midnight, to get the day. */ - value = janfirst + (rulep->r_day - 1) * SECSPERDAY; + value = (time_t)(janfirst + (rulep->r_day - 1) * SECSPERDAY); if (leapyear && rulep->r_day >= 60) value += SECSPERDAY; break; @@ -1087,7 +1164,7 @@ transtime(const time_t janfirst, const int year, const struct rule *rulep, ** Just add SECSPERDAY times the day number to the time of ** January 1, midnight, to get the day. */ - value = janfirst + rulep->r_day * SECSPERDAY; + value = (time_t)(janfirst + rulep->r_day * SECSPERDAY); break; case MONTH_NTH_DAY_OF_WEEK: @@ -1096,7 +1173,7 @@ transtime(const time_t janfirst, const int year, const struct rule *rulep, */ value = janfirst; for (i = 0; i < rulep->r_mon - 1; ++i) - value += mon_lengths[leapyear][i] * SECSPERDAY; + value += (time_t)(mon_lengths[leapyear][i] * SECSPERDAY); /* ** Use Zeller's Congruence to get day-of-week of first day of @@ -1112,7 +1189,7 @@ transtime(const time_t janfirst, const int year, const struct rule *rulep, 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. */ @@ -1129,17 +1206,17 @@ transtime(const time_t janfirst, const int year, const struct rule *rulep, /* ** "d" is the day-of-month (zero-origin) of the day we want. */ - value += d * SECSPERDAY; + value += (time_t)(d * SECSPERDAY); break; } /* ** "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. */ - return value + rulep->r_time + offset; + return (time_t)(value + rulep->r_time + offset); } /* @@ -1148,7 +1225,7 @@ transtime(const time_t janfirst, const int year, const struct rule *rulep, */ static int -tzparse(const char *name, struct state *sp, const int lastditch) +tzparse(timezone_t sp, const char *name, const int lastditch) { const char * stdname; const char * dstname; @@ -1156,10 +1233,10 @@ tzparse(const char *name, struct state *sp, const int lastditch) size_t dstlen; long stdoffset; long dstoffset; - register time_t * atp; - register unsigned char * typep; - register char * cp; - register int load_result; + time_t * atp; + unsigned char * typep; + char * cp; + int load_result; INITIALIZE(dstname); stdname = name; @@ -1170,35 +1247,52 @@ tzparse(const char *name, struct state *sp, 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; name = getoffset(name, &stdoffset); if (name == NULL) return -1; } - load_result = tzload(TZDEFRULES, sp); + load_result = tzload(sp, TZDEFRULES, 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) return -1; } else dstoffset = stdoffset - SECSPERHOUR; + if (*name == '\0' && load_result != 0) + name = TZDEFRULESTRING; if (*name == ',' || *name == ';') { struct rule start; struct rule end; - register int year; - register time_t janfirst; + int year; + time_t janfirst; time_t starttime; time_t endtime; @@ -1213,21 +1307,24 @@ tzparse(const char *name, struct state *sp, 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; + memset(sp->ttis, 0, sizeof(sp->ttis)); sp->ttis[0].tt_gmtoff = -dstoffset; sp->ttis[0].tt_isdst = 1; - sp->ttis[0].tt_abbrind = stdlen + 1; + sp->ttis[0].tt_abbrind = (int)(stdlen + 1); sp->ttis[1].tt_gmtoff = -stdoffset; sp->ttis[1].tt_isdst = 0; sp->ttis[1].tt_abbrind = 0; 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, @@ -1243,8 +1340,13 @@ tzparse(const char *name, struct state *sp, const int lastditch) *atp++ = endtime; *typep++ = 1; /* DST ends */ } - janfirst += year_lengths[isleap(year)] * - SECSPERDAY; + sp->timecnt += 2; + newfirst = janfirst; + newfirst += (time_t) + (year_lengths[isleap(year)] * SECSPERDAY); + if (newfirst <= janfirst) + break; + janfirst = newfirst; } if (sp == lclptr) { @@ -1254,17 +1356,15 @@ tzparse(const char *name, struct state *sp, const int lastditch) = -sp->ttis[0].tt_gmtoff; } } else { - register long theirstdoffset; - register long theirdstoffset; - register long theiroffset; - register int isdst; - register int i; - register int j; + long theirstdoffset; + long theirdstoffset; + long theiroffset; + int isdst; + int i; + int j; if (*name != '\0') return -1; - if (load_result != 0) - return -1; /* ** Initial values of theirstdoffset and theirdstoffset. */ @@ -1289,7 +1389,7 @@ tzparse(const char *name, struct state *sp, const int lastditch) /* ** Initially we're assumed to be in standard time. */ - isdst = false; + isdst = FALSE; theiroffset = theirstdoffset; /* ** Now juggle transition times and types @@ -1316,28 +1416,29 @@ tzparse(const char *name, struct state *sp, const int lastditch) ** offset. */ if (isdst && !sp->ttis[j].tt_ttisstd) { - sp->ats[i] += dstoffset - - theirdstoffset; + sp->ats[i] += (time_t) + (dstoffset - theirdstoffset); } else { - sp->ats[i] += stdoffset - - theirstdoffset; + sp->ats[i] += (time_t) + (stdoffset - theirstdoffset); } } theiroffset = -sp->ttis[j].tt_gmtoff; - if (sp->ttis[j].tt_isdst) - theirdstoffset = theiroffset; - else theirstdoffset = theiroffset; + if (!sp->ttis[j].tt_isdst) + theirstdoffset = theiroffset; + else theirdstoffset = theiroffset; } /* ** Finally, fill in ttis. - ** ttisstd and ttisgmt need not be handled. + ** ttisstd and ttisgmt need not be handled */ + memset(sp->ttis, 0, sizeof(sp->ttis)); sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = false; + sp->ttis[0].tt_isdst = FALSE; sp->ttis[0].tt_abbrind = 0; sp->ttis[1].tt_gmtoff = -dstoffset; - sp->ttis[1].tt_isdst = true; - sp->ttis[1].tt_abbrind = stdlen + 1; + sp->ttis[1].tt_isdst = TRUE; + sp->ttis[1].tt_abbrind = (int)(stdlen + 1); sp->typecnt = 2; if (sp == lclptr) { @@ -1351,6 +1452,7 @@ tzparse(const char *name, struct state *sp, const int lastditch) dstlen = 0; sp->typecnt = 1; /* only standard time */ sp->timecnt = 0; + memset(sp->ttis, 0, sizeof(sp->ttis)); sp->ttis[0].tt_gmtoff = -stdoffset; sp->ttis[0].tt_isdst = 0; sp->ttis[0].tt_abbrind = 0; @@ -1360,27 +1462,27 @@ tzparse(const char *name, struct state *sp, const int lastditch) __gettzinfo ()->__tzrule[1].offset = -sp->ttis[0].tt_gmtoff; } } - sp->charcnt = stdlen + 1; + sp->charcnt = (int)(stdlen + 1); if (dstlen != 0) - sp->charcnt += dstlen + 1; + sp->charcnt += (int)(dstlen + 1); if ((size_t) sp->charcnt > sizeof sp->chars) return -1; cp = sp->chars; - strncpy(cp, stdname, stdlen); + (void) strncpy(cp, stdname, stdlen); cp += stdlen; *cp++ = '\0'; if (dstlen != 0) { - strncpy(cp, dstname, dstlen); + (void) strncpy(cp, dstname, dstlen); *(cp + dstlen) = '\0'; } return 0; } static void -gmtload(struct state *sp) +gmtload(timezone_t sp) { - if (tzload(gmt, sp) != 0) - tzparse(gmt, sp, true); + if (tzload(sp, gmt, TRUE) != 0) + (void) tzparse(sp, gmt, TRUE); } #ifndef STD_INSPIRED @@ -1391,21 +1493,20 @@ gmtload(struct state *sp) static #endif /* !defined STD_INSPIRED */ void -tzsetwall P((void)) +tzsetwall (void) { if (lcl_is_set == lcl_setting) return; lcl_is_set = lcl_setting; -#ifdef ALL_STATE if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); + save_errno save; + lclptr = (timezone_t) calloc(1, sizeof *lclptr); if (lclptr == NULL) { settzname(); /* all we can do */ return; } } -#endif /* defined ALL_STATE */ #if defined (__CYGWIN__) { TIME_ZONE_INFORMATION tz; @@ -1470,7 +1571,7 @@ tzsetwall P((void)) sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wSecond); } /* printf("TZ deduced as `%s'\n", buf); */ - if (tzparse(buf, lclptr, false) == 0) { + if (tzparse(lclptr, buf, FALSE) == 0) { settzname(); lcl_is_set = lcl_from_default; strlcpy(lcl_TZname, buf, sizeof (lcl_TZname)); @@ -1483,85 +1584,130 @@ tzsetwall P((void)) } } #endif - if (tzload((char *) NULL, lclptr) != 0) + if (tzload(lclptr, NULL, TRUE) != 0) gmtload(lclptr); settzname(); } static NO_COPY muto tzset_guard; -extern "C" void -tzset P((void)) +#ifndef STD_INSPIRED +/* +** A non-static declaration of tzsetwall in a system header file +** may cause a warning about this upcoming static declaration... +*/ +static +#endif /* !defined STD_INSPIRED */ +void +tzset_unlocked(void) { - tzset_guard.init ("tzset_guard")->acquire (); - const char * name = getenv("TZ"); + const char * name; + name = getenv("TZ"); if (name == NULL) { if (lcl_is_set != lcl_from_default) tzsetwall(); - goto out; + return; } - if (lcl_is_set > 0 && strncmp(lcl_TZname, name, sizeof(lcl_TZname) - 1) == 0) - goto out; + if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) + return; lcl_is_set = (strlen(name) < sizeof (lcl_TZname)) ? lcl_from_environment : lcl_unset; if (lcl_is_set != lcl_unset) - strlcpy(lcl_TZname, name, sizeof (lcl_TZname)); + (void)strlcpy(lcl_TZname, name, sizeof (lcl_TZname)); -#ifdef ALL_STATE if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); + save_errno save; + lclptr = (timezone_t) calloc(1, sizeof *lclptr); if (lclptr == NULL) { settzname(); /* all we can do */ - goto out; + return; } } -#endif /* defined ALL_STATE */ if (*name == '\0') { /* ** User wants it fast rather than right. */ lclptr->leapcnt = 0; /* so, we're off a little */ lclptr->timecnt = 0; + lclptr->typecnt = 0; + lclptr->ttis[0].tt_isdst = 0; lclptr->ttis[0].tt_gmtoff = 0; lclptr->ttis[0].tt_abbrind = 0; - strcpy(lclptr->chars, gmt); - } else if (tzload(name, lclptr) != 0) { - if (name[0] == ':' || tzparse(name, lclptr, false) != 0) - gmtload(lclptr); - } + (void) strlcpy(lclptr->chars, gmt, sizeof(lclptr->chars)); + } else if (tzload(lclptr, name, TRUE) != 0) + if (name[0] == ':' || tzparse(lclptr, name, FALSE) != 0) + (void) gmtload(lclptr); settzname(); -out: +} + +extern "C" void +tzset(void) +{ + tzset_guard.init ("tzset_guard")->acquire (); + tzset_unlocked(); tzset_guard.release (); } /* ** 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 -localsub (const time_t * const timep, - const long offset, - struct tm * const tmp) +static struct tm * +localsub(const timezone_t sp, const time_t * const timep, const long offset, + struct tm *const tmp) { - register struct state * sp; - register const struct ttinfo * ttisp; - register int i; + const struct ttinfo * ttisp; + int i; + struct tm * result; const time_t t = *timep; - sp = lclptr; -#ifdef ALL_STATE - if (sp == NULL) { - gmtsub(timep, offset, tmp); - return; + if ((sp->goback && t < sp->ats[0]) || + (sp->goahead && t > sp->ats[sp->timecnt - 1])) { + time_t newt = t; + time_t seconds; + time_t tcycles; + int_fast64_t icycles; + + if (t < sp->ats[0]) + seconds = sp->ats[0] - t; + else seconds = t - sp->ats[sp->timecnt - 1]; + --seconds; + tcycles = (time_t) + (seconds / YEARSPERREPEAT / AVGSECSPERYEAR); + ++tcycles; + icycles = tcycles; + if (tcycles - icycles >= 1 || icycles - tcycles >= 1) + return NULL; + seconds = (time_t) 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(sp, &newt, offset, tmp); + if (result == tmp) { + time_t newy; + + newy = tmp->tm_year; + if (t < sp->ats[0]) + newy -= (time_t)icycles * YEARSPERREPEAT; + else newy += (time_t)icycles * YEARSPERREPEAT; + tmp->tm_year = (int)newy; + if (tmp->tm_year != newy) + return NULL; + } + return result; } -#endif /* defined ALL_STATE */ if (sp->timecnt == 0 || t < sp->ats[0]) { i = 0; while (sp->ttis[i].tt_isdst) @@ -1570,10 +1716,17 @@ localsub (const time_t * const timep, break; } } else { - for (i = 1; i < sp->timecnt; ++i) - if (t < sp->ats[i]) - break; - i = sp->types[i - 1]; + int lo = 1; + int hi = sp->timecnt; + + while (lo < hi) { + int mid = (lo + hi) / 2; + + if (t < sp->ats[mid]) + hi = mid; + else lo = mid + 1; + } + i = (int) sp->types[lo - 1]; } ttisp = &sp->ttis[i]; /* @@ -1582,49 +1735,58 @@ localsub (const time_t * const timep, ** t += ttisp->tt_gmtoff; ** timesub(&t, 0L, sp, tmp); */ - timesub(&t, ttisp->tt_gmtoff, sp, tmp); + result = timesub(sp, &t, ttisp->tt_gmtoff, tmp); tmp->tm_isdst = ttisp->tt_isdst; - tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; + if (sp == lclptr) + tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; #ifdef TM_ZONE tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; #endif /* defined TM_ZONE */ + return result; } +/* +** Re-entrant version of localtime. +*/ extern "C" struct tm * -localtime(const time_t *timep) +localtime_r(const time_t *timep, struct tm *tmp) { - tzset(); - localsub(timep, 0L, &tm); - return &tm; + tzset_guard.init ("tzset_guard")->acquire (); + tzset_unlocked(); + tmp = localsub(lclptr, timep, 0L, tmp); + tzset_guard.release (); + if (tmp == NULL) + errno = EOVERFLOW; + return tmp; } -/* - * Re-entrant version of localtime - */ extern "C" struct tm * -localtime_r(const time_t *timep, struct tm *tm) +localtime(const time_t *const timep) { - tzset(); - localsub(timep, 0L, tm); - return tm; + return localtime_r(timep, &tm); } /* ** gmtsub is to gmtime as localsub is to localtime. */ +static NO_COPY muto gmt_guard; -static void -gmtsub(const time_t *timep, const long offset, struct tm *tmp) +static struct tm * +gmtsub(const timezone_t sp, const time_t *const timep, const long offset, + struct tm *tmp) { + struct tm * result; + + gmt_guard.init ("gmt_guard")->acquire (); if (!gmt_is_set) { - gmt_is_set = true; -#ifdef ALL_STATE - gmtptr = (struct state *) malloc(sizeof *gmtptr); + save_errno save; + gmt_is_set = TRUE; + gmtptr = (timezone_t) calloc(1, sizeof *gmtptr); if (gmtptr != NULL) -#endif /* defined ALL_STATE */ gmtload(gmtptr); } - timesub(timep, offset, gmtptr, tmp); + gmt_guard.release (); + result = timesub(gmtptr, timep, offset, tmp); #ifdef TM_ZONE /* ** Could get fancy here and deliver something such as @@ -1634,68 +1796,84 @@ gmtsub(const time_t *timep, const long offset, struct tm *tmp) if (offset != 0) tmp->TM_ZONE = wildabbr; else { -#ifdef ALL_STATE if (gmtptr == NULL) tmp->TM_ZONE = gmt; else tmp->TM_ZONE = gmtptr->chars; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - tmp->TM_ZONE = gmtptr->chars; -#endif /* State Farm */ } #endif /* defined TM_ZONE */ + return result; } extern "C" struct tm * -gmtime(const time_t *timep) +gmtime(const time_t *const timep) { - gmtsub(timep, 0L, &tm); - return &tm; + struct tm *tmp = gmtsub(NULL, timep, 0L, &tm); + + if (tmp == NULL) + errno = EOVERFLOW; + + return tmp; } /* - * Re-entrant version of gmtime - */ +** Re-entrant version of gmtime. +*/ + extern "C" struct tm * -gmtime_r(const time_t *timep, struct tm *tm) +gmtime_r(const time_t * const timep, struct tm *tmp) { - gmtsub(timep, 0L, tm); - return tm; + tmp = gmtsub(NULL, timep, 0L, tmp); + + if (tmp == NULL) + errno = EOVERFLOW; + + return tmp; } #ifdef STD_INSPIRED extern "C" struct tm * -offtime(const time_t *timep, const long offset) +offtime(const time_t *const timep, long offset) { - gmtsub(timep, offset, &tm); - return &tm; + struct tm *tmp = gmtsub(NULL, timep, offset, &tm); + + if (tmp == NULL) + errno = EOVERFLOW; + + return tmp; } #endif /* defined STD_INSPIRED */ -static void -timesub(const time_t *timep, const long offset, const struct state *sp, - struct tm *tmp) +/* +** Return the number of leap years through the end of the given year +** where, to make the math easy, the answer for year zero is defined as zero. +*/ + +static int +leaps_thru_end_of(const int y) { - register const struct lsinfo * lp; - register long days; - register long rem; - register int y; - register int yleap; - register const int * ip; - register long corr; - register int hit; - register int i; + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : + -(leaps_thru_end_of(-(y + 1)) + 1); +} + +static struct tm * +timesub(const timezone_t sp, const time_t *const timep, const long offset, + struct tm *const tmp) +{ + const struct lsinfo * lp; + time_t tdays; + int idays; /* unsigned would be so 2003 */ + long rem; + int y; + const int * ip; + long corr; + int hit; + int i; corr = 0; hit = 0; -#ifdef ALL_STATE i = (sp == NULL) ? 0 : sp->leapcnt; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - i = sp->leapcnt; -#endif /* State Farm */ while (--i >= 0) { lp = &sp->lsis[i]; if (*timep >= lp->ls_trans) { @@ -1716,115 +1894,170 @@ timesub(const time_t *timep, const long offset, const struct state *sp, break; } } - days = *timep / SECSPERDAY; - rem = *timep % SECSPERDAY; -#ifdef mc68k - if (*timep == 0x80000000) { - /* - ** A 3B1 muffs the division on the most negative number. - */ - days = -24855; - rem = -11648; + y = EPOCH_YEAR; + tdays = (time_t)(*timep / SECSPERDAY); + rem = (long) (*timep - tdays * SECSPERDAY); + while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { + int newy; + time_t tdelta; + int idelta; + int leapdays; + + tdelta = tdays / DAYSPERLYEAR; + idelta = (int) tdelta; + if (tdelta - idelta >= 1 || idelta - tdelta >= 1) + return NULL; + if (idelta == 0) + idelta = (tdays < 0) ? -1 : 1; + newy = y; + if (increment_overflow(&newy, idelta)) + return NULL; + leapdays = leaps_thru_end_of(newy - 1) - + leaps_thru_end_of(y - 1); + tdays -= ((time_t) newy - y) * DAYSPERNYEAR; + tdays -= leapdays; + y = newy; + } + { + long seconds; + + seconds = tdays * SECSPERDAY + 0.5; + tdays = (time_t)(seconds / SECSPERDAY); + rem += (long) (seconds - tdays * SECSPERDAY); } -#endif /* defined mc68k */ - rem += (offset - corr); + /* + ** Given the range, we can now fearlessly cast... + */ + idays = (int) tdays; + rem += offset - corr; while (rem < 0) { rem += SECSPERDAY; - --days; + --idays; } while (rem >= SECSPERDAY) { rem -= SECSPERDAY; - ++days; + ++idays; + } + while (idays < 0) { + if (increment_overflow(&y, -1)) + return NULL; + idays += year_lengths[isleap(y)]; } + while (idays >= year_lengths[isleap(y)]) { + idays -= year_lengths[isleap(y)]; + if (increment_overflow(&y, 1)) + return NULL; + } + tmp->tm_year = y; + if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) + return NULL; + tmp->tm_yday = idays; + /* + ** The "extra" mods below avoid overflow problems. + */ + tmp->tm_wday = EPOCH_WDAY + + ((y - EPOCH_YEAR) % DAYSPERWEEK) * + (DAYSPERNYEAR % DAYSPERWEEK) + + leaps_thru_end_of(y - 1) - + leaps_thru_end_of(EPOCH_YEAR - 1) + + idays; + tmp->tm_wday %= DAYSPERWEEK; + if (tmp->tm_wday < 0) + tmp->tm_wday += DAYSPERWEEK; tmp->tm_hour = (int) (rem / SECSPERHOUR); - rem = rem % SECSPERHOUR; + rem %= SECSPERHOUR; tmp->tm_min = (int) (rem / SECSPERMIN); /* ** A positive leap second requires a special - ** representation. This uses "... ??:59:60" et seq. + ** representation. This uses "... ??:59:60" et seq. */ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; - tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); - if (tmp->tm_wday < 0) - tmp->tm_wday += DAYSPERWEEK; - y = EPOCH_YEAR; -#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) - while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) { - register int newy; - - newy = y + days / DAYSPERNYEAR; - if (days < 0) - --newy; - days -= (newy - y) * DAYSPERNYEAR + - LEAPS_THRU_END_OF(newy - 1) - - LEAPS_THRU_END_OF(y - 1); - y = newy; - } - tmp->tm_year = y - TM_YEAR_BASE; - tmp->tm_yday = (int) days; - ip = mon_lengths[yleap]; - for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) - days = days - (long) ip[tmp->tm_mon]; - tmp->tm_mday = (int) (days + 1); + ip = mon_lengths[isleap(y)]; + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) + idays -= ip[tmp->tm_mon]; + tmp->tm_mday = (int) (idays + 1); tmp->tm_isdst = 0; #ifdef TM_GMTOFF tmp->TM_GMTOFF = offset; #endif /* defined TM_GMTOFF */ + return tmp; } extern "C" char * -ctime(const time_t *timep) +ctime(const time_t *const timep) { /* ** Section 4.12.3.2 of X3.159-1989 requires that ** The ctime function converts the calendar time pointed to by timer -** to local time in the form of a string. It is equivalent to +** to local time in the form of a string. It is equivalent to ** asctime(localtime(timer)) */ - return asctime(localtime(timep)); + struct tm *rtm = localtime(timep); + if (rtm == NULL) + return NULL; + return asctime(rtm); } extern "C" char * -ctime_r(const time_t *timep, char *buf) +ctime_r(const time_t *const timep, char *buf) { - struct tm tm; + struct tm mytm, *rtm; - return asctime_r(localtime_r(timep, &tm), buf); + rtm = localtime_r(timep, &mytm); + if (rtm == NULL) + return NULL; + return asctime_r(rtm, buf); } /* ** Adapted from code provided by Robert Elz, who writes: ** The "best" way to do mktime I think is based on an idea of Bob ** Kridle's (so its said...) from a long time ago. -** [kridle@xinet.com as of 1996-01-16.] -** It does a binary search of the time_t space. Since time_t's are +** It does a binary search of the time_t space. Since time_t's are ** just 32 bits, its a max of 32 iterations (even at 64 bits it ** would still be very reasonable). */ #ifndef WRONG -#define WRONG (-1) +#define WRONG ((time_t)-1) #endif /* !defined WRONG */ /* -** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). +** Simplified normalize logic courtesy Paul Eggert. */ -/* Mark as noinline to prevent a compiler warning. */ -static int __attribute__((noinline)) -increment_overflow(int *number, int delta) +static int +increment_overflow(int *const ip, int j) { - int number0; + int i = *ip; - number0 = *number; - *number += delta; - return (*number < number0) != (delta < 0); + /* + ** If i >= 0 there can only be overflow if i + j > INT_MAX + ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. + ** If i < 0 there can only be overflow if i + j < INT_MIN + ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. + */ + if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) + return TRUE; + *ip += j; + return FALSE; +} + +static int +long_increment_overflow(long *const lp, int m) +{ + long l = *lp; + + if ((l >= 0) ? (m > LONG_MAX - l) : (m < LONG_MIN - l)) + return TRUE; + *lp += m; + return FALSE; } static int -normalize_overflow(int *tensptr, int *unitsptr, const int base) +normalize_overflow(int *const tensptr, int *const unitsptr, const int base) { - register int tensdelta; + int tensdelta; tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : @@ -1834,9 +2067,22 @@ normalize_overflow(int *tensptr, int *unitsptr, const int base) } static int -tmcomp(register const struct tm *atmp, register const struct tm *btmp) +long_normalize_overflow(long *const tensptr, int *const unitsptr, + const int base) { - register int result; + int tensdelta; + + tensdelta = (*unitsptr >= 0) ? + (*unitsptr / base) : + (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return long_increment_overflow(tensptr, tensdelta); +} + +static int +tmcomp(const struct tm *const atmp, const struct tm *const btmp) +{ + int result; if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && @@ -1848,63 +2094,77 @@ tmcomp(register const struct tm *atmp, register const struct tm *btmp) } static time_t -time2sub(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)), - const long offset, int *okayp, const int do_norm_secs) +time2sub(const timezone_t sp, struct tm *const tmp, subfun_t funcp, + const long offset, int *const okayp, const int do_norm_secs) { - register const struct state * sp; - register int dir; - register int bits; - register int i, j ; - register int saved_seconds; + int dir; + int i, j; + int saved_seconds; + long li; + time_t lo; + time_t hi; +#ifdef NO_ERROR_IN_DST_GAP + time_t ilo; +#endif + long y; time_t newt; time_t t; struct tm yourtm, mytm; - *okayp = false; + *okayp = FALSE; yourtm = *tmp; +#ifdef NO_ERROR_IN_DST_GAP +again: +#endif if (do_norm_secs) { if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, - SECSPERMIN)) - return WRONG; + SECSPERMIN)) + goto overflow; } if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) - return WRONG; + goto overflow; if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) - return WRONG; - if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) - return WRONG; + goto overflow; + y = yourtm.tm_year; + if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR)) + goto overflow; /* - ** Turn yourtm.tm_year into an actual year number for now. + ** Turn y into an actual year number for now. ** It is converted back to an offset from TM_YEAR_BASE later. */ - if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE)) - return WRONG; + if (long_increment_overflow(&y, TM_YEAR_BASE)) + goto overflow; while (yourtm.tm_mday <= 0) { - if (increment_overflow(&yourtm.tm_year, -1)) - return WRONG; - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(i)]; + if (long_increment_overflow(&y, -1)) + goto overflow; + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday += year_lengths[isleap(li)]; } while (yourtm.tm_mday > DAYSPERLYEAR) { - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap(i)]; - if (increment_overflow(&yourtm.tm_year, 1)) - return WRONG; + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (long_increment_overflow(&y, 1)) + goto overflow; } for ( ; ; ) { - i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon]; + i = mon_lengths[isleap(y)][yourtm.tm_mon]; if (yourtm.tm_mday <= i) break; yourtm.tm_mday -= i; if (++yourtm.tm_mon >= MONSPERYEAR) { yourtm.tm_mon = 0; - if (increment_overflow(&yourtm.tm_year, 1)) - return WRONG; + if (long_increment_overflow(&y, 1)) + goto overflow; } } - if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) - return WRONG; - if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { + if (long_increment_overflow(&y, -TM_YEAR_BASE)) + goto overflow; + yourtm.tm_year = (int)y; + if (yourtm.tm_year != y) + goto overflow; + if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) + saved_seconds = 0; + else if (y + TM_YEAR_BASE < EPOCH_YEAR) { /* ** We can't set tm_sec to 0, because that might push the ** time below the minimum representable time. @@ -1914,7 +2174,7 @@ time2sub(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)), ** which is a safer assumption than using 58 would be. */ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) - return WRONG; + goto overflow; saved_seconds = yourtm.tm_sec; yourtm.tm_sec = SECSPERMIN - 1; } else { @@ -1922,27 +2182,71 @@ time2sub(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)), yourtm.tm_sec = 0; } /* - ** Divide the search space in half - ** (this works whether time_t is signed or unsigned). + ** Do a binary search (this works whatever time_t's type is). */ - bits = TYPE_BIT(time_t) - 1; - /* - ** If time_t is signed, then 0 is just above the median, - ** assuming two's complement arithmetic. - ** If time_t is unsigned, then (1 << bits) is just above the median. - */ - t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits); + /* LINTED const not */ + if (!TYPE_SIGNED(time_t)) { + lo = 0; + hi = lo - 1; + /* LINTED const not */ + } else { + lo = 1; + for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) + lo *= 2; + hi = -(lo + 1); + } +#ifdef NO_ERROR_IN_DST_GAP + ilo = lo; +#endif for ( ; ; ) { - (*funcp)(&t, offset, &mytm); - dir = tmcomp(&mytm, &yourtm); + t = lo / 2 + hi / 2; + if (t < lo) + t = lo; + else if (t > hi) + t = hi; + if ((*funcp)(sp, &t, offset, &mytm) == NULL) { + /* + ** Assume that t is too extreme to be represented in + ** a struct tm; arrange things so that it is less + ** extreme on the next pass. + */ + dir = (t > 0) ? 1 : -1; + } else dir = tmcomp(&mytm, &yourtm); if (dir != 0) { - if (bits-- < 0) - return WRONG; - if (bits < 0) - --t; /* may be needed if new t is minimal */ - else if (dir > 0) - t -= ((time_t) 1) << bits; - else t += ((time_t) 1) << bits; + if (t == lo) { + ++t; + if (t <= lo) + goto overflow; + ++lo; + } else if (t == hi) { + --t; + if (t >= hi) + goto overflow; + --hi; + } +#ifdef NO_ERROR_IN_DST_GAP + if (ilo != lo && lo - 1 == hi && yourtm.tm_isdst < 0 && + do_norm_secs) { + for (i = sp->typecnt - 1; i >= 0; --i) { + for (j = sp->typecnt - 1; j >= 0; --j) { + time_t off; + if (sp->ttis[j].tt_isdst == + sp->ttis[i].tt_isdst) + continue; + off = sp->ttis[j].tt_gmtoff - + sp->ttis[i].tt_gmtoff; + yourtm.tm_sec += off < 0 ? + -off : off; + goto again; + } + } + } +#endif + if (lo > hi) + goto invalid; + if (dir > 0) + hi = t; + else lo = t; continue; } if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) @@ -1953,25 +2257,18 @@ time2sub(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)), ** It's okay to guess wrong since the guess ** gets checked. */ - /* - ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) - (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); -#ifdef ALL_STATE if (sp == NULL) - return WRONG; -#endif /* defined ALL_STATE */ + goto invalid; for (i = sp->typecnt - 1; i >= 0; --i) { if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) continue; for (j = sp->typecnt - 1; j >= 0; --j) { if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) continue; - newt = t + sp->ttis[j].tt_gmtoff - - sp->ttis[i].tt_gmtoff; - (*funcp)(&newt, offset, &mytm); + newt = (time_t)(t + sp->ttis[j].tt_gmtoff - + sp->ttis[i].tt_gmtoff); + if ((*funcp)(sp, &newt, offset, &mytm) == NULL) + continue; if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) @@ -1983,21 +2280,28 @@ time2sub(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)), goto label; } } - return WRONG; + goto invalid; } label: newt = t + saved_seconds; if ((newt < t) != (saved_seconds < 0)) - return WRONG; + goto overflow; t = newt; - (*funcp)(&t, offset, tmp); - *okayp = true; - return t; + if ((*funcp)(sp, &t, offset, tmp)) { + *okayp = TRUE; + return t; + } +overflow: + errno = EOVERFLOW; + return WRONG; +invalid: + errno = EINVAL; + return WRONG; } static time_t -time2(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)), - const long offset, int *okayp) +time2(const timezone_t sp, struct tm *const tmp, subfun_t funcp, + const long offset, int *const okayp) { time_t t; @@ -2006,46 +2310,33 @@ time2(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)), ** (in case tm_sec contains a value associated with a leap second). ** If that fails, try with normalization of seconds. */ - t = time2sub(tmp, funcp, offset, okayp, false); - if (*okayp) - return t; - t = time2sub(tmp, funcp, offset, okayp, true); - if (*okayp) - return t; - /* Workaround for the spring forward gap problem which results in - the autoconf mktime usability test failing. - What we do here is this: The gap has 3600 seconds. If we - subtract 3600 from the tm_sec value and get a valid result, - then we can simply add 3600 to the return value and are done. - If the result is still not valid, the problem is not the - spring forward gap and we can give up. */ - struct tm tmp2 = *tmp; - tmp2.tm_sec -= 3600; - t = time2sub(&tmp2, funcp, offset, okayp, true); - if (*okayp) - { - if (t + 3600 < 0) /* Sanity check */ - return WRONG; - return t + 3600; - } - return t; + t = time2sub(sp, tmp, funcp, offset, okayp, FALSE); + return *okayp ? t : time2sub(sp, tmp, funcp, offset, okayp, TRUE); } static time_t -time1(struct tm *tmp, void (*funcp) P((const time_t *, long, struct tm *)), - const long offset) +time1(const timezone_t sp, struct tm *const tmp, subfun_t funcp, + const long offset) { - register time_t t; - register const struct state * sp; - register int samei, otheri; + time_t t; + int samei, otheri; + int sameind, otherind; + int i; + int nseen; + int seen[TZ_MAX_TYPES]; + int types[TZ_MAX_TYPES]; int okay; + if (tmp == NULL) { + errno = EINVAL; + return WRONG; + } if (tmp->tm_isdst > 1) tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay); + t = time2(sp, tmp, funcp, offset, &okay); #ifdef PCTS /* - ** PCTS code courtesy Grant Sullivan (grant@osf.org). + ** PCTS code courtesy Grant Sullivan. */ if (okay) return t; @@ -2062,63 +2353,83 @@ time1(struct tm *tmp, void (*funcp) P((const time_t *, long, struct tm *)), ** We try to divine the type they started from and adjust to the ** type they need. */ - /* - ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); -#ifdef ALL_STATE - if (sp == NULL) + if (sp == NULL) { + errno = EINVAL; return WRONG; -#endif /* defined ALL_STATE */ - for (samei = sp->typecnt - 1; samei >= 0; --samei) { + } + for (i = 0; i < sp->typecnt; ++i) + seen[i] = FALSE; + nseen = 0; + for (i = sp->timecnt - 1; i >= 0; --i) + if (!seen[sp->types[i]]) { + seen[sp->types[i]] = TRUE; + types[nseen++] = sp->types[i]; + } + for (sameind = 0; sameind < nseen; ++sameind) { + samei = types[sameind]; if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) continue; - for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) { + for (otherind = 0; otherind < nseen; ++otherind) { + otheri = types[otherind]; if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; + tmp->tm_sec += (int)(sp->ttis[otheri].tt_gmtoff - + sp->ttis[samei].tt_gmtoff); tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay); + t = time2(sp, tmp, funcp, offset, &okay); if (okay) return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; + tmp->tm_sec -= (int)(sp->ttis[otheri].tt_gmtoff - + sp->ttis[samei].tt_gmtoff); tmp->tm_isdst = !tmp->tm_isdst; } } + errno = EOVERFLOW; return WRONG; } extern "C" time_t -mktime(struct tm *tmp) +mktime(struct tm *const tmp) { - tzset(); - return time1(tmp, localsub, 0L); + time_t result; + + tzset_guard.init ("tzset_guard")->acquire (); + tzset_unlocked(); + result = time1(lclptr, tmp, localsub, 0L); + tzset_guard.release (); + return result; } #ifdef STD_INSPIRED extern "C" time_t -timelocal(struct tm *tmp) +timelocal(struct tm *const tmp) { - tmp->tm_isdst = -1; /* in case it wasn't initialized */ + if (tmp != NULL) + tmp->tm_isdst = -1; /* in case it wasn't initialized */ return mktime(tmp); } extern "C" time_t -timegm(struct tm *tmp) +timegm(struct tm *const tmp) { - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, 0L); + time_t t; + + if (tmp != NULL) + tmp->tm_isdst = 0; + t = time1(gmtptr, tmp, gmtsub, 0L); + return t; } extern "C" time_t -timeoff(struct tm *tmp, const long offset) +timeoff(struct tm *const tmp, const long offset) { - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, offset); + time_t t; + + if (tmp != NULL) + tmp->tm_isdst = 0; + t = time1(gmtptr, tmp, gmtsub, offset); + return t; } #endif /* defined STD_INSPIRED */ @@ -2131,9 +2442,9 @@ timeoff(struct tm *tmp, const long offset) */ extern "C" long -gtime(struct tm *tmp) +gtime(struct tm *const tmp) { - const time_t t = mktime(tmp); + const time_t t = mktime(tmp); if (t == WRONG) return -1; @@ -2157,13 +2468,11 @@ gtime(struct tm *tmp) */ static long -leapcorr(time_t *timep) +leapcorr(const timezone_t sp, time_t *timep) { - register struct state * sp; - register struct lsinfo * lp; - register int i; + struct lsinfo * lp; + int i; - sp = lclptr; i = sp->leapcnt; while (--i >= 0) { lp = &sp->lsis[i]; @@ -2176,8 +2485,12 @@ leapcorr(time_t *timep) extern "C" time_t time2posix(time_t t) { - tzset(); - return t - leapcorr(&t); + time_t result; + tzset_guard.init ("tzset_guard")->acquire (); + tzset_unlocked(); + result = t - leapcorr(lclptr, &t); + tzset_guard.release (); + return (result); } extern "C" time_t @@ -2186,30 +2499,34 @@ posix2time(time_t t) time_t x; time_t y; - tzset(); + tzset_guard.init ("tzset_guard")->acquire (); + tzset_unlocked(); /* ** For a positive leap second hit, the result - ** is not unique. For a negative leap second + ** is not unique. For a negative leap second ** hit, the corresponding time doesn't exist, ** so we return an adjacent second. */ - x = t + leapcorr(&t); - y = x - leapcorr(&x); + x = (time_t)(t + leapcorr(lclptr, &t)); + y = (time_t)(x - leapcorr(lclptr, &x)); if (y < t) { do { x++; - y = x - leapcorr(&x); + y = (time_t)(x - leapcorr(lclptr, &x)); } while (y < t); - if (t != y) + if (t != y) { return x - 1; + } } else if (y > t) { do { --x; - y = x - leapcorr(&x); + y = (time_t)(x - leapcorr(lclptr, &x)); } while (y > t); - if (t != y) + if (t != y) { return x + 1; + } } + tzset_guard.release (); return x; } |