X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f X-Recipient: djgpp-workers AT delorie DOT com Message-ID: <520FA8C9.40909@gmx.de> Date: Sat, 17 Aug 2013 18:46:01 +0200 From: Juan Manuel Guerrero User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:16.0) Gecko/20121025 Thunderbird/16.0.2 MIME-Version: 1.0 To: djgpp-workers AT delorie DOT com Subject: Re: zoneinfo not able to recognize if daylight saving time is in effect. References: <51F2C9B5 DOT 3090202 AT gmx DOT de> In-Reply-To: <51F2C9B5.3090202@gmx.de> Content-Type: text/plain; charset=ISO-8859-15; format=flowed Content-Transfer-Encoding: 7bit X-Provags-ID: V03:K0:X8QHEcScmYsdfis2TPg6GIlcIGz6mfxoyeHatMttWZs4xZCtpws ZhB6w/C78Y1yvYzUam5OtX1nh0l/UJhknZGHq0nJO34j9Bx7aIFispO0Eaew9vw03PvUDNB ma1IW7IgdwhF12k4K/gYCXFHhZdCw6ui5vGS1DeDe8krN90f5awoF+guYe8hIhNIDZEDFKQ LiGihouzESTeiZHI9U9nA== Reply-To: djgpp-workers AT delorie DOT com Am 26.07.2013 21:10, schrieb Juan Manuel Guerrero: [snip] With the original djgpp code of djgpp/src/libc/ansi/time/ctime.c and zic with date before 2013-01-30 (aka djdev204 and previous) I was not able to compile the tzdata2006d tz-database and later. I got error messages like this: ./host-zic -y ./yearistype -d ../../zoneinfo -L /dev/null africa antarctica asi a australasia europe northamerica southamerica pacificnew etcetera factory backw ard systemv solar87 solar88 solar89 "northamerica", line 1638: can't determine time zone abbreviation to use just af ter until time "northamerica", line 1646: can't determine time zone abbreviation to use just af ter until time make.exe: *** [posix_only] Error 1 I was not willing to waste my time investigating this issue and to figure out how that very old code works, so I preferred to invested the time to update djgpp/src/libc/ansi/time/ctime.c and djgpp/src/libc/ansi/time/posixrul.h to the functionality provided by tz[code|data]2013d. I have also updated all the code in /djgpp/zoneinfo/src to tz[code|data]2013d so that the new zic and ctime and family of functions use the same set of auxiliary functions to encode and decode the zoneinfo database. I have also added a small test program that checks for some TZ that I am aware of. This test program is based on the sample code I send in my first post and if someone wants to contribute with test data it will be welcome. The new djgpp port of zic does not create the version 2 of data base files that support 64 bit time_t. This makes no sense for djgpp and bloats the zoneinfo files to be installed. It has been adjusted to continue producing the old version 0 (32 bit time_t) tz database files. Regards, Juan M. Guerrero 2013-08-17 Juan Manuel Guerrero * djgpp/tests/libc/ansi/time/makefile: Added tzinfo2.c * djgpp/tests/libc/ansi/time/tzinfo2.c: Checks for timezone files. 2013-08-09 Juan Manuel Guerrero * djgpp/include/tzfile.h: Update to tzcode2013d functionality. * djgpp/src/libc/ansi/time/ctime.c: Update to tzcode2013d functionality. * djgpp/src/libc/ansi/time/posixrul.h: Update to tzdata2013d posix rules as default. diff -aprNU5 djgpp.orig/include/tzfile.h djgpp/include/tzfile.h --- djgpp.orig/include/tzfile.h 2012-09-23 07:49:12 +0100 +++ djgpp/include/tzfile.h 2013-08-17 16:02:38 +0100 @@ -1,5 +1,6 @@ +/* Copyright (C) 2013 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #ifndef __dj_include_tzfile_h__ #define __dj_include_tzfile_h__ @@ -45,34 +46,39 @@ extern "C" { /* ** Information about time zone files. */ /* Time zone object file directory */ -#define TZDIR "/usr/share/zoneinfo" -#define TZDEFAULT "/etc/localtime" +#define TZDIR "/dev/env/DJDIR/share/zoneinfo" +#define TZDEFAULT "/dev/env/DJDIR/etc/localtime" #define TZDEFRULES "posixrules" /* ** Each file begins with. . . */ +#define TZ_MAGIC "TZif" + struct tzhead { - char tzh_reserved[24]; /* reserved for future use */ - 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 */ + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_version[1]; /* '\0' or '2' as of 2005 */ + char tzh_reserved[15]; /* reserved--must be zero */ + char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + 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 GMT offset in seconds +** 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 @@ -80,24 +86,34 @@ struct tzhead { ** 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. +** If tzh_version is '2' or greater, the above is followed by a second instance +** of tzhead and a second instance of the data in which each coded transition +** time uses 8 rather than 4 chars, +** then a POSIX-TZ-environment-variable-style string for use in handling +** instants after the last transition time stored in the file +** (with nothing between the newlines if there is no POSIX representation for +** such instants). +** DJGPP does not support version 2 or higher. */ /* -** 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). +** In the current implementation, "tzset()" refuses to deal with files that +** exceed any of the limits below. */ -#define TZ_MAX_TIMES 370 + +#define TZ_MAX_TIMES 1200 #define NOSOLAR /* 4BSD doesn't currently handle solar time */ #ifndef NOSOLAR #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ diff -aprNU5 djgpp.orig/src/libc/ansi/time/ctime.c djgpp/src/libc/ansi/time/ctime.c --- djgpp.orig/src/libc/ansi/time/ctime.c 2005-01-09 14:56:12 +0100 +++ djgpp/src/libc/ansi/time/ctime.c 2013-08-17 16:02:38 +0100 @@ -1,5 +1,6 @@ +/* Copyright (C) 2013 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2005 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ /* This file has been modified by DJ Delorie. These modifications are @@ -38,209 +39,259 @@ static char sccsid[] = "@(#)ctime.c 5.23 ** POSIX-style TZ environment variable handling from Guy Harris ** (guy AT auspex DOT com). */ #include +#include #include #include #include #include #include #include +#include #include +#include +#include #include #include #include #include #include "posixrul.h" -#define P(s) s -#define alloc_size_t size_t -#define qsort_size_t size_t -#define fread_size_t size_t -#define fwrite_size_t size_t +#if ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 96)) || (__GNUC__ >= 3) +# define ATTRIBUTE_CONST __attribute__ ((const)) +# define ATTRIBUTE_PURE __attribute__ ((__pure__)) +#else +# define ATTRIBUTE_CONST /* empty */ +# define ATTRIBUTE_PURE /* empty */ +#endif + +#define GRANDPARENTED "Local time zone must be set--see zic manual page" +#define TZ_ABBR_MAX_LEN 16 +#define TZ_ABBR_CHAR_SET "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._" +#define TZ_ABBR_ERR_CHAR '_' + +#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) +#define TYPE_SIGNED(type) (((type) -1) < 0) +#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) + +#define AVGSECSPERYEAR 31556952L /* The Gregorian year averages 365.2425 days, which is 31556952 seconds. */ +#define YEARSPERREPEAT 400 /* Years before a Gregorian repeat */ +#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR) +#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ + +#define ACCESS_MODE (O_RDONLY|O_BINARY) +#define OPEN_MODE (O_RDONLY|O_BINARY) +#define IS_ABSOLUTE(n) ((n)[0] == '/' || (n)[0] == '\\' || (n)[1] == ':') +#define INITIALIZE(x) ((x) = 0) + +/* Unlike 's isdigit, this also works if c < 0 | c > UCHAR_MAX. */ +#define is_digit(c) ((unsigned)(c) - '0' <= 9) -#define ACCESS_MODE O_RDONLY|O_BINARY -#define OPEN_MODE O_RDONLY|O_BINARY /* ** 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. +** 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). */ -static char WILDABBR[] = " "; +static char wildabbr[] = " "; #ifndef TRUE -#define TRUE 1 -#define FALSE 0 +#define TRUE 1 +#define FALSE 0 #endif /* !defined TRUE */ -static const char GMT[] = "GMT"; +static const char gmt[] = "GMT"; -struct ttinfo { /* time type information */ - long tt_gmtoff; /* GMT offset in seconds */ - int tt_isdst; /* used to set tm_isdst */ - int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* TRUE if transition is std time */ +/* +** 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 */ + int_fast32_t 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 */ +struct lsinfo { /* leap second information */ + time_t ls_trans; /* transition time */ + int_fast64_t 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; + int goback; + int goahead; time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; - char chars[(TZ_MAX_CHARS + 1 > sizeof GMT) ? TZ_MAX_CHARS + 1 : sizeof GMT]; + char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))]; struct lsinfo lsis[TZ_MAX_LEAPS]; + int defaulttype; /* for early times or if no transitions */ }; struct rule { - int r_type; /* type of rule--see below */ - int r_day; /* day number of rule */ - int r_week; /* week number of rule */ - int r_mon; /* month number of rule */ - long r_time; /* transition time of rule */ + int r_type; /* type of rule--see below */ + int r_day; /* day number of rule */ + int r_week; /* week number of rule */ + int r_mon; /* month number of rule */ + int_fast32_t r_time; /* transition time of rule */ }; -#define JULIAN_DAY 0 /* Jn - Julian day */ -#define DAY_OF_YEAR 1 /* n - day of year */ -#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ +#define JULIAN_DAY 0 /* Jn - Julian day */ +#define DAY_OF_YEAR 1 /* n - day of year */ +#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ /* ** Prototypes for static functions. */ -static long detzcode P((const char * codep)); -static 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 void normalize P((int * tensptr, int * unitsptr, int base)); -static void settzname P((void)); -static time_t time1 P((struct tm * tmp, void (* funcp)(const time_t * const, const long, struct tm * const), - long offset)); -static time_t time2 P((struct tm *tmp, void (* funcp)(const time_t * const, const long, struct tm * const), - long offset, int * okayp)); -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 void tmnormalize P((struct tm * tmp)); -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 */ +static int_fast32_t detzcode(const char *codep); +static int differ_by_repeat(time_t t1, time_t t0); +static const char *getnum(const char *strp, int *nump, int min, int max); +static const char *getqzname(const char * strp, const int delim) ATTRIBUTE_PURE; +static const char *getzname(const char *strp) ATTRIBUTE_PURE; +static const char *getsecs(const char *strp, int_fast32_t *const secsp); +static const char *getoffset(const char *strp, int_fast32_t *const offsetp); +static const char *getrule(const char *strp, struct rule *rulep); +static void gmtload(struct state *sp); +static struct tm *gmtsub(const time_t *const timep, const int_fast32_t offset, struct tm *const tmp); +static int increment_overflow(int *const ip, int j); +static struct tm *localsub(const time_t *const timep, const int_fast32_t offset, struct tm *const tmp); +static void settzname(void); +static time_t time1(struct tm *const tmp, struct tm *(*const funcp) (const time_t *, int_fast32_t, struct tm *), const int_fast32_t offset); +static time_t time2(struct tm *const tmp, struct tm *(*const funcp)(const time_t *, int_fast32_t, struct tm *), const int_fast32_t offset, int *const okayp); +static struct tm *timesub(const time_t *const timep, const int_fast32_t offset, register const struct state *const sp, register struct tm *const tmp); +static int tmcomp(const struct tm *atmp, const struct tm *btmp); +static time_t transtime(time_t janfirst, int year, const struct rule *rulep, int_fast32_t offset) ATTRIBUTE_PURE; +static int typesequiv(const struct state *const sp, const int a, const int b); +static int tzload(const char *name, struct state *const sp, const int doextend); +static int tzparse(const char *name, struct state *sp, int lastditch); -#ifndef ALL_STATE static struct state lclmem; static struct state gmtmem; #define lclptr (&lclmem) #define gmtptr (&gmtmem) -#endif /* State Farm */ -static int lcl_is_set; /* 0: no, 1: set by tzset, -1: set by tzsetwall */ +#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; /* 0: no, 1: set by tzset, -1: set by tzsetwall */ static int gmt_is_set; -static char lcl_tzstr[512]; -char * tzname[2] = { - WILDABBR, - WILDABBR +char *tzname[2] = { + wildabbr, + wildabbr }; -static long -detzcode(const char * const codep) +static int_fast32_t +detzcode(const char *const codep) { - long result; - int i; + register int_fast32_t result; + register int i; - result = 0; + result = (codep[0] & 0x80) ? -1 : 0; for (i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return result; } static void settzname(void) { - const struct state * const sp = lclptr; + register struct state *const sp = lclptr; int i; - tzname[0] = WILDABBR; - tzname[1] = WILDABBR; -#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[0] = wildabbr; + tzname[1] = wildabbr; - tzname[ttisp->tt_isdst] = - unconst(&sp->chars[ttisp->tt_abbrind], char *); -#if 0 - if (ttisp->tt_isdst) - _daylight = 1; - if (i == 0 || !ttisp->tt_isdst) - _timezone = -(ttisp->tt_gmtoff); - if (i == 0 || ttisp->tt_isdst) - _altzone = -(ttisp->tt_gmtoff); -#endif - } /* ** And to get the latest zone names into tzname. . . */ + for (i = 0; i < sp->typecnt; ++i) + { + register const struct ttinfo *const ttisp = &sp->ttis[i]; + + tzname[ttisp->tt_isdst] = unconst(&sp->chars[ttisp->tt_abbrind], char *); + } for (i = 0; i < sp->timecnt; ++i) { - const struct ttinfo * const ttisp = &sp->ttis[sp->types[i]]; + register const struct ttinfo *const ttisp = &sp->ttis[sp->types[i]]; tzname[ttisp->tt_isdst] = unconst(&sp->chars[ttisp->tt_abbrind], char *); } + /* + ** Finally, scrub the abbreviations. + ** First, replace bogus characters. + */ + for (i = 0; i < sp->charcnt; ++i) + if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL) + sp->chars[i] = TZ_ABBR_ERR_CHAR; + /* + ** Second, truncate long abbreviations. + */ + for (i = 0; i < sp->typecnt; ++i) + { + register const struct ttinfo *const ttisp = &sp->ttis[i]; + register char *cp = unconst(&sp->chars[ttisp->tt_abbrind], char *); + if (strlen(cp) > TZ_ABBR_MAX_LEN && strcmp(cp, GRANDPARENTED) != 0) + *(cp + TZ_ABBR_MAX_LEN) = '\0'; + } +} + +static int +differ_by_repeat(const time_t t1, const time_t t0) +{ + if (TYPE_INTEGRAL(time_t) && TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) + return 0; + return t1 - t0 == SECSPERREPEAT; } static char * tzdir(void) { - static char dir[FILENAME_MAX + 1]={0}, *cp; + static char *cp, dir[FILENAME_MAX + 1] = {0}; static int tzdir_bss_count = -1; /* Force recomputation of cached values (Emacs). */ if (tzdir_bss_count != __bss_count) { @@ -263,26 +314,28 @@ tzdir(void) } return dir; } static int -tzload(const char *name, struct state * const sp) +tzload(const char *name, struct state *const sp, const int doextend) { - const char * p; + const char *p; int i; int fid; + int nread = 0; char fullname[FILENAME_MAX + 1]; - const struct tzhead * tzhp; - char buf[sizeof *sp + sizeof *tzhp]; - int ttisstdcnt; + const struct tzhead *tzhp; + char buf[sizeof *tzhp + sizeof *sp + 2 * TZ_MAX_TIMES]; + int ttisgmtcnt, ttisstdcnt; + sp->goback = sp->goahead = FALSE; if (name == NULL && (name = TZDEFAULT) == NULL) return -1; if (name[0] == ':') ++name; - if (name[0] && name[0] != '/' && name[0] != '\\' && name[1] != ':') + if (name[0] && !IS_ABSOLUTE(name)) { if ((p = tzdir()) == NULL) return -1; if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) return -1; @@ -311,33 +364,37 @@ tzload(const char *name, struct state * memcpy(buf, _posixrules_data, sizeof(_posixrules_data)); i = sizeof(_posixrules_data); } else { - i = read(fid, buf, sizeof buf); - if (close(fid) != 0 || i < (int)sizeof *tzhp) + nread = read(fid, buf, sizeof buf); + if (close(fid) != 0 || nread < (int)sizeof *tzhp) return -1; } tzhp = (struct tzhead *) buf; + ttisgmtcnt = (int) detzcode(tzhp->tzh_ttisgmtcnt); ttisstdcnt = (int) detzcode(tzhp->tzh_ttisstdcnt); sp->leapcnt = (int) detzcode(tzhp->tzh_leapcnt); sp->timecnt = (int) detzcode(tzhp->tzh_timecnt); sp->typecnt = (int) detzcode(tzhp->tzh_typecnt); sp->charcnt = (int) detzcode(tzhp->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)) + (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || + (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) return -1; - if (i < (int)( sizeof *tzhp + - sp->timecnt * (4 + sizeof (char)) + - sp->typecnt * (4 + 2 * sizeof (char)) + - sp->charcnt * sizeof (char) + - sp->leapcnt * 2 * 4 + - ttisstdcnt * sizeof (char) )) + if (nread < (int)(sizeof *tzhp + + sp->timecnt * 4 + /* ats */ + sp->timecnt * sizeof(char) + /* types */ + sp->typecnt * (4 + 2 * sizeof(char)) + /* ttinfos */ + sp->charcnt * sizeof(char) + /* chars */ + sp->leapcnt * 2 * 4 + /* lsinfos */ + ttisstdcnt * sizeof(char) + /* ttisstds */ + ttisgmtcnt * sizeof(char))) /* ttisgmts */ return -1; p = buf + sizeof *tzhp; for (i = 0; i < sp->timecnt; ++i) { sp->ats[i] = detzcode(p); @@ -349,67 +406,227 @@ tzload(const char *name, struct state * if (sp->types[i] >= sp->typecnt) return -1; } for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; + 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) + 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 */ + sp->chars[i] = '\0'; /* ensure '\0' at end */ for (i = 0; i < sp->leapcnt; ++i) { - struct lsinfo * lsisp; + 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) { - struct ttinfo * ttisp; + 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; + 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; } } + /* + ** Out-of-sort ats should mean we're running on a + ** signed time_t system but using a data file with + ** unsigned values (or vice versa). + */ + for (i = 0; i < sp->timecnt; ++i) + if ((i < sp->timecnt - 1 && sp->ats[i] > sp->ats[i + 1]) || + (i == sp->timecnt - 1 && !TYPE_SIGNED(time_t) && sp->ats[i] > INT32_MAX)) + { + if (TYPE_SIGNED(time_t)) + { + /* + ** Ignore the end (easy). + */ + sp->timecnt = i + 1; + } + else + { + /* + ** Ignore the beginning (harder). + */ + register int j; + + /* + ** Keep the record right before the + ** epoch boundary, + ** but tweak it so that it starts + ** right with the epoch + ** (thanks to Doug Bailey). + */ + sp->ats[i] = 0; + for (j = 0; j + i < sp->timecnt; ++j) + { + sp->ats[j] = sp->ats[j + i]; + sp->types[j] = sp->types[j + i]; + } + sp->timecnt = j; + } + break; + } + + /* + ** DJGPP only supports old files (version 0). + */ +#if 0 + if (tzhp->tzh_version[0] != '\0') + return -1; +#endif + + if (doextend && nread > 2 && + buf[0] == '\n' && buf[nread - 1] == '\n' && + sp->typecnt + 2 <= TZ_MAX_TYPES) + { + struct state ts; + register int result; + + buf[nread - 1] = '\0'; + result = tzparse(&buf[1], &ts, FALSE); + if (result == 0 && ts.typecnt == 2 && sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) + { + for (i = 0; i < 2; ++i) + ts.ttis[i].tt_abbrind += sp->charcnt; + for (i = 0; i < ts.charcnt; ++i) + sp->chars[sp->charcnt++] = ts.chars[i]; + i = 0; + while (i < ts.timecnt && ts.ats[i] <= sp->ats[sp->timecnt - 1]) + ++i; + while (i < ts.timecnt && sp->timecnt < TZ_MAX_TIMES) + { + sp->ats[sp->timecnt] = ts.ats[i]; + sp->types[sp->timecnt] = sp->typecnt + ts.types[i]; + ++sp->timecnt; + ++i; + } + sp->ttis[sp->typecnt++] = ts.ttis[0]; + sp->ttis[sp->typecnt++] = ts.ttis[1]; + } + } + if (sp->timecnt > 1) + { + for (i = 1; i < sp->timecnt; ++i) + if (typesequiv(sp, sp->types[i], sp->types[0]) && + differ_by_repeat(sp->ats[i], sp->ats[0])) + { + sp->goback = TRUE; + break; + } + for (i = sp->timecnt - 2; i >= 0; --i) + if (typesequiv(sp, sp->types[sp->timecnt - 1], sp->types[i]) && + differ_by_repeat(sp->ats[sp->timecnt - 1], sp->ats[i])) + { + sp->goahead = TRUE; + break; + } + } + /* + ** If type 0 is unused in transitions, + ** it's the type to use for early times. + */ + for (i = 0; i < sp->typecnt; ++i) + if (sp->types[i] == 0) + break; + i = (i >= sp->typecnt) ? 0 : -1; + /* + ** Absent the above, + ** if there are transition times + ** and the first transition is to a daylight time + ** find the standard type less than and closest to + ** the type of the first transition. + */ + if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) + { + i = sp->types[0]; + while (--i > -1) + if (!sp->ttis[i].tt_isdst) + break; + } + /* + ** If no result yet, find the first standard type. + ** If there is none, punt to type zero. + */ + if (i < 0) + { + i = 0; + while (sp->ttis[i].tt_isdst) + if (++i >= sp->typecnt) + { + i = 0; + break; + } + } + sp->defaulttype = i; return 0; } +static int +typesequiv(const struct state *const sp, const int a, const int b) +{ + register int result; + + if (sp == NULL || a < 0 || a >= sp->typecnt || b < 0 || b >= sp->typecnt) + result = FALSE; + else + { + register const struct ttinfo *ap = &sp->ttis[a]; + register const struct ttinfo *bp = &sp->ttis[b]; + result = ap->tt_gmtoff == bp->tt_gmtoff && + ap->tt_isdst == bp->tt_isdst && + ap->tt_ttisstd == bp->tt_ttisstd && + ap->tt_ttisgmt == bp->tt_ttisgmt && + strcmp(&sp->chars[ap->tt_abbrind], &sp->chars[bp->tt_abbrind]) == 0; + } + return result; +} + static const int mon_lengths[2][MONSPERYEAR] = { -{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, -{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + {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 -}; - -static const long n_year_lengths[3] = { - 3*DAYSPERNYEAR + DAYSPERLYEAR, /* 4-year cycle length */ - 25*(3*DAYSPERNYEAR + DAYSPERLYEAR) - 1, /* 100-year cycle length */ - 4*(25*(3*DAYSPERNYEAR + DAYSPERLYEAR) - 1) + 1 /* 400-year cycle length */ + 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 @@ -419,12 +636,30 @@ static const long n_year_lengths[3] = { static const char * getzname(const char *strp) { unsigned char c; - while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' && - c != '+') + while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+') + ++strp; + return strp; +} + +/* +** Given a pointer into an extended time zone string, scan until the ending +** delimiter of the zone name is located. Return a pointer to the delimiter. +** +** As with getzname above, the legal character set is actually quite +** restricted, with other characters producing undefined results. +** We don't do any checking here; checking is done later in common-case code. +*/ + +static const char * +getqzname(const char *strp, const int delim) +{ + register int c; + + while ((c = *strp) != '\0' && c != delim) ++strp; return strp; } /* @@ -433,27 +668,27 @@ getzname(const char *strp) ** NULL. ** Otherwise, return a pointer to the first character not part of the number. */ static const char * -getnum(const char *strp, int * const nump, const int min, const int max) +getnum(const char *strp, int *const nump, const int min, const int max) { unsigned char c; int num; - if (strp == NULL || !isdigit((unsigned char)*strp)) + if (strp == NULL || !is_digit((unsigned char)*strp)) return NULL; num = 0; - while ((c = *strp) != '\0' && isdigit(c)) + while ((c = *strp) != '\0' && is_digit(c)) { num = num * 10 + (c - '0'); if (num > max) - return NULL; + return NULL; /* illegal value */ ++strp; } if (num < min) - return NULL; + return NULL; /* illegal value */ *nump = num; return strp; } /* @@ -463,31 +698,38 @@ getnum(const char *strp, int * const num ** Otherwise, return a pointer to the first character not part of the number ** of seconds. */ static const char * -getsecs(const char *strp, long * const secsp) +getsecs(const char *strp, int_fast32_t *const secsp) { int num; - strp = getnum(strp, &num, 0, HOURSPERDAY); + /* + ** `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 * SECSPERHOUR; + *secsp = num * (int_fast32_t)SECSPERHOUR; if (*strp == ':') { ++strp; strp = getnum(strp, &num, 0, MINSPERHOUR - 1); if (strp == NULL) return NULL; *secsp += num * SECSPERMIN; if (*strp == ':') { ++strp; - strp = getnum(strp, &num, 0, SECSPERMIN - 1); + /* `SECSPERMIN' allows for leap seconds. */ + strp = getnum(strp, &num, 0, SECSPERMIN); if (strp == NULL) - return NULL; + return NULL; *secsp += num; } } return strp; } @@ -498,23 +740,21 @@ getsecs(const char *strp, long * const s ** If any error occurs, return NULL. ** Otherwise, return a pointer to the first character not part of the time. */ static const char * -getoffset(const char *strp, long * const offsetp) +getoffset(const char *strp, int_fast32_t *const offsetp) { - int neg; + int neg = 0; if (*strp == '-') { neg = 1; ++strp; } - else if (isdigit((unsigned char)*strp) || *strp++ == '+') - neg = 0; - else - return NULL; /* illegal offset */ + else if (*strp == '+') + ++strp; strp = getsecs(strp, offsetp); if (strp == NULL) return NULL; /* illegal time */ if (neg) *offsetp = -*offsetp; @@ -527,11 +767,11 @@ getoffset(const char *strp, long * const ** If a valid rule is not found, return NULL. ** Otherwise, return a pointer to the first character not part of the rule. */ static const char * -getrule(const char *strp, struct rule * const rulep) +getrule(const char *strp, struct rule *const rulep) { if (*strp == 'J') { /* ** Julian day. @@ -557,20 +797,20 @@ getrule(const char *strp, struct rule * return NULL; if (*strp++ != '.') return NULL; strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); } - else if (isdigit((unsigned char)*strp)) + else if (is_digit((unsigned char)*strp)) { /* ** Day of year. */ rulep->r_type = DAY_OF_YEAR; strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); } else - return NULL; /* invalid format */ + return NULL; /* invalid format */ if (strp == NULL) return NULL; if (*strp == '/') { /* @@ -578,28 +818,29 @@ getrule(const char *strp, struct rule * */ ++strp; strp = getsecs(strp, &rulep->r_time); } else - rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ + rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ return strp; } /* -** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the -** year, a rule, and the offset from GMT at the time that rule takes effect, +** 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(const time_t janfirst, const int year, const struct rule * const rulep, const long offset) +transtime(const time_t janfirst, const int year, const struct rule *const rulep, const int_fast32_t offset) { int leapyear; - time_t value=0; + time_t value; int i; int d, m1, yy0, yy1, yy2, dow; + INITIALIZE(value); leapyear = isleap(year); switch (rulep->r_type) { case JULIAN_DAY: @@ -638,12 +879,11 @@ transtime(const time_t janfirst, const i */ 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; + 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 @@ -653,13 +893,12 @@ transtime(const time_t janfirst, const i 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; + 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. @@ -667,78 +906,100 @@ transtime(const time_t janfirst, const i value += d * SECSPERDAY; break; } /* - ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in + ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in ** question. To get the Epoch-relative time of the specified local ** time on that day, add the transition time and the current offset - ** from GMT. + ** from UTC. */ return value + rulep->r_time + offset; } /* ** Given a POSIX section 8-style TZ string, fill in the rule tables as ** appropriate. */ static int -tzparse(const char *name, struct state * const sp, const int lastditch) +tzparse(const char *name, struct state *const sp, const int lastditch) { - const char * stdname; - const char * dstname=0; + const char *stdname; + const char *dstname; size_t stdlen; - int dstlen; - long stdoffset; - long dstoffset; - time_t * atp; - unsigned char * typep; - char * cp; + size_t dstlen; + int_fast32_t stdoffset; + int_fast32_t dstoffset; + time_t *atp; + unsigned char *typep; + char *cp; int load_result; + static struct ttinfo zttinfo; + INITIALIZE(dstname); stdname = name; if (lastditch) { - stdlen = strlen(name); /* length of standard zone name */ + stdlen = strlen(name); /* length of standard zone name */ name += stdlen; if (stdlen >= sizeof sp->chars) stdlen = (sizeof sp->chars) - 1; } else { - name = getzname(name); - stdlen = name - stdname; - if (stdlen < 3) + if (*name == '<') + { + name++; + stdname = name; + name = getqzname(name, '>'); + if (*name != '>') + return -1; + stdlen = name - stdname; + name++; + } + else + { + name = getzname(name); + stdlen = name - stdname; + } + if (*name == '\0') return -1; - } - if (*name == '\0') - return -1; - else - { name = getoffset(name, &stdoffset); if (name == NULL) return -1; } - load_result = tzload(TZDEFRULES, sp); + load_result = tzload(TZDEFRULES, sp, FALSE); if (load_result != 0) - sp->leapcnt = 0; /* so, we're off a little */ + sp->leapcnt = 0; /* so, we're off a little */ if (*name != '\0') { - dstname = name; - name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ - if (dstlen < 3) - return -1; + if (*name == '<') + { + dstname = ++name; + name = getqzname(name, '>'); + if (*name != '>') + return -1; + dstlen = name - dstname; + name++; + } + else + { + dstname = name; + name = getzname(name); + dstlen = name - dstname; /* length of DST zone name */ + } if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); if (name == NULL) - return -1; + return -1; } else dstoffset = stdoffset - SECSPERHOUR; + if (*name == '\0' && load_result != 0) + name = TZDEFRULESTRING; if (*name == ',' || *name == ';') { struct rule start; struct rule end; int year; @@ -746,146 +1007,153 @@ tzparse(const char *name, struct state * time_t starttime; time_t endtime; ++name; if ((name = getrule(name, &start)) == NULL) - return -1; + return -1; if (*name++ != ',') - return -1; + return -1; if ((name = getrule(name, &end)) == NULL) - return -1; + return -1; if (*name != '\0') - return -1; - sp->typecnt = 2; /* standard time and DST */ + 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; + ** Two transitions per year, from EPOCH_YEAR forward. + */ + sp->ttis[0] = sp->ttis[1] = zttinfo; 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) + sp->timecnt = 0; + for (year = EPOCH_YEAR; sp->timecnt + 2 <= TZ_MAX_TIMES; ++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; + time_t newfirst; + 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 */ + } + sp->timecnt += 2; + newfirst = janfirst; + newfirst += year_lengths[isleap(year)] * SECSPERDAY; + if (newfirst <= janfirst) + break; + janfirst = newfirst; } } else { - int sawstd; - int sawdst; - long stdfix; - long dstfix; - long oldfix; + register int_fast32_t theirstdoffset; + register int_fast32_t theirdstoffset; + register int_fast32_t theiroffset; int isdst; - int i; + int i, j; if (*name != '\0') - return -1; - if (load_result != 0) - return -1; + return -1; /* - ** Compute the difference between the real and - ** prototype standard and summer time offsets - ** from GMT, and put the real standard and summer - ** time offsets into the rules in place of the - ** prototype offsets. - */ - sawstd = FALSE; - sawdst = FALSE; - stdfix = 0; - dstfix = 0; - for (i = 0; i < sp->typecnt; ++i) + ** 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) { - if (sp->ttis[i].tt_isdst) - { - oldfix = dstfix; - dstfix = - sp->ttis[i].tt_gmtoff + dstoffset; - if (sawdst && (oldfix != dstfix)) - return -1; - sp->ttis[i].tt_gmtoff = -dstoffset; - sp->ttis[i].tt_abbrind = stdlen + 1; - sawdst = TRUE; - } - else - { - oldfix = stdfix; - stdfix = - sp->ttis[i].tt_gmtoff + stdoffset; - if (sawstd && (oldfix != stdfix)) - return -1; - sp->ttis[i].tt_gmtoff = -stdoffset; - sp->ttis[i].tt_abbrind = 0; - sawstd = TRUE; - } + j = sp->types[i]; + if (sp->ttis[j].tt_isdst) + { + theirdstoffset = -sp->ttis[j].tt_gmtoff; + break; + } } /* - ** Make sure we have both standard and summer time. - */ - if (!sawdst || !sawstd) - return -1; + ** Initially we're assumed to be in standard time. + */ + isdst = FALSE; + theiroffset = theirstdoffset; /* - ** Now correct the transition times by shifting - ** them by the difference between the real and - ** prototype offsets. Note that this difference - ** can be different in standard and summer time; - ** the prototype probably has a 1-hour difference - ** between standard and summer time, but a different - ** difference can be specified in TZ. - */ - isdst = FALSE; /* we start in standard time */ + ** Now juggle transition times and types + ** tracking offsets as you do. + */ for (i = 0; i < sp->timecnt; ++i) { - const struct ttinfo * ttisp; - - /* - ** 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. - */ - ttisp = &sp->ttis[sp->types[i]]; - sp->ats[i] += - (isdst && !ttisp->tt_ttisstd) ? - dstfix : stdfix; - isdst = ttisp->tt_isdst; + 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. + */ + sp->ttis[0] = sp->ttis[1] = zttinfo; + 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->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; } @@ -905,14 +1173,25 @@ tzparse(const char *name, struct state * } return 0; } static void -gmtload(struct state * const sp) +gmtload(struct state *const sp) +{ + if (tzload(gmt, sp, TRUE) != 0) + (void) tzparse(gmt, sp, TRUE); +} + +void +tzsetwall(void) { - if (tzload(GMT, sp) != 0) - (void) tzparse(GMT, sp, TRUE); + if (lcl_is_set == -1) + return; + lcl_is_set = -1; + if (tzload((char *) NULL, lclptr, TRUE) != 0) + gmtload(lclptr); + settzname(); } void tzset(void) { @@ -928,328 +1207,331 @@ tzset(void) were called, we are all done here. */ last_env_changed = __environ_changed; name = getenv("TZ"); /* Use stricmp, since if TZ points to a file name, we need to be case-insensitive. */ - if (lcl_is_set > 0 && (name == NULL || stricmp(name, lcl_tzstr) == 0)) + if (lcl_is_set > 0 && (name == NULL || stricmp(name, lcl_TZname) == 0)) return; /* On to some *real* work... */ if (name == NULL) { tzsetwall(); lcl_is_set = 1; return; } - if (strlen(name) < sizeof(lcl_tzstr)) - { - lcl_is_set = 1; - strcpy(lcl_tzstr, name); - } - else - lcl_is_set = 0; -#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 */ + lcl_is_set = strlen(name) < sizeof lcl_TZname; + if (lcl_is_set) + strcpy(lcl_TZname, name); + if (*name == '\0') { /* ** User wants it fast rather than right. */ - lclptr->leapcnt = 0; /* so, we're off a little */ + lclptr->leapcnt = 0; /* so, we're off a little */ lclptr->timecnt = 0; + lclptr->ttis[0].tt_isdst = 0; lclptr->ttis[0].tt_gmtoff = 0; lclptr->ttis[0].tt_abbrind = 0; - (void) strcpy(lclptr->chars, GMT); + (void) strcpy(lclptr->chars, gmt); } - else if (tzload(name, lclptr) != 0) + else if (tzload(name, lclptr, TRUE) != 0) if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) gmtload(lclptr); settzname(); } -void -tzsetwall(void) -{ - if (lcl_is_set == -1) - 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(); -} - /* ** 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(const time_t * const timep, const long offset, struct tm * const tmp) +static struct tm * +localsub(const time_t *const timep, const int_fast32_t offset, struct tm *const tmp) { - const struct state * sp; - const struct ttinfo * ttisp; + const struct state *sp; + const struct ttinfo *ttisp; int i; + struct tm *result; const time_t t = *timep; - tzset(); sp = lclptr; -#ifdef ALL_STATE - if (sp == NULL) + if ((sp->goback && t < sp->ats[0]) || + (sp->goahead && t > sp->ats[sp->timecnt - 1])) { - gmtsub(timep, offset, tmp); - return; + time_t newt = t; + register time_t seconds; + register time_t tcycles; + register int_fast64_t icycles; + + if (t < sp->ats[0]) + seconds = sp->ats[0] - t; + else + seconds = t - sp->ats[sp->timecnt - 1]; + --seconds; + tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; + ++tcycles; + icycles = tcycles; + if (tcycles - icycles >= 1 || icycles - tcycles >= 1) + return NULL; + seconds = icycles; + seconds *= YEARSPERREPEAT; + seconds *= AVGSECSPERYEAR; + if (t < sp->ats[0]) + newt += seconds; + else + newt -= seconds; + if (newt < sp->ats[0] || newt > sp->ats[sp->timecnt - 1]) + return NULL; /* "cannot happen" */ + result = localsub(&newt, offset, tmp); + if (result == tmp) + { + register time_t newy; + + newy = tmp->tm_year; + if (t < sp->ats[0]) + newy -= icycles * YEARSPERREPEAT; + else + newy += icycles * YEARSPERREPEAT; + tmp->tm_year = newy; + if (tmp->tm_year != (int)newy) + return NULL; + } + return result; } -#endif /* defined ALL_STATE */ if (sp->timecnt == 0 || t < sp->ats[0]) - { - i = 0; - while (sp->ttis[i].tt_isdst) - if (++i >= sp->typecnt) - { - i = 0; - break; - } - } + i = sp->defaulttype; else { - for (i = 1; i < sp->timecnt; ++i) - if (t < sp->ats[i]) - break; - i = sp->types[i - 1]; + register int lo = 1; + register int hi = sp->timecnt; + + while (lo < hi) + { + register int mid = (lo + hi) >> 1; + + if (t < sp->ats[mid]) + hi = mid; + else + lo = mid + 1; + } + i = (int) sp->types[lo - 1]; } ttisp = &sp->ttis[i]; /* ** 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); + result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); tmp->tm_isdst = ttisp->tt_isdst; tzname[tmp->tm_isdst] = unconst(&sp->chars[ttisp->tt_abbrind], char *); tmp->tm_zone = unconst(&sp->chars[ttisp->tt_abbrind], char *); + return result; } struct tm * -localtime_r( const time_t * __restrict__ timep, struct tm * __restrict__ tm) +localtime(const time_t *const timep) { + static struct tm tm; - localsub(timep, 0L, tm); - return tm; + tzset(); + return localtime_r(timep, &tm); } struct tm * -localtime(const time_t * const timep) +localtime_r( const time_t * __restrict__ timep, struct tm * __restrict__ tm) { - static struct tm tm; - - return localtime_r(timep, &tm); + return localsub(timep, 0L, tm);; } /* ** gmtsub is to gmtime as localsub is to localtime. */ -static void -gmtsub(const time_t * const timep, const long offset, struct tm * const tmp) +static struct tm * +gmtsub(const time_t *const timep, const int_fast32_t offset, struct tm *const tmp) { + register struct tm *result; + 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); + gmtload(gmtptr); } - timesub(timep, offset, gmtptr, tmp); + result = timesub(timep, offset, gmtptr, tmp); /* ** Could get fancy here and deliver something such as - ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero, + ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero, ** but this is no time for a treasure hunt. */ if (offset != 0) - tmp->tm_zone = WILDABBR; + 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 */ - } + return result; } struct tm * -gmtime_r(const time_t * __restrict__ timep, struct tm * __restrict__ tm) +gmtime(const time_t *const timep) { + static struct tm tm; - gmtsub(timep, 0L, tm); - return tm; + return gmtime_r(timep, &tm); } struct tm * -gmtime(const time_t * const timep) +gmtime_r(const time_t * __restrict__ timep, struct tm * __restrict__ tm) { - static struct tm tm; - - return gmtime_r(timep, &tm); + return gmtsub(timep, 0L, tm); } -/* Return the year which is DAYS away from the year Y0. */ +/* +** Return the number of leap years through the end of the given year +** where, to make the math easy, the answer for year zero is defined as zero. +*/ + static int -days_to_years(int y0, long *days) +leaps_thru_end_of(register const int y) { - int y, dir, yleap; - - y = y0; - dir = *days >= 0 ? 1 : -1; - - /* We move by 400, 100, and 4 years at a time, to quickly reduce - DAYS to a reasonable value. */ - while (*days*dir > n_year_lengths[2]) - { - y += dir*400; - *days -= dir*n_year_lengths[2]; - } - while (*days*dir > n_year_lengths[1]) - { - y += dir*100; - *days -= dir*n_year_lengths[1]; - } - while (*days*dir > n_year_lengths[0]) - { - y += dir*4; - *days -= dir*n_year_lengths[0]; - } - if (dir == 1) - for ( ; ; ) - { - yleap = isleap(y); - if (*days < (long) year_lengths[yleap]) - break; - ++y; - *days = *days - (long) year_lengths[yleap]; - } - else - do { - --y; - yleap = isleap(y); - *days = *days + (long) year_lengths[yleap]; - } while (*days < 0); - - return y; + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : -(leaps_thru_end_of(-(y + 1)) + 1); } -static void -timesub(const time_t * const timep, const long offset, const struct state * const sp, struct tm * const tmp) +static struct tm * +timesub(const time_t *const timep, const int_fast32_t offset, register const struct state *const sp, register struct tm *const tmp) { - const struct lsinfo * lp; - long days; - long rem; + const struct lsinfo *lp; + time_t tdays; + int idays; /* unsigned would be so 2003 */ + int_fast64_t rem; int y; - int yleap; - const int * ip; - long corr; + const int *ip; + int_fast64_t corr; int hit; int i; corr = 0; - hit = FALSE; -#ifdef ALL_STATE - i = (sp == NULL) ? 0 : sp->leapcnt; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE + hit = 0; i = sp->leapcnt; -#endif /* State Farm */ - while (--i >= 0) + while (--i > -1) { 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); + { + 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) + y = EPOCH_YEAR; + tdays = *timep / SECSPERDAY; + rem = *timep - tdays * SECSPERDAY; + while (tdays < 0 || tdays >= (time_t)year_lengths[isleap(y)]) + { + int newy; + register time_t tdelta; + register int idelta; + register int leapdays; + + tdelta = tdays / DAYSPERLYEAR; + idelta = tdelta; + if (tdelta - idelta >= 1 || idelta - tdelta >= 1) + return NULL; + if (idelta == 0) + idelta = (tdays < 0) ? -1 : 1; + newy = y; + if (increment_overflow(&newy, idelta)) + return NULL; + leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1); + tdays -= ((time_t) newy - y) * DAYSPERNYEAR; + tdays -= leapdays; + y = newy; + } { - /* - ** A 3B1 muffs the division on the most negative number. - */ - days = -24855; - rem = -11648; + register int_fast32_t seconds; + register time_t half_second = 0.5; + + seconds = tdays * SECSPERDAY + half_second; + tdays = seconds / SECSPERDAY; + rem += seconds - tdays * SECSPERDAY; } -#endif /* mc68k */ - rem += (offset - corr); + /* + ** Given the range, we can now fearlessly cast... + */ + idays = tdays; + rem += offset - corr; while (rem < 0) { rem += SECSPERDAY; - --days; + --idays; } while (rem >= SECSPERDAY) { rem -= SECSPERDAY; - ++days; + ++idays; } - tmp->tm_hour = (int) (rem / SECSPERHOUR); - rem = rem % SECSPERHOUR; - tmp->tm_min = (int) (rem / SECSPERMIN); - tmp->tm_sec = (int) (rem % SECSPERMIN); - if (hit) - /* - ** A positive leap second requires a special - ** representation. This uses "... ??:59:60". - */ - ++(tmp->tm_sec); - tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); + while (idays < 0) + { + if (increment_overflow(&y, -1)) + return NULL; + idays += year_lengths[isleap(y)]; + } + while (idays >= year_lengths[isleap(y)]) + { + idays -= year_lengths[isleap(y)]; + if (increment_overflow(&y, 1)) + return NULL; + } + tmp->tm_year = y; + if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) + return NULL; + tmp->tm_yday = idays; + /* + ** The "extra" mods below avoid overflow problems. + */ + tmp->tm_wday = EPOCH_WDAY + + ((y - EPOCH_YEAR) % DAYSPERWEEK) * + (DAYSPERNYEAR % DAYSPERWEEK) + + leaps_thru_end_of(y - 1) - + leaps_thru_end_of(EPOCH_YEAR - 1) + + idays; + tmp->tm_wday %= DAYSPERWEEK; if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; - y = days_to_years(EPOCH_YEAR, &days); - yleap = isleap(y); - 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_hour = (int) (rem / SECSPERHOUR); + 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; + ip = mon_lengths[isleap(y)]; + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) + idays -= ip[tmp->tm_mon]; + tmp->tm_mday = (int) (idays + 1); tmp->tm_isdst = 0; tmp->tm_gmtoff = offset; + return tmp; } /* ** A la X3J11 */ @@ -1264,15 +1546,15 @@ asctime_r(const struct tm * __restrict__ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; (void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n", - wday_name[timeptr->tm_wday], - mon_name[timeptr->tm_mon], - timeptr->tm_mday, timeptr->tm_hour, - timeptr->tm_min, timeptr->tm_sec, - TM_YEAR_BASE + timeptr->tm_year); + wday_name[timeptr->tm_wday], + mon_name[timeptr->tm_mon], + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec, + TM_YEAR_BASE + timeptr->tm_year); return result; } char * asctime(const struct tm *timeptr) @@ -1289,50 +1571,82 @@ ctime_r(const time_t *timep, char *resul return asctime_r(localtime_r(timep, &tm), result); } char * -ctime(const time_t * const timep) +ctime(const time_t *const timep) { return asctime(localtime(timep)); } /* ** 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. (mtxinu!kridle now). -** 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). +** 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. (mtxinu!kridle now). +** It does a binary search of the time_t space. Since time_t's are +** just 32 bits, its a max of 32 iterations (even at 64 bits it +** would still be very reasonable). */ #ifndef WRONG -#define WRONG (-1) +#define WRONG (-1) #endif /* !defined WRONG */ -static void -normalize(int * const tensptr, int * const unitsptr, const int base) +/* +** Normalize logic courtesy Paul Eggert. +*/ + +static int +increment_overflow(int *const ip, int j) { - if (*unitsptr >= base) - { - *tensptr += *unitsptr / base; - *unitsptr %= base; - } - else if (*unitsptr < 0) - { - --*tensptr; - *unitsptr += base; - if (*unitsptr < 0) - { - *tensptr -= 1 + (-*unitsptr) / base; - *unitsptr = base - (-*unitsptr) % base; - } - } + register int const i = *ip; + + /* + ** If i >= 0 there can only be overflow if i + j > INT_MAX + ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. + ** If i < 0 there can only be overflow if i + j < INT_MIN + ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. + */ + if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) + return TRUE; + *ip += j; + return FALSE; } static int -tmcomp(const struct tm * const atmp, const struct tm * const btmp) +increment_overflow32(int_fast32_t *const lp, int const m) +{ + register int_fast32_t const l = *lp; + + if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) + return TRUE; + *lp += m; + return FALSE; +} + +static int +normalize_overflow(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 +normalize_overflow32(int_fast32_t *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_overflow32(tensptr, tensdelta); +} + +static int +tmcomp(const struct tm *const atmp, const struct tm *const btmp) { int result; if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && @@ -1341,220 +1655,283 @@ tmcomp(const struct tm * const atmp, con (result = (atmp->tm_min - btmp->tm_min)) == 0) result = atmp->tm_sec - btmp->tm_sec; return result; } -static void -tmnormalize(struct tm *tmp) -{ - if (tmp->tm_sec >= SECSPERMIN + 2 || tmp->tm_sec < 0) - normalize(&tmp->tm_min, &tmp->tm_sec, SECSPERMIN); - normalize(&tmp->tm_hour, &tmp->tm_min, MINSPERHOUR); - normalize(&tmp->tm_mday, &tmp->tm_hour, HOURSPERDAY); - normalize(&tmp->tm_year, &tmp->tm_mon, MONSPERYEAR); - - /* If tm_mday is negative, or positive and too large, bring it back - to the reasonable range [1..366]. */ - if (tmp->tm_mday <= 0 || tmp->tm_mday > DAYSPERLYEAR) - { - long days = tmp->tm_mday; - int yleap = isleap(tmp->tm_year + TM_YEAR_BASE); - - while (tmp->tm_mon--) - days += mon_lengths[yleap][tmp->tm_mon]; - tmp->tm_year = - days_to_years(tmp->tm_year + TM_YEAR_BASE, &days) - TM_YEAR_BASE; - tmp->tm_mday = days; - tmp->tm_mon = 0; - } - - /* Now correct tm_mon and tm_mday so that they are within their - normal ranges [0..11] and [1..mon_length[tm_mon]], respectively. */ - for ( ; ; ) - { - int i = mon_lengths[isleap(tmp->tm_year + TM_YEAR_BASE)][tmp->tm_mon]; - if (tmp->tm_mday <= i) - break; - tmp->tm_mday -= i; - if (++tmp->tm_mon >= MONSPERYEAR) - { - tmp->tm_mon = 0; - ++tmp->tm_year; - } - } -} - static time_t -time2(struct tm *tmp, void (*const funcp)(const time_t *const,const long,struct tm *), const long offset, int * const okayp) +time2sub(struct tm *const tmp, struct tm *(*const funcp)(const time_t *, int_fast32_t, struct tm *), const int_fast32_t offset, int *const okayp, const int do_norm_secs) { - const struct state * sp; - int dir; - int bits; - int i, j ; - int saved_seconds; + register const struct state *sp; + register int dir; + register int i, j; + register int saved_seconds; + register int_fast32_t li; + register time_t lo; + register time_t hi; + int_fast32_t y; time_t newt; time_t t; struct tm yourtm, mytm; *okayp = FALSE; - tmnormalize(tmp); yourtm = *tmp; - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = 0; + 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; + y = yourtm.tm_year; + if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) + return WRONG; /* - ** Calculate the number of magnitude bits in a time_t - ** (this works regardless of whether time_t is - ** signed or unsigned, though lint complains if unsigned). - */ - for (bits = 0, t = 1; t > 0; ++bits, t <<= 1) - ; + ** Turn y into an actual year number for now. + ** It is converted back to an offset from TM_YEAR_BASE later. + */ + if (increment_overflow32(&y, TM_YEAR_BASE)) + return WRONG; + while (yourtm.tm_mday <= 0) + { + if (increment_overflow32(&y, -1)) + return WRONG; + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday += year_lengths[isleap(li)]; + } + while (yourtm.tm_mday > DAYSPERLYEAR) + { + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (increment_overflow32(&y, 1)) + return WRONG; + } + for ( ; ; ) + { + i = mon_lengths[isleap(y)][yourtm.tm_mon]; + if (yourtm.tm_mday <= i) + break; + yourtm.tm_mday -= i; + if (++yourtm.tm_mon >= MONSPERYEAR) + { + yourtm.tm_mon = 0; + if (increment_overflow32(&y, 1)) + return WRONG; + } + } + if (increment_overflow32(&y, -TM_YEAR_BASE)) + return WRONG; + yourtm.tm_year = y; + if (yourtm.tm_year != y) + return WRONG; + if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) + saved_seconds = 0; + else if (y + TM_YEAR_BASE < EPOCH_YEAR) + { + /* + ** We can't set tm_sec to 0, because that might push the + ** time below the minimum representable time. + ** 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; + } /* - ** If time_t is signed, then 0 is the median value, - ** if time_t is unsigned, then 1 << bits is median. - */ - t = (time_t) 1 << bits; + ** Do a binary search (this works whatever time_t's type is). + */ + if (!TYPE_SIGNED(time_t)) + { + lo = 0; + hi = lo - 1; + } + else if (!TYPE_INTEGRAL(time_t)) + { + if (sizeof(time_t) > sizeof(float)) + hi = (time_t) DBL_MAX; + else + hi = (time_t) FLT_MAX; + lo = -hi; + } + else + { + lo = 1; + for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) + lo *= 2; + hi = -(lo + 1); + } for ( ; ; ) { - (*funcp)(&t, offset, &mytm); - dir = tmcomp(&mytm, &yourtm); + t = lo / 2 + hi / 2; + if (t < lo) + t = lo; + else if (t > hi) + t = hi; + if ((*funcp)(&t, offset, &mytm) == NULL) + { + /* + ** Assume that t is too extreme to be represented in + ** a struct tm; arrange things so that it is less + ** extreme on the next pass. + */ + dir = (t > 0) ? 1 : -1; + } + else + dir = tmcomp(&mytm, &yourtm); if (dir != 0) { - if (bits-- < 0) - return WRONG; - if (bits < 0) - --t; - else if (dir > 0) - t -= (time_t) 1 << bits; - else t += (time_t) 1 << bits; + if (t == lo) + { + ++t; + if (t <= lo) + return WRONG; + ++lo; + } + else if (t == hi) + { + --t; + if (t >= hi) + return WRONG; + --hi; + } + if (lo > hi) + return WRONG; + if (dir > 0) + hi = t; + else + lo = t; 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. - */ - sp = (const struct state *) - ((funcp == localsub) ? lclptr : gmtptr); -#ifdef ALL_STATE - if (sp == NULL) - return WRONG; -#endif /* defined ALL_STATE */ - for (i = 0; i < sp->typecnt; ++i) + ** Right time, wrong type. + ** Hunt for right time, right type. + ** It's okay to guess wrong since the guess + ** gets checked. + */ + sp = (const struct state *)((funcp == localsub) ? lclptr : gmtptr); + for (i = sp->typecnt - 1; i >= 0; --i) { if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) - continue; - for (j = 0; j < sp->typecnt; ++j) + 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; + if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) + continue; + newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; + if ((*funcp)(&newt, offset, &mytm) == NULL) + continue; + 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: - t += saved_seconds; - (*funcp)(&t, offset, tmp); - *okayp = TRUE; +label: + newt = t + saved_seconds; + if ((newt < t) != (saved_seconds < 0)) + return WRONG; + t = newt; + if ((*funcp)(&t, offset, tmp)) + *okayp = TRUE; return t; } static time_t -time1(struct tm * const tmp, void (*const funcp)(const time_t * const, const long, struct tm *), const long offset) +time2(struct tm *const tmp, struct tm *(*const funcp)(const time_t *, int_fast32_t, struct tm *), const int_fast32_t offset, int *const okayp) { time_t t; - const struct state * sp; - int samei, otheri; + + /* + ** 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(struct tm *const tmp, struct tm *(*const funcp)(const time_t *, int_fast32_t, struct tm *), const int_fast32_t 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 == NULL) + { + errno = EINVAL; + return WRONG; + } if (tmp->tm_isdst > 1) tmp->tm_isdst = 1; t = time2(tmp, funcp, offset, &okay); if (okay || tmp->tm_isdst < 0) return t; /* - ** 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. - */ - sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr); -#ifdef ALL_STATE - if (sp == NULL) - return WRONG; -#endif /* defined ALL_STATE */ - for (samei = 0; samei < sp->typecnt; ++samei) + ** 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. + */ + sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr); + for (i = 0; i < sp->typecnt; ++i) + seen[i] = FALSE; + nseen = 0; + for (i = sp->timecnt - 1; i >= 0; --i) + if (!seen[sp->types[i]]) + { + seen[sp->types[i]] = TRUE; + types[nseen++] = sp->types[i]; + } + for (sameind = 0; sameind < nseen; ++sameind) { + samei = types[sameind]; if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) continue; - for (otheri = 0; otheri < sp->typecnt; ++otheri) + for (otherind = 0; otherind < nseen; ++otherind) { + otheri = types[otherind]; if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) - continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; + 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; + 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(struct tm * tmp) { - struct tm save_tm = *tmp; - int rv = time1(tmp, localsub, 0L); - if (rv == -1) - { - /* Try again, off a few hours. This may get us out of the DST - switch zone (which has two valid time_t's) and into a "normal" - range, and we'll compensate the return value afterwards. */ - int delta = 12; - if (tmp->tm_hour > 12) - delta = -12; - - tmp->tm_hour += delta; - rv = time1(tmp, localsub, 0L); - tmp->tm_hour -= delta; - if (rv != -1) - rv -= delta*60*60; - else - { - /* tmp might point to a time structure that's before 1/1/1970. - This can happen if tmp is a result of a call to localtime(0) - issued in a timezone with a negative offset, like PST8. - Adding the timezone offset will bring the time structure into - the valid range. */ - delta = 24 - tmp->tm_hour; - tmp->tm_hour += delta; - rv = time1(tmp, localsub, 0L); - tmp->tm_hour -= delta; - if (rv != -1) - rv -= delta*60*60; - } - if (rv == -1) - *tmp = save_tm; - else - tmnormalize(tmp); - } - return rv; + tzset(); + return time1(tmp, localsub, 0L); } diff -aprNU5 djgpp.orig/src/libc/ansi/time/posixrul.h djgpp/src/libc/ansi/time/posixrul.h --- djgpp.orig/src/libc/ansi/time/posixrul.h 1995-08-27 04:52:08 +0100 +++ djgpp/src/libc/ansi/time/posixrul.h 2013-08-17 16:02:38 +0100 @@ -1,49 +1,44 @@ +/* Copyright (C) 2013 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ /* generated with bin2h from DJGPP/zoneinfo/posixrules */ unsigned char _posixrules_data[] = { -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0, -0,1,16,0,0,0,2,0,0,0,8,0,151,254,240,1,135,225,224,2,119,224,240,3,112,254,96,4,96,253,112,5,80, +84,90,105,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0, +0,0,235,0,0,0,4,0,0,0,16,158,166,30,112,159,186,235,96,160,134,0,112,161,154,205,96,162,101,226,112,163,131, +233,224,164,106,174,112,165,53,167,96,166,83,202,240,167,21,137,96,168,51,172,240,168,254,165,224,170,19,142,240,170,222,135, +224,171,243,112,240,172,190,105,224,173,211,82,240,174,158,75,224,175,179,52,240,176,126,45,224,177,156,81,112,178,103,74,96, +179,124,51,112,180,71,44,96,181,92,21,112,182,39,14,96,183,59,247,112,184,6,240,96,185,27,217,112,185,230,210,96,187, +4,245,240,187,198,180,96,188,228,215,240,189,175,208,224,190,196,185,240,191,143,178,224,192,164,155,240,193,111,148,224,194,132, +125,240,195,79,118,224,196,100,95,240,197,47,88,224,198,77,124,112,199,15,58,224,200,45,94,112,200,248,87,96,202,13,64, +112,202,216,57,96,203,136,240,112,210,35,244,112,210,96,251,224,211,117,228,240,212,64,221,224,213,85,198,240,214,32,191,224, +215,53,168,240,216,0,161,224,217,21,138,240,217,224,131,224,218,254,167,112,219,192,101,224,220,222,137,112,221,169,130,96,222, +190,107,112,223,137,100,96,224,158,77,112,225,105,70,96,226,126,47,112,227,73,40,96,228,94,17,112,229,87,46,224,230,71, +45,240,231,55,16,224,232,39,15,240,233,22,242,224,234,6,241,240,234,246,212,224,235,230,211,240,236,214,182,224,237,198,181, +240,238,191,211,96,239,175,210,112,240,159,181,96,241,143,180,112,242,127,151,96,243,111,150,112,244,95,121,96,245,79,120,112, +246,63,91,96,247,47,90,112,248,40,119,224,249,15,60,112,250,8,89,224,250,248,88,240,251,232,59,224,252,216,58,240,253, +200,29,224,254,184,28,240,255,167,255,224,0,151,254,240,1,135,225,224,2,119,224,240,3,112,254,96,4,96,253,112,5,80, 224,96,6,64,223,112,7,48,194,96,7,141,25,112,9,16,164,96,9,173,148,240,10,240,134,96,11,224,133,112,12,217,162, 224,13,192,103,112,14,185,132,224,15,169,131,240,16,153,102,224,17,137,101,240,18,121,72,224,19,105,71,240,20,89,42,224, 21,73,41,240,22,57,12,224,23,41,11,240,24,34,41,96,25,8,237,240,26,2,11,96,26,242,10,112,27,225,237,96,28, 209,236,112,29,193,207,96,30,177,206,112,31,161,177,96,32,118,0,240,33,129,147,96,34,85,226,240,35,106,175,224,36,53, 196,240,37,74,145,224,38,21,166,240,39,42,115,224,39,254,195,112,41,10,85,224,41,222,165,112,42,234,55,224,43,190,135, 112,44,211,84,96,45,158,105,112,46,179,54,96,47,126,75,112,48,147,24,96,49,103,103,240,50,114,250,96,51,71,73,240, 52,82,220,96,53,39,43,240,54,50,190,96,55,7,13,240,56,27,218,224,56,230,239,240,57,251,188,224,58,198,209,240,59, 219,158,224,60,175,238,112,61,187,128,224,62,143,208,112,63,155,98,224,64,111,178,112,65,132,127,96,66,79,148,112,67,100, -97,96,68,47,118,112,69,68,67,96,70,15,88,112,71,36,37,96,71,248,116,240,73,4,7,96,73,216,86,240,74,227,233, -96,75,184,56,240,76,205,5,224,77,152,26,240,78,172,231,224,79,119,252,240,80,140,201,224,81,97,25,112,82,108,171,224, -83,64,251,112,84,76,141,224,85,32,221,112,86,44,111,224,87,0,191,112,88,21,140,96,88,224,161,112,89,245,110,96,90, -192,131,112,91,213,80,96,92,169,159,240,93,181,50,96,94,137,129,240,95,149,20,96,96,105,99,240,97,126,48,224,98,73, -69,240,99,94,18,224,100,41,39,240,101,61,244,224,102,18,68,112,103,29,214,224,103,242,38,112,104,253,184,224,105,210,8, -112,106,221,154,224,107,177,234,112,108,198,183,96,109,145,204,112,110,166,153,96,111,113,174,112,112,134,123,96,113,90,202,240, -114,102,93,96,115,58,172,240,116,70,63,96,117,26,142,240,118,47,91,224,118,250,112,240,120,15,61,224,120,218,82,240,121, -239,31,224,122,186,52,240,123,207,1,224,124,163,81,112,125,174,227,224,126,131,51,112,127,142,197,224,128,99,21,112,129,119, -226,96,130,66,247,112,131,87,196,96,132,34,217,112,133,55,166,96,134,11,245,240,135,23,136,96,135,235,215,240,136,247,106, -96,137,203,185,240,138,215,76,96,139,171,155,240,140,192,104,224,141,139,125,240,142,160,74,224,143,107,95,240,144,128,44,224, -145,84,124,112,146,96,14,224,147,52,94,112,148,63,240,224,149,20,64,112,150,41,13,96,150,244,34,112,152,8,239,96,152, -212,4,112,153,232,209,96,154,189,32,240,155,200,179,96,156,157,2,240,157,168,149,96,158,124,228,240,159,136,119,96,160,92, -198,240,161,113,147,224,162,60,168,240,163,81,117,224,164,28,138,240,165,49,87,224,166,5,167,112,167,17,57,224,167,229,137, -112,168,241,27,224,169,197,107,112,170,218,56,96,171,165,77,112,172,186,26,96,173,133,47,112,174,153,252,96,175,101,17,112, -176,121,222,96,177,78,45,240,178,89,192,96,179,46,15,240,180,57,162,96,181,13,241,240,182,34,190,224,182,237,211,240,184, -2,160,224,184,205,181,240,185,226,130,224,186,182,210,112,187,194,100,224,188,150,180,112,189,162,70,224,190,118,150,112,191,130, -40,224,192,86,120,112,193,107,69,96,194,54,90,112,195,75,39,96,196,22,60,112,197,43,9,96,197,255,88,240,199,10,235, -96,199,223,58,240,200,234,205,96,201,191,28,240,202,211,233,224,203,158,254,240,204,179,203,224,205,126,224,240,206,147,173,224, -207,103,253,112,208,115,143,224,209,71,223,112,210,83,113,224,211,39,193,112,212,51,83,224,213,7,163,112,214,28,112,96,214, -231,133,112,215,252,82,96,216,199,103,112,217,220,52,96,218,176,131,240,219,188,22,96,220,144,101,240,221,155,248,96,222,112, -71,240,223,133,20,224,224,80,41,240,225,100,246,224,226,48,11,240,227,68,216,224,228,15,237,240,229,36,186,224,229,249,10, -112,231,4,156,224,231,216,236,112,232,228,126,224,233,184,206,112,234,205,155,96,235,152,176,112,236,173,125,96,237,120,146,112, -238,141,95,96,239,97,174,240,240,109,65,96,241,65,144,240,242,77,35,96,243,33,114,240,244,45,5,96,245,1,84,240,246, -22,33,224,246,225,54,240,247,246,3,224,248,193,24,240,249,213,229,224,250,160,250,240,251,181,199,224,252,138,23,112,253,149, -169,224,254,105,249,112,255,117,139,224,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, -1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, +97,96,68,47,118,112,69,68,67,96,69,243,168,240,71,45,95,224,71,211,138,240,73,13,65,224,73,179,108,240,74,237,35, +224,75,156,137,112,76,214,64,96,77,124,107,112,78,182,34,96,79,92,77,112,80,150,4,96,81,60,47,112,82,117,230,96, +83,28,17,112,84,85,200,96,84,251,243,112,86,53,170,96,86,229,15,240,88,30,198,224,88,196,241,240,89,254,168,224,90, +164,211,240,91,222,138,224,92,132,181,240,93,190,108,224,94,100,151,240,95,158,78,224,96,77,180,112,97,135,107,96,98,45, +150,112,99,103,77,96,100,13,120,112,101,71,47,96,101,237,90,112,103,39,17,96,103,205,60,112,105,6,243,96,105,173,30, +112,106,230,213,96,107,150,58,240,108,207,241,224,109,118,28,240,110,175,211,224,111,85,254,240,112,143,181,224,113,53,224,240, +114,111,151,224,115,21,194,240,116,79,121,224,116,254,223,112,118,56,150,96,118,222,193,112,120,24,120,96,120,190,163,112,121, +248,90,96,122,158,133,112,123,216,60,96,124,126,103,112,125,184,30,96,126,94,73,112,127,152,0,96,0,1,0,1,0,1, 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, +1,0,1,0,1,0,1,0,1,2,3,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, -1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, -0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,255,255,199,192,1,0,255,255,185,176,0,4,69,68,84, -0,69,83,84,0,0,0 -}; +1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,255,255, +199,192,1,0,255,255,185,176,0,4,255,255,199,192,1,8,255,255,199,192,1,12,69,68,84,0,69,83,84,0,69,87,84, +0,69,80,84,0,0,0,0,1,0,0,0,1,}; diff -aprNU5 djgpp.orig/tests/libc/ansi/time/makefile djgpp/tests/libc/ansi/time/makefile --- djgpp.orig/tests/libc/ansi/time/makefile 2005-01-09 15:55:52 +0100 +++ djgpp/tests/libc/ansi/time/makefile 2013-08-17 16:02:38 +0100 @@ -2,9 +2,10 @@ TOP=../.. SRC += ctime.c # There's a warning when compiling this with gcc 2.953. SRC += mktime.c SRC += strftime.c SRC += tzinfo.c +SRC += tzinfo2.c SRC += xstrftm.c # There's a warning when compiling this with gcc 2.953. include $(TOP)/../makefile.inc diff -aprNU5 djgpp.orig/tests/libc/ansi/time/tzinfo2.c djgpp/tests/libc/ansi/time/tzinfo2.c --- djgpp.orig/tests/libc/ansi/time/tzinfo2.c 1970-01-01 01:00:00 +0100 +++ djgpp/tests/libc/ansi/time/tzinfo2.c 2013-08-17 15:57:12 +0100 @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +#define buflen 80 + +void timetest(int *fail, time_t t, const char er[buflen]) +{ + char or[buflen]; + + if (!strftime (or, buflen, "%Y-%m-%d %H:%M:%S %Z", localtime(&t))) + { + fprintf(stderr, "strftime failed FAIL\n"); + exit(-1); + } + + printf("time_t %ld expected %s\n", (long)t, er); + printf(" got %s", or); + if (strcmp(er, or)) *fail = 1, printf(" FAIL"); + printf ("\n"); +} + +int main (int argc, char **argv) +{ + int fail = 0; + static const char *env_string[] = {"TZ=:America/New_York", "TZ=:Europe/Berlin", "TZ=:America/Buenos_Aires"}; + char *tz; + + if (tz = unconst(env_string[0], char *), putenv(tz)) + { + fprintf(stderr, "putenv failed FAIL\n"); + exit(-1); + } + tzset(); + + timetest(&fail, 1357016400, "2013-01-01 00:00:00 EST"); + timetest(&fail, 1362898799, "2013-03-10 01:59:59 EST"); + timetest(&fail, 1362898800, "2013-03-10 03:00:00 EDT"); + timetest(&fail, 1372780800, "2013-07-02 12:00:00 EDT"); + timetest(&fail, 1383454800, "2013-11-03 01:00:00 EDT"); + timetest(&fail, 1383458400, "2013-11-03 01:00:00 EST"); + timetest(&fail, 1388552399, "2013-12-31 23:59:59 EST"); + printf("Test #1 %s\n", (fail ? "FAILED" : "passed")); + + + printf("\n\n\n"); + + + if (tz = unconst(env_string[1], char *), putenv(tz)) + { + fprintf(stderr, "putenv failed FAIL\n"); + exit(-1); + } + tzset(); + + timetest(&fail, 1356998461, "2013-01-01 01:01:01 CET"); + timetest(&fail, 1359766922, "2013-02-02 02:02:02 CET"); + timetest(&fail, 1370491566, "2013-06-06 06:06:06 CEST"); + timetest(&fail, 1376701904, "2013-08-17 03:11:44 CEST"); + timetest(&fail, 1379401749, "2013-09-17 09:09:09 CEST"); + timetest(&fail, 1384164692, "2013-11-11 11:11:32 CET"); + timetest(&fail, 1388530799, "2013-12-31 23:59:59 CET"); + printf("Test #2 %s\n", (fail ? "FAILED" : "passed")); + + + printf("\n\n\n"); + + + if (tz = unconst(env_string[2], char *), putenv(tz)) + { + fprintf(stderr, "putenv failed FAIL\n"); + exit(-1); + } + tzset(); + + timetest(&fail, 1357012861, "2013-01-01 01:01:01 ART"); + timetest(&fail, 1359781322, "2013-02-02 02:02:02 ART"); + timetest(&fail, 1370509566, "2013-06-06 06:06:06 ART"); + timetest(&fail, 1376738799, "2013-08-17 08:26:39 ART"); + timetest(&fail, 1379417277, "2013-09-17 08:27:57 ART"); + timetest(&fail, 1384179071, "2013-11-11 11:11:11 ART"); + timetest(&fail, 1388545199, "2013-12-31 23:59:59 ART"); + printf("Test #3 %s\n", (fail ? "FAILED" : "passed")); + + return 0; +} Diffs against tz[code|data]2013d.tar.gz 2013-08-17 Juan Manuel Guerrero * djgpp/zoneinfo/src/date.c [OLD_TIME]: Only if OLD_TIME defined include utmp.h. [__MSDOS__]: For MSDOS do not declare and do not define oops(). (main): Cast variable to time_t. [TSP_SETDATE]: Only if TSP_SETDATE defined include syslog.h, sys/socket.h, netinet/in.h, netdb.h and protocols/timed.h and define TSPTYPES. (reset) [__MSDOS__]: MSDOS does not support TSP_SETDATE. Fix -Wunused-but-set-variable. (convert): Cast variable to time_t. (nondigit): Use ATTRIBUTE_PURE macro. (main): Fix -Wunused-but-set-variable. 2013-08-16 Juan Manuel Guerrero * djgpp/zoneinfo/src/zic.c [__DJGPP__]: Define ZIC_VERSION to 0. [IS_SLASH, IS_ABSOLUTE]: If not defined define them. (dolink): Use IS_ABSOLUTE for portable testing of absolute file names. Replace '+' by '%' in file names or directory names. (itsdir) [D_OK]: On MSDOS/WINDOWS use access() to check if it is a directory. (writezone): Replace '+' by '%' in file names or directory names. Make only the first pass and do not print the newline-enclosed, POSIX-TZ-environment-variable-style string to create a version 0 file. (mkdirs): Use HAS_DEVICE and IS_SLASH to handle directory craetiong in a portable way. 2013-08-14 Juan Manuel Guerrero * djgpp/zoneinfo/src/makefile: Support building with gcc 3.4.4 and from gcc 4.0.0 up to gcc 4.8.3. Install formated man pages into /share/man/catN. * djgpp/zoneinfo/src/private.h [__MSDOS__]: Define IS_SLASH(c), HAS_DEVICE, IS_ABSOLUTE and TZDIR macros. * djgpp/zoneinfo/src/localtime.c: Define IS_SLASH and IS_ABSOLUTE for portable testing of absolute file names. [STD_INSPIRED]: Provide function declarations. [!__DJGPP__]: Use DJGPP's own libc implementations of localtime, localtime_r, gmtime, gmtime_r, ctime, ctime_r and mktime. (tzload): Use IS_ABSOLUTE for portable testing of absolute file names. * djgpp/zoneinfo/src/strftime.c [!__DJGPP__]: Use DJGPP's libc implementations of asctime and asctime_r. * djgpp/zoneinfo/src/asctime.c [!__DJGPP__]: Use DJGPP's libc implementations of asctime and asctime_r. * djgpp/distrib/p/djtzn204/files: Man pages will be stored in /share/man/catN. diff -aprNU5 djgpp.orig/distrib/p/djtzn204/files djgpp/distrib/p/djtzn204/files --- djgpp.orig/distrib/p/djtzn204/files 2013-01-31 22:35:24 +0000 +++ djgpp/distrib/p/djtzn204/files 2013-08-17 14:34:22 +0000 @@ -1,14 +1,14 @@ etc/date.exe etc/tzselect etc/zdump.exe etc/zic.exe -share/man/man1/date.1 -share/man/man3/newctime.3 -share/man/man3/newtzset.3 -share/man/man3/time2posix.3 -share/man/man5/tzfile.5 -share/man/man8/tzselect.8 -share/man/man8/zdump.8 -share/man/man8/zic.8 +share/man/cat1/date.1 +share/man/cat3/newctime.3 +share/man/cat3/newtzset.3 +share/man/cat3/time2posix.3 +share/man/cat5/tzfile.5 +share/man/cat8/tzselect.8 +share/man/cat8/zdump.8 +share/man/cat8/zic.8 lib/libtz.a zoneinfo diff -aprNU5 djgpp.orig/zoneinfo/src/asctime.c djgpp/zoneinfo/src/asctime.c --- djgpp.orig/zoneinfo/src/asctime.c 2012-10-26 23:37:42 +0000 +++ djgpp/zoneinfo/src/asctime.c 2013-08-17 14:34:22 +0000 @@ -1,5 +1,9 @@ +#ifndef __DJGPP__ +/* Use DJGPP's own implementation of asctime and asctime_r. */ + + /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. */ @@ -128,5 +132,6 @@ asctime_r(register const struct tm *time char * asctime(register const struct tm *timeptr) { return asctime_r(timeptr, buf_asctime); } +#endif /* !__DJGPP__ */ diff -aprNU5 djgpp.orig/zoneinfo/src/date.c djgpp/zoneinfo/src/date.c --- djgpp.orig/zoneinfo/src/date.c 2013-05-28 03:26:18 +0000 +++ djgpp/zoneinfo/src/date.c 2013-08-17 14:34:22 +0000 @@ -28,11 +28,13 @@ static char sccsid[] = "@(#)date.c 4.23 #include "private.h" #if HAVE_ADJTIME || HAVE_SETTIMEOFDAY #include "sys/time.h" /* for struct timeval, struct timezone */ #endif /* HAVE_ADJTIME || HAVE_SETTIMEOFDAY */ #include "locale.h" +#ifdef OLD_TIME #include "utmp.h" /* for OLD_TIME (or its absence) */ +#endif #if HAVE_UTMPX_H #include "utmpx.h" #endif #ifndef OTIME_MSG @@ -52,18 +54,18 @@ static char sccsid[] = "@(#)date.c 4.23 #ifndef SECSPERMIN #define SECSPERMIN 60 #endif /* !defined SECSPERMIN */ -extern double atof(); +extern double atof(const char *s); extern char ** environ; -extern char * getlogin(); -extern time_t mktime(); +extern char * getlogin(void); +extern time_t mktime(struct tm *tptr); extern char * optarg; extern int optind; -extern char * strchr(); -extern time_t time(); +extern char * strchr(const char *s, int c); +extern time_t time(time_t *t); extern char * tzname[2]; static int retval = EXIT_SUCCESS; static void checkfinal(const char *, int, time_t, time_t); @@ -72,11 +74,13 @@ static void display(const char *); static void dogmt(void); static void errensure(void); static void iffy(time_t, time_t, const char *, const char *); int main(int, char**); static const char * nondigit(const char *); +#ifndef __MSDOS__ static void oops(const char *); +#endif /* __MSDOS__ */ static void reset(time_t, int); static int sametm(const struct tm *, const struct tm *); static void timeout(FILE *, const char *, const struct tm *); static void usage(void); static void wildinput(const char *, const char *, @@ -92,19 +96,23 @@ main(const int argc, char *argv[]) register int dousg; register int aflag = 0; register int dflag = 0; register int nflag = 0; register int tflag = 0; +#if HAVE_SETTIMEOFDAY == 2 register int minuteswest; register int dsttime; +#endif /* HAVE_SETTIMEOFDAY == 2 */ register double adjust; time_t now; time_t t; INITIALIZE(dousg); +#if HAVE_SETTIMEOFDAY == 2 INITIALIZE(minuteswest); INITIALIZE(dsttime); +#endif /* HAVE_SETTIMEOFDAY == 2 */ INITIALIZE(adjust); INITIALIZE(t); #ifdef LC_ALL (void) setlocale(LC_ALL, ""); #endif /* defined(LC_ALL) */ @@ -133,11 +141,13 @@ main(const int argc, char *argv[]) _("date: error: multiple -d's used")); usage(); } dflag = 1; cp = optarg; +#if HAVE_SETTIMEOFDAY == 2 dsttime = atoi(cp); +#endif /* HAVE_SETTIMEOFDAY == 2 */ if (*cp == '\0' || *nondigit(cp) != '\0') wildinput(_("-t value"), optarg, _("must be a non-negative number")); break; case 't': /* minutes west of UTC */ @@ -146,11 +156,13 @@ main(const int argc, char *argv[]) _("date: error: multiple -t's used")); usage(); } tflag = 1; cp = optarg; +#if HAVE_SETTIMEOFDAY == 2 minuteswest = atoi(cp); +#endif /* HAVE_SETTIMEOFDAY == 2 */ if (*cp == '+' || *cp == '-') ++cp; if (*cp == '\0' || *nondigit(cp) != '\0') wildinput(_("-d value"), optarg, _("must be a number")); @@ -424,18 +436,20 @@ reset(const time_t newt, const int nflag #ifndef BSD4_4 #define TIME_NAME "" #endif /* !defined BSD4_4 */ #endif /* !defined TIME_NAME */ +#ifdef TSP_SETDATE #include "syslog.h" #include "sys/socket.h" #include "netinet/in.h" #include "netdb.h" #define TSPTYPES #include "protocols/timed.h" +#endif -extern int logwtmp(); +extern int logwtmp(char *line, char *name, char *host); #if HAVE_SETTIMEOFDAY == 1 #define settimeofday(t, tz) (settimeofday)(t) #endif /* HAVE_SETTIMEOFDAY == 1 */ @@ -448,18 +462,21 @@ static int netsettime(struct timeval); #endif /* !defined TSP_SETDATE */ static void reset(const time_t newt, const int nflag) { register const char * username; +#ifndef __MSDOS__ static struct timeval tv; /* static so tv_usec is 0 */ +#endif /* __MSDOS__ */ #ifdef EBUG return; #endif /* defined EBUG */ username = getlogin(); if (username == NULL || *username == '\0') /* single-user or no tty */ username = "root"; +#ifndef __MSDOS__ tv.tv_sec = newt; #ifdef TSP_SETDATE if (nflag || !netsettime(tv)) #endif /* defined TSP_SETDATE */ { @@ -471,10 +488,11 @@ reset(const time_t newt, const int nflag logwtmp("{", TIME_NAME, ""); /* } */ syslog(LOG_AUTH | LOG_NOTICE, _("date set by %s"), username); } else oops("settimeofday"); } +#endif /* __MSDOS__ */ } #endif /* !defined OLD_TIME */ static void @@ -509,10 +527,11 @@ usage(void) [-t min-west] [-a sss.fff] [[yyyy]mmddhhmm[yyyy][.ss]] [+format]\n")); errensure(); exit(retval); } +#ifndef __MSDOS__ static void oops(const char *const string) { int e = errno; @@ -520,10 +539,11 @@ oops(const char *const string) errno = e; (void) perror(string); errensure(); display(NULL); } +#endif /* __MSDOS__ */ static void display(const char *const format) { struct tm tm; @@ -541,11 +561,11 @@ display(const char *const format) errensure(); } exit(retval); } -extern size_t strftime(); +extern size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *t); #define INCR 1024 static void timeout(FILE *const fp, const char *const format, const struct tm *const tmp) diff -aprNU5 djgpp.orig/zoneinfo/src/localtime.c djgpp/zoneinfo/src/localtime.c --- djgpp.orig/zoneinfo/src/localtime.c 2013-08-10 21:33:40 +0000 +++ djgpp/zoneinfo/src/localtime.c 2013-08-17 14:34:22 +0000 @@ -27,10 +27,21 @@ #ifndef TZ_ABBR_ERR_CHAR #define TZ_ABBR_ERR_CHAR '_' #endif /* !defined TZ_ABBR_ERR_CHAR */ /* +** Portable testing for absolute file names. +*/ + +#ifndef IS_SLASH +#define IS_SLASH(c) ((c) == '/') +#endif +#ifndef IS_ABSOLUTE +#define IS_ABSOLUTE(n) (IS_SLASH((n)[0])) +#endif + +/* ** SunOS 4.1.1 headers lack O_BINARY. */ #ifdef O_BINARY #define OPEN_MODE (O_RDONLY | O_BINARY) @@ -155,10 +166,19 @@ static struct tm * timesub(const time_t static int tmcomp(const struct tm * atmp, const struct tm * btmp); static time_t transtime(time_t janfirst, int year, const struct rule * rulep, int_fast32_t offset) ATTRIBUTE_PURE; static int typesequiv(const struct state * sp, int a, int b); static int tzload(const char * name, struct state * sp, int doextend); static int tzparse(const char * name, struct state * sp, int lastditch); +#ifdef STD_INSPIRED +static int_fast64_t leapcorr(time_t *timep); +time_t time2posix(time_t t); +time_t posix2time(time_t t); +struct tm * offtime(const time_t *const timep, const int_fast32_t offset); +time_t timelocal(struct tm *const tmp); +time_t timegm(struct tm *const tmp); +time_t timeoff(struct tm *const tmp, const int_fast32_t offset); +#endif /* defined STD_INSPIRED */ #ifdef ALL_STATE static struct state * lclptr; static struct state * gmtptr; #endif /* defined ALL_STATE */ @@ -176,14 +196,17 @@ static struct state gmtmem; static char lcl_TZname[TZ_STRLEN_MAX + 1]; static int lcl_is_set; static int gmt_is_set; +#ifndef __DJGPP__ +/* Use DJGPP's own definition of tzname. */ char * tzname[2] = { wildabbr, wildabbr }; +#endif /* !__DJGPP__ */ /* ** 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 @@ -341,11 +364,11 @@ tzload(register const char *name, regist */ char fullname[FILENAME_MAX + 1]; if (name[0] == ':') ++name; - doaccess = name[0] == '/'; + doaccess = IS_ABSOLUTE(name); if (!doaccess) { if ((p = TZDIR) == NULL) goto oops; if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) goto oops; @@ -1320,10 +1343,12 @@ localsub(const time_t *const timep, cons tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; #endif /* defined TM_ZONE */ return result; } +#ifndef __DJGPP__ +/* Use DJGPP's own implementation of localtime and localtime_r. */ struct tm * localtime(const time_t *const timep) { tzset(); return localsub(timep, 0L, &tm); @@ -1336,10 +1361,11 @@ localtime(const time_t *const timep) struct tm * localtime_r(const time_t *const timep, struct tm *tmp) { return localsub(timep, 0L, tmp); } +#endif /* !__DJGPP__ */ /* ** gmtsub is to gmtime as localsub is to localtime. */ @@ -1377,10 +1403,12 @@ gmtsub(const time_t *const timep, const } #endif /* defined TM_ZONE */ return result; } +#ifndef __DJGPP__ +/* Use DJGPP's own implementation of gmtime and gmtime_r. */ struct tm * gmtime(const time_t *const timep) { return gmtsub(timep, 0L, &tm); } @@ -1392,10 +1420,11 @@ gmtime(const time_t *const timep) struct tm * gmtime_r(const time_t *const timep, struct tm *tmp) { return gmtsub(timep, 0L, tmp); } +#endif /* !__DJGPP__ */ #ifdef STD_INSPIRED struct tm * offtime(const time_t *const timep, const int_fast32_t offset) @@ -1548,10 +1577,12 @@ timesub(const time_t *const timep, const tmp->TM_GMTOFF = offset; #endif /* defined TM_GMTOFF */ return tmp; } +#ifndef __DJGPP__ +/* Use DJGPP's own implementation of ctime and ctime_r. */ char * ctime(const time_t *const timep) { /* ** Section 4.12.3.2 of X3.159-1989 requires that @@ -1567,10 +1598,11 @@ ctime_r(const time_t *const timep, char { struct tm mytm; return asctime_r(localtime_r(timep, &mytm), buf); } +#endif /* !__DJGPP__ */ /* ** 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. @@ -1929,16 +1961,19 @@ time1(struct tm *const tmp, } } return WRONG; } +#ifndef __DJGPP__ +/* Use DJGPP's own implementation of mktime. */ time_t mktime(struct tm *const tmp) { tzset(); return time1(tmp, localsub, 0L); } +#endif /* !__DJGPP__ */ #ifdef STD_INSPIRED time_t timelocal(struct tm *const tmp) diff -aprNU5 djgpp.orig/zoneinfo/src/makefile djgpp/zoneinfo/src/makefile --- djgpp.orig/zoneinfo/src/makefile 2013-07-05 13:38:00 +0000 +++ djgpp/zoneinfo/src/makefile 2013-08-17 15:13:10 +0000 @@ -43,38 +43,56 @@ POSIXRULES= America/New_York # Also see TZDEFRULESTRING below, which takes effect only # if the time zone files cannot be accessed. # Everything gets put in subdirectories of. . . -TOPDIR= /usr/local +TOPDIR= ../.. # "Compiled" time zone information is placed in the "TZDIR" directory # (and subdirectories). # Use an absolute path name for TZDIR unless you're just testing the software. -TZDIR= $(TOPDIR)/etc/zoneinfo +TZDIR= $(TOPDIR)/zoneinfo # Types to try, as an alternative to time_t. int64_t should be first. TIME_T_ALTERNATIVES= int64_t int32_t uint32_t uint64_t # The "tzselect", "zic", and "zdump" commands get installed in. . . ETCDIR= $(TOPDIR)/etc # If you "make INSTALL", the "date" command gets installed in. . . -BINDIR= $(TOPDIR)/bin +BINDIR= $(TOPDIR)/etc # Manual pages go in subdirectories of. . . -MANDIR= $(TOPDIR)/man +SHAREDIR= $(TOPDIR)/share +MANDIR= $(SHAREDIR)/man # Library functions are put in an archive in LIBDIR. LIBDIR= $(TOPDIR)/lib TZLIB= $(LIBDIR)/libtz.a +# This defines several variables that enable zoneinfo/src/* files to be +# built both natively and with cross-tools on Unix. + +include $(TOPDIR)/src/makefile.def +export CROSS_BUILD + +# If cross compiling the native and cross-compiler versions may not match. +# Allow different flag settings accrording the compiler version. +GCC_MAJOR := $(word 3, $(shell ../../src/misc.exe | $(GCC) -E -dD -x c - | egrep 'define\ *__GNUC__')) +GCC_MINOR := $(word 3, $(shell ../../src/misc.exe | $(GCC) -E -dD -x c - | egrep 'define\ *__GNUC_MINOR__')) +CROSS_GCC_MAJOR := $(word 3, $(shell ../../src/misc.exe | $(CROSS_GCC) -E -dD -x c - | egrep 'define\ *__GNUC__')) +CROSS_GCC_MINOR := $(word 3, $(shell ../../src/misc.exe | $(CROSS_GCC) -E -dD -x c - | egrep 'define\ *__GNUC_MINOR__')) + +# A replacement for (possibly missing) Unix programs: + +UTIL= $(TOPDIR)/src/misc.exe + # If you always want time values interpreted as "seconds since the epoch # (not counting leap seconds)", use # REDO= posix_only # below. If you always want right time values interpreted as "seconds since # the epoch" (counting leap seconds)", use @@ -87,11 +105,11 @@ TZLIB= $(LIBDIR)/libtz.a # REDO= right_posix # below. # POSIX mandates that leap seconds not be counted; for compatibility with it, # use either "posix_only" or "posix_right". -REDO= posix_right +REDO= posix_only # Since "." may not be in PATH... YEARISTYPE= ./yearistype @@ -129,22 +147,115 @@ LDLIBS= # DST transitions if the time zone files cannot be accessed # -DZIC_MAX_ABBR_LEN_WO_WARN=3 # (or some other number) to set the maximum time zone abbreviation length # that zic will accept without a warning (the default is 6) # $(GCC_DEBUG_FLAGS) if you are using GCC and want lots of checking -GCC_DEBUG_FLAGS = -Dlint -g3 -O3 -fno-common -fstrict-aliasing \ +#GCC_DEBUG_FLAGS = -Dlint -g3 -O3 -fno-common -fstrict-aliasing \ +# -Wall -Wextra \ +# -Wbad-function-cast -Wcast-align -Wcast-qual \ +# -Wformat=2 -Winit-self \ +# -Wmissing-declarations -Wmissing-noreturn -Wmissing-prototypes \ +# -Wnested-externs \ +# -Wno-format-nonliteral -Wno-sign-compare -Wno-sign-conversion \ +# -Wno-type-limits \ +# -Wno-unused-parameter -Woverlength-strings -Wpointer-arith \ +# -Wshadow -Wstrict-prototypes -Wsuggest-attribute=const \ +# -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure -Wtrampolines \ +# -Wwrite-strings + +# Cross compiler debug flags. +CROSS_GCC_DEBUG_FLAGS_FOR_ALL = -Dlint -g2 -fno-common -fstrict-aliasing \ -Wall -Wextra \ -Wbad-function-cast -Wcast-align -Wcast-qual \ -Wformat=2 -Winit-self \ -Wmissing-declarations -Wmissing-noreturn -Wmissing-prototypes \ - -Wnested-externs \ - -Wno-format-nonliteral -Wno-sign-compare -Wno-sign-conversion \ - -Wno-type-limits \ - -Wno-unused-parameter -Woverlength-strings -Wpointer-arith \ - -Wshadow -Wstrict-prototypes -Wsuggest-attribute=const \ - -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure -Wtrampolines \ + -Wnested-externs -Wno-format-nonliteral -Wno-sign-compare \ + -Wno-unused-parameter -Wpointer-arith -Wshadow -Wstrict-prototypes \ -Wwrite-strings + +ifeq ($(CROSS_GCC_MAJOR),3) +ifeq ($(CROSS_GCC_MINOR),4) +CROSS_GCC_DEBUG_FLAGS = $(CROSS_GCC_DEBUG_FLAGS_FOR_ALL) -Wconversion -Wtraditional +endif +endif + +ifeq ($(CROSS_GCC_MAJOR),4) + ifeq ($(CROSS_GCC_MINOR),0) +CROSS_GCC_DEBUG_FLAGS_SPECIAL = + else + ifeq ($(CROSS_GCC_MINOR),1) +CROSS_GCC_DEBUG_FLAGS_SPECIAL = + else + ifeq ($(CROSS_GCC_MINOR),2) +CROSS_GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings + else + ifeq ($(CROSS_GCC_MINOR),3) +CROSS_GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings -Wno-sign-conversion -Wno-type-limits + else + ifeq ($(CROSS_GCC_MINOR),4) +CROSS_GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings -Wno-sign-conversion -Wno-type-limits + else + ifeq ($(CROSS_GCC_MINOR),5) +CROSS_GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings -Wno-sign-conversion -Wno-type-limits + else +# gcc 4.6 and later works. +CROSS_GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings -Wno-sign-conversion -Wno-type-limits -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure -Wtrampolines + endif + endif + endif + endif + endif +CROSS_GCC_DEBUG_FLAGS = $(CROSS_GCC_DEBUG_FLAGS_FOR_ALL) $(CROSS_GCC_DEBUG_FLAGS_SPECIAL) + endif +endif + +# Native compiler debug flags. +GCC_DEBUG_FLAGS_FOR_ALL = -Dlint -g2 -fno-common -fstrict-aliasing \ + -Wall -Wextra \ + -Wbad-function-cast -Wcast-align -Wcast-qual \ + -Wformat=2 -Winit-self \ + -Wmissing-declarations -Wmissing-noreturn -Wmissing-prototypes \ + -Wnested-externs -Wno-format-nonliteral -Wno-sign-compare \ + -Wno-unused-parameter -Wpointer-arith -Wshadow -Wstrict-prototypes \ + -Wwrite-strings + +ifeq ($(GCC_MAJOR),3) +ifeq ($(GCC_MINOR),4) +GCC_DEBUG_FLAGS = $(GCC_DEBUG_FLAGS_FOR_ALL) -Wconversion -Wtraditional +endif +endif + +ifeq ($(GCC_MAJOR),4) + ifeq ($(GCC_MINOR),0) +GCC_DEBUG_FLAGS_SPECIAL = + else + ifeq ($(GCC_MINOR),1) +GCC_DEBUG_FLAGS_SPECIAL = + else + ifeq ($(GCC_MINOR),2) +GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings + else + ifeq ($(GCC_MINOR),3) +GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings -Wno-sign-conversion -Wno-type-limits + else + ifeq ($(GCC_MINOR),4) +GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings -Wno-sign-conversion -Wno-type-limits + else + ifeq ($(GCC_MINOR),5) +GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings -Wno-sign-conversion -Wno-type-limits + else +# gcc 4.6 and later works. +GCC_DEBUG_FLAGS_SPECIAL = -Woverlength-strings -Wno-sign-conversion -Wno-type-limits -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure -Wtrampolines + endif + endif + endif + endif + endif +GCC_DEBUG_FLAGS = $(GCC_DEBUG_FLAGS_FOR_ALL) $(GCC_DEBUG_FLAGS_SPECIAL) + endif +endif + # # If you want to use System V compatibility code, add # -DUSG_COMPAT # to the end of the "CFLAGS=" line. This arrange for "timezone" and "daylight" # variables to be kept up-to-date by the time conversion functions. Neither @@ -229,22 +340,41 @@ GCC_DEBUG_FLAGS = -Dlint -g3 -O3 -fno-co # to the end of the "CFLAGS=" line. This causes "strftime" to always return # 53 as a week number (rather than 52 or 53) for those days in January that # before the first Monday in January when a "%V" format is used and January 1 # falls on a Friday, Saturday, or Sunday. -CFLAGS= +# If cross compiling the native and cross-compiler versions may not match. +# Allow different flag settings accrording the compiler version. +# Flags for cross compiler +CFLAGS= -DHAVE_ADJTIME=0 -DHAVE_LONG_DOUBLE=1 -DHAVE_SETTIMEOFDAY=1 \ + -DHAVE_STRERROR=1 -DHAVE_SYMLINK=0 -DHAVE_STDINT_H=1\ + -DSTD_INSPIRED \ + -DLOCALE_HOME=\"/dev/env/DJDIR~c:/djgpp~/share/locale\" \ + $(CROSS_GCC_DEBUG_FLAGS) -O2 + +# Flags for native compiler +GCCFLAGS= -DHAVE_ADJTIME=0 -DHAVE_LONG_DOUBLE=1 -DHAVE_SETTIMEOFDAY=1 \ + -DHAVE_STRERROR=1 -DHAVE_SYMLINK=0 -DHAVE_STDINT_H=1 \ + -DSTD_INSPIRED \ + -DLOCALE_HOME=\"/dev/env/DJDIR~c:/djgpp~/share/locale\" \ + $(GCC_DEBUG_FLAGS) -O2 + +# Don't use -s here, since "gcc -s" on DJ's Irix machine dumps core +# when invoked with -s. To work around, we use strip explicitly. +LFLAGS= # Linker flags. Default to $(LFLAGS) for backwards compatibility # to tzcode2012h and earlier. LDFLAGS= $(LFLAGS) -zic= ./zic +EXEEXT= .exe +zic= ./host-zic ZIC= $(zic) $(ZFLAGS) # The name of a Posix-compliant `awk' on your system. -AWK= awk +AWK= gawk # The full path name of a Posix-compliant shell that supports the Korn shell's # 'select' statement, as an extension. These days, Bash is the most popular. KSHELL= /bin/bash @@ -290,24 +420,24 @@ TARFLAGS= `if tar $(GNUTARFLAGS) --versi # Flags to give 'gzip' when making a distribution. GZIPFLAGS= -9n ############################################################################### -cc= cc -CC= $(cc) -DTZDIR=\"$(TZDIR)\" +cc= $(CROSS_GCC) +CC= $(cc) -DTZDIR=\"/dev/env/DJDIR~c:/djgpp~/zoneinfo\" TZCSRCS= zic.c localtime.c asctime.c scheck.c ialloc.c TZCOBJS= zic.o localtime.o asctime.o scheck.o ialloc.o TZDSRCS= zdump.c localtime.c ialloc.c TZDOBJS= zdump.o localtime.o ialloc.o -DATESRCS= date.c localtime.c strftime.c asctime.c -DATEOBJS= date.o localtime.o strftime.o asctime.o +DATESRCS= date.c localtime.c logwtmp.c strftime.c asctime.c +DATEOBJS= date.o localtime.o logwtmp.o strftime.o asctime.o LIBSRCS= localtime.c asctime.c difftime.c LIBOBJS= localtime.o asctime.o difftime.o HEADERS= tzfile.h private.h NONLIBSRCS= zic.c zdump.c scheck.c ialloc.c -NEWUCBSRCS= date.c strftime.c +NEWUCBSRCS= date.c logwtmp.c strftime.c SOURCES= $(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS) tzselect.ksh MANS= newctime.3 newstrftime.3 newtzset.3 time2posix.3 \ tzfile.5 tzselect.8 zic.8 zdump.8 COMMON= Makefile DOCS= README Theory $(MANS) date.1 @@ -328,59 +458,87 @@ ENCHILADA= $(COMMON) $(DOCS) $(SOURCES) # And for the benefit of csh users on systems that assume the user # shell should be used to handle commands in Makefiles. . . SHELL= /bin/sh -all: tzselect zic zdump $(LIBOBJS) - -ALL: all date +INSTALL: ALL install date.1 + -$(UTIL) mkdir $(TOPDIR) + -$(UTIL) mkdir $(BINDIR) + $(UTIL) cp date$(EXEEXT) $(BINDIR)/date$(EXEEXT) + -$(UTIL) mkdir $(SHAREDIR) + -$(UTIL) mkdir $(MANDIR) + -$(UTIL) mkdir $(MANDIR)/cat1 + -$(UTIL) rm $(MANDIR)/cat1/date.1 + groff -mman -Tascii date.1 > date.man + $(UTIL) cp date.man $(MANDIR)/cat1/date.1 install: all $(DATA) $(REDO) $(TZLIB) $(MANS) $(TABDATA) $(ZIC) -y $(YEARISTYPE) \ -d $(TZDIR) -l $(LOCALTIME) -p $(POSIXRULES) - -rm -f $(TZDIR)/iso3166.tab $(TZDIR)/zone.tab - cp iso3166.tab zone.tab $(TZDIR)/. - -mkdir $(TOPDIR) $(ETCDIR) - cp tzselect zic zdump $(ETCDIR)/. - -mkdir $(TOPDIR) $(MANDIR) \ - $(MANDIR)/man3 $(MANDIR)/man5 $(MANDIR)/man8 - -rm -f $(MANDIR)/man3/newctime.3 \ - $(MANDIR)/man3/newtzset.3 \ - $(MANDIR)/man5/tzfile.5 \ - $(MANDIR)/man8/tzselect.8 \ - $(MANDIR)/man8/zdump.8 \ - $(MANDIR)/man8/zic.8 - cp newctime.3 newtzset.3 $(MANDIR)/man3/. - cp tzfile.5 $(MANDIR)/man5/. - cp tzselect.8 zdump.8 zic.8 $(MANDIR)/man8/. + -$(UTIL) rm -f $(TZDIR)/iso3166.tab $(TZDIR)/zone.tab + $(UTIL) cp iso3166.tab $(TZDIR)/iso3166.tab + $(UTIL) cp zone.tab $(TZDIR)/zone.tab + -$(UTIL) mkdir $(TOPDIR) + -$(UTIL) mkdir $(ETCDIR) + $(UTIL) cp zic$(EXEEXT) $(ETCDIR)/zic$(EXEEXT) + $(UTIL) cp zdump$(EXEEXT) $(ETCDIR)/zdump$(EXEEXT) + $(UTIL) cp tzselect $(ETCDIR)/tzselect + -$(UTIL) mkdir $(SHAREDIR) + -$(UTIL) mkdir $(MANDIR) + -$(UTIL) mkdir $(MANDIR)/cat3 + -$(UTIL) mkdir $(MANDIR)/cat5 + -$(UTIL) mkdir $(MANDIR)/cat8 + -$(UTIL) rm -f $(MANDIR)/cat3/newctime.3 \ + $(MANDIR)/cat3/newtzset.3 \ + $(MANDIR)/cat5/tzfile.5 \ + $(MANDIR)/cat8/tzselect.8 \ + $(MANDIR)/cat8/zdump.8 \ + $(MANDIR)/cat8/zic.8 + groff -mman -Tascii newctime.3 > newctime.man + groff -mman -Tascii newtzset.3 > newtzset.man + groff -mman -Tascii time2posix.3 > time2posix.man + groff -mman -Tascii tzfile.5 > tzfile.man + groff -mman -Tascii tzselect.8 > tzselect.man + groff -mman -Tascii zdump.8 > zdump.man + groff -mman -Tascii zic.8 > zic.man + $(UTIL) cp newctime.man $(MANDIR)/cat3/newctime.3 + $(UTIL) cp newtzset.man $(MANDIR)/cat3/newtzset.3 + $(UTIL) cp time2posix.man $(MANDIR)/cat3/time2posix.3 + $(UTIL) cp tzfile.man $(MANDIR)/cat5/tzfile.5 + $(UTIL) cp tzselect.man $(MANDIR)/cat8/tzselect.8 + $(UTIL) cp zdump.man $(MANDIR)/cat8/zdump.8 + $(UTIL) cp zic.man $(MANDIR)/cat8/zic.8 -INSTALL: ALL install date.1 - -mkdir $(TOPDIR) $(BINDIR) - cp date $(BINDIR)/. - -mkdir $(TOPDIR) $(MANDIR) $(MANDIR)/man1 - -rm -f $(MANDIR)/man1/date.1 - cp date.1 $(MANDIR)/man1/. +all: tzselect host-zic zic$(EXEEXT) zdump$(EXEEXT) $(LIBOBJS) + +ALL: all date$(EXEEXT) version.h: (echo 'static char const PKGVERSION[]="($(PACKAGE)) ";' && \ echo 'static char const TZVERSION[]="$(VERSION)";' && \ echo 'static char const REPORT_BUGS_TO[]="$(BUGEMAIL)";') >$@ -zdump: $(TZDOBJS) +zdump$(EXEEXT): $(TZDOBJS) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZDOBJS) $(LDLIBS) + $(CROSS_STRIP) $@ -zic: $(TZCOBJS) yearistype +host-zic: $(TZCSRCS) yearistype version.h + $(GCC) -DTZDIR=\"/dev/env/DJDIR~c:/djgpp~/zoneinfo\" \ + $(GCCFLAGS) $(LDFLAGS) $(TZCSRCS) $(LDLIBS) -o $@ + $(STRIP) $@ + +zic$(EXEEXT): $(TZCOBJS) yearistype $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZCOBJS) $(LDLIBS) + $(CROSS_STRIP) $@ yearistype: yearistype.sh - cp yearistype.sh yearistype - chmod +x yearistype + $(UTIL) cp yearistype.sh yearistype -posix_only: zic $(TDATA) +posix_only: $(zic) $(TDATA) $(ZIC) -y $(YEARISTYPE) -d $(TZDIR) -L /dev/null $(TDATA) -right_only: zic leapseconds $(TDATA) +right_only: $(zic) leapseconds $(TDATA) $(ZIC) -y $(YEARISTYPE) -d $(TZDIR) -L leapseconds $(TDATA) # In earlier versions of this makefile, the other two directories were # subdirectories of $(TZDIR). However, this led to configuration errors. # For example, with posix_right under the earlier scheme, @@ -388,11 +546,11 @@ right_only: zic leapseconds $(TDATA) # but gmtime without leap seconds, which led to problems with applications # like sendmail that subtract gmtime from localtime. # Therefore, the other two directories are now siblings of $(TZDIR). # You must replace all of $(TZDIR) to switch from not using leap seconds # to using them, or vice versa. -other_two: zic leapseconds $(TDATA) +other_two: $(zic) leapseconds $(TDATA) $(ZIC) -y $(YEARISTYPE) -d $(TZDIR)-posix -L /dev/null $(TDATA) $(ZIC) -y $(YEARISTYPE) \ -d $(TZDIR)-leaps -L leapseconds $(TDATA) posix_right: posix_only other_two @@ -400,25 +558,30 @@ posix_right: posix_only other_two right_posix: right_only other_two zones: $(REDO) $(TZLIB): $(LIBOBJS) - -mkdir $(TOPDIR) $(LIBDIR) - ar ru $@ $(LIBOBJS) - if [ -x /usr/ucb/ranlib ] || [ -x /usr/bin/ranlib ]; \ - then ranlib $@ ; fi - -date: $(DATEOBJS) - $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(DATEOBJS) $(LDLIBS) + -$(UTIL) mkdir $(TOPDIR) + -$(UTIL) mkdir $(LIBDIR) + $(CROSS_AR) rus $@ $(LIBOBJS) + +# We use the system's logwtmp in preference to ours if available. + +date$(EXEEXT): $(DATEOBJS) + $(CROSS_AR) rs logwtmpl.a logwtmp.o + $(CC) $(CFLAGS) date.o localtime.o asctime.o strftime.o \ + $(LDLIBS) -lc logwtmpl.a -o $@ + $(CROSS_STRIP) $@ + $(UTIL) rm logwtmpl.a + $(CROSS_STRIP) $@ tzselect: tzselect.ksh sed \ - -e 's|#!/bin/bash|#!$(KSHELL)|g' \ -e 's|AWK=[^}]*|AWK=$(AWK)|g' \ -e 's|\(PKGVERSION\)=.*|\1='\''($(PACKAGE)) '\''|' \ -e 's|\(REPORT_BUGS_TO\)=.*|\1=$(BUGEMAIL)|' \ - -e 's|TZDIR=[^}]*|TZDIR=$(TZDIR)|' \ + -e 's|TZDIR=[^}]*|TZDIR=/dev/env/DJDIR/zoneinfo|' \ -e 's|\(TZVERSION\)=.*|\1=$(VERSION)|' \ <$? >$@ chmod +x $@ check: check_character_set check_tables check_web @@ -431,22 +594,22 @@ check_tables: checktab.awk $(PRIMARY_YDA check_web: $(WEB_PAGES) $(VALIDATE_ENV) $(VALIDATE) $(VALIDATE_FLAGS) $(WEB_PAGES) clean_misc: - rm -f core *.o *.out \ - date tzselect version.h zdump zic yearistype + $(UTIL) rm core *.o *.out tzselect zdump$(EXEEXT) zic$(EXEEXT) \ + yearistype date$(EXEEXT) logwtmpl* *.tar.gz host-zic *.exe *.man clean: clean_misc - rm -f -r tzpublic + $(UTIL) rm -f -r tzpublic maintainer-clean: clean @echo 'This command is intended for maintainers to use; it' @echo 'deletes files that may need special tools to rebuild.' rm -f *.[1-8].txt *.asc *.tar.gz names: - @echo $(ENCHILADA) + @$(UTIL) echo $(ENCHILADA) public: check check_public check_time_t_alternatives \ set-timestamps tarballs signatures # Set the time stamps to those of the git repository, if available, @@ -468,11 +631,11 @@ set-timestamps: # The zics below ensure that each data file can stand on its own. # We also do an all-files run to catch links to links. check_public: $(ENCHILADA) make maintainer-clean - make "CFLAGS=$(GCC_DEBUG_FLAGS)" + make "CFLAGS=$(CROSS_GCC_DEBUG_FLAGS)" mkdir tzpublic for i in $(TDATA) ; do \ $(zic) -v -d tzpublic $$i 2>&1 || exit; \ done $(zic) -v -d tzpublic $(TDATA) diff -aprNU5 djgpp.orig/zoneinfo/src/private.h djgpp/zoneinfo/src/private.h --- djgpp.orig/zoneinfo/src/private.h 2013-05-28 03:26:18 +0000 +++ djgpp/zoneinfo/src/private.h 2013-08-17 14:34:22 +0000 @@ -347,10 +347,20 @@ const char * scheck(const char * string, #ifndef GNUC_or_lint #define INITIALIZE(x) #endif /* !defined GNUC_or_lint */ #endif /* !defined INITIALIZE */ +#ifdef __MSDOS__ +#define IS_SLASH(c) ((c) == '/' || (c) == '\\') +#define HAS_DEVICE(n) ((n)[0] && (n)[1] == ':') +#define IS_ABSOLUTE(n) (IS_SLASH((n)[0]) || HAS_DEVICE(n)) +#undef TZDIR +#define TZDIR (getenv("TZDIR") ? getenv("TZDIR") : "/dev/env/DJDIR/zoneinfo") +#else /* !__MSDOS__ */ +#define HAS_DEVICE(n) 0 +#endif /* !__MSDOS__ */ + /* ** 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. */ diff -aprNU5 djgpp.orig/zoneinfo/src/strftime.c djgpp/zoneinfo/src/strftime.c --- djgpp.orig/zoneinfo/src/strftime.c 2013-05-28 03:26:18 +0000 +++ djgpp/zoneinfo/src/strftime.c 2013-08-17 14:34:22 +0000 @@ -3,10 +3,14 @@ ** appearing below. ** ** This is ANSIish only when "multibyte character == plain character". */ +#ifndef __DJGPP__ +/* Use DJGPP's own implementation of strftime. */ + + #include "private.h" /* ** Copyright (c) 1989 The Regents of the University of California. ** All rights reserved. @@ -736,5 +740,6 @@ no_locale: localebuf = C_time_locale; locale_buf = NULL; return &localebuf; } #endif /* defined LOCALE_HOME */ +#endif /* !__DJGPP__ */ diff -aprNU5 djgpp.orig/zoneinfo/src/zic.c djgpp/zoneinfo/src/zic.c --- djgpp.orig/zoneinfo/src/zic.c 2013-05-28 03:26:18 +0000 +++ djgpp/zoneinfo/src/zic.c 2013-08-17 14:34:22 +0000 @@ -5,12 +5,15 @@ #include "version.h" #include "private.h" #include "locale.h" #include "tzfile.h" - +#ifdef __DJGPP__ +#define ZIC_VERSION '\0' +#else #define ZIC_VERSION '2' +#endif typedef int_fast64_t zic_t; #define ZIC_MIN INT_FAST64_MIN #define ZIC_MAX INT_FAST64_MAX #define SCNdZIC SCNdFAST64 @@ -27,10 +30,21 @@ typedef int_fast64_t zic_t; #else #define MKDIR_UMASK 0755 #endif /* +** Portable testing for absolute file names. +*/ + +#ifndef IS_SLASH +#define IS_SLASH(c) ((c) == '/') +#endif +#ifndef IS_ABSOLUTE +#define IS_ABSOLUTE(n) (IS_SLASH((n)[0])) +#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. @@ -579,24 +593,37 @@ static void dolink(const char *const fromfield, const char *const tofield) { register char * fromname; register char * toname; - if (fromfield[0] == '/') + if (IS_ABSOLUTE(fromfield)) fromname = ecpyalloc(fromfield); else { fromname = ecpyalloc(directory); fromname = ecatalloc(fromname, "/"); fromname = ecatalloc(fromname, fromfield); } - if (tofield[0] == '/') + if (IS_ABSOLUTE(tofield)) toname = ecpyalloc(tofield); else { toname = ecpyalloc(directory); toname = ecatalloc(toname, "/"); toname = ecatalloc(toname, tofield); } + /* Some zone names use `+' as part of their names, but DOS + doesn't allow `+' in file names. Replace with a `%'. */ + if (getenv ("COMSPEC") || getenv ("CROSS_BUILD")) + { + char *p; + + for (p = fromname; *p; p++) + if (*p == '+') + *p = '%'; + for (p = toname; *p; p++) + if (*p == '+') + *p = '%'; + } /* ** We get to be careful here since ** there's a fair chance of root running us. */ if (!itsdir(toname)) @@ -648,17 +675,24 @@ static const zic_t min_time = (zic_t) -1 static const zic_t max_time = -1 - ((zic_t) -1 << (TIME_T_BITS_IN_FILE - 1)); static int itsdir(const char *const name) { - register char * myname; register int accres; +#ifdef D_OK + /* MS-DOS/MS-Windows normalize "foo/." to "foo" before testing, + so we think foo is a directory. Use D_OK instead. */ + accres = access(name, D_OK); +#else + register char * myname; + myname = ecpyalloc(name); myname = ecatalloc(myname, "/."); accres = access(myname, F_OK); free(myname); +#endif return accres == 0; } /* ** Associate sets of rules with zones. @@ -1473,10 +1507,24 @@ writezone(const char *const name, const ++leapi32; } fullname = erealloc(fullname, strlen(directory) + 1 + strlen(name) + 1); (void) sprintf(fullname, "%s/%s", directory, name); + /* Some zone names use `+' as part of their names, but DOS + doesn't allow `+' in file names. Replace with a `%'. */ + if (getenv ("COMSPEC") || getenv ("CROSS_BUILD")) + { + char new_name[FILENAME_MAX + 1], *p; + + strcpy(new_name, name); + for (p = new_name; *p; p++) + if (*p == '+') + *p = '%'; + (void) sprintf(fullname, "%s/%s", directory, new_name); + } + else + (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); @@ -1494,11 +1542,15 @@ writezone(const char *const name, const (void) fprintf(stderr, _("%s: Can't create %s: %s\n"), progname, fullname, e); exit(EXIT_FAILURE); } } +#if defined(ZIC_VERSION) && ZIC_VERSION == 2 for (pass = 1; pass <= 2; ++pass) { +#else /* ZIC_VERSION == 0 */ + for (pass = 1; pass < 2; ++pass) { +#endif /* ZIC_VERSION == 0 */ register int thistimei, thistimecnt; register int thisleapi, thisleapcnt; register int thistimelim, thisleaplim; int writetype[TZ_MAX_TIMES]; int typemap[TZ_MAX_TYPES]; @@ -1704,11 +1756,13 @@ writezone(const char *const name, const (void) putc(ttisstds[i], fp); for (i = 0; i < typecnt; ++i) if (writetype[i]) (void) putc(ttisgmts[i], fp); } +#if defined(ZIC_VERSION) && ZIC_VERSION == 2 (void) fprintf(fp, "\n%s\n", string); +#endif /* ZIC_VERSION */ if (ferror(fp) || fclose(fp)) { (void) fprintf(stderr, _("%s: Error writing %s\n"), progname, fullname); exit(EXIT_FAILURE); } @@ -2608,42 +2662,40 @@ mkdirs(char *argname) register char * cp; if (argname == NULL || *argname == '\0') return 0; cp = name = ecpyalloc(argname); - while ((cp = strchr(cp + 1, '/')) != 0) { - *cp = '\0'; -#ifdef HAVE_DOS_FILE_NAMES - /* - ** DOS drive specifier? - */ - if (isalpha((unsigned char) name[0]) && - name[1] == ':' && name[2] == '\0') { - *cp = '/'; - continue; - } -#endif - 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); - free(name); - return -1; - } - } - } - *cp = '/'; + /* + ** Get past a DOS-style drive specifier, if any. + */ + if (HAS_DEVICE(name)) + cp += 2; + while (*cp++) { + if (IS_SLASH(*cp)) { + *cp = '\0'; + 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); + free(name); + return -1; + } + } + } + *cp = '/'; + } } free(name); return 0; }