From 21583e9453ceb90054d9b518afbb893818b30316 Mon Sep 17 00:00:00 2001 From: tilghman Date: Wed, 12 Sep 2007 21:25:57 +0000 Subject: [PATCH] Merged revisions 82285 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r82285 | tilghman | 2007-09-12 15:12:06 -0500 (Wed, 12 Sep 2007) | 4 lines Working on issue #10531 exposed a rather nasty 64-bit issue on ast_mktime, so we updated the localtime.c file from source. Next we'll have to write ast_strptime to match. ........ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@82290 f38db490-d61c-443f-a65b-d21fe96a405b --- apps/app_sms.c | 30 +- funcs/func_strings.c | 3 +- include/asterisk/localtime.h | 5 +- main/stdtime/localtime.c | 1357 +++++++++++++++++++--------------- main/stdtime/private.h | 220 ++++-- main/stdtime/tzfile.h | 97 ++- 6 files changed, 996 insertions(+), 716 deletions(-) diff --git a/apps/app_sms.c b/apps/app_sms.c index 942b33c12..8dd468b26 100644 --- a/apps/app_sms.c +++ b/apps/app_sms.c @@ -202,7 +202,7 @@ typedef struct sms_s { char queue[30]; /*!< queue name */ char oa[20]; /*!< originating address */ char da[20]; /*!< destination address */ - time_t scts; /*!< time stamp, UTC */ + struct timeval scts; /*!< time stamp, UTC */ unsigned char pid; /*!< protocol ID */ unsigned char dcs; /*!< data coding scheme */ short mr; /*!< message reference - actually a byte, but use -1 for not set */ @@ -766,7 +766,7 @@ static void sms_readfile(sms_t * h, char *fn) h->rx = h->udl = *h->oa = *h->da = h->pid = h->srr = h->udhi = h->rp = h->vp = h->udhl = 0; h->mr = -1; h->dcs = 0xF1; /* normal messages class 1 */ - h->scts = time(NULL); + h->scts = ast_tvnow(); s = fopen(fn, "r"); if (s) { if (unlink(fn)) { /* concurrent access, we lost */ @@ -825,7 +825,7 @@ static void sms_readfile(sms_t * h, char *fn) M, S; if (sscanf (p, "%d-%d-%dT%d:%d:%d", &Y, &m, &d, &H, &M, &S) == 6) { - struct tm t; + struct ast_tm t = { 0, }; t.tm_year = Y - 1900; t.tm_mon = m - 1; t.tm_mday = d; @@ -833,8 +833,8 @@ static void sms_readfile(sms_t * h, char *fn) t.tm_min = M; t.tm_sec = S; t.tm_isdst = -1; - h->scts = mktime(&t); - if (h->scts == (time_t) - 1) + h->scts = ast_mktime(&t, NULL); + if (h->scts.tv_sec == 0) ast_log(LOG_WARNING, "Bad date/timein %s: %s", fn, p); } } else @@ -925,7 +925,7 @@ static void sms_writefile(sms_t * h) snprintf(fn, sizeof(fn), "%s/sms/%s", ast_config_AST_SPOOL_DIR, h->smsc ? h->rx ? "morx" : "mttx" : h->rx ? "mtrx" : "motx"); ast_mkdir(fn, 0777); /* ensure it exists */ ast_copy_string(fn2, fn, sizeof(fn2)); - snprintf(fn2 + strlen(fn2), sizeof(fn2) - strlen(fn2), "/%s.%s-%d", h->queue, isodate(h->scts, buf, sizeof(buf)), seq++); + snprintf(fn2 + strlen(fn2), sizeof(fn2) - strlen(fn2), "/%s.%s-%d", h->queue, isodate(h->scts.tv_sec, buf, sizeof(buf)), seq++); snprintf(fn + strlen(fn), sizeof(fn) - strlen(fn), "/.%s", fn2 + strlen(fn) + 1); o = fopen(fn, "w"); if (o == NULL) @@ -982,9 +982,9 @@ static void sms_writefile(sms_t * h) } } } - if (h->scts) { + if (h->scts.tv_sec) { char buf[30]; - fprintf(o, "scts=%s\n", isodate(h->scts, buf, sizeof(buf))); + fprintf(o, "scts=%s\n", isodate(h->scts.tv_sec, buf, sizeof(buf))); } if (h->pid) fprintf(o, "pid=%d\n", h->pid); @@ -1027,7 +1027,7 @@ static unsigned char sms_handleincoming (sms_t * h) h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0); h->rp = ((h->imsg[2] & 0x80) ? 1 : 0); ast_copy_string(h->oa, h->cli, sizeof(h->oa)); - h->scts = time(NULL); + h->scts = ast_tvnow(); h->mr = h->imsg[p++]; p += unpackaddress(h->da, h->imsg + p); h->pid = h->imsg[p++]; @@ -1065,7 +1065,7 @@ static unsigned char sms_handleincoming (sms_t * h) p += unpackaddress(h->oa, h->imsg + p); h->pid = h->imsg[p++]; h->dcs = h->imsg[p++]; - h->scts = unpackdate(h->imsg + p); + h->scts.tv_sec = unpackdate(h->imsg + p); p += 7; p += unpacksms(h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi); h->rx = 1; /* received message */ @@ -1116,7 +1116,7 @@ static void putdummydata_proto2(sms_t *h) static void sms_compose2(sms_t *h, int more) { struct ast_tm tm; - struct timeval tv = { h->scts, 0 }; + struct timeval tv = h->scts; char stm[9]; h->omsg[0] = 0x00; /* set later... */ @@ -1171,7 +1171,7 @@ static int sms_handleincoming_proto2(sms_t *h) /* ast_verb(3, "SMS-P2 Frame: %s\n", sms_hexdump(h->imsg, sz, debug_buf)); */ /* Parse message body (called payload) */ - tv.tv_sec = h->scts = time(NULL); + tv = h->scts = ast_tvnow(); for (f = 4; f < sz; ) { msg = h->imsg[f++]; msgsz = h->imsg[f++]; @@ -1186,7 +1186,7 @@ static int sms_handleincoming_proto2(sms_t *h) h->udl = msgsz; break; case 0x14: /* Date SCTS */ - tv.tv_sec = h->scts = time(NULL); + tv = h->scts = ast_tvnow(); ast_localtime(&tv, &tm, NULL); tm.tm_mon = ( (h->imsg[f] * 10) + h->imsg[f + 1] ) - 1; tm.tm_mday = ( (h->imsg[f + 2] * 10) + h->imsg[f + 3] ); @@ -1302,7 +1302,7 @@ static void sms_compose1(sms_t *h, int more) p += packaddress(h->omsg + p, h->oa); h->omsg[p++] = h->pid; h->omsg[p++] = h->dcs; - packdate(h->omsg + p, h->scts); + packdate(h->omsg + p, h->scts.tv_sec); p += 7; p += packsms(h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud); } else { /* submit */ @@ -1823,7 +1823,7 @@ static int sms_exec(struct ast_channel *chan, void *data) /* submitting a message, not taking call. */ /* deprecated, use smsq instead */ - h.scts = time(NULL); + h.scts = ast_tvnow(); if (ast_strlen_zero(sms_args.addr) || strlen(sms_args.addr) >= sizeof(h.oa)) { ast_log(LOG_ERROR, "Address too long %s\n", sms_args.addr); goto done; diff --git a/funcs/func_strings.c b/funcs/func_strings.c index 5eab9930f..db14c07fd 100644 --- a/funcs/func_strings.c +++ b/funcs/func_strings.c @@ -659,7 +659,8 @@ static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data, if (!strptime(args.timestring, args.format, &t.time)) { ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n"); } else { - snprintf(buf, len, "%d", (int) ast_mktime(&t.atm, args.timezone)); + struct timeval tv = ast_mktime(&t.atm, args.timezone); + snprintf(buf, len, "%d", (int) tv.tv_sec); } return 0; diff --git a/include/asterisk/localtime.h b/include/asterisk/localtime.h index cf2256ee6..4d46198c7 100644 --- a/include/asterisk/localtime.h +++ b/include/asterisk/localtime.h @@ -40,11 +40,8 @@ struct ast_tm { int tm_usec; /* microseconds */ }; -int ast_tzsetwall(void); -void ast_tzset(const char *name); struct ast_tm *ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone); -time_t ast_mktime(struct ast_tm * const tmp, const char *zone); -char *ast_ctime(const struct timeval * const timep, char *buf); +struct timeval ast_mktime(struct ast_tm * const tmp, const char *zone); int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm); #endif /* _ASTERISK_LOCALTIME_H */ diff --git a/main/stdtime/localtime.c b/main/stdtime/localtime.c index 4426feb97..94513ae76 100644 --- a/main/stdtime/localtime.c +++ b/main/stdtime/localtime.c @@ -26,21 +26,20 @@ /*! \file * * Multi-timezone Localtime code - * - * \author Leap second handling Bradley White (bww@k.gp.cs.cmu.edu). - * \author POSIX-style TZ environment variable handling from Guy Harris (guy@auspex.com). * + * The original source from this file may be obtained from ftp://elsie.nci.nih.gov/pub/ */ /* - * Asterisk defines - * - * Don't mess with these unless you're really sure you know what you're doing. - */ -#ifndef _THREAD_SAFE -#define _THREAD_SAFE -#endif -#define TZ_STRLEN_MAX 255 +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +*/ + +/* +** Leap second handling from Bradley White. +** POSIX-style TZ environment variable handling from Guy Harris. +*/ + /* #define DEBUG */ /*LINTLIBRARY*/ @@ -51,10 +50,11 @@ #ifdef DEBUG #include #endif +#include + #include "private.h" #include "tzfile.h" - #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") @@ -62,14 +62,27 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/localtime.h" #include "asterisk/strings.h" +#include "asterisk/linkedlists.h" +#include "asterisk/utils.h" #ifndef lint #ifndef NOID -static const char elsieid[] = "@(#)localtime.c 7.57"; +static char __attribute__((unused)) elsieid[] = "@(#)localtime.c 8.5"; #endif /* !defined NOID */ #endif /* !defined lint */ +#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 */ /* ** SunOS 4.1.1 headers lack O_BINARY. @@ -82,55 +95,33 @@ static const char elsieid[] = "@(#)localtime.c 7.57"; #define OPEN_MODE O_RDONLY #endif /* !defined O_BINARY */ -#ifdef SOLARIS -#undef TM_ZONE -#undef TM_GMTOFF -#endif +static const char gmt[] = "GMT"; +static const struct timeval WRONG = { 0, 0 }; -#ifdef TM_ZONE -#ifndef WILDABBR /*! \note - * Someone might make incorrect use of a time zone abbreviation: - * 1. They might reference tzname[0] before calling ast_tzset (explicitly - * or implicitly). - * 2. They might reference tzname[1] before calling ast_tzset (explicitly - * or implicitly). - * 3. They might reference tzname[1] after setting to a time zone - * in which Daylight Saving Time is never observed. - * 4. They might reference tzname[0] after setting to a time zone - * in which Standard Time is never observed. - * 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 - * string "tzname[0] used before set", and similarly for the other cases. - * 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). + * 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. */ -#define WILDABBR " " -#endif /* !defined WILDABBR */ - -static char wildabbr[] = "WILDABBR"; -#endif /* TM_ZONE */ - -/*! \brief FreeBSD defines 'zone' in 'struct tm' as non-const, so don't declare this - string as const. */ -static char gmt[] = "GMT"; +#ifndef TZDEFRULESTRING +#define TZDEFRULESTRING ",M4.1.0,M10.5.0" +#endif /* !defined TZDEFDST */ /*!< \brief time type information */ -struct ttinfo { - long tt_gmtoff; /*!< GMT 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 GMT */ +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 */ }; /*! \brief leap second information */ -struct lsinfo { - time_t ls_trans; /*!< transition time */ - long ls_corr; /*!< correction to apply */ +struct lsinfo { /* leap second information */ + time_t ls_trans; /* transition time */ + long ls_corr; /* correction to apply */ }; #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) @@ -141,119 +132,152 @@ struct lsinfo { #ifndef TZNAME_MAX #define MY_TZNAME_MAX 255 #endif /* !defined TZNAME_MAX */ +#ifndef TZ_STRLEN_MAX +#define TZ_STRLEN_MAX 255 +#endif /* !defined TZ_STRLEN_MAX */ struct state { - char name[TZ_STRLEN_MAX + 1]; + /*! Name of the file that this references */ + char name[TZ_STRLEN_MAX + 1]; 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)))]; struct lsinfo lsis[TZ_MAX_LEAPS]; - struct state *next; + AST_LIST_ENTRY(state) list; }; struct rule { - int r_type; /*!< type of rule--see below */ - int r_day; /*!< day number of rule */ - int r_week; /*!< week number of rule */ - int r_mon; /*!< month number of rule */ - long r_time; /*!< transition time of rule */ + int r_type; /* type of rule--see below */ + int r_day; /* day number of rule */ + int r_week; /* week number of rule */ + int r_mon; /* month number of rule */ + long r_time; /* transition time of rule */ }; -#define JULIAN_DAY 0 /*!< Jn - Julian day */ -#define DAY_OF_YEAR 1 /*!< n - day of year */ -#define MONTH_NTH_DAY_OF_WEEK 2 /*!< Mm.n.d - month, week, day of week */ +#define JULIAN_DAY 0 /* Jn - Julian day */ +#define DAY_OF_YEAR 1 /* n - day of year */ +#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ /* ** Prototypes for static functions. */ static long detzcode P((const char * codep)); +static time_t detzcode64 P((const char * codep)); +static int differ_by_repeat P((time_t t1, time_t t0)); +static const char * getzname P((const char * strp)); +static const char * getqzname P((const char * strp, const int delim)); 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 struct timeval * timep, long offset, - struct ast_tm * tmp, const char * zone)); -static void localsub P((const struct timeval * timep, long offset, - struct ast_tm * tmp, const char * zone)); +static int gmtload P((struct state * sp)); +static struct ast_tm * gmtsub P((const struct timeval * timep, long offset, + struct ast_tm * tmp)); +static struct ast_tm * localsub P((const struct timeval * timep, long offset, + struct ast_tm * tmp, const struct state *sp)); static int increment_overflow P((int * number, int delta)); +static int leaps_thru_end_of P((int y)); +static int long_increment_overflow P((long * number, int delta)); +static int long_normalize_overflow P((long * tensptr, + int * unitsptr, const int base)); static int normalize_overflow P((int * tensptr, int * unitsptr, - int base)); -static time_t time1 P((struct ast_tm * tmp, - void(*funcp) P((const struct timeval *, - long, struct ast_tm *, const char *)), - long offset, const char * zone)); -static time_t time2 P((struct ast_tm *tmp, - void(*funcp) P((const struct timeval *, - long, struct ast_tm *, const char *)), - long offset, int * okayp, const char * zone)); -static void timesub P((const struct timeval * timep, long offset, - const struct state * sp, struct ast_tm *tmp)); -static int tmcomp P((const struct ast_tm *atmp, + const int base)); +static struct timeval time1 P((struct ast_tm * tmp, + struct ast_tm * (*funcp) P((const struct timeval *, + long, struct ast_tm *, const struct state *sp)), + long offset, const struct state *sp)); +static struct timeval time2 P((struct ast_tm *tmp, + struct ast_tm * (*funcp) P((const struct timeval *, + long, struct ast_tm*, const struct state *sp)), + long offset, int * okayp, const struct state *sp)); +static struct timeval time2sub P((struct ast_tm *tmp, + struct ast_tm * (*funcp) (const struct timeval *, + long, struct ast_tm*, const struct state *sp), + long offset, int * okayp, int do_norm_secs, const struct state *sp)); +static struct ast_tm * timesub P((const struct timeval * timep, long offset, + const struct state * sp, struct ast_tm * tmp)); +static int tmcomp P((const struct ast_tm * atmp, const struct ast_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 tzload P((const char * name, struct state * sp, + int doextend)); static int tzparse P((const char * name, struct state * sp, int lastditch)); -static struct state * lclptr = NULL; -static struct state * last_lclptr = NULL; -static struct state * gmtptr = NULL; +static AST_LIST_HEAD_STATIC(zonelist, state); #ifndef TZ_STRLEN_MAX #define TZ_STRLEN_MAX 255 #endif /* !defined TZ_STRLEN_MAX */ -static int gmt_is_set; -#ifdef _THREAD_SAFE -AST_MUTEX_DEFINE_STATIC(lcl_mutex); -AST_MUTEX_DEFINE_STATIC(tzset_mutex); -AST_MUTEX_DEFINE_STATIC(tzsetwall_mutex); -AST_MUTEX_DEFINE_STATIC(gmt_mutex); -#endif - -/* +/*! \note ** Section 4.12.3 of X3.159-1989 requires that ** 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 long detzcode(const char * const codep) +static long 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 int tzload(register const char *name, register struct state *const sp) +static time_t detzcode64(const char * const codep) { - register const char * p; - register int i; - register int fid; + time_t result; + 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 int differ_by_repeat(const time_t t1, const time_t t0) +{ + const long long at1 = t1, at0 = t0; + if (TYPE_INTEGRAL(time_t) && + TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) + return 0; + return at1 - at0 == SECSPERREPEAT; +} + +static int tzload(const char *name, struct state * const sp, 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; -#ifdef DEBUG - fprintf(stderr,"tzload called with name=%s, sp=%d\n", name, sp); -#endif if (name == NULL && (name = TZDEFAULT) == NULL) return -1; { - register int doaccess; - struct stat stab; + int doaccess; /* ** Section 4.9.1 of the C standard says that ** "FILENAME_MAX expands to an integral constant expression @@ -261,7 +285,7 @@ static int tzload(register const char *name, register struct state *const sp) ** to hold the longest file name string that the implementation ** guarantees can be opened." */ - char fullname[FILENAME_MAX + 1] = ""; + char fullname[FILENAME_MAX + 1]; if (name[0] == ':') ++name; @@ -269,11 +293,11 @@ static int tzload(register const char *name, register struct state *const sp) if (!doaccess) { if ((p = TZDIR) == NULL) return -1; - if ((strlen(p) + 1 + strlen(name) + 1) >= sizeof fullname) + if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) return -1; - (void) strncpy(fullname, p, sizeof(fullname) - 1); - (void) strncat(fullname, "/", sizeof(fullname) - strlen(fullname) - 1); - (void) strncat(fullname, name, sizeof(fullname) - strlen(fullname) - 1); + (void) strcpy(fullname, p); + (void) strcat(fullname, "/"); + (void) strcat(fullname, name); /* ** Set doaccess if '.' (as in "../") shows up in name. */ @@ -282,37 +306,24 @@ static int tzload(register const char *name, register struct state *const sp) name = fullname; } if (doaccess && access(name, R_OK) != 0) - return -1; + return -1; if ((fid = open(name, OPEN_MODE)) == -1) return -1; - if ((fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) { - close(fid); - return -1; - } } - { - struct tzhead * tzhp; - char buf[sizeof *sp + sizeof *tzhp]; + 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, buf, sizeof buf); - if (close(fid) != 0) - return -1; - p = buf; - p += (sizeof tzhp->tzh_magic) + (sizeof tzhp->tzh_reserved); - ttisstdcnt = (int) detzcode(p); - p += 4; - ttisgmtcnt = (int) detzcode(p); - p += 4; - sp->leapcnt = (int) detzcode(p); - p += 4; - sp->timecnt = (int) detzcode(p); - p += 4; - sp->typecnt = (int) detzcode(p); - p += 4; - sp->charcnt = (int) detzcode(p); - p += 4; + ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); + ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); + 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; 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 || @@ -320,17 +331,19 @@ static int tzload(register const char *name, register struct state *const sp) (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) return -1; - if (i - (p - 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++; @@ -338,7 +351,7 @@ static int tzload(register const char *name, register struct state *const sp) return -1; } for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; + struct ttinfo * ttisp; ttisp = &sp->ttis[i]; ttisp->tt_gmtoff = detzcode(p); @@ -355,16 +368,17 @@ static int tzload(register const char *name, register struct state *const sp) 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 = (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) @@ -377,7 +391,7 @@ static int tzload(register const char *name, register struct state *const sp) } } for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; + struct ttinfo * ttisp; ttisp = &sp->ttis[i]; if (ttisgmtcnt == 0) @@ -389,7 +403,90 @@ static int tzload(register const char *name, register 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). + */ + 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; + 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]; + } + } + i = 2 * YEARSPERREPEAT; + sp->goback = sp->goahead = sp->timecnt > i; + sp->goback = sp->goback && sp->types[i] == sp->types[0] && + differ_by_repeat(sp->ats[i], sp->ats[0]); + sp->goahead = sp->goahead && + sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] && + differ_by_repeat(sp->ats[sp->timecnt - 1], + sp->ats[sp->timecnt - 1 - i]); return 0; } @@ -403,16 +500,50 @@ static const int year_lengths[2] = { }; /*! \brief - * Given a pointer into a time zone string, extract a number from that string. - * \return Check that the number is within a specified range; if it is not, return - * NULL. - * Otherwise, return a pointer to the first character not part of the number. +** 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 +** character. */ -static const char *getnum(register const char *strp, int * const nump, const int min, const int max) +static const char * getzname(const char *strp) { - register char c; - register int num; + char c; + + while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && + c != '+') + ++strp; + return strp; +} + +/*! \brief +** 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; +} + +/*! \brief +** 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. +** Otherwise, return a pointer to the first character not part of the number. +*/ + +static const char *getnum(const char *strp, int *nump, const int min, const int max) +{ + char c; + int num; if (strp == NULL || !is_digit(c = *strp)) return NULL; @@ -430,14 +561,14 @@ static const char *getnum(register const char *strp, int * const nump, const int } /*! \brief - * Given a pointer into a time zone string, extract a number of seconds, - * in hh[:mm[:ss]] form, from the string. - * \return If any error occurs, return NULL. - * Otherwise, return a pointer to the first character not part of the number - * of seconds. +** Given a pointer into a time zone string, extract a number of seconds, +** in hh[:mm[:ss]] form, from the string. +** If any error occurs, return NULL. +** Otherwise, return a pointer to the first character not part of the number +** of seconds. */ -static const char *getsecs(register const char *strp, long * const secsp) +static const char *getsecs(const char *strp, long * const secsp) { int num; @@ -459,7 +590,7 @@ static const char *getsecs(register const char *strp, 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; @@ -470,15 +601,15 @@ static const char *getsecs(register const char *strp, long * const secsp) } /*! \brief - * Given a pointer into a time zone string, extract an offset, in - * [+-]hh[:mm[:ss]] form, from the string. - * \return If any error occurs, return NULL. - * Otherwise, return a pointer to the first character not part of the time. +** Given a pointer into a time zone string, extract an offset, in +** [+-]hh[:mm[:ss]] form, from the string. +** If any error occurs, return NULL. +** Otherwise, return a pointer to the first character not part of the time. */ -static const char * getoffset(register const char *strp, long * const offsetp) +static const char *getoffset(const char *strp, long *offsetp) { - register int neg = 0; + int neg = 0; if (*strp == '-') { neg = 1; @@ -494,13 +625,13 @@ static const char * getoffset(register const char *strp, long * const offsetp) } /*! \brief - * 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". - * \return If a valid rule is not found, return NULL. - * Otherwise, return a pointer to the first character not part of the rule. +** 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". +** 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, register struct rule * const rulep) +static const char *getrule(const char *strp, struct rule *rulep) { if (*strp == 'J') { /* @@ -546,22 +677,19 @@ static const char *getrule(const char *strp, register struct rule * const rulep) } /*! \brief - * Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the - * year, a rule, and the offset from GMT at the time that rule takes effect, - * calculate the Epoch-relative time that rule takes effect. +** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the +** year, a rule, and the offset from UTC at the time that rule takes effect, +** calculate the Epoch-relative time that rule takes effect. */ -static time_t transtime(janfirst, year, rulep, offset) -const time_t janfirst; -const int year; -register const struct rule * const rulep; -const long offset; +static time_t transtime(const time_t janfirst, const int year, const struct rule *rulep, const long offset) { - register int leapyear; - register time_t value = 0; - register int i; + int leapyear; + time_t value; + int i; int d, m1, yy0, yy1, yy2, dow; + INITIALIZE(value); leapyear = isleap(year); switch (rulep->r_type) { @@ -609,7 +737,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. */ @@ -631,54 +759,87 @@ const long offset; } /* - ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in - ** question. To get the Epoch-relative time of the specified local + ** "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 ** time on that day, add the transition time and the current offset - ** from GMT. + ** from UTC. */ return value + rulep->r_time + offset; } -/* +/*! \note ** Given a POSIX section 8-style TZ string, fill in the rule tables as ** appropriate. */ -static int -tzparse(name, sp, lastditch) -const char * name; -register struct state * const sp; -const int lastditch; +static int tzparse(const char *name, struct state *sp, const int lastditch) { const char * stdname; - const char * dstname = NULL; - size_t stdlen = 0; - size_t dstlen = 0; - long stdoffset = 0L; - long dstoffset = 0L; - register time_t * atp; - register unsigned char * typep; - register char * cp; - register int load_result; + const char * dstname; + size_t stdlen; + size_t dstlen; + long stdoffset; + long dstoffset; + time_t * atp; + unsigned char * typep; + char * cp; + int load_result; + INITIALIZE(dstname); stdname = name; -#ifdef DEBUG - fprintf(stderr, "tzparse(): loading default rules\n"); -#endif - load_result = tzload(TZDEFRULES, sp); + if (lastditch) { + stdlen = strlen(name); /* length of standard zone name */ + name += stdlen; + if (stdlen >= sizeof sp->chars) + stdlen = (sizeof sp->chars) - 1; + stdoffset = 0; + } else { + 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, FALSE); if (load_result != 0) sp->leapcnt = 0; /* so, we're off a little */ if (*name != '\0') { + 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; @@ -693,11 +854,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; @@ -707,7 +865,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, @@ -723,21 +886,24 @@ 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 { - 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. */ @@ -811,6 +977,7 @@ const int lastditch; sp->ttis[1].tt_gmtoff = -dstoffset; sp->ttis[1].tt_isdst = TRUE; sp->ttis[1].tt_abbrind = stdlen + 1; + sp->typecnt = 2; } } else { dstlen = 0; @@ -823,7 +990,7 @@ const int lastditch; sp->charcnt = stdlen + 1; if (dstlen != 0) sp->charcnt += dstlen + 1; - if (sp->charcnt > sizeof sp->chars) + if ((size_t) sp->charcnt > sizeof sp->chars) return -1; cp = sp->chars; (void) strncpy(cp, stdname, stdlen); @@ -836,202 +1003,124 @@ const int lastditch; return 0; } -static void -gmtload(sp) -struct state * const sp; +static int gmtload(struct state *sp) { - if (tzload(gmt, sp) != 0) - (void) tzparse(gmt, sp, TRUE); -} - -/* -** A non-static declaration of ast_tzsetwall in a system header file -** may cause a warning about this upcoming static declaration... -*/ -static -#ifdef _THREAD_SAFE -int -ast_tzsetwall_basic P((void)) -#else -int -ast_tzsetwall P((void)) -#endif -{ - struct state *cur_state = lclptr; - - /* Find the appropriate structure, if already parsed */ - while (cur_state != NULL) { - if (cur_state->name[0] == '\0') - break; - cur_state = cur_state->next; - } - if (cur_state != NULL) - return 0; - cur_state = malloc(sizeof(struct state)); - if (cur_state == NULL) { - return -1; - } - memset(cur_state,0,sizeof(struct state)); - if (tzload((char *) NULL, cur_state) != 0) -#ifdef DEBUG - { - fprintf(stderr, "ast_tzsetwall: calling gmtload()\n"); -#endif - gmtload(cur_state); -#ifdef DEBUG - } -#endif - - if (last_lclptr) - last_lclptr->next = cur_state; + if (tzload(gmt, sp, TRUE) != 0) + return tzparse(gmt, sp, TRUE); else - lclptr = cur_state; - last_lclptr = cur_state; - return 0; -} - -#ifdef _THREAD_SAFE -int -ast_tzsetwall P((void)) -{ - ast_mutex_lock(&tzsetwall_mutex); - ast_tzsetwall_basic(); - ast_mutex_unlock(&tzsetwall_mutex); - return 0; -} -#endif - -#ifdef _THREAD_SAFE -static int -ast_tzset_basic P((const char *name)) -#else -int -ast_tzset P((const char *name)) -#endif -{ - struct state *cur_state = lclptr; - - /* Not set at all */ - if (name == NULL) { - return ast_tzsetwall(); - } - - /* Find the appropriate structure, if already parsed */ - while (cur_state != NULL) { - if (!strcmp(cur_state->name,name)) - break; - cur_state = cur_state->next; - } - if (cur_state != NULL) - return 0; - - cur_state = malloc(sizeof(struct state)); - if (cur_state == NULL) { return -1; - } - memset(cur_state,0,sizeof(*cur_state)); +} - /* Name is set, but set to the empty string == no adjustments */ - if (name[0] == '\0') { - /* - ** User wants it fast rather than right. - */ - cur_state->leapcnt = 0; /* so, we're off a little */ - cur_state->timecnt = 0; - cur_state->ttis[0].tt_gmtoff = 0; - cur_state->ttis[0].tt_abbrind = 0; - (void) strncpy(cur_state->chars, gmt, sizeof(cur_state->chars) - 1); - } else if (tzload(name, cur_state) != 0) { - if (name[0] == ':') { - (void) gmtload(cur_state); - } else if (tzparse(name, cur_state, FALSE) != 0) { - /* If not found, load localtime */ - if (tzload("/etc/localtime", cur_state) != 0) - /* Last ditch, get GMT */ - (void) gmtload(cur_state); +static const struct state *ast_tzset(const char *zone) +{ + struct state *sp; + + if (ast_strlen_zero(zone)) + zone = "/etc/localtime"; + + AST_LIST_LOCK(&zonelist); + AST_LIST_TRAVERSE(&zonelist, sp, list) { + if (!strcmp(sp->name, zone)) { + AST_LIST_UNLOCK(&zonelist); + return sp; } } - strncpy(cur_state->name, name, sizeof(cur_state->name) - 1); - if (last_lclptr) - last_lclptr->next = cur_state; - else - lclptr = cur_state; - last_lclptr = cur_state; - return 0; + AST_LIST_UNLOCK(&zonelist); + + if (!(sp = ast_calloc(1, sizeof *sp))) + return NULL; + + if (tzload(zone, sp, TRUE) != 0) { + if (zone[0] == ':' || tzparse(zone, sp, FALSE) != 0) + (void) gmtload(sp); + } + ast_copy_string(sp->name, zone, sizeof(sp->name)); + AST_LIST_LOCK(&zonelist); + AST_LIST_INSERT_TAIL(&zonelist, sp, list); + AST_LIST_UNLOCK(&zonelist); + return sp; } -#ifdef _THREAD_SAFE -void -ast_tzset P((const char *name)) -{ - ast_mutex_lock(&tzset_mutex); - ast_tzset_basic(name); - ast_mutex_unlock(&tzset_mutex); -} -#endif - -/* +/*! \note ** 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(timep, offset, tmp, zone) -const struct timeval * const timep; -const long offset; -struct ast_tm * const tmp; -const char * const zone; +static struct ast_tm *localsub(const struct timeval *timep, const long offset, struct ast_tm *tmp, const struct state *sp) { - register struct state * sp; - register const struct ttinfo * ttisp; - register int i; + const struct ttinfo * ttisp; + int i; + struct ast_tm * result; struct timeval t; memcpy(&t, timep, sizeof(t)); - sp = lclptr; - /* Find the right zone record */ - if (zone == NULL) - sp = NULL; - else - while (sp != NULL) { - if (!strcmp(sp->name,zone)) - break; - sp = sp->next; - } + if (sp == NULL) + return gmtsub(timep, offset, tmp); + if ((sp->goback && t.tv_sec < sp->ats[0]) || + (sp->goahead && t.tv_sec > sp->ats[sp->timecnt - 1])) { + struct timeval newt = t; + time_t seconds; + time_t tcycles; + int_fast64_t icycles; - if (sp == NULL) { - ast_tzsetwall(); - sp = lclptr; - /* Find the default zone record */ - while (sp != NULL) { - if (sp->name[0] == '\0') - break; - sp = sp->next; - } - } + if (t.tv_sec < sp->ats[0]) + seconds = sp->ats[0] - t.tv_sec; + else seconds = t.tv_sec - 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.tv_sec < sp->ats[0]) + newt.tv_sec += seconds; + else newt.tv_sec -= seconds; + if (newt.tv_sec < sp->ats[0] || + newt.tv_sec > sp->ats[sp->timecnt - 1]) + return NULL; /* "cannot happen" */ + result = localsub(&newt, offset, tmp, sp); + if (result == tmp) { + time_t newy; - /* Last ditch effort, use GMT */ - if (sp == NULL) { - gmtsub(timep, offset, tmp, zone); - return; + newy = tmp->tm_year; + if (t.tv_sec < 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.tv_sec < sp->ats[0]) { i = 0; - while (sp->ttis[i].tt_isdst) + while (sp->ttis[i].tt_isdst) { if (++i >= sp->typecnt) { i = 0; break; } + } } else { - for (i = 1; i < sp->timecnt; ++i) - if (t.tv_sec < sp->ats[i]) - break; - i = sp->types[i - 1]; + int lo = 1; + int hi = sp->timecnt; + + while (lo < hi) { + int mid = (lo + hi) >> 1; + + if (t.tv_sec < sp->ats[mid]) + hi = mid; + else + lo = mid + 1; + } + i = (int) sp->types[lo - 1]; } ttisp = &sp->ttis[i]; /* @@ -1040,86 +1129,83 @@ const char * const zone; ** 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 */ tmp->tm_usec = timep->tv_usec; + return result; } -struct ast_tm * -ast_localtime(timep, p_tm, zone) -const struct timeval * const timep; -struct ast_tm *p_tm; -const char * const zone; +struct ast_tm *ast_localtime(const struct timeval *timep, struct ast_tm *tmp, const char *zone) { -#ifdef _THREAD_SAFE - ast_mutex_lock(&lcl_mutex); -#endif - ast_tzset(ast_strlen_zero(zone) ? "/etc/localtime" : zone); - localsub(timep, 0L, p_tm, zone); -#ifdef _THREAD_SAFE - ast_mutex_unlock(&lcl_mutex); -#endif - return(p_tm); + const struct state *sp = ast_tzset(zone); + return sp ? localsub(timep, 0L, tmp, sp) : NULL; } /* ** gmtsub is to gmtime as localsub is to localtime. */ -static void -gmtsub(timep, offset, tmp, zone) -const struct timeval * const timep; -const long offset; -struct ast_tm * const tmp; -const char * const zone; +static struct ast_tm *gmtsub(const struct timeval *timep, const long offset, struct ast_tm *tmp) { -#ifdef _THREAD_SAFE - ast_mutex_lock(&gmt_mutex); -#endif - if (!gmt_is_set) { - gmt_is_set = TRUE; - gmtptr = (struct state *) malloc(sizeof *gmtptr); - if (gmtptr != NULL) - gmtload(gmtptr); + struct ast_tm * result; + struct state *sp; + + AST_LIST_LOCK(&zonelist); + AST_LIST_TRAVERSE(&zonelist, sp, list) { + if (!strcmp(sp->name, "UTC")) + break; } - ast_mutex_unlock(&gmt_mutex); - timesub(timep, offset, gmtptr, tmp); + + if (!sp) { + if (!(sp = (struct state *) ast_calloc(1, sizeof *sp))) + return NULL; + gmtload(sp); + AST_LIST_INSERT_TAIL(&zonelist, sp, list); + } + AST_LIST_UNLOCK(&zonelist); + + result = timesub(timep, offset, sp, tmp); #ifdef TM_ZONE /* ** Could get fancy here and deliver something such as - ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero, + ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero, ** but this is no time for a treasure hunt. */ if (offset != 0) - tmp->TM_ZONE = wildabbr; - else { - if (gmtptr == NULL) - tmp->TM_ZONE = gmt; - else tmp->TM_ZONE = gmtptr->chars; - } + tmp->TM_ZONE = " "; + else + tmp->TM_ZONE = sp->chars; #endif /* defined TM_ZONE */ + return result; } -static void -timesub(timep, offset, sp, tmp) -const struct timeval * const timep; -const long offset; -register const struct state * const sp; -register struct ast_tm * const tmp; +/*! \brief +** 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 ast_tm *timesub(const struct timeval *timep, const long offset, const struct state *sp, struct ast_tm *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; + long seconds; + corr = 0; hit = 0; @@ -1144,94 +1230,108 @@ register struct ast_tm * const tmp; break; } } - days = timep->tv_sec / SECSPERDAY; - rem = timep->tv_sec % SECSPERDAY; -#ifdef mc68k - if (timep->tv_sec == 0x80000000) { - /* - ** A 3B1 muffs the division on the most negative number. - */ - days = -24855; - rem = -11648; + y = EPOCH_YEAR; + tdays = timep->tv_sec / SECSPERDAY; + rem = timep->tv_sec - tdays * SECSPERDAY; + while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { + int newy; + time_t tdelta; + int idelta; + int leapdays; + + tdelta = tdays / DAYSPERLYEAR; + idelta = 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; } -#endif /* defined mc68k */ - rem += (offset - corr); + + seconds = tdays * SECSPERDAY + 0.5; + tdays = seconds / SECSPERDAY; + rem += seconds - tdays * SECSPERDAY; + + /* + ** Given the range, we can now fearlessly cast... + */ + idays = 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 */ tmp->tm_usec = timep->tv_usec; + return tmp; } -char * -ast_ctime(timep, buf) -const struct timeval * const timep; -char *buf; -{ - struct ast_tm tm; - return asctime_r((struct tm *)ast_localtime(timep, &tm, NULL), buf); -} - -/* +/*! \note ** 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) -#endif /* !defined WRONG */ - -/* -** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). +/*! \brief +** Simplified normalize logic courtesy Paul Eggert. */ -static int -increment_overflow(number, delta) -int * number; -int delta; +static int increment_overflow(int *number, int delta) { int number0; @@ -1240,13 +1340,18 @@ int delta; return (*number < number0) != (delta < 0); } -static int -normalize_overflow(tensptr, unitsptr, base) -int * const tensptr; -int * const unitsptr; -const int base; +static int long_increment_overflow(long *number, int delta) { - register int tensdelta; + long number0; + + number0 = *number; + *number += delta; + return (*number < number0) != (delta < 0); +} + +static int normalize_overflow(int *tensptr, int *unitsptr, const int base) +{ + int tensdelta; tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : @@ -1255,79 +1360,95 @@ const int base; return increment_overflow(tensptr, tensdelta); } -static int -tmcomp(atmp, btmp) -register const struct ast_tm * const atmp; -register const struct ast_tm * const btmp; +static int long_normalize_overflow(long *tensptr, int *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 ast_tm *atmp, const struct ast_tm *btmp) +{ + int result; if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && - (result = (atmp->tm_min - btmp->tm_min)) == 0) - result = atmp->tm_sec - btmp->tm_sec; + (result = (atmp->tm_min - btmp->tm_min)) == 0 && + (result = (atmp->tm_sec - btmp->tm_sec)) == 0) + result = atmp->tm_usec - btmp->tm_usec; return result; } -static time_t -time2(tmp, funcp, offset, okayp, zone) -struct ast_tm * const tmp; -void (* const funcp) P((const struct timeval *, long, struct ast_tm*, const char*)); -const long offset; -int * const okayp; -const char * const zone; +static struct timeval time2sub(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm *, const struct state *), const long offset, int *okayp, const int do_norm_secs, const struct state *sp) { - 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; + long y; struct timeval newt = { 0, 0 }; struct timeval t = { 0, 0 }; struct ast_tm yourtm, mytm; *okayp = FALSE; yourtm = *tmp; + if (do_norm_secs) { + if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, + SECSPERMIN)) + return WRONG; + } if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) return WRONG; if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) return WRONG; - if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) + y = yourtm.tm_year; + if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR)) return WRONG; /* - ** 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)) + if (long_increment_overflow(&y, TM_YEAR_BASE)) return WRONG; while (yourtm.tm_mday <= 0) { - if (increment_overflow(&yourtm.tm_year, -1)) + if (long_increment_overflow(&y, -1)) return WRONG; - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(i)]; + 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)) + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (long_increment_overflow(&y, 1)) return WRONG; } 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)) + if (long_increment_overflow(&y, 1)) return WRONG; } } - if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) + if (long_increment_overflow(&y, -TM_YEAR_BASE)) return WRONG; - if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { + yourtm.tm_year = y; + if (yourtm.tm_year != y) + return WRONG; + 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. @@ -1345,27 +1466,53 @@ const char * const zone; 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.tv_sec = 0; + if (!TYPE_SIGNED(time_t)) { + lo = 0; + hi = lo - 1; + } else if (!TYPE_INTEGRAL(time_t)) { + if (sizeof(time_t) > sizeof(float)) + hi = (time_t) DBL_MAX; + else hi = (time_t) FLT_MAX; + lo = -hi; + } else { + lo = 1; + for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) + lo *= 2; + hi = -(lo + 1); + } for ( ; ; ) { - (*funcp)(&t, offset, &mytm, zone); - dir = tmcomp(&mytm, &yourtm); + t.tv_sec = lo / 2 + hi / 2; + if (t.tv_sec < lo) + t.tv_sec = lo; + else if (t.tv_sec > hi) + t.tv_sec = hi; + if ((*funcp)(&t, offset, &mytm, sp) == NULL) { + /* + ** Assume that t is too extreme to be represented in + ** a struct ast_tm; arrange things so that it is less + ** extreme on the next pass. + */ + dir = (t.tv_sec > 0) ? 1 : -1; + } else dir = tmcomp(&mytm, &yourtm); if (dir != 0) { - if (bits-- < 0) + if (t.tv_sec == lo) { + ++t.tv_sec; + if (t.tv_sec <= lo) + return WRONG; + ++lo; + } else if (t.tv_sec == hi) { + --t.tv_sec; + if (t.tv_sec >= hi) + return WRONG; + --hi; + } + if (lo > hi) return WRONG; - if (bits < 0) - --t.tv_sec; /* may be needed if new t is minimal */ - else if (dir > 0) - t.tv_sec -= ((time_t) 1) << bits; - else t.tv_sec += ((time_t) 1) << bits; + if (dir > 0) + hi = t.tv_sec; + else lo = t.tv_sec; continue; } if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) @@ -1379,11 +1526,6 @@ const char * const zone; /* ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. */ - sp = (const struct state *) - (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); - if (sp == NULL) - return WRONG; for (i = sp->typecnt - 1; i >= 0; --i) { if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) continue; @@ -1392,7 +1534,8 @@ const char * const zone; continue; newt.tv_sec = t.tv_sec + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; - (*funcp)(&newt, offset, &mytm, zone); + if ((*funcp)(&newt, offset, &mytm, sp) == NULL) + continue; if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) @@ -1411,29 +1554,41 @@ label: if ((newt.tv_sec < t.tv_sec) != (saved_seconds < 0)) return WRONG; t.tv_sec = newt.tv_sec; - (*funcp)(&t, offset, tmp, zone); - *okayp = TRUE; - return t.tv_sec; + if ((*funcp)(&t, offset, tmp, sp)) + *okayp = TRUE; + return t; } -static time_t -time1(tmp, funcp, offset, zone) -struct ast_tm * const tmp; -void (* const funcp) P((const struct timeval *, long, struct ast_tm *, const char*)); -const long offset; -const char * const zone; +static struct timeval time2(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm*, const struct state *sp), const long offset, int *okayp, const struct state *sp) { - register time_t t; - register const struct state * sp; - register int samei, otheri; + struct timeval t; + + /*! \note + ** First try without normalization of seconds + ** (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, sp); + return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp); +} + +static struct timeval time1(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm *, const struct state *), const long offset, const struct state *sp) +{ + struct timeval 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->tm_isdst > 1) tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay, zone); + t = time2(tmp, funcp, offset, &okay, sp); #ifdef PCTS /* - ** PCTS code courtesy Grant Sullivan (grant@osf.org). + ** PCTS code courtesy Grant Sullivan. */ if (okay) return t; @@ -1446,27 +1601,32 @@ const char * const zone; #endif /* !defined PCTS */ /* ** We're supposed to assume that somebody took a time of one type - ** and did some math on it that yielded a "struct tm" that's bad. + ** and did some math on it that yielded a "struct ast_tm" that's bad. ** 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); if (sp == NULL) return WRONG; - 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_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay, zone); + t = time2(tmp, funcp, offset, &okay, sp); if (okay) return t; tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - @@ -1477,21 +1637,12 @@ const char * const zone; return WRONG; } -time_t -ast_mktime(tmp,zone) -struct ast_tm * const tmp; -const char * const zone; +struct timeval ast_mktime(struct ast_tm *tmp, const char *zone) { - time_t mktime_return_value; -#ifdef _THREAD_SAFE - ast_mutex_lock(&lcl_mutex); -#endif - ast_tzset(!ast_strlen_zero(zone) ? zone : "/etc/localtime"); - mktime_return_value = time1(tmp, localsub, 0L, !ast_strlen_zero(zone) ? zone : "/etc/localtime"); -#ifdef _THREAD_SAFE - ast_mutex_unlock(&lcl_mutex); -#endif - return(mktime_return_value); + const struct state *sp; + if (!(sp = ast_tzset(zone))) + return WRONG; + return time1(tmp, localsub, 0L, sp); } int ast_strftime(char *buf, size_t len, const char *tmp, const struct ast_tm *tm) diff --git a/main/stdtime/private.h b/main/stdtime/private.h index 8ef9ccfdd..11793088d 100644 --- a/main/stdtime/private.h +++ b/main/stdtime/private.h @@ -1,32 +1,12 @@ -/* $FreeBSD: src/lib/libc/stdtime/private.h,v 1.6.8.1 2000/08/23 00:19:15 jhb Exp $ */ - #ifndef PRIVATE_H #define PRIVATE_H + /* ** 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. */ -/* Stuff moved from Makefile.inc to reduce clutter */ -#ifndef __CYGWIN__ -#ifndef TM_GMTOFF -#define TM_GMTOFF tm_gmtoff -#define TM_ZONE tm_zone -#endif -#define STD_INSPIRED 1 -#define PCTS 1 -#define HAVE_LONG_DOUBLE 1 -#define HAVE_STRERROR 1 -#define HAVE_UNISTD_H 1 -#define LOCALE_HOME _PATH_LOCALE -#ifdef SOLARIS -#define TZDIR "/usr/share/lib/zoneinfo" -#else -#define TZDIR "/usr/share/zoneinfo" -#endif /* def SOLARIS */ -#endif /* ndef TM_GMTOFF */ - /* ** This header is for use ONLY with the time conversion code. ** There is no guarantee that it will remain unchanged, @@ -41,12 +21,12 @@ #ifndef lint #ifndef NOID -/* -static char privatehid[] = "@(#)private.h 7.43"; -*/ +static char __attribute__((unused)) privatehid[] = "@(#)private.h 8.3"; #endif /* !defined NOID */ #endif /* !defined lint */ +#define GRANDPARENTED "Local time zone must be set--see zic manual page" + /* ** Defaults for preprocessor symbols. ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. @@ -60,14 +40,30 @@ static char privatehid[] = "@(#)private.h 7.43"; #define HAVE_GETTEXT 0 #endif /* !defined HAVE_GETTEXT */ +#ifndef HAVE_INCOMPATIBLE_CTIME_R +#define HAVE_INCOMPATIBLE_CTIME_R 0 +#endif /* !defined INCOMPATIBLE_CTIME_R */ + #ifndef HAVE_SETTIMEOFDAY #define HAVE_SETTIMEOFDAY 3 #endif /* !defined HAVE_SETTIMEOFDAY */ #ifndef HAVE_STRERROR -#define HAVE_STRERROR 0 +#define HAVE_STRERROR 1 #endif /* !defined HAVE_STRERROR */ +#ifndef HAVE_SYMLINK +#define HAVE_SYMLINK 1 +#endif /* !defined HAVE_SYMLINK */ + +#ifndef HAVE_SYS_STAT_H +#define HAVE_SYS_STAT_H 1 +#endif /* !defined HAVE_SYS_STAT_H */ + +#ifndef HAVE_SYS_WAIT_H +#define HAVE_SYS_WAIT_H 1 +#endif /* !defined HAVE_SYS_WAIT_H */ + #ifndef HAVE_UNISTD_H #define HAVE_UNISTD_H 1 #endif /* !defined HAVE_UNISTD_H */ @@ -80,6 +76,11 @@ static char privatehid[] = "@(#)private.h 7.43"; #define LOCALE_HOME "/usr/lib/locale" #endif /* !defined LOCALE_HOME */ +#if HAVE_INCOMPATIBLE_CTIME_R +#define asctime_r _incompatible_asctime_r +#define ctime_r _incompatible_ctime_r +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + /* ** Nested includes */ @@ -88,43 +89,105 @@ static char privatehid[] = "@(#)private.h 7.43"; #include "stdio.h" #include "errno.h" #include "string.h" -#include "limits.h" /* for CHAR_BIT */ +#include "limits.h" /* for CHAR_BIT et al. */ #include "time.h" #include "stdlib.h" -#if HAVE_GETTEXT - 0 +#if HAVE_GETTEXT #include "libintl.h" -#endif /* HAVE_GETTEXT - 0 */ +#endif /* HAVE_GETTEXT */ -#if HAVE_UNISTD_H - 0 +#if HAVE_SYS_WAIT_H +#include /* for WIFEXITED and WEXITSTATUS */ +#endif /* HAVE_SYS_WAIT_H */ + +#ifndef WIFEXITED +#define WIFEXITED(status) (((status) & 0xff) == 0) +#endif /* !defined WIFEXITED */ +#ifndef WEXITSTATUS +#define WEXITSTATUS(status) (((status) >> 8) & 0xff) +#endif /* !defined WEXITSTATUS */ + +#if HAVE_UNISTD_H #include "unistd.h" /* for F_OK and R_OK */ -#endif /* HAVE_UNISTD_H - 0 */ +#endif /* HAVE_UNISTD_H */ -#if !(HAVE_UNISTD_H - 0) +#if !HAVE_UNISTD_H #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) */ +#endif /* !HAVE_UNISTD_H */ -/* Unlike 's isdigit, this also works if c < 0 | c > UCHAR_MAX. */ +/* Unlike 's isdigit, this also works if c < 0 | c > UCHAR_MAX. */ #define is_digit(c) ((unsigned)(c) - '0' <= 9) +/* +** Define HAVE_STDINT_H's default value here, rather than at the +** start, since __GLIBC__'s value depends on previously-included +** files. +** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.) +*/ +#ifndef HAVE_STDINT_H +#define HAVE_STDINT_H \ + (199901 <= __STDC_VERSION__ || \ + 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__))) +#endif /* !defined HAVE_STDINT_H */ + +#if HAVE_STDINT_H +#include "stdint.h" +#endif /* !HAVE_STDINT_H */ + +#ifndef INT_FAST64_MAX +/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ +#if defined LLONG_MAX || defined __LONG_LONG_MAX__ +typedef long long int_fast64_t; +#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ +#if (LONG_MAX >> 31) < 0xffffffff +Please use a compiler that supports a 64-bit integer type (or wider); +you may need to compile with "-DHAVE_STDINT_H". +#endif /* (LONG_MAX >> 31) < 0xffffffff */ +typedef long int_fast64_t; +#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ +#endif /* !defined INT_FAST64_MAX */ + +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif /* !defined INT32_MAX */ +#ifndef INT32_MIN +#define INT32_MIN (-1 - INT32_MAX) +#endif /* !defined INT32_MIN */ + /* ** Workarounds for compilers/systems. */ +/* +** If your compiler lacks prototypes, "#define P(x) ()". +*/ + #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. */ @@ -146,6 +209,38 @@ static char privatehid[] = "@(#)private.h 7.43"; #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 */ + +/* +** Some ancient errno.h implementations don't declare errno. +** But some newer errno.h implementations define it as a macro. +** Fix the former without affecting the latter. +*/ + +#ifndef errno +extern int errno; +#endif /* !defined errno */ + +/* +** Private function declarations. +*/ + +char * icalloc P((int nelem, int elsize)); +char * icatalloc P((char * old, const char * new)); +char * icpyalloc P((const char * string)); +char * imalloc P((int n)); +void * irealloc P((void * pointer, int size)); +void icfree P((char * pointer)); +void ifree P((char * pointer)); +const char * scheck P((const char * string, const char * format)); + /* ** Finally, some convenience items. */ @@ -166,6 +261,15 @@ static char privatehid[] = "@(#)private.h 7.43"; #define TYPE_SIGNED(type) (((type) -1) < 0) #endif /* !defined TYPE_SIGNED */ +/* +** Since the definition of TYPE_INTEGRAL contains floating point numbers, +** it cannot be used in preprocessor directives. +*/ + +#ifndef TYPE_INTEGRAL +#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) +#endif /* !defined TYPE_INTEGRAL */ + #ifndef INT_STRLEN_MAXIMUM /* ** 302 / 1000 is log10(2.0) rounded up. @@ -174,7 +278,8 @@ static char privatehid[] = "@(#)private.h 7.43"; ** add one more for a minus sign if the type is signed. */ #define INT_STRLEN_MAXIMUM(type) \ - ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type)) + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ + 1 + TYPE_SIGNED(type)) #endif /* !defined INT_STRLEN_MAXIMUM */ /* @@ -208,19 +313,46 @@ static char privatehid[] = "@(#)private.h 7.43"; */ #ifndef _ -#if HAVE_GETTEXT - 0 +#if HAVE_GETTEXT #define _(msgid) gettext(msgid) -#else /* !(HAVE_GETTEXT - 0) */ +#else /* !HAVE_GETTEXT */ #define _(msgid) msgid -#endif /* !(HAVE_GETTEXT - 0) */ +#endif /* !HAVE_GETTEXT */ #endif /* !defined _ */ #ifndef TZ_DOMAIN #define TZ_DOMAIN "tz" #endif /* !defined TZ_DOMAIN */ +#if HAVE_INCOMPATIBLE_CTIME_R +#undef asctime_r +#undef ctime_r +char *asctime_r P((struct tm const *, char *)); +char *ctime_r P((time_t const *, char *)); +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + +#ifndef YEARSPERREPEAT +#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ +#endif /* !defined YEARSPERREPEAT */ + /* -** UNIX was a registered trademark of UNIX System Laboratories in 1993. +** The Gregorian year averages 365.2425 days, which is 31556952 seconds. +*/ + +#ifndef AVGSECSPERYEAR +#define AVGSECSPERYEAR 31556952L +#endif /* !defined AVGSECSPERYEAR */ + +#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 The Open Group in 2003. */ #endif /* !defined PRIVATE_H */ diff --git a/main/stdtime/tzfile.h b/main/stdtime/tzfile.h index f16d5a05c..2637ab960 100644 --- a/main/stdtime/tzfile.h +++ b/main/stdtime/tzfile.h @@ -2,17 +2,18 @@ #define TZFILE_H -/*! \brief - * This file is in the public domain, so clarified as of - * 1996-06-05 by - * \author Arthur David Olson (arthur_david_olson@nih.gov). - * - * \note This header is for use ONLY with the time conversion code. - * There is no guarantee that it will remain unchanged, - * or that it will remain at all. - * Do NOT copy it to any system include directory. - * Thank you! - */ +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +*/ + +/* +** This header is for use ONLY with the time conversion code. +** There is no guarantee that it will remain unchanged, +** or that it will remain at all. +** Do NOT copy it to any system include directory. +** Thank you! +*/ /* ** ID @@ -20,9 +21,7 @@ #ifndef lint #ifndef NOID -/* -static char tzfilehid[] = "@(#)tzfile.h 7.14"; -*/ +static char __attribute__((unused)) tzfilehid[] = "@(#)tzfile.h 8.1"; #endif /* !defined NOID */ #endif /* !defined lint */ @@ -31,11 +30,19 @@ static char tzfilehid[] = "@(#)tzfile.h 7.14"; */ #ifndef TZDIR -#define TZDIR "/usr/share/zoneinfo" /*!< Time zone object file directory */ +#ifdef SOLARIS +#define TZDIR "/usr/share/lib/zoneinfo" +#else +#ifdef FREEBSD +#define TZDIR "/usr/local/etc/zoneinfo" /* Time zone object file directory */ +#else +#define TZDIR "/usr/share/zoneinfo" +#endif /* defined FREEBSD */ +#endif /* defined SOLARIS */ #endif /* !defined TZDIR */ #ifndef TZDEFAULT -#define TZDEFAULT "/etc/localtime" +#define TZDEFAULT "localtime" #endif /* !defined TZDEFAULT */ #ifndef TZDEFRULES @@ -49,8 +56,9 @@ static char tzfilehid[] = "@(#)tzfile.h 7.14"; #define TZ_MAGIC "TZif" struct tzhead { - char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_reserved[16]; /* reserved for future use */ + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_version[1]; /* '\0' or '2' as of 2005 */ + char tzh_reserved[15]; /* reserved--must be zero */ 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 */ @@ -84,19 +92,23 @@ struct tzhead { ** 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. */ #ifndef TZ_MAX_TIMES -/* -** The TZ_MAX_TIMES value below is enough to handle a bit more than a -** year's worth of solar time (corrected daily to the nearest second) or -** 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 @@ -106,7 +118,7 @@ struct tzhead { #ifdef NOSOLAR /* ** Must be at least 14 for Europe/Riga as of Jan 12 1995, -** as noted by Earl Chew . +** as noted by Earl Chew. */ #define TZ_MAX_TYPES 20 /* Maximum number of local time types */ #endif /* !defined NOSOLAR */ @@ -157,33 +169,20 @@ struct tzhead { #define EPOCH_YEAR 1970 #define EPOCH_WDAY TM_THURSDAY -/* -** Accurate only for the past couple of centuries; -** that will probably do. -*/ - #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. +** Since everything in isleap is modulo 400 (or a factor of 400), we know that +** isleap(y) == isleap(y % 400) +** and so +** isleap(a + b) == isleap((a + b) % 400) +** or +** isleap(a + b) == isleap(a % 400 + b % 400) +** This is true even if % means modulo rather than Fortran remainder +** (which is allowed by C89 but not C99). +** We use this to avoid addition overflow problems. */ -#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 */ +#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) #endif /* !defined TZFILE_H */