From e9a028f81f10a63b340696fc34c4dac9849a8f43 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 30 Apr 2004 04:44:06 +0000 Subject: [PATCH] Convert DOS newlines to Unix newlines. --- src/timezone/asctime.c | 150 +- src/timezone/difftime.c | 168 +- src/timezone/ialloc.c | 162 +- src/timezone/localtime.c | 3424 ++++++++++++++--------------- src/timezone/pgtz.h | 2 - src/timezone/private.h | 588 ++--- src/timezone/scheck.c | 118 +- src/timezone/tzfile.h | 376 ++-- src/timezone/zic.c | 4516 +++++++++++++++++++------------------- 9 files changed, 4751 insertions(+), 4753 deletions(-) diff --git a/src/timezone/asctime.c b/src/timezone/asctime.c index 31a6275edc..d19e1dc192 100644 --- a/src/timezone/asctime.c +++ b/src/timezone/asctime.c @@ -1,75 +1,75 @@ -#include "pgtz.h" -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). -*/ - -#ifndef lint -#ifndef NOID -static char elsieid[] = "@(#)asctime.c 7.9"; -#endif /* !defined NOID */ -#endif /* !defined lint */ - -/*LINTLIBRARY*/ - -#include "private.h" -#include "tzfile.h" - -/* -** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12. -*/ - -char * -asctime_r(timeptr, buf) -register const struct tm * timeptr; -char * buf; -{ - static const char wday_name[][3] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - static const char mon_name[][3] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - register const char * wn; - register const char * mn; - - if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) - wn = "???"; - else wn = wday_name[timeptr->tm_wday]; - if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) - mn = "???"; - else mn = mon_name[timeptr->tm_mon]; - /* - ** The X3J11-suggested format is - ** "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n" - ** Since the .2 in 02.2d is ignored, we drop it. - */ - (void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %d\n", - wn, mn, - timeptr->tm_mday, timeptr->tm_hour, - timeptr->tm_min, timeptr->tm_sec, - TM_YEAR_BASE + timeptr->tm_year); - return buf; -} - -/* -** A la X3J11, with core dump avoidance. -*/ - -char * -asctime(timeptr) -register const struct tm * timeptr; -{ - /* - ** Big enough for something such as - ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n - ** (two three-character abbreviations, five strings denoting integers, - ** three explicit spaces, two explicit colons, a newline, - ** and a trailing ASCII nul). - */ - static char result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + - 3 + 2 + 1 + 1]; - - return asctime_r(timeptr, result); -} +#include "pgtz.h" +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +*/ + +#ifndef lint +#ifndef NOID +static char elsieid[] = "@(#)asctime.c 7.9"; +#endif /* !defined NOID */ +#endif /* !defined lint */ + +/*LINTLIBRARY*/ + +#include "private.h" +#include "tzfile.h" + +/* +** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12. +*/ + +char * +asctime_r(timeptr, buf) +register const struct tm * timeptr; +char * buf; +{ + static const char wday_name[][3] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static const char mon_name[][3] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + register const char * wn; + register const char * mn; + + if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) + wn = "???"; + else wn = wday_name[timeptr->tm_wday]; + if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) + mn = "???"; + else mn = mon_name[timeptr->tm_mon]; + /* + ** The X3J11-suggested format is + ** "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n" + ** Since the .2 in 02.2d is ignored, we drop it. + */ + (void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %d\n", + wn, mn, + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec, + TM_YEAR_BASE + timeptr->tm_year); + return buf; +} + +/* +** A la X3J11, with core dump avoidance. +*/ + +char * +asctime(timeptr) +register const struct tm * timeptr; +{ + /* + ** Big enough for something such as + ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n + ** (two three-character abbreviations, five strings denoting integers, + ** three explicit spaces, two explicit colons, a newline, + ** and a trailing ASCII nul). + */ + static char result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + + 3 + 2 + 1 + 1]; + + return asctime_r(timeptr, result); +} diff --git a/src/timezone/difftime.c b/src/timezone/difftime.c index 636640d6f5..57b88deafe 100644 --- a/src/timezone/difftime.c +++ b/src/timezone/difftime.c @@ -1,84 +1,84 @@ -#include "pgtz.h" -/* -** This file is in the public domain, so clarified as of -** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov). -*/ - -#ifndef lint -#ifndef NOID -static char elsieid[] = "@(#)difftime.c 7.9"; -#endif /* !defined NOID */ -#endif /* !defined lint */ - -/*LINTLIBRARY*/ - -#include "private.h" - -/* -** Algorithm courtesy Paul Eggert (eggert@twinsun.com). -*/ - -#ifdef HAVE_LONG_DOUBLE -#define long_double long double -#endif /* defined HAVE_LONG_DOUBLE */ -#ifndef HAVE_LONG_DOUBLE -#define long_double double -#endif /* !defined HAVE_LONG_DOUBLE */ - -double -difftime(time1, time0) -const time_t time1; -const time_t time0; -{ - time_t delta; - time_t hibit; - - { - time_t tt; - double d; - long_double ld; - - if (sizeof tt < sizeof d) - return (double) time1 - (double) time0; - if (sizeof tt < sizeof ld) - return (long_double) time1 - (long_double) time0; - } - if (time1 < time0) - return -difftime(time0, time1); - /* - ** As much as possible, avoid loss of precision - ** by computing the difference before converting to double. - */ - delta = time1 - time0; - if (delta >= 0) - return delta; - /* - ** Repair delta overflow. - */ - hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1); - /* - ** The following expression rounds twice, which means - ** the result may not be the closest to the true answer. - ** For example, suppose time_t is 64-bit signed int, - ** long_double is IEEE 754 double with default rounding, - ** time1 = 9223372036854775807 and time0 = -1536. - ** Then the true difference is 9223372036854777343, - ** which rounds to 9223372036854777856 - ** with a total error of 513. - ** But delta overflows to -9223372036854774273, - ** which rounds to -9223372036854774784, and correcting - ** this by subtracting 2 * (long_double) hibit - ** (i.e. by adding 2**64 = 18446744073709551616) - ** yields 9223372036854776832, which - ** rounds to 9223372036854775808 - ** with a total error of 1535 instead. - ** This problem occurs only with very large differences. - ** It's too painful to fix this portably. - ** We are not alone in this problem; - ** some C compilers round twice when converting - ** large unsigned types to small floating types, - ** so if time_t is unsigned the "return delta" above - ** has the same double-rounding problem with those compilers. - */ - return delta - 2 * (long_double) hibit; -} +#include "pgtz.h" +/* +** This file is in the public domain, so clarified as of +** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov). +*/ + +#ifndef lint +#ifndef NOID +static char elsieid[] = "@(#)difftime.c 7.9"; +#endif /* !defined NOID */ +#endif /* !defined lint */ + +/*LINTLIBRARY*/ + +#include "private.h" + +/* +** Algorithm courtesy Paul Eggert (eggert@twinsun.com). +*/ + +#ifdef HAVE_LONG_DOUBLE +#define long_double long double +#endif /* defined HAVE_LONG_DOUBLE */ +#ifndef HAVE_LONG_DOUBLE +#define long_double double +#endif /* !defined HAVE_LONG_DOUBLE */ + +double +difftime(time1, time0) +const time_t time1; +const time_t time0; +{ + time_t delta; + time_t hibit; + + { + time_t tt; + double d; + long_double ld; + + if (sizeof tt < sizeof d) + return (double) time1 - (double) time0; + if (sizeof tt < sizeof ld) + return (long_double) time1 - (long_double) time0; + } + if (time1 < time0) + return -difftime(time0, time1); + /* + ** As much as possible, avoid loss of precision + ** by computing the difference before converting to double. + */ + delta = time1 - time0; + if (delta >= 0) + return delta; + /* + ** Repair delta overflow. + */ + hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1); + /* + ** The following expression rounds twice, which means + ** the result may not be the closest to the true answer. + ** For example, suppose time_t is 64-bit signed int, + ** long_double is IEEE 754 double with default rounding, + ** time1 = 9223372036854775807 and time0 = -1536. + ** Then the true difference is 9223372036854777343, + ** which rounds to 9223372036854777856 + ** with a total error of 513. + ** But delta overflows to -9223372036854774273, + ** which rounds to -9223372036854774784, and correcting + ** this by subtracting 2 * (long_double) hibit + ** (i.e. by adding 2**64 = 18446744073709551616) + ** yields 9223372036854776832, which + ** rounds to 9223372036854775808 + ** with a total error of 1535 instead. + ** This problem occurs only with very large differences. + ** It's too painful to fix this portably. + ** We are not alone in this problem; + ** some C compilers round twice when converting + ** large unsigned types to small floating types, + ** so if time_t is unsigned the "return delta" above + ** has the same double-rounding problem with those compilers. + */ + return delta - 2 * (long_double) hibit; +} diff --git a/src/timezone/ialloc.c b/src/timezone/ialloc.c index ea457aeb66..8a0c701578 100644 --- a/src/timezone/ialloc.c +++ b/src/timezone/ialloc.c @@ -1,81 +1,81 @@ -#ifndef lint -#ifndef NOID -static char elsieid[] = "@(#)ialloc.c 8.29"; -#endif /* !defined NOID */ -#endif /* !defined lint */ - -/*LINTLIBRARY*/ - -#include "private.h" - -#define nonzero(n) (((n) == 0) ? 1 : (n)) - -char * -imalloc(n) -const int n; -{ - return malloc((size_t) nonzero(n)); -} - -char * -icalloc(nelem, elsize) -int nelem; -int elsize; -{ - if (nelem == 0 || elsize == 0) - nelem = elsize = 1; - return calloc((size_t) nelem, (size_t) elsize); -} - -void * -irealloc(pointer, size) -void * const pointer; -const int size; -{ - if (pointer == NULL) - return imalloc(size); - return realloc((void *) pointer, (size_t) nonzero(size)); -} - -char * -icatalloc(old, new) -char * const old; -const char * const new; -{ - register char * result; - register int oldsize, newsize; - - newsize = (new == NULL) ? 0 : strlen(new); - if (old == NULL) - oldsize = 0; - else if (newsize == 0) - return old; - else oldsize = strlen(old); - if ((result = irealloc(old, oldsize + newsize + 1)) != NULL) - if (new != NULL) - (void) strcpy(result + oldsize, new); - return result; -} - -char * -icpyalloc(string) -const char * const string; -{ - return icatalloc((char *) NULL, string); -} - -void -ifree(p) -char * const p; -{ - if (p != NULL) - (void) free(p); -} - -void -icfree(p) -char * const p; -{ - if (p != NULL) - (void) free(p); -} +#ifndef lint +#ifndef NOID +static char elsieid[] = "@(#)ialloc.c 8.29"; +#endif /* !defined NOID */ +#endif /* !defined lint */ + +/*LINTLIBRARY*/ + +#include "private.h" + +#define nonzero(n) (((n) == 0) ? 1 : (n)) + +char * +imalloc(n) +const int n; +{ + return malloc((size_t) nonzero(n)); +} + +char * +icalloc(nelem, elsize) +int nelem; +int elsize; +{ + if (nelem == 0 || elsize == 0) + nelem = elsize = 1; + return calloc((size_t) nelem, (size_t) elsize); +} + +void * +irealloc(pointer, size) +void * const pointer; +const int size; +{ + if (pointer == NULL) + return imalloc(size); + return realloc((void *) pointer, (size_t) nonzero(size)); +} + +char * +icatalloc(old, new) +char * const old; +const char * const new; +{ + register char * result; + register int oldsize, newsize; + + newsize = (new == NULL) ? 0 : strlen(new); + if (old == NULL) + oldsize = 0; + else if (newsize == 0) + return old; + else oldsize = strlen(old); + if ((result = irealloc(old, oldsize + newsize + 1)) != NULL) + if (new != NULL) + (void) strcpy(result + oldsize, new); + return result; +} + +char * +icpyalloc(string) +const char * const string; +{ + return icatalloc((char *) NULL, string); +} + +void +ifree(p) +char * const p; +{ + if (p != NULL) + (void) free(p); +} + +void +icfree(p) +char * const p; +{ + if (p != NULL) + (void) free(p); +} diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 4f389b52de..6936cd674b 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -1,1712 +1,1712 @@ -#include "pgtz.h" -#undef open -#define timezone pg_timezone -#define USG_COMPAT -extern time_t pg_timezone; -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). -*/ - -#ifndef lint -#ifndef NOID -static char elsieid[] = "@(#)localtime.c 7.78"; -#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). -*/ - -/*LINTLIBRARY*/ - -#include "private.h" -#include "tzfile.h" -#include "fcntl.h" - -/* -** SunOS 4.1.1 headers lack O_BINARY. -*/ - -#ifdef O_BINARY -#define OPEN_MODE (O_RDONLY | O_BINARY) -#endif /* defined O_BINARY */ -#ifndef O_BINARY -#define OPEN_MODE O_RDONLY -#endif /* !defined O_BINARY */ - -#ifndef WILDABBR -/* -** Someone might make incorrect use of a time zone abbreviation: -** 1. They might reference tzname[0] before calling tzset (explicitly -** or implicitly). -** 2. They might reference tzname[1] before calling 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). -*/ -#define WILDABBR " " -#endif /* !defined WILDABBR */ - -static char wildabbr[] = "WILDABBR"; - -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 */ -}; - -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)) - -#ifdef TZNAME_MAX -#define MY_TZNAME_MAX TZNAME_MAX -#endif /* defined TZNAME_MAX */ -#ifndef TZNAME_MAX -#define MY_TZNAME_MAX 255 -#endif /* !defined TZNAME_MAX */ - -struct state { - int leapcnt; - int timecnt; - int typecnt; - int charcnt; - 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 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 */ - -/* -** 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 */ - -#ifndef TZ_STRLEN_MAX -#define TZ_STRLEN_MAX 255 -#endif /* !defined TZ_STRLEN_MAX */ - -static char lcl_TZname[TZ_STRLEN_MAX + 1]; -static int lcl_is_set; -static int gmt_is_set; - -char * tzname[2] = { - wildabbr, - wildabbr -}; - -/* -** 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. -*/ - -static struct tm tm; - -#ifdef USG_COMPAT -time_t timezone = 0; -int daylight = 0; -#endif /* defined USG_COMPAT */ - -#ifdef ALTZONE -time_t altzone = 0; -#endif /* defined ALTZONE */ - -static long -detzcode(codep) -const char * const codep; -{ - register long result; - register int i; - - result = (codep[0] & 0x80) ? ~0L : 0L; - for (i = 0; i < 4; ++i) - result = (result << 8) | (codep[i] & 0xff); - return result; -} - -static void -settzname P((void)) -{ - register struct state * const sp = lclptr; - register int i; - - tzname[0] = wildabbr; - tzname[1] = wildabbr; -#ifdef USG_COMPAT - daylight = 0; - timezone = 0; -#endif /* defined USG_COMPAT */ -#ifdef ALTZONE - altzone = 0; -#endif /* defined ALTZONE */ -#ifdef ALL_STATE - if (sp == NULL) { - tzname[0] = tzname[1] = gmt; - return; - } -#endif /* defined ALL_STATE */ - for (i = 0; i < sp->typecnt; ++i) { - register 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) - timezone = -(ttisp->tt_gmtoff); -#endif /* defined USG_COMPAT */ -#ifdef ALTZONE - if (i == 0 || ttisp->tt_isdst) - altzone = -(ttisp->tt_gmtoff); -#endif /* defined ALTZONE */ - } - /* - ** 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]]; - - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; - } -} - -static int -tzload(name, sp) -register const char * name; -register struct state * const sp; -{ - register const char * p; - register int i; - register int fid; - - if (name == NULL && (name = TZDEFAULT) == NULL) - return -1; - { - register int doaccess; - /* - ** Section 4.9.1 of the C standard says that - ** "FILENAME_MAX expands to an integral constant expression - ** that is the size needed for an array of char large enough - ** to hold the longest file name string that the implementation - ** guarantees can be opened." - */ - char fullname[FILENAME_MAX + 1]; - - if (name[0] == ':') - ++name; - doaccess = name[0] == '/'; - if (!doaccess) { - if ((p = TZDIR) == NULL) - return -1; - if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) - return -1; - (void) strcpy(fullname, p); - (void) strcat(fullname, "/"); - (void) strcat(fullname, name); - /* - ** Set doaccess if '.' (as in "../") shows up in name. - */ - if (strchr(name, '.') != NULL) - doaccess = TRUE; - name = fullname; - } - if (doaccess && access(name, R_OK) != 0) - return -1; - if ((fid = open(name, OPEN_MODE)) == -1) - return -1; - } - { - struct tzhead * tzhp; - union { - struct tzhead tzhead; - char buf[sizeof *sp + sizeof *tzhp]; - } u; - int ttisstdcnt; - int ttisgmtcnt; - - i = read(fid, u.buf, sizeof u.buf); - if (close(fid) != 0) - return -1; - ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); - ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); - sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); - 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 || - 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 */ - sp->timecnt + /* types */ - sp->typecnt * (4 + 2) + /* ttinfos */ - sp->charcnt + /* chars */ - sp->leapcnt * (4 + 4) + /* lsinfos */ - ttisstdcnt + /* ttisstds */ - ttisgmtcnt) /* ttisgmts */ - return -1; - for (i = 0; i < sp->timecnt; ++i) { - sp->ats[i] = detzcode(p); - p += 4; - } - for (i = 0; i < sp->timecnt; ++i) { - sp->types[i] = (unsigned char) *p++; - if (sp->types[i] >= sp->typecnt) - return -1; - } - for (i = 0; i < sp->typecnt; ++i) { - register 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; - ttisp->tt_abbrind = (unsigned char) *p++; - if (ttisp->tt_abbrind < 0 || - ttisp->tt_abbrind > sp->charcnt) - return -1; - } - 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; - - lsisp = &sp->lsis[i]; - lsisp->ls_trans = detzcode(p); - p += 4; - lsisp->ls_corr = detzcode(p); - p += 4; - } - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisstdcnt == 0) - ttisp->tt_ttisstd = FALSE; - else { - ttisp->tt_ttisstd = *p++; - if (ttisp->tt_ttisstd != TRUE && - ttisp->tt_ttisstd != FALSE) - return -1; - } - } - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisgmtcnt == 0) - ttisp->tt_ttisgmt = FALSE; - else { - ttisp->tt_ttisgmt = *p++; - if (ttisp->tt_ttisgmt != TRUE && - ttisp->tt_ttisgmt != FALSE) - return -1; - } - } - } - return 0; -} - -static const int mon_lengths[2][MONSPERYEAR] = { - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } -}; - -static const int year_lengths[2] = { - DAYSPERNYEAR, DAYSPERLYEAR -}; - -/* -** 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 * -getzname(strp) -register const char * strp; -{ - register char c; - - while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && - c != '+') - ++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. -** Otherwise, return a pointer to the first character not part of the number. -*/ - -static const char * -getnum(strp, nump, min, max) -register const char * strp; -int * const nump; -const int min; -const int max; -{ - register char c; - register int num; - - if (strp == NULL || !is_digit(c = *strp)) - return NULL; - num = 0; - do { - num = num * 10 + (c - '0'); - if (num > max) - return NULL; /* illegal value */ - c = *++strp; - } while (is_digit(c)); - if (num < min) - return NULL; /* illegal value */ - *nump = num; - return strp; -} - -/* -** 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(strp, secsp) -register const char * strp; -long * const secsp; -{ - int num; - - /* - ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like - ** "M10.4.6/26", which does not conform to Posix, - ** but which specifies the equivalent of - ** ``02:00 on the first Sunday on or after 23 Oct''. - */ - strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); - if (strp == NULL) - return NULL; - *secsp = num * (long) SECSPERHOUR; - if (*strp == ':') { - ++strp; - strp = getnum(strp, &num, 0, MINSPERHOUR - 1); - if (strp == NULL) - return NULL; - *secsp += num * SECSPERMIN; - if (*strp == ':') { - ++strp; - /* `SECSPERMIN' allows for leap seconds. */ - strp = getnum(strp, &num, 0, SECSPERMIN); - if (strp == NULL) - return NULL; - *secsp += num; - } - } - return strp; -} - -/* -** 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(strp, offsetp) -register const char * strp; -long * const offsetp; -{ - register int neg = 0; - - if (*strp == '-') { - neg = 1; - ++strp; - } else if (*strp == '+') - ++strp; - strp = getsecs(strp, offsetp); - if (strp == NULL) - return NULL; /* illegal time */ - if (neg) - *offsetp = -*offsetp; - return strp; -} - -/* -** 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(strp, rulep) -const char * strp; -register struct rule * const rulep; -{ - if (*strp == 'J') { - /* - ** Julian day. - */ - rulep->r_type = JULIAN_DAY; - ++strp; - strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); - } else if (*strp == 'M') { - /* - ** Month, week, day. - */ - rulep->r_type = MONTH_NTH_DAY_OF_WEEK; - ++strp; - strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); - if (strp == NULL) - return NULL; - if (*strp++ != '.') - return NULL; - strp = getnum(strp, &rulep->r_week, 1, 5); - if (strp == NULL) - return NULL; - if (*strp++ != '.') - return NULL; - strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); - } else if (is_digit(*strp)) { - /* - ** Day of year. - */ - rulep->r_type = DAY_OF_YEAR; - strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); - } else return NULL; /* invalid format */ - if (strp == NULL) - return NULL; - if (*strp == '/') { - /* - ** Time specified. - */ - ++strp; - strp = getsecs(strp, &rulep->r_time); - } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ - return strp; -} - -/* -** 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; -{ - register int leapyear; - register time_t value; - register int i; - int d, m1, yy0, yy1, yy2, dow; - - INITIALIZE(value); - leapyear = isleap(year); - switch (rulep->r_type) { - - case JULIAN_DAY: - /* - ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap - ** years. - ** In non-leap years, or if the day number is 59 or less, just - ** 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; - if (leapyear && rulep->r_day >= 60) - value += SECSPERDAY; - break; - - case DAY_OF_YEAR: - /* - ** n - day of year. - ** Just add SECSPERDAY times the day number to the time of - ** January 1, midnight, to get the day. - */ - value = janfirst + rulep->r_day * SECSPERDAY; - break; - - case MONTH_NTH_DAY_OF_WEEK: - /* - ** Mm.n.d - nth "dth day" of month m. - */ - value = janfirst; - for (i = 0; i < rulep->r_mon - 1; ++i) - value += mon_lengths[leapyear][i] * SECSPERDAY; - - /* - ** Use Zeller's Congruence to get day-of-week of first day of - ** month. - */ - m1 = (rulep->r_mon + 9) % 12 + 1; - yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; - yy1 = yy0 / 100; - yy2 = yy0 % 100; - dow = ((26 * m1 - 2) / 10 + - 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; - if (dow < 0) - dow += DAYSPERWEEK; - - /* - ** "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. - */ - d = rulep->r_day - dow; - if (d < 0) - d += DAYSPERWEEK; - for (i = 1; i < rulep->r_week; ++i) { - if (d + DAYSPERWEEK >= - mon_lengths[leapyear][rulep->r_mon - 1]) - break; - d += DAYSPERWEEK; - } - - /* - ** "d" is the day-of-month (zero-origin) of the day we want. - */ - value += 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 - ** time on that day, add the transition time and the current offset - ** from UTC. - */ - return value + rulep->r_time + offset; -} - -/* -** 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; -{ - const char * stdname; - const char * dstname; - size_t stdlen; - size_t dstlen; - long stdoffset; - long dstoffset; - register time_t * atp; - register unsigned char * typep; - register char * cp; - register int load_result; - - INITIALIZE(dstname); - stdname = name; - 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 { - name = getzname(name); - stdlen = name - stdname; - if (stdlen < 3) - return -1; - if (*name == '\0') - return -1; - name = getoffset(name, &stdoffset); - if (name == NULL) - return -1; - } - load_result = tzload(TZDEFRULES, sp); - 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 != '\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; - time_t starttime; - time_t endtime; - - ++name; - if ((name = getrule(name, &start)) == NULL) - return -1; - if (*name++ != ',') - return -1; - if ((name = getrule(name, &end)) == NULL) - return -1; - if (*name != '\0') - return -1; - sp->typecnt = 2; /* standard time and DST */ - /* - ** Two transitions per year, from EPOCH_YEAR to 2037. - */ - 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; - 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) { - starttime = transtime(janfirst, year, &start, - stdoffset); - endtime = transtime(janfirst, year, &end, - dstoffset); - if (starttime > endtime) { - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - } else { - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - } - janfirst += year_lengths[isleap(year)] * - SECSPERDAY; - } - } else { - register long theirstdoffset; - register long theirdstoffset; - register long theiroffset; - register int isdst; - register int i; - register int j; - - if (*name != '\0') - return -1; - /* - ** Initial values of theirstdoffset and theirdstoffset. - */ - theirstdoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (!sp->ttis[j].tt_isdst) { - theirstdoffset = - -sp->ttis[j].tt_gmtoff; - break; - } - } - theirdstoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (sp->ttis[j].tt_isdst) { - theirdstoffset = - -sp->ttis[j].tt_gmtoff; - break; - } - } - /* - ** Initially we're assumed to be in standard time. - */ - isdst = FALSE; - theiroffset = theirstdoffset; - /* - ** Now juggle transition times and types - ** tracking offsets as you do. - */ - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - sp->types[i] = sp->ttis[j].tt_isdst; - if (sp->ttis[j].tt_ttisgmt) { - /* No adjustment to transition time */ - } else { - /* - ** If summer time is in effect, and the - ** transition time was not specified as - ** standard time, add the summer time - ** offset to the transition time; - ** otherwise, add the standard time - ** offset to the transition time. - */ - /* - ** Transitions from DST to DDST - ** will effectively disappear since - ** POSIX provides for only one DST - ** offset. - */ - if (isdst && !sp->ttis[j].tt_ttisstd) { - sp->ats[i] += dstoffset - - theirdstoffset; - } else { - sp->ats[i] += stdoffset - - theirstdoffset; - } - } - theiroffset = -sp->ttis[j].tt_gmtoff; - if (sp->ttis[j].tt_isdst) - theirdstoffset = theiroffset; - else theirstdoffset = theiroffset; - } - /* - ** Finally, fill in ttis. - ** ttisstd and ttisgmt need not be handled. - */ - sp->ttis[0].tt_gmtoff = -stdoffset; - 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->typecnt = 2; - } - } else { - dstlen = 0; - sp->typecnt = 1; /* only standard time */ - sp->timecnt = 0; - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = 0; - sp->ttis[0].tt_abbrind = 0; - } - sp->charcnt = stdlen + 1; - if (dstlen != 0) - sp->charcnt += dstlen + 1; - if ((size_t) sp->charcnt > sizeof sp->chars) - return -1; - cp = sp->chars; - (void) strncpy(cp, stdname, stdlen); - cp += stdlen; - *cp++ = '\0'; - if (dstlen != 0) { - (void) strncpy(cp, dstname, dstlen); - *(cp + dstlen) = '\0'; - } - return 0; -} - -static void -gmtload(sp) -struct state * const sp; -{ - if (tzload(gmt, sp) != 0) - (void) tzparse(gmt, sp, TRUE); -} - -#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 -tzsetwall P((void)) -{ - if (lcl_is_set < 0) - return; - lcl_is_set = -1; - -#ifdef ALL_STATE - if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); - if (lclptr == NULL) { - settzname(); /* all we can do */ - return; - } - } -#endif /* defined ALL_STATE */ - if (tzload((char *) NULL, lclptr) != 0) - gmtload(lclptr); - settzname(); -} - -void -tzset P((void)) -{ - register const char * name; - - name = getenv("TZ"); - if (name == NULL) { - tzsetwall(); - return; - } - - if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) - return; - lcl_is_set = strlen(name) < sizeof lcl_TZname; - if (lcl_is_set) - (void) strcpy(lcl_TZname, name); - -#ifdef ALL_STATE - if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); - if (lclptr == NULL) { - settzname(); /* all we can do */ - 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; - (void) strcpy(lclptr->chars, gmt); - } else if (tzload(name, lclptr) != 0) - if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) - (void) gmtload(lclptr); - settzname(); -} - -/* -** 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-- -** but it *is* desirable.) -** -** The unused offset argument is for the benefit of mktime variants. -*/ - -/*ARGSUSED*/ -static void -localsub(timep, offset, tmp) -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 time_t t = *timep; - - sp = lclptr; -#ifdef ALL_STATE - if (sp == NULL) { - gmtsub(timep, offset, tmp); - return; - } -#endif /* defined ALL_STATE */ - if (sp->timecnt == 0 || t < sp->ats[0]) { - i = 0; - while (sp->ttis[i].tt_isdst) - if (++i >= sp->typecnt) { - i = 0; - break; - } - } else { - for (i = 1; i < sp->timecnt; ++i) - if (t < sp->ats[i]) - break; - i = sp->types[i - 1]; - } - ttisp = &sp->ttis[i]; - /* - ** To get (wrong) behavior that's compatible with System V Release 2.0 - ** you'd replace the statement below with - ** t += ttisp->tt_gmtoff; - ** timesub(&t, 0L, sp, tmp); - */ - 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 */ -} - -struct tm * -localtime(timep) -const time_t * const timep; -{ - tzset(); - localsub(timep, 0L, &tm); - return &tm; -} - -/* -** Re-entrant version of localtime. -*/ - -struct tm * -localtime_r(timep, tm) -const time_t * const timep; -struct tm * tm; -{ - localsub(timep, 0L, tm); - return tm; -} - -/* -** gmtsub is to gmtime as localsub is to localtime. -*/ - -static void -gmtsub(timep, offset, tmp) -const time_t * const timep; -const long offset; -struct tm * const tmp; -{ - if (!gmt_is_set) { - gmt_is_set = TRUE; -#ifdef ALL_STATE - gmtptr = (struct state *) malloc(sizeof *gmtptr); - if (gmtptr != NULL) -#endif /* defined ALL_STATE */ - gmtload(gmtptr); - } - timesub(timep, offset, gmtptr, tmp); -#ifdef TM_ZONE - /* - ** Could get fancy here and deliver something such as - ** "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 { -#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 */ -} - -struct tm * -gmtime(timep) -const time_t * const timep; -{ - gmtsub(timep, 0L, &tm); - return &tm; -} - -/* -* Re-entrant version of gmtime. -*/ - -struct tm * -gmtime_r(timep, tm) -const time_t * const timep; -struct tm * tm; -{ - gmtsub(timep, 0L, tm); - return tm; -} - -#ifdef STD_INSPIRED - -struct tm * -offtime(timep, offset) -const time_t * const timep; -const long offset; -{ - gmtsub(timep, offset, &tm); - return &tm; -} - -#endif /* defined STD_INSPIRED */ - -static void -timesub(timep, offset, sp, tmp) -const time_t * const timep; -const long offset; -register const struct state * const sp; -register struct tm * const tmp; -{ - 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; - - 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) { - if (*timep == lp->ls_trans) { - hit = ((i == 0 && lp->ls_corr > 0) || - lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && - sp->lsis[i].ls_trans == - sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == - sp->lsis[i - 1].ls_corr + 1) { - ++hit; - --i; - } - } - corr = lp->ls_corr; - 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; - } -#endif /* defined mc68k */ - rem += (offset - corr); - while (rem < 0) { - rem += SECSPERDAY; - --days; - } - while (rem >= SECSPERDAY) { - rem -= SECSPERDAY; - ++days; - } - tmp->tm_hour = (int) (rem / SECSPERHOUR); - rem = rem % SECSPERHOUR; - tmp->tm_min = (int) (rem / SECSPERMIN); - /* - ** A positive leap second requires a special - ** 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); - tmp->tm_isdst = 0; -#ifdef TM_GMTOFF - tmp->TM_GMTOFF = offset; -#endif /* defined TM_GMTOFF */ -} - -char * -ctime(timep) -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 -** asctime(localtime(timer)) -*/ - return asctime(localtime(timep)); -} - -char * -ctime_r(timep, buf) -const time_t * const timep; -char * buf; -{ - struct tm tm; - - return asctime_r(localtime_r(timep, &tm), 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 -** 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). -*/ - -static int -increment_overflow(number, delta) -int * number; -int delta; -{ - int number0; - - number0 = *number; - *number += delta; - return (*number < number0) != (delta < 0); -} - -static int -normalize_overflow(tensptr, unitsptr, base) -int * const tensptr; -int * const unitsptr; -const int base; -{ - register int tensdelta; - - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow(tensptr, tensdelta); -} - -static int -tmcomp(atmp, btmp) -register const struct tm * const atmp; -register const struct tm * const btmp; -{ - register 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; - return result; -} - -static time_t -time2sub(tmp, funcp, offset, okayp, do_norm_secs) -struct tm * const tmp; -void (* const funcp) P((const time_t*, long, struct tm*)); -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; - time_t newt; - time_t t; - struct 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)) - return WRONG; - /* - ** Turn yourtm.tm_year 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; - 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)]; - } - 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; - } - for ( ; ; ) { - i = mon_lengths[isleap(yourtm.tm_year)][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 (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) - return WRONG; - if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) - saved_seconds = 0; - else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { - /* - ** We can't set tm_sec to 0, because that might push the - ** time below the minimum representable time. - ** Set tm_sec to 59 instead. - ** This assumes that the minimum representable time is - ** not in the same minute that a leap second was deleted from, - ** which is a safer assumption than using 58 would be. - */ - if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) - return WRONG; - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = SECSPERMIN - 1; - } else { - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = 0; - } - /* - ** Divide the search space in half - ** (this works whether time_t is signed or unsigned). - */ - 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); - for ( ; ; ) { - (*funcp)(&t, offset, &mytm); - 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; - continue; - } - if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) - break; - /* - ** Right time, wrong type. - ** Hunt for right time, right type. - ** 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 */ - 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); - if (tmcomp(&mytm, &yourtm) != 0) - continue; - if (mytm.tm_isdst != yourtm.tm_isdst) - continue; - /* - ** We have a match. - */ - t = newt; - goto label; - } - } - return WRONG; - } -label: - newt = t + saved_seconds; - if ((newt < t) != (saved_seconds < 0)) - return WRONG; - t = newt; - (*funcp)(&t, offset, tmp); - *okayp = TRUE; - return t; -} - -static time_t -time2(tmp, funcp, offset, okayp) -struct tm * const tmp; -void (* const funcp) P((const time_t*, long, struct tm*)); -const long offset; -int * const okayp; -{ - time_t t; - - /* - ** 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); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); -} - -static time_t -time1(tmp, funcp, offset) -struct tm * const tmp; -void (* const funcp) P((const time_t *, long, struct tm *)); -const long offset; -{ - register time_t t; - register const struct state * sp; - register int samei, otheri; - register int sameind, otherind; - register int i; - register 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); -#ifdef PCTS - /* - ** PCTS code courtesy Grant Sullivan (grant@osf.org). - */ - if (okay) - return t; - if (tmp->tm_isdst < 0) - tmp->tm_isdst = 0; /* reset to std and try again */ -#endif /* defined PCTS */ -#ifndef PCTS - if (okay || tmp->tm_isdst < 0) - return t; -#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. - ** 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) - return WRONG; -#endif /* defined ALL_STATE */ - 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 (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); - if (okay) - return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - } - } - return WRONG; -} - -time_t -mktime(tmp) -struct tm * const tmp; -{ - tzset(); - return time1(tmp, localsub, 0L); -} - -#ifdef STD_INSPIRED - -time_t -timelocal(tmp) -struct tm * const tmp; -{ - tmp->tm_isdst = -1; /* in case it wasn't initialized */ - return mktime(tmp); -} - -time_t -timegm(tmp) -struct tm * const tmp; -{ - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, 0L); -} - -time_t -timeoff(tmp, offset) -struct tm * const tmp; -const long offset; -{ - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, offset); -} - -#endif /* defined STD_INSPIRED */ - -#ifdef CMUCS - -/* -** The following is supplied for compatibility with -** previous versions of the CMUCS runtime library. -*/ - -long -gtime(tmp) -struct tm * const tmp; -{ - const time_t t = mktime(tmp); - - if (t == WRONG) - return -1; - return t; -} - -#endif /* defined CMUCS */ - -/* -** XXX--is the below the right way to conditionalize?? -*/ - -#ifdef STD_INSPIRED - -/* -** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 -** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which -** is not the case if we are accounting for leap seconds. -** So, we provide the following conversion routines for use -** when exchanging timestamps with POSIX conforming systems. -*/ - -static long -leapcorr(timep) -time_t * timep; -{ - register struct state * sp; - register struct lsinfo * lp; - register int i; - - sp = lclptr; - i = sp->leapcnt; - while (--i >= 0) { - lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) - return lp->ls_corr; - } - return 0; -} - -time_t -time2posix(t) -time_t t; -{ - tzset(); - return t - leapcorr(&t); -} - -time_t -posix2time(t) -time_t t; -{ - time_t x; - time_t y; - - tzset(); - /* - ** For a positive leap second hit, the result - ** 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); - if (y < t) { - do { - x++; - y = x - leapcorr(&x); - } while (y < t); - if (t != y) - return x - 1; - } else if (y > t) { - do { - --x; - y = x - leapcorr(&x); - } while (y > t); - if (t != y) - return x + 1; - } - return x; -} - -#endif /* defined STD_INSPIRED */ +#include "pgtz.h" +#undef open +#define timezone pg_timezone +#define USG_COMPAT +extern time_t pg_timezone; +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +*/ + +#ifndef lint +#ifndef NOID +static char elsieid[] = "@(#)localtime.c 7.78"; +#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). +*/ + +/*LINTLIBRARY*/ + +#include "private.h" +#include "tzfile.h" +#include "fcntl.h" + +/* +** SunOS 4.1.1 headers lack O_BINARY. +*/ + +#ifdef O_BINARY +#define OPEN_MODE (O_RDONLY | O_BINARY) +#endif /* defined O_BINARY */ +#ifndef O_BINARY +#define OPEN_MODE O_RDONLY +#endif /* !defined O_BINARY */ + +#ifndef WILDABBR +/* +** Someone might make incorrect use of a time zone abbreviation: +** 1. They might reference tzname[0] before calling tzset (explicitly +** or implicitly). +** 2. They might reference tzname[1] before calling 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). +*/ +#define WILDABBR " " +#endif /* !defined WILDABBR */ + +static char wildabbr[] = "WILDABBR"; + +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 */ +}; + +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)) + +#ifdef TZNAME_MAX +#define MY_TZNAME_MAX TZNAME_MAX +#endif /* defined TZNAME_MAX */ +#ifndef TZNAME_MAX +#define MY_TZNAME_MAX 255 +#endif /* !defined TZNAME_MAX */ + +struct state { + int leapcnt; + int timecnt; + int typecnt; + int charcnt; + 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 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 */ + +/* +** 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 */ + +#ifndef TZ_STRLEN_MAX +#define TZ_STRLEN_MAX 255 +#endif /* !defined TZ_STRLEN_MAX */ + +static char lcl_TZname[TZ_STRLEN_MAX + 1]; +static int lcl_is_set; +static int gmt_is_set; + +char * tzname[2] = { + wildabbr, + wildabbr +}; + +/* +** 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. +*/ + +static struct tm tm; + +#ifdef USG_COMPAT +time_t timezone = 0; +int daylight = 0; +#endif /* defined USG_COMPAT */ + +#ifdef ALTZONE +time_t altzone = 0; +#endif /* defined ALTZONE */ + +static long +detzcode(codep) +const char * const codep; +{ + register long result; + register int i; + + result = (codep[0] & 0x80) ? ~0L : 0L; + for (i = 0; i < 4; ++i) + result = (result << 8) | (codep[i] & 0xff); + return result; +} + +static void +settzname P((void)) +{ + register struct state * const sp = lclptr; + register int i; + + tzname[0] = wildabbr; + tzname[1] = wildabbr; +#ifdef USG_COMPAT + daylight = 0; + timezone = 0; +#endif /* defined USG_COMPAT */ +#ifdef ALTZONE + altzone = 0; +#endif /* defined ALTZONE */ +#ifdef ALL_STATE + if (sp == NULL) { + tzname[0] = tzname[1] = gmt; + return; + } +#endif /* defined ALL_STATE */ + for (i = 0; i < sp->typecnt; ++i) { + register 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) + timezone = -(ttisp->tt_gmtoff); +#endif /* defined USG_COMPAT */ +#ifdef ALTZONE + if (i == 0 || ttisp->tt_isdst) + altzone = -(ttisp->tt_gmtoff); +#endif /* defined ALTZONE */ + } + /* + ** 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]]; + + tzname[ttisp->tt_isdst] = + &sp->chars[ttisp->tt_abbrind]; + } +} + +static int +tzload(name, sp) +register const char * name; +register struct state * const sp; +{ + register const char * p; + register int i; + register int fid; + + if (name == NULL && (name = TZDEFAULT) == NULL) + return -1; + { + register int doaccess; + /* + ** Section 4.9.1 of the C standard says that + ** "FILENAME_MAX expands to an integral constant expression + ** that is the size needed for an array of char large enough + ** to hold the longest file name string that the implementation + ** guarantees can be opened." + */ + char fullname[FILENAME_MAX + 1]; + + if (name[0] == ':') + ++name; + doaccess = name[0] == '/'; + if (!doaccess) { + if ((p = TZDIR) == NULL) + return -1; + if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) + return -1; + (void) strcpy(fullname, p); + (void) strcat(fullname, "/"); + (void) strcat(fullname, name); + /* + ** Set doaccess if '.' (as in "../") shows up in name. + */ + if (strchr(name, '.') != NULL) + doaccess = TRUE; + name = fullname; + } + if (doaccess && access(name, R_OK) != 0) + return -1; + if ((fid = open(name, OPEN_MODE)) == -1) + return -1; + } + { + struct tzhead * tzhp; + union { + struct tzhead tzhead; + char buf[sizeof *sp + sizeof *tzhp]; + } u; + int ttisstdcnt; + int ttisgmtcnt; + + i = read(fid, u.buf, sizeof u.buf); + if (close(fid) != 0) + return -1; + ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); + ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); + sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); + 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 || + 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 */ + sp->timecnt + /* types */ + sp->typecnt * (4 + 2) + /* ttinfos */ + sp->charcnt + /* chars */ + sp->leapcnt * (4 + 4) + /* lsinfos */ + ttisstdcnt + /* ttisstds */ + ttisgmtcnt) /* ttisgmts */ + return -1; + for (i = 0; i < sp->timecnt; ++i) { + sp->ats[i] = detzcode(p); + p += 4; + } + for (i = 0; i < sp->timecnt; ++i) { + sp->types[i] = (unsigned char) *p++; + if (sp->types[i] >= sp->typecnt) + return -1; + } + for (i = 0; i < sp->typecnt; ++i) { + register 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; + ttisp->tt_abbrind = (unsigned char) *p++; + if (ttisp->tt_abbrind < 0 || + ttisp->tt_abbrind > sp->charcnt) + return -1; + } + 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; + + lsisp = &sp->lsis[i]; + lsisp->ls_trans = detzcode(p); + p += 4; + lsisp->ls_corr = detzcode(p); + p += 4; + } + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + + ttisp = &sp->ttis[i]; + if (ttisstdcnt == 0) + ttisp->tt_ttisstd = FALSE; + else { + ttisp->tt_ttisstd = *p++; + if (ttisp->tt_ttisstd != TRUE && + ttisp->tt_ttisstd != FALSE) + return -1; + } + } + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + + ttisp = &sp->ttis[i]; + if (ttisgmtcnt == 0) + ttisp->tt_ttisgmt = FALSE; + else { + ttisp->tt_ttisgmt = *p++; + if (ttisp->tt_ttisgmt != TRUE && + ttisp->tt_ttisgmt != FALSE) + return -1; + } + } + } + return 0; +} + +static const int mon_lengths[2][MONSPERYEAR] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; + +static const int year_lengths[2] = { + DAYSPERNYEAR, DAYSPERLYEAR +}; + +/* +** 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 * +getzname(strp) +register const char * strp; +{ + register char c; + + while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && + c != '+') + ++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. +** Otherwise, return a pointer to the first character not part of the number. +*/ + +static const char * +getnum(strp, nump, min, max) +register const char * strp; +int * const nump; +const int min; +const int max; +{ + register char c; + register int num; + + if (strp == NULL || !is_digit(c = *strp)) + return NULL; + num = 0; + do { + num = num * 10 + (c - '0'); + if (num > max) + return NULL; /* illegal value */ + c = *++strp; + } while (is_digit(c)); + if (num < min) + return NULL; /* illegal value */ + *nump = num; + return strp; +} + +/* +** 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(strp, secsp) +register const char * strp; +long * const secsp; +{ + int num; + + /* + ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like + ** "M10.4.6/26", which does not conform to Posix, + ** but which specifies the equivalent of + ** ``02:00 on the first Sunday on or after 23 Oct''. + */ + strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); + if (strp == NULL) + return NULL; + *secsp = num * (long) SECSPERHOUR; + if (*strp == ':') { + ++strp; + strp = getnum(strp, &num, 0, MINSPERHOUR - 1); + if (strp == NULL) + return NULL; + *secsp += num * SECSPERMIN; + if (*strp == ':') { + ++strp; + /* `SECSPERMIN' allows for leap seconds. */ + strp = getnum(strp, &num, 0, SECSPERMIN); + if (strp == NULL) + return NULL; + *secsp += num; + } + } + return strp; +} + +/* +** 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(strp, offsetp) +register const char * strp; +long * const offsetp; +{ + register int neg = 0; + + if (*strp == '-') { + neg = 1; + ++strp; + } else if (*strp == '+') + ++strp; + strp = getsecs(strp, offsetp); + if (strp == NULL) + return NULL; /* illegal time */ + if (neg) + *offsetp = -*offsetp; + return strp; +} + +/* +** 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(strp, rulep) +const char * strp; +register struct rule * const rulep; +{ + if (*strp == 'J') { + /* + ** Julian day. + */ + rulep->r_type = JULIAN_DAY; + ++strp; + strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); + } else if (*strp == 'M') { + /* + ** Month, week, day. + */ + rulep->r_type = MONTH_NTH_DAY_OF_WEEK; + ++strp; + strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); + if (strp == NULL) + return NULL; + if (*strp++ != '.') + return NULL; + strp = getnum(strp, &rulep->r_week, 1, 5); + if (strp == NULL) + return NULL; + if (*strp++ != '.') + return NULL; + strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); + } else if (is_digit(*strp)) { + /* + ** Day of year. + */ + rulep->r_type = DAY_OF_YEAR; + strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); + } else return NULL; /* invalid format */ + if (strp == NULL) + return NULL; + if (*strp == '/') { + /* + ** Time specified. + */ + ++strp; + strp = getsecs(strp, &rulep->r_time); + } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ + return strp; +} + +/* +** 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; +{ + register int leapyear; + register time_t value; + register int i; + int d, m1, yy0, yy1, yy2, dow; + + INITIALIZE(value); + leapyear = isleap(year); + switch (rulep->r_type) { + + case JULIAN_DAY: + /* + ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap + ** years. + ** In non-leap years, or if the day number is 59 or less, just + ** 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; + if (leapyear && rulep->r_day >= 60) + value += SECSPERDAY; + break; + + case DAY_OF_YEAR: + /* + ** n - day of year. + ** Just add SECSPERDAY times the day number to the time of + ** January 1, midnight, to get the day. + */ + value = janfirst + rulep->r_day * SECSPERDAY; + break; + + case MONTH_NTH_DAY_OF_WEEK: + /* + ** Mm.n.d - nth "dth day" of month m. + */ + value = janfirst; + for (i = 0; i < rulep->r_mon - 1; ++i) + value += mon_lengths[leapyear][i] * SECSPERDAY; + + /* + ** Use Zeller's Congruence to get day-of-week of first day of + ** month. + */ + m1 = (rulep->r_mon + 9) % 12 + 1; + yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; + yy1 = yy0 / 100; + yy2 = yy0 % 100; + dow = ((26 * m1 - 2) / 10 + + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; + if (dow < 0) + dow += DAYSPERWEEK; + + /* + ** "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. + */ + d = rulep->r_day - dow; + if (d < 0) + d += DAYSPERWEEK; + for (i = 1; i < rulep->r_week; ++i) { + if (d + DAYSPERWEEK >= + mon_lengths[leapyear][rulep->r_mon - 1]) + break; + d += DAYSPERWEEK; + } + + /* + ** "d" is the day-of-month (zero-origin) of the day we want. + */ + value += 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 + ** time on that day, add the transition time and the current offset + ** from UTC. + */ + return value + rulep->r_time + offset; +} + +/* +** 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; +{ + const char * stdname; + const char * dstname; + size_t stdlen; + size_t dstlen; + long stdoffset; + long dstoffset; + register time_t * atp; + register unsigned char * typep; + register char * cp; + register int load_result; + + INITIALIZE(dstname); + stdname = name; + 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 { + name = getzname(name); + stdlen = name - stdname; + if (stdlen < 3) + return -1; + if (*name == '\0') + return -1; + name = getoffset(name, &stdoffset); + if (name == NULL) + return -1; + } + load_result = tzload(TZDEFRULES, sp); + 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 != '\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; + time_t starttime; + time_t endtime; + + ++name; + if ((name = getrule(name, &start)) == NULL) + return -1; + if (*name++ != ',') + return -1; + if ((name = getrule(name, &end)) == NULL) + return -1; + if (*name != '\0') + return -1; + sp->typecnt = 2; /* standard time and DST */ + /* + ** Two transitions per year, from EPOCH_YEAR to 2037. + */ + 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; + 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) { + starttime = transtime(janfirst, year, &start, + stdoffset); + endtime = transtime(janfirst, year, &end, + dstoffset); + if (starttime > endtime) { + *atp++ = endtime; + *typep++ = 1; /* DST ends */ + *atp++ = starttime; + *typep++ = 0; /* DST begins */ + } else { + *atp++ = starttime; + *typep++ = 0; /* DST begins */ + *atp++ = endtime; + *typep++ = 1; /* DST ends */ + } + janfirst += year_lengths[isleap(year)] * + SECSPERDAY; + } + } else { + register long theirstdoffset; + register long theirdstoffset; + register long theiroffset; + register int isdst; + register int i; + register int j; + + if (*name != '\0') + return -1; + /* + ** Initial values of theirstdoffset and theirdstoffset. + */ + theirstdoffset = 0; + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + if (!sp->ttis[j].tt_isdst) { + theirstdoffset = + -sp->ttis[j].tt_gmtoff; + break; + } + } + theirdstoffset = 0; + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + if (sp->ttis[j].tt_isdst) { + theirdstoffset = + -sp->ttis[j].tt_gmtoff; + break; + } + } + /* + ** Initially we're assumed to be in standard time. + */ + isdst = FALSE; + theiroffset = theirstdoffset; + /* + ** Now juggle transition times and types + ** tracking offsets as you do. + */ + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + sp->types[i] = sp->ttis[j].tt_isdst; + if (sp->ttis[j].tt_ttisgmt) { + /* No adjustment to transition time */ + } else { + /* + ** If summer time is in effect, and the + ** transition time was not specified as + ** standard time, add the summer time + ** offset to the transition time; + ** otherwise, add the standard time + ** offset to the transition time. + */ + /* + ** Transitions from DST to DDST + ** will effectively disappear since + ** POSIX provides for only one DST + ** offset. + */ + if (isdst && !sp->ttis[j].tt_ttisstd) { + sp->ats[i] += dstoffset - + theirdstoffset; + } else { + sp->ats[i] += stdoffset - + theirstdoffset; + } + } + theiroffset = -sp->ttis[j].tt_gmtoff; + if (sp->ttis[j].tt_isdst) + theirdstoffset = theiroffset; + else theirstdoffset = theiroffset; + } + /* + ** Finally, fill in ttis. + ** ttisstd and ttisgmt need not be handled. + */ + sp->ttis[0].tt_gmtoff = -stdoffset; + 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->typecnt = 2; + } + } else { + dstlen = 0; + sp->typecnt = 1; /* only standard time */ + sp->timecnt = 0; + sp->ttis[0].tt_gmtoff = -stdoffset; + sp->ttis[0].tt_isdst = 0; + sp->ttis[0].tt_abbrind = 0; + } + sp->charcnt = stdlen + 1; + if (dstlen != 0) + sp->charcnt += dstlen + 1; + if ((size_t) sp->charcnt > sizeof sp->chars) + return -1; + cp = sp->chars; + (void) strncpy(cp, stdname, stdlen); + cp += stdlen; + *cp++ = '\0'; + if (dstlen != 0) { + (void) strncpy(cp, dstname, dstlen); + *(cp + dstlen) = '\0'; + } + return 0; +} + +static void +gmtload(sp) +struct state * const sp; +{ + if (tzload(gmt, sp) != 0) + (void) tzparse(gmt, sp, TRUE); +} + +#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 +tzsetwall P((void)) +{ + if (lcl_is_set < 0) + return; + lcl_is_set = -1; + +#ifdef ALL_STATE + if (lclptr == NULL) { + lclptr = (struct state *) malloc(sizeof *lclptr); + if (lclptr == NULL) { + settzname(); /* all we can do */ + return; + } + } +#endif /* defined ALL_STATE */ + if (tzload((char *) NULL, lclptr) != 0) + gmtload(lclptr); + settzname(); +} + +void +tzset P((void)) +{ + register const char * name; + + name = getenv("TZ"); + if (name == NULL) { + tzsetwall(); + return; + } + + if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) + return; + lcl_is_set = strlen(name) < sizeof lcl_TZname; + if (lcl_is_set) + (void) strcpy(lcl_TZname, name); + +#ifdef ALL_STATE + if (lclptr == NULL) { + lclptr = (struct state *) malloc(sizeof *lclptr); + if (lclptr == NULL) { + settzname(); /* all we can do */ + 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; + (void) strcpy(lclptr->chars, gmt); + } else if (tzload(name, lclptr) != 0) + if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) + (void) gmtload(lclptr); + settzname(); +} + +/* +** 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-- +** but it *is* desirable.) +** +** The unused offset argument is for the benefit of mktime variants. +*/ + +/*ARGSUSED*/ +static void +localsub(timep, offset, tmp) +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 time_t t = *timep; + + sp = lclptr; +#ifdef ALL_STATE + if (sp == NULL) { + gmtsub(timep, offset, tmp); + return; + } +#endif /* defined ALL_STATE */ + if (sp->timecnt == 0 || t < sp->ats[0]) { + i = 0; + while (sp->ttis[i].tt_isdst) + if (++i >= sp->typecnt) { + i = 0; + break; + } + } else { + for (i = 1; i < sp->timecnt; ++i) + if (t < sp->ats[i]) + break; + i = sp->types[i - 1]; + } + ttisp = &sp->ttis[i]; + /* + ** To get (wrong) behavior that's compatible with System V Release 2.0 + ** you'd replace the statement below with + ** t += ttisp->tt_gmtoff; + ** timesub(&t, 0L, sp, tmp); + */ + 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 */ +} + +struct tm * +localtime(timep) +const time_t * const timep; +{ + tzset(); + localsub(timep, 0L, &tm); + return &tm; +} + +/* +** Re-entrant version of localtime. +*/ + +struct tm * +localtime_r(timep, tm) +const time_t * const timep; +struct tm * tm; +{ + localsub(timep, 0L, tm); + return tm; +} + +/* +** gmtsub is to gmtime as localsub is to localtime. +*/ + +static void +gmtsub(timep, offset, tmp) +const time_t * const timep; +const long offset; +struct tm * const tmp; +{ + if (!gmt_is_set) { + gmt_is_set = TRUE; +#ifdef ALL_STATE + gmtptr = (struct state *) malloc(sizeof *gmtptr); + if (gmtptr != NULL) +#endif /* defined ALL_STATE */ + gmtload(gmtptr); + } + timesub(timep, offset, gmtptr, tmp); +#ifdef TM_ZONE + /* + ** Could get fancy here and deliver something such as + ** "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 { +#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 */ +} + +struct tm * +gmtime(timep) +const time_t * const timep; +{ + gmtsub(timep, 0L, &tm); + return &tm; +} + +/* +* Re-entrant version of gmtime. +*/ + +struct tm * +gmtime_r(timep, tm) +const time_t * const timep; +struct tm * tm; +{ + gmtsub(timep, 0L, tm); + return tm; +} + +#ifdef STD_INSPIRED + +struct tm * +offtime(timep, offset) +const time_t * const timep; +const long offset; +{ + gmtsub(timep, offset, &tm); + return &tm; +} + +#endif /* defined STD_INSPIRED */ + +static void +timesub(timep, offset, sp, tmp) +const time_t * const timep; +const long offset; +register const struct state * const sp; +register struct tm * const tmp; +{ + 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; + + 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) { + if (*timep == lp->ls_trans) { + hit = ((i == 0 && lp->ls_corr > 0) || + lp->ls_corr > sp->lsis[i - 1].ls_corr); + if (hit) + while (i > 0 && + sp->lsis[i].ls_trans == + sp->lsis[i - 1].ls_trans + 1 && + sp->lsis[i].ls_corr == + sp->lsis[i - 1].ls_corr + 1) { + ++hit; + --i; + } + } + corr = lp->ls_corr; + 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; + } +#endif /* defined mc68k */ + rem += (offset - corr); + while (rem < 0) { + rem += SECSPERDAY; + --days; + } + while (rem >= SECSPERDAY) { + rem -= SECSPERDAY; + ++days; + } + tmp->tm_hour = (int) (rem / SECSPERHOUR); + rem = rem % SECSPERHOUR; + tmp->tm_min = (int) (rem / SECSPERMIN); + /* + ** A positive leap second requires a special + ** 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); + tmp->tm_isdst = 0; +#ifdef TM_GMTOFF + tmp->TM_GMTOFF = offset; +#endif /* defined TM_GMTOFF */ +} + +char * +ctime(timep) +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 +** asctime(localtime(timer)) +*/ + return asctime(localtime(timep)); +} + +char * +ctime_r(timep, buf) +const time_t * const timep; +char * buf; +{ + struct tm tm; + + return asctime_r(localtime_r(timep, &tm), 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 +** 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). +*/ + +static int +increment_overflow(number, delta) +int * number; +int delta; +{ + int number0; + + number0 = *number; + *number += delta; + return (*number < number0) != (delta < 0); +} + +static int +normalize_overflow(tensptr, unitsptr, base) +int * const tensptr; +int * const unitsptr; +const int base; +{ + register int tensdelta; + + tensdelta = (*unitsptr >= 0) ? + (*unitsptr / base) : + (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return increment_overflow(tensptr, tensdelta); +} + +static int +tmcomp(atmp, btmp) +register const struct tm * const atmp; +register const struct tm * const btmp; +{ + register 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; + return result; +} + +static time_t +time2sub(tmp, funcp, offset, okayp, do_norm_secs) +struct tm * const tmp; +void (* const funcp) P((const time_t*, long, struct tm*)); +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; + time_t newt; + time_t t; + struct 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)) + return WRONG; + /* + ** Turn yourtm.tm_year 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; + 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)]; + } + 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; + } + for ( ; ; ) { + i = mon_lengths[isleap(yourtm.tm_year)][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 (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) + return WRONG; + if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) + saved_seconds = 0; + else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { + /* + ** We can't set tm_sec to 0, because that might push the + ** time below the minimum representable time. + ** Set tm_sec to 59 instead. + ** This assumes that the minimum representable time is + ** not in the same minute that a leap second was deleted from, + ** which is a safer assumption than using 58 would be. + */ + if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) + return WRONG; + saved_seconds = yourtm.tm_sec; + yourtm.tm_sec = SECSPERMIN - 1; + } else { + saved_seconds = yourtm.tm_sec; + yourtm.tm_sec = 0; + } + /* + ** Divide the search space in half + ** (this works whether time_t is signed or unsigned). + */ + 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); + for ( ; ; ) { + (*funcp)(&t, offset, &mytm); + 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; + continue; + } + if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) + break; + /* + ** Right time, wrong type. + ** Hunt for right time, right type. + ** 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 */ + 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); + if (tmcomp(&mytm, &yourtm) != 0) + continue; + if (mytm.tm_isdst != yourtm.tm_isdst) + continue; + /* + ** We have a match. + */ + t = newt; + goto label; + } + } + return WRONG; + } +label: + newt = t + saved_seconds; + if ((newt < t) != (saved_seconds < 0)) + return WRONG; + t = newt; + (*funcp)(&t, offset, tmp); + *okayp = TRUE; + return t; +} + +static time_t +time2(tmp, funcp, offset, okayp) +struct tm * const tmp; +void (* const funcp) P((const time_t*, long, struct tm*)); +const long offset; +int * const okayp; +{ + time_t t; + + /* + ** 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); + return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); +} + +static time_t +time1(tmp, funcp, offset) +struct tm * const tmp; +void (* const funcp) P((const time_t *, long, struct tm *)); +const long offset; +{ + register time_t t; + register const struct state * sp; + register int samei, otheri; + register int sameind, otherind; + register int i; + register 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); +#ifdef PCTS + /* + ** PCTS code courtesy Grant Sullivan (grant@osf.org). + */ + if (okay) + return t; + if (tmp->tm_isdst < 0) + tmp->tm_isdst = 0; /* reset to std and try again */ +#endif /* defined PCTS */ +#ifndef PCTS + if (okay || tmp->tm_isdst < 0) + return t; +#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. + ** 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) + return WRONG; +#endif /* defined ALL_STATE */ + 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 (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); + if (okay) + return t; + tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - + sp->ttis[samei].tt_gmtoff; + tmp->tm_isdst = !tmp->tm_isdst; + } + } + return WRONG; +} + +time_t +mktime(tmp) +struct tm * const tmp; +{ + tzset(); + return time1(tmp, localsub, 0L); +} + +#ifdef STD_INSPIRED + +time_t +timelocal(tmp) +struct tm * const tmp; +{ + tmp->tm_isdst = -1; /* in case it wasn't initialized */ + return mktime(tmp); +} + +time_t +timegm(tmp) +struct tm * const tmp; +{ + tmp->tm_isdst = 0; + return time1(tmp, gmtsub, 0L); +} + +time_t +timeoff(tmp, offset) +struct tm * const tmp; +const long offset; +{ + tmp->tm_isdst = 0; + return time1(tmp, gmtsub, offset); +} + +#endif /* defined STD_INSPIRED */ + +#ifdef CMUCS + +/* +** The following is supplied for compatibility with +** previous versions of the CMUCS runtime library. +*/ + +long +gtime(tmp) +struct tm * const tmp; +{ + const time_t t = mktime(tmp); + + if (t == WRONG) + return -1; + return t; +} + +#endif /* defined CMUCS */ + +/* +** XXX--is the below the right way to conditionalize?? +*/ + +#ifdef STD_INSPIRED + +/* +** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 +** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which +** is not the case if we are accounting for leap seconds. +** So, we provide the following conversion routines for use +** when exchanging timestamps with POSIX conforming systems. +*/ + +static long +leapcorr(timep) +time_t * timep; +{ + register struct state * sp; + register struct lsinfo * lp; + register int i; + + sp = lclptr; + i = sp->leapcnt; + while (--i >= 0) { + lp = &sp->lsis[i]; + if (*timep >= lp->ls_trans) + return lp->ls_corr; + } + return 0; +} + +time_t +time2posix(t) +time_t t; +{ + tzset(); + return t - leapcorr(&t); +} + +time_t +posix2time(t) +time_t t; +{ + time_t x; + time_t y; + + tzset(); + /* + ** For a positive leap second hit, the result + ** 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); + if (y < t) { + do { + x++; + y = x - leapcorr(&x); + } while (y < t); + if (t != y) + return x - 1; + } else if (y > t) { + do { + --x; + y = x - leapcorr(&x); + } while (y > t); + if (t != y) + return x + 1; + } + return x; +} + +#endif /* defined STD_INSPIRED */ diff --git a/src/timezone/pgtz.h b/src/timezone/pgtz.h index 17df71cbec..d30ac7aac3 100644 --- a/src/timezone/pgtz.h +++ b/src/timezone/pgtz.h @@ -1,8 +1,6 @@ #include "postgres.h" #define NOID -#define HAVE_SYMLINK 0 -#define HAVE_SYS_WAIT_H 0 #define TZDIR pgwin32_TZDIR() char *pgwin32_TZDIR(void); diff --git a/src/timezone/private.h b/src/timezone/private.h index 9333529bcc..c8f4548683 100644 --- a/src/timezone/private.h +++ b/src/timezone/private.h @@ -1,294 +1,294 @@ -#ifndef PRIVATE_H - -#define PRIVATE_H - -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). -*/ - -/* -** 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 -*/ - -#ifndef lint -#ifndef NOID -static char privatehid[] = "@(#)private.h 7.53"; -#endif /* !defined NOID */ -#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_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 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 */ - -#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 */ - -#if HAVE_INCOMPATIBLE_CTIME_R -#define asctime_r _incompatible_asctime_r -#define ctime_r _incompatible_ctime_r -#endif /* HAVE_INCOMPATIBLE_CTIME_R */ - -/* -** Nested includes -*/ - -#include "sys/types.h" /* for time_t */ -#include "stdio.h" -#include "errno.h" -#include "string.h" -#include "limits.h" /* for CHAR_BIT */ -#include "time.h" -#include "stdlib.h" - -#if HAVE_GETTEXT - 0 -#include "libintl.h" -#endif /* HAVE_GETTEXT - 0 */ - -#if HAVE_SYS_WAIT_H - 0 -#include /* for WIFEXITED and WEXITSTATUS */ -#endif /* HAVE_SYS_WAIT_H - 0 */ - -#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 - 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 '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 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 -#include "sys/param.h" -#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 */ - -/* -** 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)); -char * scheck P((const char *string, const char *format)); - - -/* -** Finally, some convenience items. -*/ - -#ifndef TRUE -#define TRUE 1 -#endif /* !defined TRUE */ - -#ifndef FALSE -#define FALSE 0 -#endif /* !defined FALSE */ - -#ifndef TYPE_BIT -#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) -#endif /* !defined TYPE_BIT */ - -#ifndef TYPE_SIGNED -#define TYPE_SIGNED(type) (((type) -1) < 0) -#endif /* !defined TYPE_SIGNED */ - -#ifndef INT_STRLEN_MAXIMUM -/* -** 302 / 1000 is log10(2.0) rounded up. -** Subtract one for the sign bit if the type is signed; -** add one for integer division truncation; -** 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)) -#endif /* !defined INT_STRLEN_MAXIMUM */ - -/* -** INITIALIZE(x) -*/ - -#ifndef GNUC_or_lint -#ifdef lint -#define GNUC_or_lint -#endif /* defined lint */ -#ifndef lint -#ifdef __GNUC__ -#define GNUC_or_lint -#endif /* defined __GNUC__ */ -#endif /* !defined lint */ -#endif /* !defined GNUC_or_lint */ - -#ifndef INITIALIZE -#ifdef GNUC_or_lint -#define INITIALIZE(x) ((x) = 0) -#endif /* defined GNUC_or_lint */ -#ifndef GNUC_or_lint -#define INITIALIZE(x) -#endif /* !defined GNUC_or_lint */ -#endif /* !defined INITIALIZE */ - -/* -** 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. -*/ - -#ifndef _ -#if HAVE_GETTEXT - 0 -#define _(msgid) gettext(msgid) -#else /* !(HAVE_GETTEXT - 0) */ -#define _(msgid) msgid -#endif /* !(HAVE_GETTEXT - 0) */ -#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 */ - -/* -** UNIX was a registered trademark of The Open Group in 2003. -*/ - -#endif /* !defined PRIVATE_H */ +#ifndef PRIVATE_H + +#define PRIVATE_H + +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +*/ + +/* +** 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 +*/ + +#ifndef lint +#ifndef NOID +static char privatehid[] = "@(#)private.h 7.53"; +#endif /* !defined NOID */ +#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_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 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 */ + +#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 */ + +#if HAVE_INCOMPATIBLE_CTIME_R +#define asctime_r _incompatible_asctime_r +#define ctime_r _incompatible_ctime_r +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + +/* +** Nested includes +*/ + +#include "sys/types.h" /* for time_t */ +#include "stdio.h" +#include "errno.h" +#include "string.h" +#include "limits.h" /* for CHAR_BIT */ +#include "time.h" +#include "stdlib.h" + +#if HAVE_GETTEXT - 0 +#include "libintl.h" +#endif /* HAVE_GETTEXT - 0 */ + +#if HAVE_SYS_WAIT_H - 0 +#include /* for WIFEXITED and WEXITSTATUS */ +#endif /* HAVE_SYS_WAIT_H - 0 */ + +#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 - 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 '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 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 +#include "sys/param.h" +#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 */ + +/* +** 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)); +char * scheck P((const char *string, const char *format)); + + +/* +** Finally, some convenience items. +*/ + +#ifndef TRUE +#define TRUE 1 +#endif /* !defined TRUE */ + +#ifndef FALSE +#define FALSE 0 +#endif /* !defined FALSE */ + +#ifndef TYPE_BIT +#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) +#endif /* !defined TYPE_BIT */ + +#ifndef TYPE_SIGNED +#define TYPE_SIGNED(type) (((type) -1) < 0) +#endif /* !defined TYPE_SIGNED */ + +#ifndef INT_STRLEN_MAXIMUM +/* +** 302 / 1000 is log10(2.0) rounded up. +** Subtract one for the sign bit if the type is signed; +** add one for integer division truncation; +** 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)) +#endif /* !defined INT_STRLEN_MAXIMUM */ + +/* +** INITIALIZE(x) +*/ + +#ifndef GNUC_or_lint +#ifdef lint +#define GNUC_or_lint +#endif /* defined lint */ +#ifndef lint +#ifdef __GNUC__ +#define GNUC_or_lint +#endif /* defined __GNUC__ */ +#endif /* !defined lint */ +#endif /* !defined GNUC_or_lint */ + +#ifndef INITIALIZE +#ifdef GNUC_or_lint +#define INITIALIZE(x) ((x) = 0) +#endif /* defined GNUC_or_lint */ +#ifndef GNUC_or_lint +#define INITIALIZE(x) +#endif /* !defined GNUC_or_lint */ +#endif /* !defined INITIALIZE */ + +/* +** 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. +*/ + +#ifndef _ +#if HAVE_GETTEXT - 0 +#define _(msgid) gettext(msgid) +#else /* !(HAVE_GETTEXT - 0) */ +#define _(msgid) msgid +#endif /* !(HAVE_GETTEXT - 0) */ +#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 */ + +/* +** UNIX was a registered trademark of The Open Group in 2003. +*/ + +#endif /* !defined PRIVATE_H */ diff --git a/src/timezone/scheck.c b/src/timezone/scheck.c index 6bcfe87950..39feeba701 100644 --- a/src/timezone/scheck.c +++ b/src/timezone/scheck.c @@ -1,59 +1,59 @@ -#ifndef lint -#ifndef NOID -static char elsieid[] = "@(#)scheck.c 8.15"; -#endif /* !defined lint */ -#endif /* !defined NOID */ - -/*LINTLIBRARY*/ - -#include "private.h" - -char * -scheck(string, format) -const char * const string; -const char * const format; -{ - register char * fbuf; - register const char * fp; - register char * tp; - register int c; - register char * result; - char dummy; - static char nada; - - result = &nada; - if (string == NULL || format == NULL) - return result; - fbuf = imalloc((int) (2 * strlen(format) + 4)); - if (fbuf == NULL) - return result; - fp = format; - tp = fbuf; - while ((*tp++ = c = *fp++) != '\0') { - if (c != '%') - continue; - if (*fp == '%') { - *tp++ = *fp++; - continue; - } - *tp++ = '*'; - if (*fp == '*') - ++fp; - while (is_digit(*fp)) - *tp++ = *fp++; - if (*fp == 'l' || *fp == 'h') - *tp++ = *fp++; - else if (*fp == '[') - do *tp++ = *fp++; - while (*fp != '\0' && *fp != ']'); - if ((*tp++ = *fp++) == '\0') - break; - } - *(tp - 1) = '%'; - *tp++ = 'c'; - *tp = '\0'; - if (sscanf(string, fbuf, &dummy) != 1) - result = (char *) format; - ifree(fbuf); - return result; -} +#ifndef lint +#ifndef NOID +static char elsieid[] = "@(#)scheck.c 8.15"; +#endif /* !defined lint */ +#endif /* !defined NOID */ + +/*LINTLIBRARY*/ + +#include "private.h" + +char * +scheck(string, format) +const char * const string; +const char * const format; +{ + register char * fbuf; + register const char * fp; + register char * tp; + register int c; + register char * result; + char dummy; + static char nada; + + result = &nada; + if (string == NULL || format == NULL) + return result; + fbuf = imalloc((int) (2 * strlen(format) + 4)); + if (fbuf == NULL) + return result; + fp = format; + tp = fbuf; + while ((*tp++ = c = *fp++) != '\0') { + if (c != '%') + continue; + if (*fp == '%') { + *tp++ = *fp++; + continue; + } + *tp++ = '*'; + if (*fp == '*') + ++fp; + while (is_digit(*fp)) + *tp++ = *fp++; + if (*fp == 'l' || *fp == 'h') + *tp++ = *fp++; + else if (*fp == '[') + do *tp++ = *fp++; + while (*fp != '\0' && *fp != ']'); + if ((*tp++ = *fp++) == '\0') + break; + } + *(tp - 1) = '%'; + *tp++ = 'c'; + *tp = '\0'; + if (sscanf(string, fbuf, &dummy) != 1) + result = (char *) format; + ifree(fbuf); + return result; +} diff --git a/src/timezone/tzfile.h b/src/timezone/tzfile.h index 2d07a867e2..0921c3c339 100644 --- a/src/timezone/tzfile.h +++ b/src/timezone/tzfile.h @@ -1,188 +1,188 @@ -#ifndef TZFILE_H - -#define TZFILE_H - -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). -*/ - -/* -** 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 -*/ - -#ifndef lint -#ifndef NOID -static char tzfilehid[] = "@(#)tzfile.h 7.14"; -#endif /* !defined NOID */ -#endif /* !defined lint */ - -/* -** Information about time zone files. -*/ - -#ifndef TZDIR -#define TZDIR "/usr/local/etc/zoneinfo" /* Time zone object file directory */ -#endif /* !defined TZDIR */ - -#ifndef TZDEFAULT -#define TZDEFAULT "localtime" -#endif /* !defined TZDEFAULT */ - -#ifndef TZDEFRULES -#define TZDEFRULES "posixrules" -#endif /* !defined TZDEFRULES */ - -/* -** Each file begins with. . . -*/ - -#define TZ_MAGIC "TZif" - -struct tzhead { - char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_reserved[16]; /* 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 */ - char tzh_timecnt[4]; /* coded number of transition times */ - char tzh_typecnt[4]; /* coded number of local time types */ - char tzh_charcnt[4]; /* coded number of abbr. chars */ -}; - -/* -** . . .followed by. . . -** -** tzh_timecnt (char [4])s coded transition times a la time(2) -** tzh_timecnt (unsigned char)s types of local time starting at above -** tzh_typecnt repetitions of -** one (char [4]) coded UTC offset in seconds -** one (unsigned char) used to set tm_isdst -** one (unsigned char) that's an abbreviation list index -** tzh_charcnt (char)s '\0'-terminated zone abbreviations -** 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, -** 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, -** transition time is local time -** if absent, transition times are -** assumed to be local time -*/ - -/* -** 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 -#endif /* !defined TZ_MAX_TIMES */ - -#ifndef TZ_MAX_TYPES -#ifndef NOSOLAR -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ -#endif /* !defined NOSOLAR */ -#ifdef NOSOLAR -/* -** Must be at least 14 for Europe/Riga as of Jan 12 1995, -** as noted by Earl Chew . -*/ -#define TZ_MAX_TYPES 20 /* Maximum number of local time types */ -#endif /* !defined NOSOLAR */ -#endif /* !defined TZ_MAX_TYPES */ - -#ifndef TZ_MAX_CHARS -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ - /* (limited by what unsigned chars can hold) */ -#endif /* !defined TZ_MAX_CHARS */ - -#ifndef TZ_MAX_LEAPS -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ -#endif /* !defined TZ_MAX_LEAPS */ - -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -#define TM_YEAR_BASE 1900 - -#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. -*/ - -#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 */ +#ifndef TZFILE_H + +#define TZFILE_H + +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +*/ + +/* +** 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 +*/ + +#ifndef lint +#ifndef NOID +static char tzfilehid[] = "@(#)tzfile.h 7.14"; +#endif /* !defined NOID */ +#endif /* !defined lint */ + +/* +** Information about time zone files. +*/ + +#ifndef TZDIR +#define TZDIR "/usr/local/etc/zoneinfo" /* Time zone object file directory */ +#endif /* !defined TZDIR */ + +#ifndef TZDEFAULT +#define TZDEFAULT "localtime" +#endif /* !defined TZDEFAULT */ + +#ifndef TZDEFRULES +#define TZDEFRULES "posixrules" +#endif /* !defined TZDEFRULES */ + +/* +** Each file begins with. . . +*/ + +#define TZ_MAGIC "TZif" + +struct tzhead { + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_reserved[16]; /* 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 */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ +}; + +/* +** . . .followed by. . . +** +** tzh_timecnt (char [4])s coded transition times a la time(2) +** tzh_timecnt (unsigned char)s types of local time starting at above +** tzh_typecnt repetitions of +** one (char [4]) coded UTC offset in seconds +** one (unsigned char) used to set tm_isdst +** one (unsigned char) that's an abbreviation list index +** tzh_charcnt (char)s '\0'-terminated zone abbreviations +** 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, +** 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, +** transition time is local time +** if absent, transition times are +** assumed to be local time +*/ + +/* +** 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 +#endif /* !defined TZ_MAX_TIMES */ + +#ifndef TZ_MAX_TYPES +#ifndef NOSOLAR +#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ +#endif /* !defined NOSOLAR */ +#ifdef NOSOLAR +/* +** Must be at least 14 for Europe/Riga as of Jan 12 1995, +** as noted by Earl Chew . +*/ +#define TZ_MAX_TYPES 20 /* Maximum number of local time types */ +#endif /* !defined NOSOLAR */ +#endif /* !defined TZ_MAX_TYPES */ + +#ifndef TZ_MAX_CHARS +#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ + /* (limited by what unsigned chars can hold) */ +#endif /* !defined TZ_MAX_CHARS */ + +#ifndef TZ_MAX_LEAPS +#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ +#endif /* !defined TZ_MAX_LEAPS */ + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) +#define MONSPERYEAR 12 + +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define TM_TUESDAY 2 +#define TM_WEDNESDAY 3 +#define TM_THURSDAY 4 +#define TM_FRIDAY 5 +#define TM_SATURDAY 6 + +#define TM_JANUARY 0 +#define TM_FEBRUARY 1 +#define TM_MARCH 2 +#define TM_APRIL 3 +#define TM_MAY 4 +#define TM_JUNE 5 +#define TM_JULY 6 +#define TM_AUGUST 7 +#define TM_SEPTEMBER 8 +#define TM_OCTOBER 9 +#define TM_NOVEMBER 10 +#define TM_DECEMBER 11 + +#define TM_YEAR_BASE 1900 + +#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. +*/ + +#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 */ diff --git a/src/timezone/zic.c b/src/timezone/zic.c index ec187addf3..21d665c715 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -1,2258 +1,2258 @@ -static char elsieid[] = "@(#)zic.c 7.115"; -#include "pgtz.h" -#undef unlink -#undef TZDIR -#define TZDIR "data" -#ifdef WIN32 -#include -#endif - -#include "private.h" -#include "locale.h" -#include "tzfile.h" - -#if HAVE_SYS_STAT_H -#include "sys/stat.h" -#endif -#ifndef WIN32 -#ifdef S_IRUSR -#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) -#else -#define MKDIR_UMASK 0755 -#endif -#endif - -/* -** On some ancient hosts, predicates like `isspace(C)' are defined -** only if isascii(C) || C == EOF. Modern hosts obey the C Standard, -** which says they are defined only if C == ((unsigned char) C) || C == EOF. -** Neither the C Standard nor Posix require that `isascii' exist. -** For portability, we check both ancient and modern requirements. -** If isascii is not defined, the isascii check succeeds trivially. -*/ -#include "ctype.h" -#ifndef isascii -#define isascii(x) 1 -#endif - -struct rule { - const char * r_filename; - int r_linenum; - const char * r_name; - - int r_loyear; /* for example, 1986 */ - int r_hiyear; /* for example, 1986 */ - const char * r_yrtype; - - int r_month; /* 0..11 */ - - int r_dycode; /* see below */ - int r_dayofmonth; - int r_wday; - - long r_tod; /* time from midnight */ - int r_todisstd; /* above is standard time if TRUE */ - /* or wall clock time if FALSE */ - int r_todisgmt; /* above is GMT if TRUE */ - /* or local time if FALSE */ - long r_stdoff; /* offset from standard time */ - const char * r_abbrvar; /* variable part of abbreviation */ - - int r_todo; /* a rule to do (used in outzone) */ - time_t r_temp; /* used in outzone */ -}; - -/* -** r_dycode r_dayofmonth r_wday -*/ - -#define DC_DOM 0 /* 1..31 */ /* unused */ -#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */ -#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */ - -struct zone { - const char * z_filename; - int z_linenum; - - const char * z_name; - long z_gmtoff; - const char * z_rule; - const char * z_format; - - long z_stdoff; - - struct rule * z_rules; - int z_nrules; - - struct rule z_untilrule; - time_t z_untiltime; -}; - -extern int getopt P((int argc, char * const argv[], - const char * options)); -extern int link P((const char * fromname, const char * toname)); -extern char * optarg; -extern int optind; - -static void addtt P((time_t starttime, int type)); -static int addtype P((long gmtoff, const char * abbr, int isdst, - int ttisstd, int ttisgmt)); -static void leapadd P((time_t t, int positive, int rolling, int count)); -static void adjleap P((void)); -static void associate P((void)); -static int ciequal P((const char * ap, const char * bp)); -static void convert P((long val, char * buf)); -static void dolink P((const char * fromfile, const char * tofile)); -static void doabbr P((char * abbr, const char * format, - const char * letters, int isdst)); -static void eat P((const char * name, int num)); -static void eats P((const char * name, int num, - const char * rname, int rnum)); -static long eitol P((int i)); -static void error P((const char * message)); -static char ** getfields P((char * buf)); -static long gethms P((const char * string, const char * errstrng, - int signable)); -static void infile P((const char * filename)); -static void inleap P((char ** fields, int nfields)); -static void inlink P((char ** fields, int nfields)); -static void inrule P((char ** fields, int nfields)); -static int inzcont P((char ** fields, int nfields)); -static int inzone P((char ** fields, int nfields)); -static int inzsub P((char ** fields, int nfields, int iscont)); -static int itsabbr P((const char * abbr, const char * word)); -static int itsdir P((const char * name)); -static int lowerit P((int c)); -static char * memcheck P((char * tocheck)); -static int mkdirs P((char * filename)); -static void newabbr P((const char * abbr)); -static long oadd P((long t1, long t2)); -static void outzone P((const struct zone * zp, int ntzones)); -static void puttzcode P((long code, FILE * fp)); -static int rcomp P((const void * leftp, const void * rightp)); -static time_t rpytime P((const struct rule * rp, int wantedy)); -static void rulesub P((struct rule * rp, - const char * loyearp, const char * hiyearp, - const char * typep, const char * monthp, - const char * dayp, const char * timep)); -static void setboundaries P((void)); -static time_t tadd P((time_t t1, long t2)); -static void usage P((void)); -static void writezone P((const char * name)); -static int yearistype P((int year, const char * type)); - -#if !(HAVE_STRERROR - 0) -static char * strerror P((int)); -#endif /* !(HAVE_STRERROR - 0) */ - -static int charcnt; -static int errors; -static const char * filename; -static int leapcnt; -static int linenum; -static time_t max_time; -static int max_year; -static int max_year_representable; -static time_t min_time; -static int min_year; -static int min_year_representable; -static int noise; -static const char * rfilename; -static int rlinenum; -static const char * progname; -static int timecnt; -static int typecnt; - -/* -** Line codes. -*/ - -#define LC_RULE 0 -#define LC_ZONE 1 -#define LC_LINK 2 -#define LC_LEAP 3 - -/* -** Which fields are which on a Zone line. -*/ - -#define ZF_NAME 1 -#define ZF_GMTOFF 2 -#define ZF_RULE 3 -#define ZF_FORMAT 4 -#define ZF_TILYEAR 5 -#define ZF_TILMONTH 6 -#define ZF_TILDAY 7 -#define ZF_TILTIME 8 -#define ZONE_MINFIELDS 5 -#define ZONE_MAXFIELDS 9 - -/* -** Which fields are which on a Zone continuation line. -*/ - -#define ZFC_GMTOFF 0 -#define ZFC_RULE 1 -#define ZFC_FORMAT 2 -#define ZFC_TILYEAR 3 -#define ZFC_TILMONTH 4 -#define ZFC_TILDAY 5 -#define ZFC_TILTIME 6 -#define ZONEC_MINFIELDS 3 -#define ZONEC_MAXFIELDS 7 - -/* -** Which files are which on a Rule line. -*/ - -#define RF_NAME 1 -#define RF_LOYEAR 2 -#define RF_HIYEAR 3 -#define RF_COMMAND 4 -#define RF_MONTH 5 -#define RF_DAY 6 -#define RF_TOD 7 -#define RF_STDOFF 8 -#define RF_ABBRVAR 9 -#define RULE_FIELDS 10 - -/* -** Which fields are which on a Link line. -*/ - -#define LF_FROM 1 -#define LF_TO 2 -#define LINK_FIELDS 3 - -/* -** Which fields are which on a Leap line. -*/ - -#define LP_YEAR 1 -#define LP_MONTH 2 -#define LP_DAY 3 -#define LP_TIME 4 -#define LP_CORR 5 -#define LP_ROLL 6 -#define LEAP_FIELDS 7 - -/* -** Year synonyms. -*/ - -#define YR_MINIMUM 0 -#define YR_MAXIMUM 1 -#define YR_ONLY 2 - -static struct rule * rules; -static int nrules; /* number of rules */ - -static struct zone * zones; -static int nzones; /* number of zones */ - -struct link { - const char * l_filename; - int l_linenum; - const char * l_from; - const char * l_to; -}; - -static struct link * links; -static int nlinks; - -struct lookup { - const char * l_word; - const int l_value; -}; - -static struct lookup const * byword P((const char * string, - const struct lookup * lp)); - -static struct lookup const line_codes[] = { - { "Rule", LC_RULE }, - { "Zone", LC_ZONE }, - { "Link", LC_LINK }, - { "Leap", LC_LEAP }, - { NULL, 0} -}; - -static struct lookup const mon_names[] = { - { "January", TM_JANUARY }, - { "February", TM_FEBRUARY }, - { "March", TM_MARCH }, - { "April", TM_APRIL }, - { "May", TM_MAY }, - { "June", TM_JUNE }, - { "July", TM_JULY }, - { "August", TM_AUGUST }, - { "September", TM_SEPTEMBER }, - { "October", TM_OCTOBER }, - { "November", TM_NOVEMBER }, - { "December", TM_DECEMBER }, - { NULL, 0 } -}; - -static struct lookup const wday_names[] = { - { "Sunday", TM_SUNDAY }, - { "Monday", TM_MONDAY }, - { "Tuesday", TM_TUESDAY }, - { "Wednesday", TM_WEDNESDAY }, - { "Thursday", TM_THURSDAY }, - { "Friday", TM_FRIDAY }, - { "Saturday", TM_SATURDAY }, - { NULL, 0 } -}; - -static struct lookup const lasts[] = { - { "last-Sunday", TM_SUNDAY }, - { "last-Monday", TM_MONDAY }, - { "last-Tuesday", TM_TUESDAY }, - { "last-Wednesday", TM_WEDNESDAY }, - { "last-Thursday", TM_THURSDAY }, - { "last-Friday", TM_FRIDAY }, - { "last-Saturday", TM_SATURDAY }, - { NULL, 0 } -}; - -static struct lookup const begin_years[] = { - { "minimum", YR_MINIMUM }, - { "maximum", YR_MAXIMUM }, - { NULL, 0 } -}; - -static struct lookup const end_years[] = { - { "minimum", YR_MINIMUM }, - { "maximum", YR_MAXIMUM }, - { "only", YR_ONLY }, - { NULL, 0 } -}; - -static struct lookup const leap_types[] = { - { "Rolling", TRUE }, - { "Stationary", FALSE }, - { NULL, 0 } -}; - -static const int len_months[2][MONSPERYEAR] = { - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } -}; - -static const int len_years[2] = { - DAYSPERNYEAR, DAYSPERLYEAR -}; - -static struct attype { - time_t at; - unsigned char type; -} attypes[TZ_MAX_TIMES]; -static long gmtoffs[TZ_MAX_TYPES]; -static char isdsts[TZ_MAX_TYPES]; -static unsigned char abbrinds[TZ_MAX_TYPES]; -static char ttisstds[TZ_MAX_TYPES]; -static char ttisgmts[TZ_MAX_TYPES]; -static char chars[TZ_MAX_CHARS]; -static time_t trans[TZ_MAX_LEAPS]; -static long corr[TZ_MAX_LEAPS]; -static char roll[TZ_MAX_LEAPS]; - -/* -** Memory allocation. -*/ - -static char * -memcheck(ptr) -char * const ptr; -{ - if (ptr == NULL) { - const char *e = strerror(errno); - - (void) fprintf(stderr, _("%s: Memory exhausted: %s\n"), - progname, e); - (void) exit(EXIT_FAILURE); - } - return ptr; -} - -#define emalloc(size) memcheck(imalloc(size)) -#define erealloc(ptr, size) memcheck(irealloc((ptr), (size))) -#define ecpyalloc(ptr) memcheck(icpyalloc(ptr)) -#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp))) - -/* -** Error handling. -*/ - -#if !(HAVE_STRERROR - 0) -static char * -strerror(errnum) -int errnum; -{ - extern char * sys_errlist[]; - extern int sys_nerr; - - return (errnum > 0 && errnum <= sys_nerr) ? - sys_errlist[errnum] : _("Unknown system error"); -} -#endif /* !(HAVE_STRERROR - 0) */ - -static void -eats(name, num, rname, rnum) -const char * const name; -const int num; -const char * const rname; -const int rnum; -{ - filename = name; - linenum = num; - rfilename = rname; - rlinenum = rnum; -} - -static void -eat(name, num) -const char * const name; -const int num; -{ - eats(name, num, (char *) NULL, -1); -} - -static void -error(string) -const char * const string; -{ - /* - ** Match the format of "cc" to allow sh users to - ** zic ... 2>&1 | error -t "*" -v - ** on BSD systems. - */ - (void) fprintf(stderr, _("\"%s\", line %d: %s"), - filename, linenum, string); - if (rfilename != NULL) - (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"), - rfilename, rlinenum); - (void) fprintf(stderr, "\n"); - ++errors; -} - -static void -warning(string) -const char * const string; -{ - char * cp; - - cp = ecpyalloc(_("warning: ")); - cp = ecatalloc(cp, string); - error(cp); - ifree(cp); - --errors; -} - -static void -usage P((void)) -{ - (void) fprintf(stderr, _("%s: usage is %s [ --version ] [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"), - progname, progname); - (void) exit(EXIT_FAILURE); -} - -static const char * psxrules; -static const char * lcltime; -static const char * directory; -static const char * leapsec; -static const char * yitcommand; -static int sflag = FALSE; - -int -main(argc, argv) -int argc; -char * argv[]; -{ - register int i; - register int j; - register int c; - -#ifdef unix - (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); -#endif /* defined unix */ -#if HAVE_GETTEXT - 0 - (void) setlocale(LC_MESSAGES, ""); -#ifdef TZ_DOMAINDIR - (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); -#endif /* defined TEXTDOMAINDIR */ - (void) textdomain(TZ_DOMAIN); -#endif /* HAVE_GETTEXT - 0 */ - progname = argv[0]; - for (i = 1; i < argc; ++i) - if (strcmp(argv[i], "--version") == 0) { - (void) printf("%s\n", elsieid); - (void) exit(EXIT_SUCCESS); - } - while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1) - switch (c) { - default: - usage(); - case 'd': - if (directory == NULL) - directory = optarg; - else { - (void) fprintf(stderr, -_("%s: More than one -d option specified\n"), - progname); - (void) exit(EXIT_FAILURE); - } - break; - case 'l': - if (lcltime == NULL) - lcltime = optarg; - else { - (void) fprintf(stderr, -_("%s: More than one -l option specified\n"), - progname); - (void) exit(EXIT_FAILURE); - } - break; - case 'p': - if (psxrules == NULL) - psxrules = optarg; - else { - (void) fprintf(stderr, -_("%s: More than one -p option specified\n"), - progname); - (void) exit(EXIT_FAILURE); - } - break; - case 'y': - if (yitcommand == NULL) - yitcommand = optarg; - else { - (void) fprintf(stderr, -_("%s: More than one -y option specified\n"), - progname); - (void) exit(EXIT_FAILURE); - } - break; - case 'L': - if (leapsec == NULL) - leapsec = optarg; - else { - (void) fprintf(stderr, -_("%s: More than one -L option specified\n"), - progname); - (void) exit(EXIT_FAILURE); - } - break; - case 'v': - noise = TRUE; - break; - case 's': - sflag = TRUE; - break; - } - if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) - usage(); /* usage message by request */ - if (directory == NULL) - directory = TZDIR; - if (yitcommand == NULL) - yitcommand = "yearistype"; - - setboundaries(); - - if (optind < argc && leapsec != NULL) { - infile(leapsec); - adjleap(); - } - - for (i = optind; i < argc; ++i) - infile(argv[i]); - if (errors) - (void) exit(EXIT_FAILURE); - associate(); - for (i = 0; i < nzones; i = j) { - /* - ** Find the next non-continuation zone entry. - */ - for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) - continue; - outzone(&zones[i], j - i); - } - /* - ** Make links. - */ - for (i = 0; i < nlinks; ++i) { - eat(links[i].l_filename, links[i].l_linenum); - dolink(links[i].l_from, links[i].l_to); - } - if (lcltime != NULL) { - eat("command line", 1); - dolink(lcltime, TZDEFAULT); - } - if (psxrules != NULL) { - eat("command line", 1); - dolink(psxrules, TZDEFRULES); - } - return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static void -dolink(fromfile, tofile) -const char * const fromfile; -const char * const tofile; -{ - register char * fromname; - register char * toname; - - if (fromfile[0] == '/') - fromname = ecpyalloc(fromfile); - else { - fromname = ecpyalloc(directory); - fromname = ecatalloc(fromname, "/"); - fromname = ecatalloc(fromname, fromfile); - } - if (tofile[0] == '/') - toname = ecpyalloc(tofile); - else { - toname = ecpyalloc(directory); - toname = ecatalloc(toname, "/"); - toname = ecatalloc(toname, tofile); - } - /* - ** We get to be careful here since - ** there's a fair chance of root running us. - */ - if (!itsdir(toname)) - (void) remove(toname); - if (link(fromname, toname) != 0) { - int result; - - if (mkdirs(toname) != 0) - (void) exit(EXIT_FAILURE); - - result = link(fromname, toname); -#if (HAVE_SYMLINK - 0) - if (result != 0 && - access(fromname, F_OK) == 0 && - !itsdir(fromname)) { - const char *s = tofile; - register char * symlinkcontents = NULL; - while ((s = strchr(s+1, '/')) != NULL) - symlinkcontents = ecatalloc(symlinkcontents, "../"); - symlinkcontents = ecatalloc(symlinkcontents, fromfile); - - result = symlink(symlinkcontents, toname); - if (result == 0) -warning(_("hard link failed, symbolic link used")); - ifree(symlinkcontents); - } -#endif - if (result != 0) { - const char *e = strerror(errno); - - (void) fprintf(stderr, - _("%s: Can't link from %s to %s: %s\n"), - progname, fromname, toname, e); - (void) exit(EXIT_FAILURE); - } - } - ifree(fromname); - ifree(toname); -} - -#ifndef INT_MAX -#define INT_MAX ((int) (((unsigned)~0)>>1)) -#endif /* !defined INT_MAX */ - -#ifndef INT_MIN -#define INT_MIN ((int) ~(((unsigned)~0)>>1)) -#endif /* !defined INT_MIN */ - -/* -** The tz file format currently allows at most 32-bit quantities. -** This restriction should be removed before signed 32-bit values -** wrap around in 2038, but unfortunately this will require a -** change to the tz file format. -*/ - -#define MAX_BITS_IN_FILE 32 -#define TIME_T_BITS_IN_FILE ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE) - -static void -setboundaries P((void)) -{ - if (TYPE_SIGNED(time_t)) { - min_time = ~ (time_t) 0; - min_time <<= TIME_T_BITS_IN_FILE - 1; - max_time = ~ (time_t) 0 - min_time; - if (sflag) - min_time = 0; - } else { - min_time = 0; - max_time = 2 - sflag; - max_time <<= TIME_T_BITS_IN_FILE - 1; - --max_time; - } - min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year; - max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year; - min_year_representable = min_year; - max_year_representable = max_year; -} - -static int -itsdir(name) -const char * const name; -{ - register char * myname; - register int accres; - - myname = ecpyalloc(name); - myname = ecatalloc(myname, "/."); - accres = access(myname, F_OK); - ifree(myname); - return accres == 0; -} - -/* -** Associate sets of rules with zones. -*/ - -/* -** Sort by rule name. -*/ - -static int -rcomp(cp1, cp2) -const void * cp1; -const void * cp2; -{ - return strcmp(((const struct rule *) cp1)->r_name, - ((const struct rule *) cp2)->r_name); -} - -static void -associate P((void)) -{ - register struct zone * zp; - register struct rule * rp; - register int base, out; - register int i, j; - - if (nrules != 0) { - (void) qsort((void *) rules, (size_t) nrules, - (size_t) sizeof *rules, rcomp); - for (i = 0; i < nrules - 1; ++i) { - if (strcmp(rules[i].r_name, - rules[i + 1].r_name) != 0) - continue; - if (strcmp(rules[i].r_filename, - rules[i + 1].r_filename) == 0) - continue; - eat(rules[i].r_filename, rules[i].r_linenum); - warning(_("same rule name in multiple files")); - eat(rules[i + 1].r_filename, rules[i + 1].r_linenum); - warning(_("same rule name in multiple files")); - for (j = i + 2; j < nrules; ++j) { - if (strcmp(rules[i].r_name, - rules[j].r_name) != 0) - break; - if (strcmp(rules[i].r_filename, - rules[j].r_filename) == 0) - continue; - if (strcmp(rules[i + 1].r_filename, - rules[j].r_filename) == 0) - continue; - break; - } - i = j - 1; - } - } - for (i = 0; i < nzones; ++i) { - zp = &zones[i]; - zp->z_rules = NULL; - zp->z_nrules = 0; - } - for (base = 0; base < nrules; base = out) { - rp = &rules[base]; - for (out = base + 1; out < nrules; ++out) - if (strcmp(rp->r_name, rules[out].r_name) != 0) - break; - for (i = 0; i < nzones; ++i) { - zp = &zones[i]; - if (strcmp(zp->z_rule, rp->r_name) != 0) - continue; - zp->z_rules = rp; - zp->z_nrules = out - base; - } - } - for (i = 0; i < nzones; ++i) { - zp = &zones[i]; - if (zp->z_nrules == 0) { - /* - ** Maybe we have a local standard time offset. - */ - eat(zp->z_filename, zp->z_linenum); - zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), - TRUE); - /* - ** Note, though, that if there's no rule, - ** a '%s' in the format is a bad thing. - */ - if (strchr(zp->z_format, '%') != 0) - error(_("%s in ruleless zone")); - } - } - if (errors) - (void) exit(EXIT_FAILURE); -} - -static void -infile(name) -const char * name; -{ - register FILE * fp; - register char ** fields; - register char * cp; - register const struct lookup * lp; - register int nfields; - register int wantcont; - register int num; - char buf[BUFSIZ]; - - if (strcmp(name, "-") == 0) { - name = _("standard input"); - fp = stdin; - } else if ((fp = fopen(name, "r")) == NULL) { - const char *e = strerror(errno); - - (void) fprintf(stderr, _("%s: Can't open %s: %s\n"), - progname, name, e); - (void) exit(EXIT_FAILURE); - } - wantcont = FALSE; - for (num = 1; ; ++num) { - eat(name, num); - if (fgets(buf, (int) sizeof buf, fp) != buf) - break; - cp = strchr(buf, '\n'); - if (cp == NULL) { - error(_("line too long")); - (void) exit(EXIT_FAILURE); - } - *cp = '\0'; - fields = getfields(buf); - nfields = 0; - while (fields[nfields] != NULL) { - static char nada; - - if (strcmp(fields[nfields], "-") == 0) - fields[nfields] = &nada; - ++nfields; - } - if (nfields == 0) { - /* nothing to do */ - } else if (wantcont) { - wantcont = inzcont(fields, nfields); - } else { - lp = byword(fields[0], line_codes); - if (lp == NULL) - error(_("input line of unknown type")); - else switch ((int) (lp->l_value)) { - case LC_RULE: - inrule(fields, nfields); - wantcont = FALSE; - break; - case LC_ZONE: - wantcont = inzone(fields, nfields); - break; - case LC_LINK: - inlink(fields, nfields); - wantcont = FALSE; - break; - case LC_LEAP: - if (name != leapsec) - (void) fprintf(stderr, -_("%s: Leap line in non leap seconds file %s\n"), - progname, name); - else inleap(fields, nfields); - wantcont = FALSE; - break; - default: /* "cannot happen" */ - (void) fprintf(stderr, -_("%s: panic: Invalid l_value %d\n"), - progname, lp->l_value); - (void) exit(EXIT_FAILURE); - } - } - ifree((char *) fields); - } - if (ferror(fp)) { - (void) fprintf(stderr, _("%s: Error reading %s\n"), - progname, filename); - (void) exit(EXIT_FAILURE); - } - if (fp != stdin && fclose(fp)) { - const char *e = strerror(errno); - - (void) fprintf(stderr, _("%s: Error closing %s: %s\n"), - progname, filename, e); - (void) exit(EXIT_FAILURE); - } - if (wantcont) - error(_("expected continuation line not found")); -} - -/* -** Convert a string of one of the forms -** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss -** into a number of seconds. -** A null string maps to zero. -** Call error with errstring and return zero on errors. -*/ - -static long -gethms(string, errstring, signable) -const char * string; -const char * const errstring; -const int signable; -{ - int hh, mm, ss, sign; - - if (string == NULL || *string == '\0') - return 0; - if (!signable) - sign = 1; - else if (*string == '-') { - sign = -1; - ++string; - } else sign = 1; - if (sscanf(string, scheck(string, "%d"), &hh) == 1) - mm = ss = 0; - else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2) - ss = 0; - else if (sscanf(string, scheck(string, "%d:%d:%d"), - &hh, &mm, &ss) != 3) { - error(errstring); - return 0; - } - if ((hh < 0 || hh >= HOURSPERDAY || - mm < 0 || mm >= MINSPERHOUR || - ss < 0 || ss > SECSPERMIN) && - !(hh == HOURSPERDAY && mm == 0 && ss == 0)) { - error(errstring); - return 0; - } - if (noise && hh == HOURSPERDAY) - warning(_("24:00 not handled by pre-1998 versions of zic")); - return eitol(sign) * - (eitol(hh * MINSPERHOUR + mm) * - eitol(SECSPERMIN) + eitol(ss)); -} - -static void -inrule(fields, nfields) -register char ** const fields; -const int nfields; -{ - static struct rule r; - - if (nfields != RULE_FIELDS) { - error(_("wrong number of fields on Rule line")); - return; - } - if (*fields[RF_NAME] == '\0') { - error(_("nameless rule")); - return; - } - r.r_filename = filename; - r.r_linenum = linenum; - r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE); - rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], - fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); - r.r_name = ecpyalloc(fields[RF_NAME]); - r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); - rules = (struct rule *) (void *) erealloc((char *) rules, - (int) ((nrules + 1) * sizeof *rules)); - rules[nrules++] = r; -} - -static int -inzone(fields, nfields) -register char ** const fields; -const int nfields; -{ - register int i; - static char * buf; - - if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { - error(_("wrong number of fields on Zone line")); - return FALSE; - } - if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) { - buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT))); - (void) sprintf(buf, -_("\"Zone %s\" line and -l option are mutually exclusive"), - TZDEFAULT); - error(buf); - return FALSE; - } - if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { - buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES))); - (void) sprintf(buf, -_("\"Zone %s\" line and -p option are mutually exclusive"), - TZDEFRULES); - error(buf); - return FALSE; - } - for (i = 0; i < nzones; ++i) - if (zones[i].z_name != NULL && - strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { - buf = erealloc(buf, (int) (132 + - strlen(fields[ZF_NAME]) + - strlen(zones[i].z_filename))); - (void) sprintf(buf, -_("duplicate zone name %s (file \"%s\", line %d)"), - fields[ZF_NAME], - zones[i].z_filename, - zones[i].z_linenum); - error(buf); - return FALSE; - } - return inzsub(fields, nfields, FALSE); -} - -static int -inzcont(fields, nfields) -register char ** const fields; -const int nfields; -{ - if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { - error(_("wrong number of fields on Zone continuation line")); - return FALSE; - } - return inzsub(fields, nfields, TRUE); -} - -static int -inzsub(fields, nfields, iscont) -register char ** const fields; -const int nfields; -const int iscont; -{ - register char * cp; - static struct zone z; - register int i_gmtoff, i_rule, i_format; - register int i_untilyear, i_untilmonth; - register int i_untilday, i_untiltime; - register int hasuntil; - - if (iscont) { - i_gmtoff = ZFC_GMTOFF; - i_rule = ZFC_RULE; - i_format = ZFC_FORMAT; - i_untilyear = ZFC_TILYEAR; - i_untilmonth = ZFC_TILMONTH; - i_untilday = ZFC_TILDAY; - i_untiltime = ZFC_TILTIME; - z.z_name = NULL; - } else { - i_gmtoff = ZF_GMTOFF; - i_rule = ZF_RULE; - i_format = ZF_FORMAT; - i_untilyear = ZF_TILYEAR; - i_untilmonth = ZF_TILMONTH; - i_untilday = ZF_TILDAY; - i_untiltime = ZF_TILTIME; - z.z_name = ecpyalloc(fields[ZF_NAME]); - } - z.z_filename = filename; - z.z_linenum = linenum; - z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE); - if ((cp = strchr(fields[i_format], '%')) != 0) { - if (*++cp != 's' || strchr(cp, '%') != 0) { - error(_("invalid abbreviation format")); - return FALSE; - } - } - z.z_rule = ecpyalloc(fields[i_rule]); - z.z_format = ecpyalloc(fields[i_format]); - hasuntil = nfields > i_untilyear; - if (hasuntil) { - z.z_untilrule.r_filename = filename; - z.z_untilrule.r_linenum = linenum; - rulesub(&z.z_untilrule, - fields[i_untilyear], - "only", - "", - (nfields > i_untilmonth) ? - fields[i_untilmonth] : "Jan", - (nfields > i_untilday) ? fields[i_untilday] : "1", - (nfields > i_untiltime) ? fields[i_untiltime] : "0"); - z.z_untiltime = rpytime(&z.z_untilrule, - z.z_untilrule.r_loyear); - if (iscont && nzones > 0 && - z.z_untiltime > min_time && - z.z_untiltime < max_time && - zones[nzones - 1].z_untiltime > min_time && - zones[nzones - 1].z_untiltime < max_time && - zones[nzones - 1].z_untiltime >= z.z_untiltime) { - error(_("Zone continuation line end time is not after end time of previous line")); - return FALSE; - } - } - zones = (struct zone *) (void *) erealloc((char *) zones, - (int) ((nzones + 1) * sizeof *zones)); - zones[nzones++] = z; - /* - ** If there was an UNTIL field on this line, - ** there's more information about the zone on the next line. - */ - return hasuntil; -} - -static void -inleap(fields, nfields) -register char ** const fields; -const int nfields; -{ - register const char * cp; - register const struct lookup * lp; - register int i, j; - int year, month, day; - long dayoff, tod; - time_t t; - - if (nfields != LEAP_FIELDS) { - error(_("wrong number of fields on Leap line")); - return; - } - dayoff = 0; - cp = fields[LP_YEAR]; - if (sscanf(cp, scheck(cp, "%d"), &year) != 1) { - /* - * Leapin' Lizards! - */ - error(_("invalid leaping year")); - return; - } - j = EPOCH_YEAR; - while (j != year) { - if (year > j) { - i = len_years[isleap(j)]; - ++j; - } else { - --j; - i = -len_years[isleap(j)]; - } - dayoff = oadd(dayoff, eitol(i)); - } - if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { - error(_("invalid month name")); - return; - } - month = lp->l_value; - j = TM_JANUARY; - while (j != month) { - i = len_months[isleap(year)][j]; - dayoff = oadd(dayoff, eitol(i)); - ++j; - } - cp = fields[LP_DAY]; - if (sscanf(cp, scheck(cp, "%d"), &day) != 1 || - day <= 0 || day > len_months[isleap(year)][month]) { - error(_("invalid day of month")); - return; - } - dayoff = oadd(dayoff, eitol(day - 1)); - if (dayoff < 0 && !TYPE_SIGNED(time_t)) { - error(_("time before zero")); - return; - } - if (dayoff < min_time / SECSPERDAY) { - error(_("time too small")); - return; - } - if (dayoff > max_time / SECSPERDAY) { - error(_("time too large")); - return; - } - t = (time_t) dayoff * SECSPERDAY; - tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); - cp = fields[LP_CORR]; - { - register int positive; - int count; - - if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ - positive = FALSE; - count = 1; - } else if (strcmp(cp, "--") == 0) { - positive = FALSE; - count = 2; - } else if (strcmp(cp, "+") == 0) { - positive = TRUE; - count = 1; - } else if (strcmp(cp, "++") == 0) { - positive = TRUE; - count = 2; - } else { - error(_("illegal CORRECTION field on Leap line")); - return; - } - if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) { - error(_("illegal Rolling/Stationary field on Leap line")); - return; - } - leapadd(tadd(t, tod), positive, lp->l_value, count); - } -} - -static void -inlink(fields, nfields) -register char ** const fields; -const int nfields; -{ - struct link l; - - if (nfields != LINK_FIELDS) { - error(_("wrong number of fields on Link line")); - return; - } - if (*fields[LF_FROM] == '\0') { - error(_("blank FROM field on Link line")); - return; - } - if (*fields[LF_TO] == '\0') { - error(_("blank TO field on Link line")); - return; - } - l.l_filename = filename; - l.l_linenum = linenum; - l.l_from = ecpyalloc(fields[LF_FROM]); - l.l_to = ecpyalloc(fields[LF_TO]); - links = (struct link *) (void *) erealloc((char *) links, - (int) ((nlinks + 1) * sizeof *links)); - links[nlinks++] = l; -} - -static void -rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep) -register struct rule * const rp; -const char * const loyearp; -const char * const hiyearp; -const char * const typep; -const char * const monthp; -const char * const dayp; -const char * const timep; -{ - register const struct lookup * lp; - register const char * cp; - register char * dp; - register char * ep; - - if ((lp = byword(monthp, mon_names)) == NULL) { - error(_("invalid month name")); - return; - } - rp->r_month = lp->l_value; - rp->r_todisstd = FALSE; - rp->r_todisgmt = FALSE; - dp = ecpyalloc(timep); - if (*dp != '\0') { - ep = dp + strlen(dp) - 1; - switch (lowerit(*ep)) { - case 's': /* Standard */ - rp->r_todisstd = TRUE; - rp->r_todisgmt = FALSE; - *ep = '\0'; - break; - case 'w': /* Wall */ - rp->r_todisstd = FALSE; - rp->r_todisgmt = FALSE; - *ep = '\0'; - break; - case 'g': /* Greenwich */ - case 'u': /* Universal */ - case 'z': /* Zulu */ - rp->r_todisstd = TRUE; - rp->r_todisgmt = TRUE; - *ep = '\0'; - break; - } - } - rp->r_tod = gethms(dp, _("invalid time of day"), FALSE); - ifree(dp); - /* - ** Year work. - */ - cp = loyearp; - lp = byword(cp, begin_years); - if (lp != NULL) switch ((int) lp->l_value) { - case YR_MINIMUM: - rp->r_loyear = INT_MIN; - break; - case YR_MAXIMUM: - rp->r_loyear = INT_MAX; - break; - default: /* "cannot happen" */ - (void) fprintf(stderr, - _("%s: panic: Invalid l_value %d\n"), - progname, lp->l_value); - (void) exit(EXIT_FAILURE); - } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) { - error(_("invalid starting year")); - return; - } else if (noise) { - if (rp->r_loyear < min_year_representable) - warning(_("starting year too low to be represented")); - else if (rp->r_loyear > max_year_representable) - warning(_("starting year too high to be represented")); - } - cp = hiyearp; - if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) { - case YR_MINIMUM: - rp->r_hiyear = INT_MIN; - break; - case YR_MAXIMUM: - rp->r_hiyear = INT_MAX; - break; - case YR_ONLY: - rp->r_hiyear = rp->r_loyear; - break; - default: /* "cannot happen" */ - (void) fprintf(stderr, - _("%s: panic: Invalid l_value %d\n"), - progname, lp->l_value); - (void) exit(EXIT_FAILURE); - } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) { - error(_("invalid ending year")); - return; - } else if (noise) { - if (rp->r_loyear < min_year_representable) - warning(_("ending year too low to be represented")); - else if (rp->r_loyear > max_year_representable) - warning(_("ending year too high to be represented")); - } - if (rp->r_loyear > rp->r_hiyear) { - error(_("starting year greater than ending year")); - return; - } - if (*typep == '\0') - rp->r_yrtype = NULL; - else { - if (rp->r_loyear == rp->r_hiyear) { - error(_("typed single year")); - return; - } - rp->r_yrtype = ecpyalloc(typep); - } - if (rp->r_loyear < min_year && rp->r_loyear > 0) - min_year = rp->r_loyear; - /* - ** Day work. - ** Accept things such as: - ** 1 - ** last-Sunday - ** Sun<=20 - ** Sun>=7 - */ - dp = ecpyalloc(dayp); - if ((lp = byword(dp, lasts)) != NULL) { - rp->r_dycode = DC_DOWLEQ; - rp->r_wday = lp->l_value; - rp->r_dayofmonth = len_months[1][rp->r_month]; - } else { - if ((ep = strchr(dp, '<')) != 0) - rp->r_dycode = DC_DOWLEQ; - else if ((ep = strchr(dp, '>')) != 0) - rp->r_dycode = DC_DOWGEQ; - else { - ep = dp; - rp->r_dycode = DC_DOM; - } - if (rp->r_dycode != DC_DOM) { - *ep++ = 0; - if (*ep++ != '=') { - error(_("invalid day of month")); - ifree(dp); - return; - } - if ((lp = byword(dp, wday_names)) == NULL) { - error(_("invalid weekday name")); - ifree(dp); - return; - } - rp->r_wday = lp->l_value; - } - if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 || - rp->r_dayofmonth <= 0 || - (rp->r_dayofmonth > len_months[1][rp->r_month])) { - error(_("invalid day of month")); - ifree(dp); - return; - } - } - ifree(dp); -} - -static void -convert(val, buf) -const long val; -char * const buf; -{ - register int i; - register long shift; - - for (i = 0, shift = 24; i < 4; ++i, shift -= 8) - buf[i] = val >> shift; -} - -static void -puttzcode(val, fp) -const long val; -FILE * const fp; -{ - char buf[4]; - - convert(val, buf); - (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); -} - -static int -atcomp(avp, bvp) -void * avp; -void * bvp; -{ - if (((struct attype *) avp)->at < ((struct attype *) bvp)->at) - return -1; - else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at) - return 1; - else return 0; -} - -static void -writezone(name) -const char * const name; -{ - register FILE * fp; - register int i, j; - static char * fullname; - static struct tzhead tzh; - time_t ats[TZ_MAX_TIMES]; - unsigned char types[TZ_MAX_TIMES]; - - /* - ** Sort. - */ - if (timecnt > 1) - (void) qsort((void *) attypes, (size_t) timecnt, - (size_t) sizeof *attypes, atcomp); - /* - ** Optimize. - */ - { - int fromi; - int toi; - - toi = 0; - fromi = 0; - while (fromi < timecnt && attypes[fromi].at < min_time) - ++fromi; - if (isdsts[0] == 0) - while (fromi < timecnt && attypes[fromi].type == 0) - ++fromi; /* handled by default rule */ - for ( ; fromi < timecnt; ++fromi) { - if (toi != 0 - && ((attypes[fromi].at - + gmtoffs[attypes[toi - 1].type]) - <= (attypes[toi - 1].at - + gmtoffs[toi == 1 ? 0 - : attypes[toi - 2].type]))) { - attypes[toi - 1].type = attypes[fromi].type; - continue; - } - if (toi == 0 || - attypes[toi - 1].type != attypes[fromi].type) - attypes[toi++] = attypes[fromi]; - } - timecnt = toi; - } - /* - ** Transfer. - */ - for (i = 0; i < timecnt; ++i) { - ats[i] = attypes[i].at; - types[i] = attypes[i].type; - } - fullname = erealloc(fullname, - (int) (strlen(directory) + 1 + strlen(name) + 1)); - (void) sprintf(fullname, "%s/%s", directory, name); - /* - ** Remove old file, if any, to snap links. - */ - if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) { - const char *e = strerror(errno); - - (void) fprintf(stderr, _("%s: Can't remove %s: %s\n"), - progname, fullname, e); - (void) exit(EXIT_FAILURE); - } - if ((fp = fopen(fullname, "wb")) == NULL) { - if (mkdirs(fullname) != 0) - (void) exit(EXIT_FAILURE); - if ((fp = fopen(fullname, "wb")) == NULL) { - const char *e = strerror(errno); - - (void) fprintf(stderr, _("%s: Can't create %s: %s\n"), - progname, fullname, e); - (void) exit(EXIT_FAILURE); - } - } - convert(eitol(typecnt), tzh.tzh_ttisgmtcnt); - convert(eitol(typecnt), tzh.tzh_ttisstdcnt); - convert(eitol(leapcnt), tzh.tzh_leapcnt); - convert(eitol(timecnt), tzh.tzh_timecnt); - convert(eitol(typecnt), tzh.tzh_typecnt); - convert(eitol(charcnt), tzh.tzh_charcnt); - (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); -#define DO(field) (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp) - DO(tzh_magic); - DO(tzh_reserved); - DO(tzh_ttisgmtcnt); - DO(tzh_ttisstdcnt); - DO(tzh_leapcnt); - DO(tzh_timecnt); - DO(tzh_typecnt); - DO(tzh_charcnt); -#undef DO - for (i = 0; i < timecnt; ++i) { - j = leapcnt; - while (--j >= 0) - if (ats[i] >= trans[j]) { - ats[i] = tadd(ats[i], corr[j]); - break; - } - puttzcode((long) ats[i], fp); - } - if (timecnt > 0) - (void) fwrite((void *) types, (size_t) sizeof types[0], - (size_t) timecnt, fp); - for (i = 0; i < typecnt; ++i) { - puttzcode((long) gmtoffs[i], fp); - (void) putc(isdsts[i], fp); - (void) putc(abbrinds[i], fp); - } - if (charcnt != 0) - (void) fwrite((void *) chars, (size_t) sizeof chars[0], - (size_t) charcnt, fp); - for (i = 0; i < leapcnt; ++i) { - if (roll[i]) { - if (timecnt == 0 || trans[i] < ats[0]) { - j = 0; - while (isdsts[j]) - if (++j >= typecnt) { - j = 0; - break; - } - } else { - j = 1; - while (j < timecnt && trans[i] >= ats[j]) - ++j; - j = types[j - 1]; - } - puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp); - } else puttzcode((long) trans[i], fp); - puttzcode((long) corr[i], fp); - } - for (i = 0; i < typecnt; ++i) - (void) putc(ttisstds[i], fp); - for (i = 0; i < typecnt; ++i) - (void) putc(ttisgmts[i], fp); - if (ferror(fp) || fclose(fp)) { - (void) fprintf(stderr, _("%s: Error writing %s\n"), - progname, fullname); - (void) exit(EXIT_FAILURE); - } -} - -static void -doabbr(abbr, format, letters, isdst) -char * const abbr; -const char * const format; -const char * const letters; -const int isdst; -{ - if (strchr(format, '/') == NULL) { - if (letters == NULL) - (void) strcpy(abbr, format); - else (void) sprintf(abbr, format, letters); - } else if (isdst) - (void) strcpy(abbr, strchr(format, '/') + 1); - else { - (void) strcpy(abbr, format); - *strchr(abbr, '/') = '\0'; - } -} - -static void -outzone(zpfirst, zonecount) -const struct zone * const zpfirst; -const int zonecount; -{ - register const struct zone * zp; - register struct rule * rp; - register int i, j; - register int usestart, useuntil; - register time_t starttime, untiltime; - register long gmtoff; - register long stdoff; - register int year; - register long startoff; - register int startttisstd; - register int startttisgmt; - register int type; - char startbuf[BUFSIZ]; - - INITIALIZE(untiltime); - INITIALIZE(starttime); - /* - ** Now. . .finally. . .generate some useful data! - */ - timecnt = 0; - typecnt = 0; - charcnt = 0; - /* - ** Thanks to Earl Chew (earl@dnd.icp.nec.com.au) - ** for noting the need to unconditionally initialize startttisstd. - */ - startttisstd = FALSE; - startttisgmt = FALSE; - for (i = 0; i < zonecount; ++i) { - /* - ** A guess that may well be corrected later. - */ - stdoff = 0; - zp = &zpfirst[i]; - usestart = i > 0 && (zp - 1)->z_untiltime > min_time; - useuntil = i < (zonecount - 1); - if (useuntil && zp->z_untiltime <= min_time) - continue; - gmtoff = zp->z_gmtoff; - eat(zp->z_filename, zp->z_linenum); - *startbuf = '\0'; - startoff = zp->z_gmtoff; - if (zp->z_nrules == 0) { - stdoff = zp->z_stdoff; - doabbr(startbuf, zp->z_format, - (char *) NULL, stdoff != 0); - type = addtype(oadd(zp->z_gmtoff, stdoff), - startbuf, stdoff != 0, startttisstd, - startttisgmt); - if (usestart) { - addtt(starttime, type); - usestart = FALSE; - } else if (stdoff != 0) - addtt(min_time, type); - } else for (year = min_year; year <= max_year; ++year) { - if (useuntil && year > zp->z_untilrule.r_hiyear) - break; - /* - ** Mark which rules to do in the current year. - ** For those to do, calculate rpytime(rp, year); - */ - for (j = 0; j < zp->z_nrules; ++j) { - rp = &zp->z_rules[j]; - eats(zp->z_filename, zp->z_linenum, - rp->r_filename, rp->r_linenum); - rp->r_todo = year >= rp->r_loyear && - year <= rp->r_hiyear && - yearistype(year, rp->r_yrtype); - if (rp->r_todo) - rp->r_temp = rpytime(rp, year); - } - for ( ; ; ) { - register int k; - register time_t jtime, ktime; - register long offset; - char buf[BUFSIZ]; - - INITIALIZE(ktime); - if (useuntil) { - /* - ** Turn untiltime into UTC - ** assuming the current gmtoff and - ** stdoff values. - */ - untiltime = zp->z_untiltime; - if (!zp->z_untilrule.r_todisgmt) - untiltime = tadd(untiltime, - -gmtoff); - if (!zp->z_untilrule.r_todisstd) - untiltime = tadd(untiltime, - -stdoff); - } - /* - ** Find the rule (of those to do, if any) - ** that takes effect earliest in the year. - */ - k = -1; - for (j = 0; j < zp->z_nrules; ++j) { - rp = &zp->z_rules[j]; - if (!rp->r_todo) - continue; - eats(zp->z_filename, zp->z_linenum, - rp->r_filename, rp->r_linenum); - offset = rp->r_todisgmt ? 0 : gmtoff; - if (!rp->r_todisstd) - offset = oadd(offset, stdoff); - jtime = rp->r_temp; - if (jtime == min_time || - jtime == max_time) - continue; - jtime = tadd(jtime, -offset); - if (k < 0 || jtime < ktime) { - k = j; - ktime = jtime; - } - } - if (k < 0) - break; /* go on to next year */ - rp = &zp->z_rules[k]; - rp->r_todo = FALSE; - if (useuntil && ktime >= untiltime) - break; - stdoff = rp->r_stdoff; - if (usestart && ktime == starttime) - usestart = FALSE; - if (usestart) { - if (ktime < starttime) { - startoff = oadd(zp->z_gmtoff, - stdoff); - doabbr(startbuf, zp->z_format, - rp->r_abbrvar, - rp->r_stdoff != 0); - continue; - } - if (*startbuf == '\0' && - startoff == oadd(zp->z_gmtoff, - stdoff)) { - doabbr(startbuf, zp->z_format, - rp->r_abbrvar, - rp->r_stdoff != 0); - } - } - eats(zp->z_filename, zp->z_linenum, - rp->r_filename, rp->r_linenum); - doabbr(buf, zp->z_format, rp->r_abbrvar, - rp->r_stdoff != 0); - offset = oadd(zp->z_gmtoff, rp->r_stdoff); - type = addtype(offset, buf, rp->r_stdoff != 0, - rp->r_todisstd, rp->r_todisgmt); - addtt(ktime, type); - } - } - if (usestart) { - if (*startbuf == '\0' && - zp->z_format != NULL && - strchr(zp->z_format, '%') == NULL && - strchr(zp->z_format, '/') == NULL) - (void) strcpy(startbuf, zp->z_format); - eat(zp->z_filename, zp->z_linenum); - if (*startbuf == '\0') -error(_("can't determine time zone abbreviation to use just after until time")); - else addtt(starttime, - addtype(startoff, startbuf, - startoff != zp->z_gmtoff, - startttisstd, - startttisgmt)); - } - /* - ** Now we may get to set starttime for the next zone line. - */ - if (useuntil) { - startttisstd = zp->z_untilrule.r_todisstd; - startttisgmt = zp->z_untilrule.r_todisgmt; - starttime = zp->z_untiltime; - if (!startttisstd) - starttime = tadd(starttime, -stdoff); - if (!startttisgmt) - starttime = tadd(starttime, -gmtoff); - } - } - writezone(zpfirst->z_name); -} - -static void -addtt(starttime, type) -const time_t starttime; -int type; -{ - if (starttime <= min_time || - (timecnt == 1 && attypes[0].at < min_time)) { - gmtoffs[0] = gmtoffs[type]; - isdsts[0] = isdsts[type]; - ttisstds[0] = ttisstds[type]; - ttisgmts[0] = ttisgmts[type]; - if (abbrinds[type] != 0) - (void) strcpy(chars, &chars[abbrinds[type]]); - abbrinds[0] = 0; - charcnt = strlen(chars) + 1; - typecnt = 1; - timecnt = 0; - type = 0; - } - if (timecnt >= TZ_MAX_TIMES) { - error(_("too many transitions?!")); - (void) exit(EXIT_FAILURE); - } - attypes[timecnt].at = starttime; - attypes[timecnt].type = type; - ++timecnt; -} - -static int -addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt) -const long gmtoff; -const char * const abbr; -const int isdst; -const int ttisstd; -const int ttisgmt; -{ - register int i, j; - - if (isdst != TRUE && isdst != FALSE) { - error(_("internal error - addtype called with bad isdst")); - (void) exit(EXIT_FAILURE); - } - if (ttisstd != TRUE && ttisstd != FALSE) { - error(_("internal error - addtype called with bad ttisstd")); - (void) exit(EXIT_FAILURE); - } - if (ttisgmt != TRUE && ttisgmt != FALSE) { - error(_("internal error - addtype called with bad ttisgmt")); - (void) exit(EXIT_FAILURE); - } - /* - ** See if there's already an entry for this zone type. - ** If so, just return its index. - */ - for (i = 0; i < typecnt; ++i) { - if (gmtoff == gmtoffs[i] && isdst == isdsts[i] && - strcmp(abbr, &chars[abbrinds[i]]) == 0 && - ttisstd == ttisstds[i] && - ttisgmt == ttisgmts[i]) - return i; - } - /* - ** There isn't one; add a new one, unless there are already too - ** many. - */ - if (typecnt >= TZ_MAX_TYPES) { - error(_("too many local time types")); - (void) exit(EXIT_FAILURE); - } - gmtoffs[i] = gmtoff; - isdsts[i] = isdst; - ttisstds[i] = ttisstd; - ttisgmts[i] = ttisgmt; - - for (j = 0; j < charcnt; ++j) - if (strcmp(&chars[j], abbr) == 0) - break; - if (j == charcnt) - newabbr(abbr); - abbrinds[i] = j; - ++typecnt; - return i; -} - -static void -leapadd(t, positive, rolling, count) -const time_t t; -const int positive; -const int rolling; -int count; -{ - register int i, j; - - if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) { - error(_("too many leap seconds")); - (void) exit(EXIT_FAILURE); - } - for (i = 0; i < leapcnt; ++i) - if (t <= trans[i]) { - if (t == trans[i]) { - error(_("repeated leap second moment")); - (void) exit(EXIT_FAILURE); - } - break; - } - do { - for (j = leapcnt; j > i; --j) { - trans[j] = trans[j - 1]; - corr[j] = corr[j - 1]; - roll[j] = roll[j - 1]; - } - trans[i] = t; - corr[i] = positive ? 1L : eitol(-count); - roll[i] = rolling; - ++leapcnt; - } while (positive && --count != 0); -} - -static void -adjleap P((void)) -{ - register int i; - register long last = 0; - - /* - ** propagate leap seconds forward - */ - for (i = 0; i < leapcnt; ++i) { - trans[i] = tadd(trans[i], last); - last = corr[i] += last; - } -} - -static int -yearistype(year, type) -const int year; -const char * const type; -{ - static char * buf; - int result; - - if (type == NULL || *type == '\0') - return TRUE; - buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type))); - (void) sprintf(buf, "%s %d %s", yitcommand, year, type); - result = system(buf); - if (WIFEXITED(result)) switch (WEXITSTATUS(result)) { - case 0: - return TRUE; - case 1: - return FALSE; - } - error(_("Wild result from command execution")); - (void) fprintf(stderr, _("%s: command was '%s', result was %d\n"), - progname, buf, result); - for ( ; ; ) - (void) exit(EXIT_FAILURE); -} - -static int -lowerit(a) -int a; -{ - a = (unsigned char) a; - return (isascii(a) && isupper(a)) ? tolower(a) : a; -} - -static int -ciequal(ap, bp) /* case-insensitive equality */ -register const char * ap; -register const char * bp; -{ - while (lowerit(*ap) == lowerit(*bp++)) - if (*ap++ == '\0') - return TRUE; - return FALSE; -} - -static int -itsabbr(abbr, word) -register const char * abbr; -register const char * word; -{ - if (lowerit(*abbr) != lowerit(*word)) - return FALSE; - ++word; - while (*++abbr != '\0') - do { - if (*word == '\0') - return FALSE; - } while (lowerit(*word++) != lowerit(*abbr)); - return TRUE; -} - -static const struct lookup * -byword(word, table) -register const char * const word; -register const struct lookup * const table; -{ - register const struct lookup * foundlp; - register const struct lookup * lp; - - if (word == NULL || table == NULL) - return NULL; - /* - ** Look for exact match. - */ - for (lp = table; lp->l_word != NULL; ++lp) - if (ciequal(word, lp->l_word)) - return lp; - /* - ** Look for inexact match. - */ - foundlp = NULL; - for (lp = table; lp->l_word != NULL; ++lp) - if (itsabbr(word, lp->l_word)) { - if (foundlp == NULL) - foundlp = lp; - else return NULL; /* multiple inexact matches */ - } - return foundlp; -} - -static char ** -getfields(cp) -register char * cp; -{ - register char * dp; - register char ** array; - register int nsubs; - - if (cp == NULL) - return NULL; - array = (char **) (void *) - emalloc((int) ((strlen(cp) + 1) * sizeof *array)); - nsubs = 0; - for ( ; ; ) { - while (isascii(*cp) && isspace((unsigned char) *cp)) - ++cp; - if (*cp == '\0' || *cp == '#') - break; - array[nsubs++] = dp = cp; - do { - if ((*dp = *cp++) != '"') - ++dp; - else while ((*dp = *cp++) != '"') - if (*dp != '\0') - ++dp; - else error(_("Odd number of quotation marks")); - } while (*cp != '\0' && *cp != '#' && - (!isascii(*cp) || !isspace((unsigned char) *cp))); - if (isascii(*cp) && isspace((unsigned char) *cp)) - ++cp; - *dp = '\0'; - } - array[nsubs] = NULL; - return array; -} - -static long -oadd(t1, t2) -const long t1; -const long t2; -{ - register long t; - - t = t1 + t2; - if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { - error(_("time overflow")); - (void) exit(EXIT_FAILURE); - } - return t; -} - -static time_t -tadd(t1, t2) -const time_t t1; -const long t2; -{ - register time_t t; - - if (t1 == max_time && t2 > 0) - return max_time; - if (t1 == min_time && t2 < 0) - return min_time; - t = t1 + t2; - if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { - error(_("time overflow")); - (void) exit(EXIT_FAILURE); - } - return t; -} - -/* -** Given a rule, and a year, compute the date - in seconds since January 1, -** 1970, 00:00 LOCAL time - in that year that the rule refers to. -*/ - -static time_t -rpytime(rp, wantedy) -register const struct rule * const rp; -register const int wantedy; -{ - register int y, m, i; - register long dayoff; /* with a nod to Margaret O. */ - register time_t t; - - if (wantedy == INT_MIN) - return min_time; - if (wantedy == INT_MAX) - return max_time; - dayoff = 0; - m = TM_JANUARY; - y = EPOCH_YEAR; - while (wantedy != y) { - if (wantedy > y) { - i = len_years[isleap(y)]; - ++y; - } else { - --y; - i = -len_years[isleap(y)]; - } - dayoff = oadd(dayoff, eitol(i)); - } - while (m != rp->r_month) { - i = len_months[isleap(y)][m]; - dayoff = oadd(dayoff, eitol(i)); - ++m; - } - i = rp->r_dayofmonth; - if (m == TM_FEBRUARY && i == 29 && !isleap(y)) { - if (rp->r_dycode == DC_DOWLEQ) - --i; - else { - error(_("use of 2/29 in non leap-year")); - (void) exit(EXIT_FAILURE); - } - } - --i; - dayoff = oadd(dayoff, eitol(i)); - if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { - register long wday; - -#define LDAYSPERWEEK ((long) DAYSPERWEEK) - wday = eitol(EPOCH_WDAY); - /* - ** Don't trust mod of negative numbers. - */ - if (dayoff >= 0) - wday = (wday + dayoff) % LDAYSPERWEEK; - else { - wday -= ((-dayoff) % LDAYSPERWEEK); - if (wday < 0) - wday += LDAYSPERWEEK; - } - while (wday != eitol(rp->r_wday)) - if (rp->r_dycode == DC_DOWGEQ) { - dayoff = oadd(dayoff, (long) 1); - if (++wday >= LDAYSPERWEEK) - wday = 0; - ++i; - } else { - dayoff = oadd(dayoff, (long) -1); - if (--wday < 0) - wday = LDAYSPERWEEK - 1; - --i; - } - if (i < 0 || i >= len_months[isleap(y)][m]) { - error(_("no day in month matches rule")); - (void) exit(EXIT_FAILURE); - } - } - if (dayoff < 0 && !TYPE_SIGNED(time_t)) - return min_time; - if (dayoff < min_time / SECSPERDAY) - return min_time; - if (dayoff > max_time / SECSPERDAY) - return max_time; - t = (time_t) dayoff * SECSPERDAY; - return tadd(t, rp->r_tod); -} - -static void -newabbr(string) -const char * const string; -{ - register int i; - - i = strlen(string) + 1; - if (charcnt + i > TZ_MAX_CHARS) { - error(_("too many, or too long, time zone abbreviations")); - (void) exit(EXIT_FAILURE); - } - (void) strcpy(&chars[charcnt], string); - charcnt += eitol(i); -} - -static int -mkdirs(argname) -char * const argname; -{ - register char * name; - register char * cp; - - if (argname == NULL || *argname == '\0') - return 0; - cp = name = ecpyalloc(argname); - while ((cp = strchr(cp + 1, '/')) != 0) { - *cp = '\0'; -#ifndef unix - /* - ** DOS drive specifier? - */ - if (isalpha((unsigned char) name[0]) && - name[1] == ':' && name[2] == '\0') { - *cp = '/'; - continue; - } -#endif /* !defined unix */ - if (!itsdir(name)) { - /* - ** It doesn't seem to exist, so we try to create it. - ** Creation may fail because of the directory being - ** created by some other multiprocessor, so we get - ** to do extra checking. - */ - if (mkdir(name, MKDIR_UMASK) != 0) { - const char *e = strerror(errno); - - if (errno != EEXIST || !itsdir(name)) { - (void) fprintf(stderr, -_("%s: Can't create directory %s: %s\n"), - progname, name, e); - ifree(name); - return -1; - } - } - } - *cp = '/'; - } - ifree(name); - return 0; -} - -static long -eitol(i) -const int i; -{ - long l; - - l = i; - if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) { - (void) fprintf(stderr, - _("%s: %d did not sign extend correctly\n"), - progname, i); - (void) exit(EXIT_FAILURE); - } - return l; -} - -/* -** UNIX was a registered trademark of The Open Group in 2003. -*/ - - -#ifdef WIN32 -/* - * To run on win32 - */ -int link(const char *oldpath, const char *newpath) { - if (!CopyFileEx(oldpath, newpath, NULL, NULL, FALSE, 0)) { - return -1; - } - return 0; -} -#endif +static char elsieid[] = "@(#)zic.c 7.115"; +#include "pgtz.h" +#undef unlink +#undef TZDIR +#define TZDIR "data" +#ifdef WIN32 +#include +#endif + +#include "private.h" +#include "locale.h" +#include "tzfile.h" + +#if HAVE_SYS_STAT_H +#include "sys/stat.h" +#endif +#ifndef WIN32 +#ifdef S_IRUSR +#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) +#else +#define MKDIR_UMASK 0755 +#endif +#endif + +/* +** On some ancient hosts, predicates like `isspace(C)' are defined +** only if isascii(C) || C == EOF. Modern hosts obey the C Standard, +** which says they are defined only if C == ((unsigned char) C) || C == EOF. +** Neither the C Standard nor Posix require that `isascii' exist. +** For portability, we check both ancient and modern requirements. +** If isascii is not defined, the isascii check succeeds trivially. +*/ +#include "ctype.h" +#ifndef isascii +#define isascii(x) 1 +#endif + +struct rule { + const char * r_filename; + int r_linenum; + const char * r_name; + + int r_loyear; /* for example, 1986 */ + int r_hiyear; /* for example, 1986 */ + const char * r_yrtype; + + int r_month; /* 0..11 */ + + int r_dycode; /* see below */ + int r_dayofmonth; + int r_wday; + + long r_tod; /* time from midnight */ + int r_todisstd; /* above is standard time if TRUE */ + /* or wall clock time if FALSE */ + int r_todisgmt; /* above is GMT if TRUE */ + /* or local time if FALSE */ + long r_stdoff; /* offset from standard time */ + const char * r_abbrvar; /* variable part of abbreviation */ + + int r_todo; /* a rule to do (used in outzone) */ + time_t r_temp; /* used in outzone */ +}; + +/* +** r_dycode r_dayofmonth r_wday +*/ + +#define DC_DOM 0 /* 1..31 */ /* unused */ +#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */ +#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */ + +struct zone { + const char * z_filename; + int z_linenum; + + const char * z_name; + long z_gmtoff; + const char * z_rule; + const char * z_format; + + long z_stdoff; + + struct rule * z_rules; + int z_nrules; + + struct rule z_untilrule; + time_t z_untiltime; +}; + +extern int getopt P((int argc, char * const argv[], + const char * options)); +extern int link P((const char * fromname, const char * toname)); +extern char * optarg; +extern int optind; + +static void addtt P((time_t starttime, int type)); +static int addtype P((long gmtoff, const char * abbr, int isdst, + int ttisstd, int ttisgmt)); +static void leapadd P((time_t t, int positive, int rolling, int count)); +static void adjleap P((void)); +static void associate P((void)); +static int ciequal P((const char * ap, const char * bp)); +static void convert P((long val, char * buf)); +static void dolink P((const char * fromfile, const char * tofile)); +static void doabbr P((char * abbr, const char * format, + const char * letters, int isdst)); +static void eat P((const char * name, int num)); +static void eats P((const char * name, int num, + const char * rname, int rnum)); +static long eitol P((int i)); +static void error P((const char * message)); +static char ** getfields P((char * buf)); +static long gethms P((const char * string, const char * errstrng, + int signable)); +static void infile P((const char * filename)); +static void inleap P((char ** fields, int nfields)); +static void inlink P((char ** fields, int nfields)); +static void inrule P((char ** fields, int nfields)); +static int inzcont P((char ** fields, int nfields)); +static int inzone P((char ** fields, int nfields)); +static int inzsub P((char ** fields, int nfields, int iscont)); +static int itsabbr P((const char * abbr, const char * word)); +static int itsdir P((const char * name)); +static int lowerit P((int c)); +static char * memcheck P((char * tocheck)); +static int mkdirs P((char * filename)); +static void newabbr P((const char * abbr)); +static long oadd P((long t1, long t2)); +static void outzone P((const struct zone * zp, int ntzones)); +static void puttzcode P((long code, FILE * fp)); +static int rcomp P((const void * leftp, const void * rightp)); +static time_t rpytime P((const struct rule * rp, int wantedy)); +static void rulesub P((struct rule * rp, + const char * loyearp, const char * hiyearp, + const char * typep, const char * monthp, + const char * dayp, const char * timep)); +static void setboundaries P((void)); +static time_t tadd P((time_t t1, long t2)); +static void usage P((void)); +static void writezone P((const char * name)); +static int yearistype P((int year, const char * type)); + +#if !(HAVE_STRERROR - 0) +static char * strerror P((int)); +#endif /* !(HAVE_STRERROR - 0) */ + +static int charcnt; +static int errors; +static const char * filename; +static int leapcnt; +static int linenum; +static time_t max_time; +static int max_year; +static int max_year_representable; +static time_t min_time; +static int min_year; +static int min_year_representable; +static int noise; +static const char * rfilename; +static int rlinenum; +static const char * progname; +static int timecnt; +static int typecnt; + +/* +** Line codes. +*/ + +#define LC_RULE 0 +#define LC_ZONE 1 +#define LC_LINK 2 +#define LC_LEAP 3 + +/* +** Which fields are which on a Zone line. +*/ + +#define ZF_NAME 1 +#define ZF_GMTOFF 2 +#define ZF_RULE 3 +#define ZF_FORMAT 4 +#define ZF_TILYEAR 5 +#define ZF_TILMONTH 6 +#define ZF_TILDAY 7 +#define ZF_TILTIME 8 +#define ZONE_MINFIELDS 5 +#define ZONE_MAXFIELDS 9 + +/* +** Which fields are which on a Zone continuation line. +*/ + +#define ZFC_GMTOFF 0 +#define ZFC_RULE 1 +#define ZFC_FORMAT 2 +#define ZFC_TILYEAR 3 +#define ZFC_TILMONTH 4 +#define ZFC_TILDAY 5 +#define ZFC_TILTIME 6 +#define ZONEC_MINFIELDS 3 +#define ZONEC_MAXFIELDS 7 + +/* +** Which files are which on a Rule line. +*/ + +#define RF_NAME 1 +#define RF_LOYEAR 2 +#define RF_HIYEAR 3 +#define RF_COMMAND 4 +#define RF_MONTH 5 +#define RF_DAY 6 +#define RF_TOD 7 +#define RF_STDOFF 8 +#define RF_ABBRVAR 9 +#define RULE_FIELDS 10 + +/* +** Which fields are which on a Link line. +*/ + +#define LF_FROM 1 +#define LF_TO 2 +#define LINK_FIELDS 3 + +/* +** Which fields are which on a Leap line. +*/ + +#define LP_YEAR 1 +#define LP_MONTH 2 +#define LP_DAY 3 +#define LP_TIME 4 +#define LP_CORR 5 +#define LP_ROLL 6 +#define LEAP_FIELDS 7 + +/* +** Year synonyms. +*/ + +#define YR_MINIMUM 0 +#define YR_MAXIMUM 1 +#define YR_ONLY 2 + +static struct rule * rules; +static int nrules; /* number of rules */ + +static struct zone * zones; +static int nzones; /* number of zones */ + +struct link { + const char * l_filename; + int l_linenum; + const char * l_from; + const char * l_to; +}; + +static struct link * links; +static int nlinks; + +struct lookup { + const char * l_word; + const int l_value; +}; + +static struct lookup const * byword P((const char * string, + const struct lookup * lp)); + +static struct lookup const line_codes[] = { + { "Rule", LC_RULE }, + { "Zone", LC_ZONE }, + { "Link", LC_LINK }, + { "Leap", LC_LEAP }, + { NULL, 0} +}; + +static struct lookup const mon_names[] = { + { "January", TM_JANUARY }, + { "February", TM_FEBRUARY }, + { "March", TM_MARCH }, + { "April", TM_APRIL }, + { "May", TM_MAY }, + { "June", TM_JUNE }, + { "July", TM_JULY }, + { "August", TM_AUGUST }, + { "September", TM_SEPTEMBER }, + { "October", TM_OCTOBER }, + { "November", TM_NOVEMBER }, + { "December", TM_DECEMBER }, + { NULL, 0 } +}; + +static struct lookup const wday_names[] = { + { "Sunday", TM_SUNDAY }, + { "Monday", TM_MONDAY }, + { "Tuesday", TM_TUESDAY }, + { "Wednesday", TM_WEDNESDAY }, + { "Thursday", TM_THURSDAY }, + { "Friday", TM_FRIDAY }, + { "Saturday", TM_SATURDAY }, + { NULL, 0 } +}; + +static struct lookup const lasts[] = { + { "last-Sunday", TM_SUNDAY }, + { "last-Monday", TM_MONDAY }, + { "last-Tuesday", TM_TUESDAY }, + { "last-Wednesday", TM_WEDNESDAY }, + { "last-Thursday", TM_THURSDAY }, + { "last-Friday", TM_FRIDAY }, + { "last-Saturday", TM_SATURDAY }, + { NULL, 0 } +}; + +static struct lookup const begin_years[] = { + { "minimum", YR_MINIMUM }, + { "maximum", YR_MAXIMUM }, + { NULL, 0 } +}; + +static struct lookup const end_years[] = { + { "minimum", YR_MINIMUM }, + { "maximum", YR_MAXIMUM }, + { "only", YR_ONLY }, + { NULL, 0 } +}; + +static struct lookup const leap_types[] = { + { "Rolling", TRUE }, + { "Stationary", FALSE }, + { NULL, 0 } +}; + +static const int len_months[2][MONSPERYEAR] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; + +static const int len_years[2] = { + DAYSPERNYEAR, DAYSPERLYEAR +}; + +static struct attype { + time_t at; + unsigned char type; +} attypes[TZ_MAX_TIMES]; +static long gmtoffs[TZ_MAX_TYPES]; +static char isdsts[TZ_MAX_TYPES]; +static unsigned char abbrinds[TZ_MAX_TYPES]; +static char ttisstds[TZ_MAX_TYPES]; +static char ttisgmts[TZ_MAX_TYPES]; +static char chars[TZ_MAX_CHARS]; +static time_t trans[TZ_MAX_LEAPS]; +static long corr[TZ_MAX_LEAPS]; +static char roll[TZ_MAX_LEAPS]; + +/* +** Memory allocation. +*/ + +static char * +memcheck(ptr) +char * const ptr; +{ + if (ptr == NULL) { + const char *e = strerror(errno); + + (void) fprintf(stderr, _("%s: Memory exhausted: %s\n"), + progname, e); + (void) exit(EXIT_FAILURE); + } + return ptr; +} + +#define emalloc(size) memcheck(imalloc(size)) +#define erealloc(ptr, size) memcheck(irealloc((ptr), (size))) +#define ecpyalloc(ptr) memcheck(icpyalloc(ptr)) +#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp))) + +/* +** Error handling. +*/ + +#if !(HAVE_STRERROR - 0) +static char * +strerror(errnum) +int errnum; +{ + extern char * sys_errlist[]; + extern int sys_nerr; + + return (errnum > 0 && errnum <= sys_nerr) ? + sys_errlist[errnum] : _("Unknown system error"); +} +#endif /* !(HAVE_STRERROR - 0) */ + +static void +eats(name, num, rname, rnum) +const char * const name; +const int num; +const char * const rname; +const int rnum; +{ + filename = name; + linenum = num; + rfilename = rname; + rlinenum = rnum; +} + +static void +eat(name, num) +const char * const name; +const int num; +{ + eats(name, num, (char *) NULL, -1); +} + +static void +error(string) +const char * const string; +{ + /* + ** Match the format of "cc" to allow sh users to + ** zic ... 2>&1 | error -t "*" -v + ** on BSD systems. + */ + (void) fprintf(stderr, _("\"%s\", line %d: %s"), + filename, linenum, string); + if (rfilename != NULL) + (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"), + rfilename, rlinenum); + (void) fprintf(stderr, "\n"); + ++errors; +} + +static void +warning(string) +const char * const string; +{ + char * cp; + + cp = ecpyalloc(_("warning: ")); + cp = ecatalloc(cp, string); + error(cp); + ifree(cp); + --errors; +} + +static void +usage P((void)) +{ + (void) fprintf(stderr, _("%s: usage is %s [ --version ] [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"), + progname, progname); + (void) exit(EXIT_FAILURE); +} + +static const char * psxrules; +static const char * lcltime; +static const char * directory; +static const char * leapsec; +static const char * yitcommand; +static int sflag = FALSE; + +int +main(argc, argv) +int argc; +char * argv[]; +{ + register int i; + register int j; + register int c; + +#ifdef unix + (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); +#endif /* defined unix */ +#if HAVE_GETTEXT - 0 + (void) setlocale(LC_MESSAGES, ""); +#ifdef TZ_DOMAINDIR + (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); +#endif /* defined TEXTDOMAINDIR */ + (void) textdomain(TZ_DOMAIN); +#endif /* HAVE_GETTEXT - 0 */ + progname = argv[0]; + for (i = 1; i < argc; ++i) + if (strcmp(argv[i], "--version") == 0) { + (void) printf("%s\n", elsieid); + (void) exit(EXIT_SUCCESS); + } + while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1) + switch (c) { + default: + usage(); + case 'd': + if (directory == NULL) + directory = optarg; + else { + (void) fprintf(stderr, +_("%s: More than one -d option specified\n"), + progname); + (void) exit(EXIT_FAILURE); + } + break; + case 'l': + if (lcltime == NULL) + lcltime = optarg; + else { + (void) fprintf(stderr, +_("%s: More than one -l option specified\n"), + progname); + (void) exit(EXIT_FAILURE); + } + break; + case 'p': + if (psxrules == NULL) + psxrules = optarg; + else { + (void) fprintf(stderr, +_("%s: More than one -p option specified\n"), + progname); + (void) exit(EXIT_FAILURE); + } + break; + case 'y': + if (yitcommand == NULL) + yitcommand = optarg; + else { + (void) fprintf(stderr, +_("%s: More than one -y option specified\n"), + progname); + (void) exit(EXIT_FAILURE); + } + break; + case 'L': + if (leapsec == NULL) + leapsec = optarg; + else { + (void) fprintf(stderr, +_("%s: More than one -L option specified\n"), + progname); + (void) exit(EXIT_FAILURE); + } + break; + case 'v': + noise = TRUE; + break; + case 's': + sflag = TRUE; + break; + } + if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) + usage(); /* usage message by request */ + if (directory == NULL) + directory = TZDIR; + if (yitcommand == NULL) + yitcommand = "yearistype"; + + setboundaries(); + + if (optind < argc && leapsec != NULL) { + infile(leapsec); + adjleap(); + } + + for (i = optind; i < argc; ++i) + infile(argv[i]); + if (errors) + (void) exit(EXIT_FAILURE); + associate(); + for (i = 0; i < nzones; i = j) { + /* + ** Find the next non-continuation zone entry. + */ + for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) + continue; + outzone(&zones[i], j - i); + } + /* + ** Make links. + */ + for (i = 0; i < nlinks; ++i) { + eat(links[i].l_filename, links[i].l_linenum); + dolink(links[i].l_from, links[i].l_to); + } + if (lcltime != NULL) { + eat("command line", 1); + dolink(lcltime, TZDEFAULT); + } + if (psxrules != NULL) { + eat("command line", 1); + dolink(psxrules, TZDEFRULES); + } + return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static void +dolink(fromfile, tofile) +const char * const fromfile; +const char * const tofile; +{ + register char * fromname; + register char * toname; + + if (fromfile[0] == '/') + fromname = ecpyalloc(fromfile); + else { + fromname = ecpyalloc(directory); + fromname = ecatalloc(fromname, "/"); + fromname = ecatalloc(fromname, fromfile); + } + if (tofile[0] == '/') + toname = ecpyalloc(tofile); + else { + toname = ecpyalloc(directory); + toname = ecatalloc(toname, "/"); + toname = ecatalloc(toname, tofile); + } + /* + ** We get to be careful here since + ** there's a fair chance of root running us. + */ + if (!itsdir(toname)) + (void) remove(toname); + if (link(fromname, toname) != 0) { + int result; + + if (mkdirs(toname) != 0) + (void) exit(EXIT_FAILURE); + + result = link(fromname, toname); +#if (HAVE_SYMLINK - 0) + if (result != 0 && + access(fromname, F_OK) == 0 && + !itsdir(fromname)) { + const char *s = tofile; + register char * symlinkcontents = NULL; + while ((s = strchr(s+1, '/')) != NULL) + symlinkcontents = ecatalloc(symlinkcontents, "../"); + symlinkcontents = ecatalloc(symlinkcontents, fromfile); + + result = symlink(symlinkcontents, toname); + if (result == 0) +warning(_("hard link failed, symbolic link used")); + ifree(symlinkcontents); + } +#endif + if (result != 0) { + const char *e = strerror(errno); + + (void) fprintf(stderr, + _("%s: Can't link from %s to %s: %s\n"), + progname, fromname, toname, e); + (void) exit(EXIT_FAILURE); + } + } + ifree(fromname); + ifree(toname); +} + +#ifndef INT_MAX +#define INT_MAX ((int) (((unsigned)~0)>>1)) +#endif /* !defined INT_MAX */ + +#ifndef INT_MIN +#define INT_MIN ((int) ~(((unsigned)~0)>>1)) +#endif /* !defined INT_MIN */ + +/* +** The tz file format currently allows at most 32-bit quantities. +** This restriction should be removed before signed 32-bit values +** wrap around in 2038, but unfortunately this will require a +** change to the tz file format. +*/ + +#define MAX_BITS_IN_FILE 32 +#define TIME_T_BITS_IN_FILE ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE) + +static void +setboundaries P((void)) +{ + if (TYPE_SIGNED(time_t)) { + min_time = ~ (time_t) 0; + min_time <<= TIME_T_BITS_IN_FILE - 1; + max_time = ~ (time_t) 0 - min_time; + if (sflag) + min_time = 0; + } else { + min_time = 0; + max_time = 2 - sflag; + max_time <<= TIME_T_BITS_IN_FILE - 1; + --max_time; + } + min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year; + max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year; + min_year_representable = min_year; + max_year_representable = max_year; +} + +static int +itsdir(name) +const char * const name; +{ + register char * myname; + register int accres; + + myname = ecpyalloc(name); + myname = ecatalloc(myname, "/."); + accres = access(myname, F_OK); + ifree(myname); + return accres == 0; +} + +/* +** Associate sets of rules with zones. +*/ + +/* +** Sort by rule name. +*/ + +static int +rcomp(cp1, cp2) +const void * cp1; +const void * cp2; +{ + return strcmp(((const struct rule *) cp1)->r_name, + ((const struct rule *) cp2)->r_name); +} + +static void +associate P((void)) +{ + register struct zone * zp; + register struct rule * rp; + register int base, out; + register int i, j; + + if (nrules != 0) { + (void) qsort((void *) rules, (size_t) nrules, + (size_t) sizeof *rules, rcomp); + for (i = 0; i < nrules - 1; ++i) { + if (strcmp(rules[i].r_name, + rules[i + 1].r_name) != 0) + continue; + if (strcmp(rules[i].r_filename, + rules[i + 1].r_filename) == 0) + continue; + eat(rules[i].r_filename, rules[i].r_linenum); + warning(_("same rule name in multiple files")); + eat(rules[i + 1].r_filename, rules[i + 1].r_linenum); + warning(_("same rule name in multiple files")); + for (j = i + 2; j < nrules; ++j) { + if (strcmp(rules[i].r_name, + rules[j].r_name) != 0) + break; + if (strcmp(rules[i].r_filename, + rules[j].r_filename) == 0) + continue; + if (strcmp(rules[i + 1].r_filename, + rules[j].r_filename) == 0) + continue; + break; + } + i = j - 1; + } + } + for (i = 0; i < nzones; ++i) { + zp = &zones[i]; + zp->z_rules = NULL; + zp->z_nrules = 0; + } + for (base = 0; base < nrules; base = out) { + rp = &rules[base]; + for (out = base + 1; out < nrules; ++out) + if (strcmp(rp->r_name, rules[out].r_name) != 0) + break; + for (i = 0; i < nzones; ++i) { + zp = &zones[i]; + if (strcmp(zp->z_rule, rp->r_name) != 0) + continue; + zp->z_rules = rp; + zp->z_nrules = out - base; + } + } + for (i = 0; i < nzones; ++i) { + zp = &zones[i]; + if (zp->z_nrules == 0) { + /* + ** Maybe we have a local standard time offset. + */ + eat(zp->z_filename, zp->z_linenum); + zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), + TRUE); + /* + ** Note, though, that if there's no rule, + ** a '%s' in the format is a bad thing. + */ + if (strchr(zp->z_format, '%') != 0) + error(_("%s in ruleless zone")); + } + } + if (errors) + (void) exit(EXIT_FAILURE); +} + +static void +infile(name) +const char * name; +{ + register FILE * fp; + register char ** fields; + register char * cp; + register const struct lookup * lp; + register int nfields; + register int wantcont; + register int num; + char buf[BUFSIZ]; + + if (strcmp(name, "-") == 0) { + name = _("standard input"); + fp = stdin; + } else if ((fp = fopen(name, "r")) == NULL) { + const char *e = strerror(errno); + + (void) fprintf(stderr, _("%s: Can't open %s: %s\n"), + progname, name, e); + (void) exit(EXIT_FAILURE); + } + wantcont = FALSE; + for (num = 1; ; ++num) { + eat(name, num); + if (fgets(buf, (int) sizeof buf, fp) != buf) + break; + cp = strchr(buf, '\n'); + if (cp == NULL) { + error(_("line too long")); + (void) exit(EXIT_FAILURE); + } + *cp = '\0'; + fields = getfields(buf); + nfields = 0; + while (fields[nfields] != NULL) { + static char nada; + + if (strcmp(fields[nfields], "-") == 0) + fields[nfields] = &nada; + ++nfields; + } + if (nfields == 0) { + /* nothing to do */ + } else if (wantcont) { + wantcont = inzcont(fields, nfields); + } else { + lp = byword(fields[0], line_codes); + if (lp == NULL) + error(_("input line of unknown type")); + else switch ((int) (lp->l_value)) { + case LC_RULE: + inrule(fields, nfields); + wantcont = FALSE; + break; + case LC_ZONE: + wantcont = inzone(fields, nfields); + break; + case LC_LINK: + inlink(fields, nfields); + wantcont = FALSE; + break; + case LC_LEAP: + if (name != leapsec) + (void) fprintf(stderr, +_("%s: Leap line in non leap seconds file %s\n"), + progname, name); + else inleap(fields, nfields); + wantcont = FALSE; + break; + default: /* "cannot happen" */ + (void) fprintf(stderr, +_("%s: panic: Invalid l_value %d\n"), + progname, lp->l_value); + (void) exit(EXIT_FAILURE); + } + } + ifree((char *) fields); + } + if (ferror(fp)) { + (void) fprintf(stderr, _("%s: Error reading %s\n"), + progname, filename); + (void) exit(EXIT_FAILURE); + } + if (fp != stdin && fclose(fp)) { + const char *e = strerror(errno); + + (void) fprintf(stderr, _("%s: Error closing %s: %s\n"), + progname, filename, e); + (void) exit(EXIT_FAILURE); + } + if (wantcont) + error(_("expected continuation line not found")); +} + +/* +** Convert a string of one of the forms +** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss +** into a number of seconds. +** A null string maps to zero. +** Call error with errstring and return zero on errors. +*/ + +static long +gethms(string, errstring, signable) +const char * string; +const char * const errstring; +const int signable; +{ + int hh, mm, ss, sign; + + if (string == NULL || *string == '\0') + return 0; + if (!signable) + sign = 1; + else if (*string == '-') { + sign = -1; + ++string; + } else sign = 1; + if (sscanf(string, scheck(string, "%d"), &hh) == 1) + mm = ss = 0; + else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2) + ss = 0; + else if (sscanf(string, scheck(string, "%d:%d:%d"), + &hh, &mm, &ss) != 3) { + error(errstring); + return 0; + } + if ((hh < 0 || hh >= HOURSPERDAY || + mm < 0 || mm >= MINSPERHOUR || + ss < 0 || ss > SECSPERMIN) && + !(hh == HOURSPERDAY && mm == 0 && ss == 0)) { + error(errstring); + return 0; + } + if (noise && hh == HOURSPERDAY) + warning(_("24:00 not handled by pre-1998 versions of zic")); + return eitol(sign) * + (eitol(hh * MINSPERHOUR + mm) * + eitol(SECSPERMIN) + eitol(ss)); +} + +static void +inrule(fields, nfields) +register char ** const fields; +const int nfields; +{ + static struct rule r; + + if (nfields != RULE_FIELDS) { + error(_("wrong number of fields on Rule line")); + return; + } + if (*fields[RF_NAME] == '\0') { + error(_("nameless rule")); + return; + } + r.r_filename = filename; + r.r_linenum = linenum; + r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE); + rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], + fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); + r.r_name = ecpyalloc(fields[RF_NAME]); + r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); + rules = (struct rule *) (void *) erealloc((char *) rules, + (int) ((nrules + 1) * sizeof *rules)); + rules[nrules++] = r; +} + +static int +inzone(fields, nfields) +register char ** const fields; +const int nfields; +{ + register int i; + static char * buf; + + if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { + error(_("wrong number of fields on Zone line")); + return FALSE; + } + if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) { + buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT))); + (void) sprintf(buf, +_("\"Zone %s\" line and -l option are mutually exclusive"), + TZDEFAULT); + error(buf); + return FALSE; + } + if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) { + buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES))); + (void) sprintf(buf, +_("\"Zone %s\" line and -p option are mutually exclusive"), + TZDEFRULES); + error(buf); + return FALSE; + } + for (i = 0; i < nzones; ++i) + if (zones[i].z_name != NULL && + strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { + buf = erealloc(buf, (int) (132 + + strlen(fields[ZF_NAME]) + + strlen(zones[i].z_filename))); + (void) sprintf(buf, +_("duplicate zone name %s (file \"%s\", line %d)"), + fields[ZF_NAME], + zones[i].z_filename, + zones[i].z_linenum); + error(buf); + return FALSE; + } + return inzsub(fields, nfields, FALSE); +} + +static int +inzcont(fields, nfields) +register char ** const fields; +const int nfields; +{ + if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { + error(_("wrong number of fields on Zone continuation line")); + return FALSE; + } + return inzsub(fields, nfields, TRUE); +} + +static int +inzsub(fields, nfields, iscont) +register char ** const fields; +const int nfields; +const int iscont; +{ + register char * cp; + static struct zone z; + register int i_gmtoff, i_rule, i_format; + register int i_untilyear, i_untilmonth; + register int i_untilday, i_untiltime; + register int hasuntil; + + if (iscont) { + i_gmtoff = ZFC_GMTOFF; + i_rule = ZFC_RULE; + i_format = ZFC_FORMAT; + i_untilyear = ZFC_TILYEAR; + i_untilmonth = ZFC_TILMONTH; + i_untilday = ZFC_TILDAY; + i_untiltime = ZFC_TILTIME; + z.z_name = NULL; + } else { + i_gmtoff = ZF_GMTOFF; + i_rule = ZF_RULE; + i_format = ZF_FORMAT; + i_untilyear = ZF_TILYEAR; + i_untilmonth = ZF_TILMONTH; + i_untilday = ZF_TILDAY; + i_untiltime = ZF_TILTIME; + z.z_name = ecpyalloc(fields[ZF_NAME]); + } + z.z_filename = filename; + z.z_linenum = linenum; + z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE); + if ((cp = strchr(fields[i_format], '%')) != 0) { + if (*++cp != 's' || strchr(cp, '%') != 0) { + error(_("invalid abbreviation format")); + return FALSE; + } + } + z.z_rule = ecpyalloc(fields[i_rule]); + z.z_format = ecpyalloc(fields[i_format]); + hasuntil = nfields > i_untilyear; + if (hasuntil) { + z.z_untilrule.r_filename = filename; + z.z_untilrule.r_linenum = linenum; + rulesub(&z.z_untilrule, + fields[i_untilyear], + "only", + "", + (nfields > i_untilmonth) ? + fields[i_untilmonth] : "Jan", + (nfields > i_untilday) ? fields[i_untilday] : "1", + (nfields > i_untiltime) ? fields[i_untiltime] : "0"); + z.z_untiltime = rpytime(&z.z_untilrule, + z.z_untilrule.r_loyear); + if (iscont && nzones > 0 && + z.z_untiltime > min_time && + z.z_untiltime < max_time && + zones[nzones - 1].z_untiltime > min_time && + zones[nzones - 1].z_untiltime < max_time && + zones[nzones - 1].z_untiltime >= z.z_untiltime) { + error(_("Zone continuation line end time is not after end time of previous line")); + return FALSE; + } + } + zones = (struct zone *) (void *) erealloc((char *) zones, + (int) ((nzones + 1) * sizeof *zones)); + zones[nzones++] = z; + /* + ** If there was an UNTIL field on this line, + ** there's more information about the zone on the next line. + */ + return hasuntil; +} + +static void +inleap(fields, nfields) +register char ** const fields; +const int nfields; +{ + register const char * cp; + register const struct lookup * lp; + register int i, j; + int year, month, day; + long dayoff, tod; + time_t t; + + if (nfields != LEAP_FIELDS) { + error(_("wrong number of fields on Leap line")); + return; + } + dayoff = 0; + cp = fields[LP_YEAR]; + if (sscanf(cp, scheck(cp, "%d"), &year) != 1) { + /* + * Leapin' Lizards! + */ + error(_("invalid leaping year")); + return; + } + j = EPOCH_YEAR; + while (j != year) { + if (year > j) { + i = len_years[isleap(j)]; + ++j; + } else { + --j; + i = -len_years[isleap(j)]; + } + dayoff = oadd(dayoff, eitol(i)); + } + if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { + error(_("invalid month name")); + return; + } + month = lp->l_value; + j = TM_JANUARY; + while (j != month) { + i = len_months[isleap(year)][j]; + dayoff = oadd(dayoff, eitol(i)); + ++j; + } + cp = fields[LP_DAY]; + if (sscanf(cp, scheck(cp, "%d"), &day) != 1 || + day <= 0 || day > len_months[isleap(year)][month]) { + error(_("invalid day of month")); + return; + } + dayoff = oadd(dayoff, eitol(day - 1)); + if (dayoff < 0 && !TYPE_SIGNED(time_t)) { + error(_("time before zero")); + return; + } + if (dayoff < min_time / SECSPERDAY) { + error(_("time too small")); + return; + } + if (dayoff > max_time / SECSPERDAY) { + error(_("time too large")); + return; + } + t = (time_t) dayoff * SECSPERDAY; + tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); + cp = fields[LP_CORR]; + { + register int positive; + int count; + + if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ + positive = FALSE; + count = 1; + } else if (strcmp(cp, "--") == 0) { + positive = FALSE; + count = 2; + } else if (strcmp(cp, "+") == 0) { + positive = TRUE; + count = 1; + } else if (strcmp(cp, "++") == 0) { + positive = TRUE; + count = 2; + } else { + error(_("illegal CORRECTION field on Leap line")); + return; + } + if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) { + error(_("illegal Rolling/Stationary field on Leap line")); + return; + } + leapadd(tadd(t, tod), positive, lp->l_value, count); + } +} + +static void +inlink(fields, nfields) +register char ** const fields; +const int nfields; +{ + struct link l; + + if (nfields != LINK_FIELDS) { + error(_("wrong number of fields on Link line")); + return; + } + if (*fields[LF_FROM] == '\0') { + error(_("blank FROM field on Link line")); + return; + } + if (*fields[LF_TO] == '\0') { + error(_("blank TO field on Link line")); + return; + } + l.l_filename = filename; + l.l_linenum = linenum; + l.l_from = ecpyalloc(fields[LF_FROM]); + l.l_to = ecpyalloc(fields[LF_TO]); + links = (struct link *) (void *) erealloc((char *) links, + (int) ((nlinks + 1) * sizeof *links)); + links[nlinks++] = l; +} + +static void +rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep) +register struct rule * const rp; +const char * const loyearp; +const char * const hiyearp; +const char * const typep; +const char * const monthp; +const char * const dayp; +const char * const timep; +{ + register const struct lookup * lp; + register const char * cp; + register char * dp; + register char * ep; + + if ((lp = byword(monthp, mon_names)) == NULL) { + error(_("invalid month name")); + return; + } + rp->r_month = lp->l_value; + rp->r_todisstd = FALSE; + rp->r_todisgmt = FALSE; + dp = ecpyalloc(timep); + if (*dp != '\0') { + ep = dp + strlen(dp) - 1; + switch (lowerit(*ep)) { + case 's': /* Standard */ + rp->r_todisstd = TRUE; + rp->r_todisgmt = FALSE; + *ep = '\0'; + break; + case 'w': /* Wall */ + rp->r_todisstd = FALSE; + rp->r_todisgmt = FALSE; + *ep = '\0'; + break; + case 'g': /* Greenwich */ + case 'u': /* Universal */ + case 'z': /* Zulu */ + rp->r_todisstd = TRUE; + rp->r_todisgmt = TRUE; + *ep = '\0'; + break; + } + } + rp->r_tod = gethms(dp, _("invalid time of day"), FALSE); + ifree(dp); + /* + ** Year work. + */ + cp = loyearp; + lp = byword(cp, begin_years); + if (lp != NULL) switch ((int) lp->l_value) { + case YR_MINIMUM: + rp->r_loyear = INT_MIN; + break; + case YR_MAXIMUM: + rp->r_loyear = INT_MAX; + break; + default: /* "cannot happen" */ + (void) fprintf(stderr, + _("%s: panic: Invalid l_value %d\n"), + progname, lp->l_value); + (void) exit(EXIT_FAILURE); + } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) { + error(_("invalid starting year")); + return; + } else if (noise) { + if (rp->r_loyear < min_year_representable) + warning(_("starting year too low to be represented")); + else if (rp->r_loyear > max_year_representable) + warning(_("starting year too high to be represented")); + } + cp = hiyearp; + if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) { + case YR_MINIMUM: + rp->r_hiyear = INT_MIN; + break; + case YR_MAXIMUM: + rp->r_hiyear = INT_MAX; + break; + case YR_ONLY: + rp->r_hiyear = rp->r_loyear; + break; + default: /* "cannot happen" */ + (void) fprintf(stderr, + _("%s: panic: Invalid l_value %d\n"), + progname, lp->l_value); + (void) exit(EXIT_FAILURE); + } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) { + error(_("invalid ending year")); + return; + } else if (noise) { + if (rp->r_loyear < min_year_representable) + warning(_("ending year too low to be represented")); + else if (rp->r_loyear > max_year_representable) + warning(_("ending year too high to be represented")); + } + if (rp->r_loyear > rp->r_hiyear) { + error(_("starting year greater than ending year")); + return; + } + if (*typep == '\0') + rp->r_yrtype = NULL; + else { + if (rp->r_loyear == rp->r_hiyear) { + error(_("typed single year")); + return; + } + rp->r_yrtype = ecpyalloc(typep); + } + if (rp->r_loyear < min_year && rp->r_loyear > 0) + min_year = rp->r_loyear; + /* + ** Day work. + ** Accept things such as: + ** 1 + ** last-Sunday + ** Sun<=20 + ** Sun>=7 + */ + dp = ecpyalloc(dayp); + if ((lp = byword(dp, lasts)) != NULL) { + rp->r_dycode = DC_DOWLEQ; + rp->r_wday = lp->l_value; + rp->r_dayofmonth = len_months[1][rp->r_month]; + } else { + if ((ep = strchr(dp, '<')) != 0) + rp->r_dycode = DC_DOWLEQ; + else if ((ep = strchr(dp, '>')) != 0) + rp->r_dycode = DC_DOWGEQ; + else { + ep = dp; + rp->r_dycode = DC_DOM; + } + if (rp->r_dycode != DC_DOM) { + *ep++ = 0; + if (*ep++ != '=') { + error(_("invalid day of month")); + ifree(dp); + return; + } + if ((lp = byword(dp, wday_names)) == NULL) { + error(_("invalid weekday name")); + ifree(dp); + return; + } + rp->r_wday = lp->l_value; + } + if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 || + rp->r_dayofmonth <= 0 || + (rp->r_dayofmonth > len_months[1][rp->r_month])) { + error(_("invalid day of month")); + ifree(dp); + return; + } + } + ifree(dp); +} + +static void +convert(val, buf) +const long val; +char * const buf; +{ + register int i; + register long shift; + + for (i = 0, shift = 24; i < 4; ++i, shift -= 8) + buf[i] = val >> shift; +} + +static void +puttzcode(val, fp) +const long val; +FILE * const fp; +{ + char buf[4]; + + convert(val, buf); + (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); +} + +static int +atcomp(avp, bvp) +void * avp; +void * bvp; +{ + if (((struct attype *) avp)->at < ((struct attype *) bvp)->at) + return -1; + else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at) + return 1; + else return 0; +} + +static void +writezone(name) +const char * const name; +{ + register FILE * fp; + register int i, j; + static char * fullname; + static struct tzhead tzh; + time_t ats[TZ_MAX_TIMES]; + unsigned char types[TZ_MAX_TIMES]; + + /* + ** Sort. + */ + if (timecnt > 1) + (void) qsort((void *) attypes, (size_t) timecnt, + (size_t) sizeof *attypes, atcomp); + /* + ** Optimize. + */ + { + int fromi; + int toi; + + toi = 0; + fromi = 0; + while (fromi < timecnt && attypes[fromi].at < min_time) + ++fromi; + if (isdsts[0] == 0) + while (fromi < timecnt && attypes[fromi].type == 0) + ++fromi; /* handled by default rule */ + for ( ; fromi < timecnt; ++fromi) { + if (toi != 0 + && ((attypes[fromi].at + + gmtoffs[attypes[toi - 1].type]) + <= (attypes[toi - 1].at + + gmtoffs[toi == 1 ? 0 + : attypes[toi - 2].type]))) { + attypes[toi - 1].type = attypes[fromi].type; + continue; + } + if (toi == 0 || + attypes[toi - 1].type != attypes[fromi].type) + attypes[toi++] = attypes[fromi]; + } + timecnt = toi; + } + /* + ** Transfer. + */ + for (i = 0; i < timecnt; ++i) { + ats[i] = attypes[i].at; + types[i] = attypes[i].type; + } + fullname = erealloc(fullname, + (int) (strlen(directory) + 1 + strlen(name) + 1)); + (void) sprintf(fullname, "%s/%s", directory, name); + /* + ** Remove old file, if any, to snap links. + */ + if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) { + const char *e = strerror(errno); + + (void) fprintf(stderr, _("%s: Can't remove %s: %s\n"), + progname, fullname, e); + (void) exit(EXIT_FAILURE); + } + if ((fp = fopen(fullname, "wb")) == NULL) { + if (mkdirs(fullname) != 0) + (void) exit(EXIT_FAILURE); + if ((fp = fopen(fullname, "wb")) == NULL) { + const char *e = strerror(errno); + + (void) fprintf(stderr, _("%s: Can't create %s: %s\n"), + progname, fullname, e); + (void) exit(EXIT_FAILURE); + } + } + convert(eitol(typecnt), tzh.tzh_ttisgmtcnt); + convert(eitol(typecnt), tzh.tzh_ttisstdcnt); + convert(eitol(leapcnt), tzh.tzh_leapcnt); + convert(eitol(timecnt), tzh.tzh_timecnt); + convert(eitol(typecnt), tzh.tzh_typecnt); + convert(eitol(charcnt), tzh.tzh_charcnt); + (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); +#define DO(field) (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp) + DO(tzh_magic); + DO(tzh_reserved); + DO(tzh_ttisgmtcnt); + DO(tzh_ttisstdcnt); + DO(tzh_leapcnt); + DO(tzh_timecnt); + DO(tzh_typecnt); + DO(tzh_charcnt); +#undef DO + for (i = 0; i < timecnt; ++i) { + j = leapcnt; + while (--j >= 0) + if (ats[i] >= trans[j]) { + ats[i] = tadd(ats[i], corr[j]); + break; + } + puttzcode((long) ats[i], fp); + } + if (timecnt > 0) + (void) fwrite((void *) types, (size_t) sizeof types[0], + (size_t) timecnt, fp); + for (i = 0; i < typecnt; ++i) { + puttzcode((long) gmtoffs[i], fp); + (void) putc(isdsts[i], fp); + (void) putc(abbrinds[i], fp); + } + if (charcnt != 0) + (void) fwrite((void *) chars, (size_t) sizeof chars[0], + (size_t) charcnt, fp); + for (i = 0; i < leapcnt; ++i) { + if (roll[i]) { + if (timecnt == 0 || trans[i] < ats[0]) { + j = 0; + while (isdsts[j]) + if (++j >= typecnt) { + j = 0; + break; + } + } else { + j = 1; + while (j < timecnt && trans[i] >= ats[j]) + ++j; + j = types[j - 1]; + } + puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp); + } else puttzcode((long) trans[i], fp); + puttzcode((long) corr[i], fp); + } + for (i = 0; i < typecnt; ++i) + (void) putc(ttisstds[i], fp); + for (i = 0; i < typecnt; ++i) + (void) putc(ttisgmts[i], fp); + if (ferror(fp) || fclose(fp)) { + (void) fprintf(stderr, _("%s: Error writing %s\n"), + progname, fullname); + (void) exit(EXIT_FAILURE); + } +} + +static void +doabbr(abbr, format, letters, isdst) +char * const abbr; +const char * const format; +const char * const letters; +const int isdst; +{ + if (strchr(format, '/') == NULL) { + if (letters == NULL) + (void) strcpy(abbr, format); + else (void) sprintf(abbr, format, letters); + } else if (isdst) + (void) strcpy(abbr, strchr(format, '/') + 1); + else { + (void) strcpy(abbr, format); + *strchr(abbr, '/') = '\0'; + } +} + +static void +outzone(zpfirst, zonecount) +const struct zone * const zpfirst; +const int zonecount; +{ + register const struct zone * zp; + register struct rule * rp; + register int i, j; + register int usestart, useuntil; + register time_t starttime, untiltime; + register long gmtoff; + register long stdoff; + register int year; + register long startoff; + register int startttisstd; + register int startttisgmt; + register int type; + char startbuf[BUFSIZ]; + + INITIALIZE(untiltime); + INITIALIZE(starttime); + /* + ** Now. . .finally. . .generate some useful data! + */ + timecnt = 0; + typecnt = 0; + charcnt = 0; + /* + ** Thanks to Earl Chew (earl@dnd.icp.nec.com.au) + ** for noting the need to unconditionally initialize startttisstd. + */ + startttisstd = FALSE; + startttisgmt = FALSE; + for (i = 0; i < zonecount; ++i) { + /* + ** A guess that may well be corrected later. + */ + stdoff = 0; + zp = &zpfirst[i]; + usestart = i > 0 && (zp - 1)->z_untiltime > min_time; + useuntil = i < (zonecount - 1); + if (useuntil && zp->z_untiltime <= min_time) + continue; + gmtoff = zp->z_gmtoff; + eat(zp->z_filename, zp->z_linenum); + *startbuf = '\0'; + startoff = zp->z_gmtoff; + if (zp->z_nrules == 0) { + stdoff = zp->z_stdoff; + doabbr(startbuf, zp->z_format, + (char *) NULL, stdoff != 0); + type = addtype(oadd(zp->z_gmtoff, stdoff), + startbuf, stdoff != 0, startttisstd, + startttisgmt); + if (usestart) { + addtt(starttime, type); + usestart = FALSE; + } else if (stdoff != 0) + addtt(min_time, type); + } else for (year = min_year; year <= max_year; ++year) { + if (useuntil && year > zp->z_untilrule.r_hiyear) + break; + /* + ** Mark which rules to do in the current year. + ** For those to do, calculate rpytime(rp, year); + */ + for (j = 0; j < zp->z_nrules; ++j) { + rp = &zp->z_rules[j]; + eats(zp->z_filename, zp->z_linenum, + rp->r_filename, rp->r_linenum); + rp->r_todo = year >= rp->r_loyear && + year <= rp->r_hiyear && + yearistype(year, rp->r_yrtype); + if (rp->r_todo) + rp->r_temp = rpytime(rp, year); + } + for ( ; ; ) { + register int k; + register time_t jtime, ktime; + register long offset; + char buf[BUFSIZ]; + + INITIALIZE(ktime); + if (useuntil) { + /* + ** Turn untiltime into UTC + ** assuming the current gmtoff and + ** stdoff values. + */ + untiltime = zp->z_untiltime; + if (!zp->z_untilrule.r_todisgmt) + untiltime = tadd(untiltime, + -gmtoff); + if (!zp->z_untilrule.r_todisstd) + untiltime = tadd(untiltime, + -stdoff); + } + /* + ** Find the rule (of those to do, if any) + ** that takes effect earliest in the year. + */ + k = -1; + for (j = 0; j < zp->z_nrules; ++j) { + rp = &zp->z_rules[j]; + if (!rp->r_todo) + continue; + eats(zp->z_filename, zp->z_linenum, + rp->r_filename, rp->r_linenum); + offset = rp->r_todisgmt ? 0 : gmtoff; + if (!rp->r_todisstd) + offset = oadd(offset, stdoff); + jtime = rp->r_temp; + if (jtime == min_time || + jtime == max_time) + continue; + jtime = tadd(jtime, -offset); + if (k < 0 || jtime < ktime) { + k = j; + ktime = jtime; + } + } + if (k < 0) + break; /* go on to next year */ + rp = &zp->z_rules[k]; + rp->r_todo = FALSE; + if (useuntil && ktime >= untiltime) + break; + stdoff = rp->r_stdoff; + if (usestart && ktime == starttime) + usestart = FALSE; + if (usestart) { + if (ktime < starttime) { + startoff = oadd(zp->z_gmtoff, + stdoff); + doabbr(startbuf, zp->z_format, + rp->r_abbrvar, + rp->r_stdoff != 0); + continue; + } + if (*startbuf == '\0' && + startoff == oadd(zp->z_gmtoff, + stdoff)) { + doabbr(startbuf, zp->z_format, + rp->r_abbrvar, + rp->r_stdoff != 0); + } + } + eats(zp->z_filename, zp->z_linenum, + rp->r_filename, rp->r_linenum); + doabbr(buf, zp->z_format, rp->r_abbrvar, + rp->r_stdoff != 0); + offset = oadd(zp->z_gmtoff, rp->r_stdoff); + type = addtype(offset, buf, rp->r_stdoff != 0, + rp->r_todisstd, rp->r_todisgmt); + addtt(ktime, type); + } + } + if (usestart) { + if (*startbuf == '\0' && + zp->z_format != NULL && + strchr(zp->z_format, '%') == NULL && + strchr(zp->z_format, '/') == NULL) + (void) strcpy(startbuf, zp->z_format); + eat(zp->z_filename, zp->z_linenum); + if (*startbuf == '\0') +error(_("can't determine time zone abbreviation to use just after until time")); + else addtt(starttime, + addtype(startoff, startbuf, + startoff != zp->z_gmtoff, + startttisstd, + startttisgmt)); + } + /* + ** Now we may get to set starttime for the next zone line. + */ + if (useuntil) { + startttisstd = zp->z_untilrule.r_todisstd; + startttisgmt = zp->z_untilrule.r_todisgmt; + starttime = zp->z_untiltime; + if (!startttisstd) + starttime = tadd(starttime, -stdoff); + if (!startttisgmt) + starttime = tadd(starttime, -gmtoff); + } + } + writezone(zpfirst->z_name); +} + +static void +addtt(starttime, type) +const time_t starttime; +int type; +{ + if (starttime <= min_time || + (timecnt == 1 && attypes[0].at < min_time)) { + gmtoffs[0] = gmtoffs[type]; + isdsts[0] = isdsts[type]; + ttisstds[0] = ttisstds[type]; + ttisgmts[0] = ttisgmts[type]; + if (abbrinds[type] != 0) + (void) strcpy(chars, &chars[abbrinds[type]]); + abbrinds[0] = 0; + charcnt = strlen(chars) + 1; + typecnt = 1; + timecnt = 0; + type = 0; + } + if (timecnt >= TZ_MAX_TIMES) { + error(_("too many transitions?!")); + (void) exit(EXIT_FAILURE); + } + attypes[timecnt].at = starttime; + attypes[timecnt].type = type; + ++timecnt; +} + +static int +addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt) +const long gmtoff; +const char * const abbr; +const int isdst; +const int ttisstd; +const int ttisgmt; +{ + register int i, j; + + if (isdst != TRUE && isdst != FALSE) { + error(_("internal error - addtype called with bad isdst")); + (void) exit(EXIT_FAILURE); + } + if (ttisstd != TRUE && ttisstd != FALSE) { + error(_("internal error - addtype called with bad ttisstd")); + (void) exit(EXIT_FAILURE); + } + if (ttisgmt != TRUE && ttisgmt != FALSE) { + error(_("internal error - addtype called with bad ttisgmt")); + (void) exit(EXIT_FAILURE); + } + /* + ** See if there's already an entry for this zone type. + ** If so, just return its index. + */ + for (i = 0; i < typecnt; ++i) { + if (gmtoff == gmtoffs[i] && isdst == isdsts[i] && + strcmp(abbr, &chars[abbrinds[i]]) == 0 && + ttisstd == ttisstds[i] && + ttisgmt == ttisgmts[i]) + return i; + } + /* + ** There isn't one; add a new one, unless there are already too + ** many. + */ + if (typecnt >= TZ_MAX_TYPES) { + error(_("too many local time types")); + (void) exit(EXIT_FAILURE); + } + gmtoffs[i] = gmtoff; + isdsts[i] = isdst; + ttisstds[i] = ttisstd; + ttisgmts[i] = ttisgmt; + + for (j = 0; j < charcnt; ++j) + if (strcmp(&chars[j], abbr) == 0) + break; + if (j == charcnt) + newabbr(abbr); + abbrinds[i] = j; + ++typecnt; + return i; +} + +static void +leapadd(t, positive, rolling, count) +const time_t t; +const int positive; +const int rolling; +int count; +{ + register int i, j; + + if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) { + error(_("too many leap seconds")); + (void) exit(EXIT_FAILURE); + } + for (i = 0; i < leapcnt; ++i) + if (t <= trans[i]) { + if (t == trans[i]) { + error(_("repeated leap second moment")); + (void) exit(EXIT_FAILURE); + } + break; + } + do { + for (j = leapcnt; j > i; --j) { + trans[j] = trans[j - 1]; + corr[j] = corr[j - 1]; + roll[j] = roll[j - 1]; + } + trans[i] = t; + corr[i] = positive ? 1L : eitol(-count); + roll[i] = rolling; + ++leapcnt; + } while (positive && --count != 0); +} + +static void +adjleap P((void)) +{ + register int i; + register long last = 0; + + /* + ** propagate leap seconds forward + */ + for (i = 0; i < leapcnt; ++i) { + trans[i] = tadd(trans[i], last); + last = corr[i] += last; + } +} + +static int +yearistype(year, type) +const int year; +const char * const type; +{ + static char * buf; + int result; + + if (type == NULL || *type == '\0') + return TRUE; + buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type))); + (void) sprintf(buf, "%s %d %s", yitcommand, year, type); + result = system(buf); + if (WIFEXITED(result)) switch (WEXITSTATUS(result)) { + case 0: + return TRUE; + case 1: + return FALSE; + } + error(_("Wild result from command execution")); + (void) fprintf(stderr, _("%s: command was '%s', result was %d\n"), + progname, buf, result); + for ( ; ; ) + (void) exit(EXIT_FAILURE); +} + +static int +lowerit(a) +int a; +{ + a = (unsigned char) a; + return (isascii(a) && isupper(a)) ? tolower(a) : a; +} + +static int +ciequal(ap, bp) /* case-insensitive equality */ +register const char * ap; +register const char * bp; +{ + while (lowerit(*ap) == lowerit(*bp++)) + if (*ap++ == '\0') + return TRUE; + return FALSE; +} + +static int +itsabbr(abbr, word) +register const char * abbr; +register const char * word; +{ + if (lowerit(*abbr) != lowerit(*word)) + return FALSE; + ++word; + while (*++abbr != '\0') + do { + if (*word == '\0') + return FALSE; + } while (lowerit(*word++) != lowerit(*abbr)); + return TRUE; +} + +static const struct lookup * +byword(word, table) +register const char * const word; +register const struct lookup * const table; +{ + register const struct lookup * foundlp; + register const struct lookup * lp; + + if (word == NULL || table == NULL) + return NULL; + /* + ** Look for exact match. + */ + for (lp = table; lp->l_word != NULL; ++lp) + if (ciequal(word, lp->l_word)) + return lp; + /* + ** Look for inexact match. + */ + foundlp = NULL; + for (lp = table; lp->l_word != NULL; ++lp) + if (itsabbr(word, lp->l_word)) { + if (foundlp == NULL) + foundlp = lp; + else return NULL; /* multiple inexact matches */ + } + return foundlp; +} + +static char ** +getfields(cp) +register char * cp; +{ + register char * dp; + register char ** array; + register int nsubs; + + if (cp == NULL) + return NULL; + array = (char **) (void *) + emalloc((int) ((strlen(cp) + 1) * sizeof *array)); + nsubs = 0; + for ( ; ; ) { + while (isascii(*cp) && isspace((unsigned char) *cp)) + ++cp; + if (*cp == '\0' || *cp == '#') + break; + array[nsubs++] = dp = cp; + do { + if ((*dp = *cp++) != '"') + ++dp; + else while ((*dp = *cp++) != '"') + if (*dp != '\0') + ++dp; + else error(_("Odd number of quotation marks")); + } while (*cp != '\0' && *cp != '#' && + (!isascii(*cp) || !isspace((unsigned char) *cp))); + if (isascii(*cp) && isspace((unsigned char) *cp)) + ++cp; + *dp = '\0'; + } + array[nsubs] = NULL; + return array; +} + +static long +oadd(t1, t2) +const long t1; +const long t2; +{ + register long t; + + t = t1 + t2; + if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { + error(_("time overflow")); + (void) exit(EXIT_FAILURE); + } + return t; +} + +static time_t +tadd(t1, t2) +const time_t t1; +const long t2; +{ + register time_t t; + + if (t1 == max_time && t2 > 0) + return max_time; + if (t1 == min_time && t2 < 0) + return min_time; + t = t1 + t2; + if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { + error(_("time overflow")); + (void) exit(EXIT_FAILURE); + } + return t; +} + +/* +** Given a rule, and a year, compute the date - in seconds since January 1, +** 1970, 00:00 LOCAL time - in that year that the rule refers to. +*/ + +static time_t +rpytime(rp, wantedy) +register const struct rule * const rp; +register const int wantedy; +{ + register int y, m, i; + register long dayoff; /* with a nod to Margaret O. */ + register time_t t; + + if (wantedy == INT_MIN) + return min_time; + if (wantedy == INT_MAX) + return max_time; + dayoff = 0; + m = TM_JANUARY; + y = EPOCH_YEAR; + while (wantedy != y) { + if (wantedy > y) { + i = len_years[isleap(y)]; + ++y; + } else { + --y; + i = -len_years[isleap(y)]; + } + dayoff = oadd(dayoff, eitol(i)); + } + while (m != rp->r_month) { + i = len_months[isleap(y)][m]; + dayoff = oadd(dayoff, eitol(i)); + ++m; + } + i = rp->r_dayofmonth; + if (m == TM_FEBRUARY && i == 29 && !isleap(y)) { + if (rp->r_dycode == DC_DOWLEQ) + --i; + else { + error(_("use of 2/29 in non leap-year")); + (void) exit(EXIT_FAILURE); + } + } + --i; + dayoff = oadd(dayoff, eitol(i)); + if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { + register long wday; + +#define LDAYSPERWEEK ((long) DAYSPERWEEK) + wday = eitol(EPOCH_WDAY); + /* + ** Don't trust mod of negative numbers. + */ + if (dayoff >= 0) + wday = (wday + dayoff) % LDAYSPERWEEK; + else { + wday -= ((-dayoff) % LDAYSPERWEEK); + if (wday < 0) + wday += LDAYSPERWEEK; + } + while (wday != eitol(rp->r_wday)) + if (rp->r_dycode == DC_DOWGEQ) { + dayoff = oadd(dayoff, (long) 1); + if (++wday >= LDAYSPERWEEK) + wday = 0; + ++i; + } else { + dayoff = oadd(dayoff, (long) -1); + if (--wday < 0) + wday = LDAYSPERWEEK - 1; + --i; + } + if (i < 0 || i >= len_months[isleap(y)][m]) { + error(_("no day in month matches rule")); + (void) exit(EXIT_FAILURE); + } + } + if (dayoff < 0 && !TYPE_SIGNED(time_t)) + return min_time; + if (dayoff < min_time / SECSPERDAY) + return min_time; + if (dayoff > max_time / SECSPERDAY) + return max_time; + t = (time_t) dayoff * SECSPERDAY; + return tadd(t, rp->r_tod); +} + +static void +newabbr(string) +const char * const string; +{ + register int i; + + i = strlen(string) + 1; + if (charcnt + i > TZ_MAX_CHARS) { + error(_("too many, or too long, time zone abbreviations")); + (void) exit(EXIT_FAILURE); + } + (void) strcpy(&chars[charcnt], string); + charcnt += eitol(i); +} + +static int +mkdirs(argname) +char * const argname; +{ + register char * name; + register char * cp; + + if (argname == NULL || *argname == '\0') + return 0; + cp = name = ecpyalloc(argname); + while ((cp = strchr(cp + 1, '/')) != 0) { + *cp = '\0'; +#ifndef unix + /* + ** DOS drive specifier? + */ + if (isalpha((unsigned char) name[0]) && + name[1] == ':' && name[2] == '\0') { + *cp = '/'; + continue; + } +#endif /* !defined unix */ + if (!itsdir(name)) { + /* + ** It doesn't seem to exist, so we try to create it. + ** Creation may fail because of the directory being + ** created by some other multiprocessor, so we get + ** to do extra checking. + */ + if (mkdir(name, MKDIR_UMASK) != 0) { + const char *e = strerror(errno); + + if (errno != EEXIST || !itsdir(name)) { + (void) fprintf(stderr, +_("%s: Can't create directory %s: %s\n"), + progname, name, e); + ifree(name); + return -1; + } + } + } + *cp = '/'; + } + ifree(name); + return 0; +} + +static long +eitol(i) +const int i; +{ + long l; + + l = i; + if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) { + (void) fprintf(stderr, + _("%s: %d did not sign extend correctly\n"), + progname, i); + (void) exit(EXIT_FAILURE); + } + return l; +} + +/* +** UNIX was a registered trademark of The Open Group in 2003. +*/ + + +#ifdef WIN32 +/* + * To run on win32 + */ +int link(const char *oldpath, const char *newpath) { + if (!CopyFileEx(oldpath, newpath, NULL, NULL, FALSE, 0)) { + return -1; + } + return 0; +} +#endif