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