[PATCH 0/3 v3] Cygwin: tzcode resync

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH 0/3 v3] Cygwin: tzcode resync

Mark Geisert
This is v3 of this patch set incorporating review comments on v2.  I've
minimized the size of the localtime.c.patch file by beefing up #defines
in the wrapper localtime.cc.  I believe I've addressed all comments.

This patch set has been tested on both 64- and 32-bit Cygwin.  The
initial shell opens with timezone correctly set.  xclock, date, uptime
all show the correct local time.  Overriding TZ in the environment on a
call to date, as in 'TZ=Asia/Tokyo date' shows correctly adjusted time.

..mark

[PATCH 1/3 v3] Cygwin: tzcode resync: basics
[PATCH 2/3 v3] Cygwin: tzcode resync: imports
[PATCH 3/3 v3] Cygwin: tzcode resync: details

Reply | Threaded
Open this post in threaded view
|

[PATCH 1/3 v3] Cygwin: tzcode resync: basics

Mark Geisert
Modifies winsup/cygwin/Makefile.in to build localtime.o from items in
new winsup/cygwin/tzcode subdirectory.  Compiler option "-fpermissive"
is used to accept warnings about missing casts on the return values of
malloc() calls.  This patch also removes existing localtime.cc and
tz_posixrules.h from winsup/cygwin as they are superseded by the
subsequent patches in this set.

---
 winsup/cygwin/Makefile.in     |   15 +-
 winsup/cygwin/localtime.cc    | 2597 ---------------------------------
 winsup/cygwin/tz_posixrules.h |   48 -
 3 files changed, 12 insertions(+), 2648 deletions(-)
 delete mode 100644 winsup/cygwin/localtime.cc
 delete mode 100644 winsup/cygwin/tz_posixrules.h

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index f273ba793..2ac8bcbd8 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -27,7 +27,7 @@ export CCWRAP_HEADERS:=. ${srcdir}
 export CCWRAP_SYSTEM_HEADERS:=@cygwin_headers@ @newlib_headers@
 export CCWRAP_DIRAFTER_HEADERS:=@windows_headers@
 
-VPATH+=$(srcdir)/regex $(srcdir)/lib $(srcdir)/libc $(srcdir)/math
+VPATH+=$(srcdir)/regex $(srcdir)/lib $(srcdir)/libc $(srcdir)/math $(srcdir)/tzcode
 
 target_cpu:=@target_cpu@
 target_alias:=@target_alias@
@@ -246,6 +246,15 @@ MATH_OFILES:= \
  tgammal.o \
  truncl.o
 
+TZCODE_OFILES:=localtime.o
+
+localtime.o: $(srcdir)/tzcode/localtime.cc $(srcdir)/tzcode/localtime.c.patch
+ (cd $(srcdir)/tzcode && \
+ patch -u -o localtime.c.patched localtime.c localtime.c.patch)
+ $(CXX) ${CXXFLAGS} ${localtime_CFLAGS} \
+ -I$(target_builddir)/winsup/cygwin \
+ -I$(srcdir) -I$(srcdir)/tzcode -c -o $@ $<
+
 DLL_OFILES:= \
  advapi32.o \
  aio.o \
@@ -333,7 +342,6 @@ DLL_OFILES:= \
  ldap.o \
  libstdcxx_wrapper.o \
  loadavg.o \
- localtime.o \
  lsearch.o \
  malloc_wrapper.o \
  minires-os-if.o \
@@ -412,6 +420,7 @@ DLL_OFILES:= \
  $(EXTRA_OFILES) \
  $(MALLOC_OFILES) \
  $(MATH_OFILES) \
+ $(TZCODE_OFILES) \
  $(MT_SAFE_OBJECTS)
 
 EXCLUDE_STATIC_OFILES:=$(addprefix --exclude=,\
@@ -559,7 +568,7 @@ TARGET_LIBS:=$(LIB_NAME) $(CYGWIN_START) $(GMON_START) $(LIBGMON_A) $(SUBLIBS) $
 
 ifneq "${filter -O%,$(CFLAGS)}" ""
 dtable_CFLAGS:=-fcheck-new
-localtime_CFLAGS:=-fwrapv
+localtime_CFLAGS:=-fwrapv -fpermissive
 malloc_CFLAGS:=-O3
 sync_CFLAGS:=-O3
 ifeq ($(target_cpu),i686)
diff --git a/winsup/cygwin/localtime.cc b/winsup/cygwin/localtime.cc
deleted file mode 100644
index 010376637..000000000
--- a/winsup/cygwin/localtime.cc
+++ /dev/null
@@ -1,2597 +0,0 @@
-/* $NetBSD: localtime.c,v 1.72 2012/10/28 19:02:29 christos Exp $ */
-
-/* Don't reformat the code arbitrarily.
-
-   It uses in wide parts the exact formatting as the upstream NetBSD
-   versions.  The purpose is to simplify subsequent diffs to the NetBSD
-   version, should the need arise again at one point. */
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-/* Temporarily merged private.h and tzfile.h for ease of management - DJ */
-
-#include "winsup.h"
-#include "cygerrno.h"
-#include "sync.h"
-#include <ctype.h>
-#define STD_INSPIRED
-#define lint
-
-#define USG_COMPAT
-
-#ifndef lint
-#ifndef NOID
-static char elsieid[] = "@(#)localtime.c 8.17";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
-/*
-** Leap second handling from Bradley White.
-** POSIX-style TZ environment variable handling from Guy Harris.
-*/
-
-#define NO_ERROR_IN_DST_GAP
-
-/*LINTLIBRARY*/
-
-#ifndef PRIVATE_H
-
-#define PRIVATE_H
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson
-*/
-
-/*
-** This header is for use ONLY with the time conversion code.
-** There is no guarantee that it will remain unchanged,
-** or that it will remain at all.
-** Do NOT copy it to any system include directory.
-** Thank you!
-*/
-
-/*
-** ID
-*/
-
-#ifndef lint
-#ifndef NOID
-static char privatehid[] = "@(#)private.h 7.48";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
-/*
-** Nested includes
-*/
-
-#include "stdio.h"
-#include "limits.h" /* for CHAR_BIT */
-#include "stdlib.h"
-#include "unistd.h" /* for F_OK and R_OK */
-
-/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX.  */
-#define is_digit(c) ((unsigned)(c) - '0' <= 9)
-
-#ifndef __pure
-#if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
-# define __pure __attribute__ ((__pure__))
-#else
-# define __pure /* empty */
-#endif
-#endif
-
-/*
-** Finally, some convenience items.
-*/
-
-#ifndef TYPE_INTEGRAL
-#define TYPE_INTEGRAL(type) (/*CONSTCOND*/((type) 0.5) != 0.5)
-#endif /* !defined TYPE_INTEGRAL */
-
-#ifndef TYPE_BIT
-#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
-#endif /* !defined TYPE_BIT */
-
-#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 */
-
-#ifndef TZ_DOMAIN
-#define TZ_DOMAIN "tz"
-#endif /* !defined TZ_DOMAIN */
-
-#ifndef YEARSPERREPEAT
-#define YEARSPERREPEAT  400     /* years before a Gregorian repeat */
-#endif /* !defined YEARSPERREPEAT */
-
-/*
-** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
-*/
-
-#ifndef AVGSECSPERYEAR
-#define AVGSECSPERYEAR  31556952L
-#endif /* !defined AVGSECSPERYEAR */
-
-#ifndef SECSPERREPEAT
-#define SECSPERREPEAT   ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
-#endif /* !defined SECSPERREPEAT */
-
-#ifndef SECSPERREPEAT_BITS
-#define SECSPERREPEAT_BITS      34      /* ceil(log2(SECSPERREPEAT)) */
-#endif /* !defined SECSPERREPEAT_BITS */
-
-/*
-** UNIX was a registered trademark of UNIX System Laboratories in 1993.
-*/
-
-#endif /* !defined PRIVATE_H */
-
-#ifndef TZFILE_H
-
-#define TZFILE_H
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-
-/*
-** 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/share/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_version[1]; /* '\0' or '2' as of 2005 */
- char tzh_reserved[15]; /* reserved for future use */
- char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
- char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
- char tzh_leapcnt[4]; /* coded number of leap seconds */
- 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
-*/
-
-/*
-** If tzh_version is '2' or greater, the above is followed by a second instance
-** of tzhead and a second instance of the data in which each coded transition
-** time uses 8 rather than 4 chars,
-** then a POSIX-TZ-environment-variable-style string for use in handling
-** instants after the last transition time stored in the file
-** (with nothing between the newlines if there is no POSIX representation for
-** such instants).
-*/
-
-/*
-** In the current implementation, "tzset()" refuses to deal with files that
-** exceed any of the limits below.
-*/
-
-#ifndef TZ_MAX_TIMES
-/*
-** The TZ_MAX_TIMES value below is enough to handle a bit more than a
-** year's worth of solar time (corrected daily to the nearest second) or
-** 138 years of Pacific Presidential Election time
-** (where there are three time zone transitions every fourth year).
-*/
-#define TZ_MAX_TIMES 1200
-#endif /* !defined TZ_MAX_TIMES */
-
-#ifndef TZ_MAX_TYPES
-#ifndef NOSOLAR
-#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
-#endif /* !defined NOSOLAR */
-#ifdef NOSOLAR
-/*
-** Must be at least 14 for Europe/Riga as of Jan 12 1995,
-** as noted by Earl Chew.
-*/
-#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
-#endif /* !defined NOSOLAR */
-#endif /* !defined TZ_MAX_TYPES */
-
-#ifndef TZ_MAX_CHARS
-#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
- /* (limited by what unsigned chars can hold) */
-#endif /* !defined TZ_MAX_CHARS */
-
-#ifndef TZ_MAX_LEAPS
-#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
-#endif /* !defined TZ_MAX_LEAPS */
-
-#define SECSPERMIN 60
-#define MINSPERHOUR 60
-#define HOURSPERDAY 24
-#define DAYSPERWEEK 7
-#define DAYSPERNYEAR 365
-#define DAYSPERLYEAR 366
-#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
-#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
-#define MONSPERYEAR 12
-
-#define TM_SUNDAY 0
-#define TM_MONDAY 1
-#define TM_TUESDAY 2
-#define TM_WEDNESDAY 3
-#define TM_THURSDAY 4
-#define TM_FRIDAY 5
-#define TM_SATURDAY 6
-
-#define TM_JANUARY 0
-#define TM_FEBRUARY 1
-#define TM_MARCH 2
-#define TM_APRIL 3
-#define TM_MAY 4
-#define TM_JUNE 5
-#define TM_JULY 6
-#define TM_AUGUST 7
-#define TM_SEPTEMBER 8
-#define TM_OCTOBER 9
-#define TM_NOVEMBER 10
-#define TM_DECEMBER 11
-
-#define TM_YEAR_BASE 1900
-
-#define EPOCH_YEAR 1970
-#define EPOCH_WDAY TM_THURSDAY
-
-/*
-** Accurate only for the past couple of centuries;
-** that will probably do.
-*/
-
-#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
-
-#endif /* !defined TZFILE_H */
-
-#include "fcntl.h"
-
-#ifdef __TM_GMTOFF
-# define TM_GMTOFF __TM_GMTOFF
-#endif
-#ifdef __TM_ZONE
-# define TM_ZONE __TM_ZONE
-#endif
-
-/*
-** 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 const 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;
- int goback;
- int goahead;
- time_t ats[TZ_MAX_TIMES];
- unsigned char types[TZ_MAX_TIMES];
- struct ttinfo ttis[TZ_MAX_TYPES];
- char chars[/*CONSTCOND*/BIGGEST(BIGGEST(TZ_MAX_CHARS + 1,
- sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))];
- struct lsinfo lsis[TZ_MAX_LEAPS];
-};
-
-typedef struct __state *timezone_t;
-
-struct rule {
- int r_type; /* type of rule--see below */
- int r_day; /* day number of rule */
- 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 */
-
-typedef struct tm *(*subfun_t)(const timezone_t sp, const time_t *timep,
-       long offset, struct tm *tmp);
-
-/*
-** Prototypes for static functions.
-*/
-
-static long detzcode(const char * codep);
-static time_t detzcode64(const char * codep);
-static int differ_by_repeat(time_t t1, time_t t0);
-static const char * getzname(const char * strp) __pure;
-static const char * getqzname(const char * strp, const int delim) __pure;
-static const char * getnum(const char * strp, int * nump, int min,
- int max);
-static const char * getsecs(const char * strp, long * secsp);
-static const char * getoffset(const char * strp, long * offsetp);
-static const char * getrule(const char * strp, struct rule * rulep);
-static void gmtload(timezone_t sp);
-static struct tm * gmtsub(const timezone_t sp, const time_t *timep,
- long offset, struct tm * tmp);
-static struct tm * localsub(const timezone_t sp, const time_t *timep,
- long offset, struct tm *tmp);
-static int increment_overflow(int * number, int delta);
-static int leaps_thru_end_of(int y) __pure;
-static int long_increment_overflow(long * number, int delta);
-static int long_normalize_overflow(long * tensptr,
- int * unitsptr, int base);
-static int normalize_overflow(int * tensptr, int * unitsptr,
- int base);
-static void settzname(void);
-static time_t time1(const timezone_t sp, struct tm * const tmp,
- subfun_t funcp, const long offset);
-static time_t time2(const timezone_t sp, struct tm * const tmp,
- subfun_t funcp,
- const long offset, int *const okayp);
-static time_t time2sub(const timezone_t sp, struct tm * const tmp,
- subfun_t funcp, const long offset,
- int *const okayp, const int do_norm_secs);
-static struct tm * timesub(const timezone_t sp, const time_t * timep,
- long offset, struct tm * tmp);
-static int tmcomp(const struct tm * atmp,
- const struct tm * btmp);
-static time_t transtime(time_t janfirst, int year,
- const struct rule * rulep, long offset) __pure;
-static int typesequiv(const timezone_t sp, int a, int b);
-static int tzload(timezone_t sp, const char * name,
- int doextend);
-static int tzparse(timezone_t sp, const char * name,
- int lastditch);
-#ifdef __CYGWIN__
-extern "C" void tzset_unlocked(void);
-#else
-static void tzset_unlocked(void);
-#endif
-static long leapcorr(const timezone_t sp, time_t * timep);
-
-static timezone_t lclptr;
-static timezone_t gmtptr;
-
-#ifndef TZ_STRLEN_MAX
-#define TZ_STRLEN_MAX 255
-#endif /* !defined TZ_STRLEN_MAX */
-
-static char lcl_TZname[TZ_STRLEN_MAX + 1];
-static enum lcl_states
-{
-  lcl_setting = -1,
-  lcl_unset = 0,
-  lcl_from_environment = 1,
-  lcl_from_default = 2
-} lcl_is_set;
-static int gmt_is_set;
-
-#define tzname _tzname
-#undef _tzname
-
-char * tzname[2] = {
- (char *) wildabbr,
- (char *) 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 for noting this.
-*/
-
-static struct tm tm;
-
-/* These variables are initialized by tzset.  The macro versions are
-   defined in time.h, and indirect through the __imp_ pointers.  */
-
-#define timezone _timezone
-#define daylight _daylight
-#undef _timezone
-#undef _daylight
-
-#ifdef USG_COMPAT
-long timezone = 0;
-int daylight;
-#endif /* defined USG_COMPAT */
-
-#ifdef ALTZONE
-time_t altzone = 0;
-#endif /* defined ALTZONE */
-
-static long
-detzcode(const char *const codep)
-{
- long result;
- int i;
-
- result = (codep[0] & 0x80) ? ~0L : 0;
- for (i = 0; i < 4; ++i)
- result = (result << 8) | (codep[i] & 0xff);
- return result;
-}
-
-static time_t
-detzcode64(const char *const codep)
-{
- time_t result;
- int i;
-
- result = (time_t)((codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0);
- for (i = 0; i < 8; ++i)
- result = result * 256 + (codep[i] & 0xff);
- return result;
-}
-
-static void
-settzname (void)
-{
- timezone_t const sp = lclptr;
- int i;
-
- tzname[0] = (char *) wildabbr;
- tzname[1] = (char *) wildabbr;
-#ifdef USG_COMPAT
- daylight = 0;
- timezone = 0;
-#endif /* defined USG_COMPAT */
-#ifdef ALTZONE
- altzone = 0;
-#endif /* defined ALTZONE */
- if (sp == NULL) {
- tzname[0] = tzname[1] = (char *) gmt;
- return;
- }
- for (i = 0; i < sp->typecnt; ++i) {
- const struct ttinfo * const ttisp = &sp->ttis[i];
-
- tzname[ttisp->tt_isdst] =
- &sp->chars[ttisp->tt_abbrind];
-#ifdef USG_COMPAT
- if (ttisp->tt_isdst)
- daylight = 1;
- if (!ttisp->tt_isdst)
- timezone = -(ttisp->tt_gmtoff);
-#endif /* defined USG_COMPAT */
-#ifdef ALTZONE
- if (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) {
- const struct ttinfo *const ttisp = &sp->ttis[sp->types[i]];
-
- tzname[ttisp->tt_isdst] =
- &sp->chars[ttisp->tt_abbrind];
- }
-}
-
-#include "tz_posixrules.h"
-
-static int
-differ_by_repeat(const time_t t1, const time_t t0)
-{
- if (TYPE_INTEGRAL(time_t) &&
- TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
- return 0;
- return (int_fast64_t)t1 - (int_fast64_t)t0 == SECSPERREPEAT;
-}
-
-static int
-tzload(timezone_t sp, const char *name, const int doextend)
-{
- const char * p;
- int i;
- int fid;
- int stored;
- ssize_t nread;
- typedef union {
- struct tzhead tzhead;
- char buf[2 * sizeof(struct tzhead) +
- 2 * sizeof *sp +
- 4 * TZ_MAX_TIMES];
- } u_t;
- u_t * up;
- save_errno save;
-
- up = (u_t *) calloc(1, sizeof *up);
- if (up == NULL)
- return -1;
-
- sp->goback = sp->goahead = FALSE;
- if (name == NULL && (name = TZDEFAULT) == NULL)
- goto oops;
- {
- 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)
- goto oops;
- if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
- goto oops;
- (void) strcpy(fullname, p); /* XXX strcpy is safe */
- (void) strcat(fullname, "/"); /* XXX strcat is safe */
- (void) strcat(fullname, name); /* XXX strcat is safe */
- /*
- ** Set doaccess if '.' (as in "../") shows up in name.
- */
- if (strchr(name, '.') != NULL)
- doaccess = TRUE;
- name = fullname;
- }
- if ((doaccess && access(name, R_OK) != 0)
-    || (fid = open(name, OPEN_MODE)) == -1)
-  {
-    const char *base = strrchr(name, '/');
-    if (base)
-      base++;
-    else
-      base = name;
-    if (strcmp(base, "posixrules"))
-      goto oops;
-
-    /* We've got a built-in copy of posixrules just in case */
-    fid = -2;
-  }
- }
- if (fid == -2)
-  {
-    memcpy(up->buf, _posixrules_data, sizeof (_posixrules_data));
-    nread = sizeof (_posixrules_data);
-  }
- else
-  {
-    nread = read(fid, up->buf, sizeof up->buf);
-    if (close(fid) < 0 || nread <= 0)
-      goto oops;
-  }
- for (stored = 4; stored <= 8; stored *= 2) {
- int ttisstdcnt;
- int ttisgmtcnt;
-
- ttisstdcnt = (int) detzcode(up->tzhead.tzh_ttisstdcnt);
- ttisgmtcnt = (int) detzcode(up->tzhead.tzh_ttisgmtcnt);
- sp->leapcnt = (int) detzcode(up->tzhead.tzh_leapcnt);
- sp->timecnt = (int) detzcode(up->tzhead.tzh_timecnt);
- sp->typecnt = (int) detzcode(up->tzhead.tzh_typecnt);
- sp->charcnt = (int) detzcode(up->tzhead.tzh_charcnt);
- p = up->tzhead.tzh_charcnt + sizeof up->tzhead.tzh_charcnt;
- if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
- sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
- sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
- sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
- (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
- (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
- goto oops;
- if (nread - (p - up->buf) <
- sp->timecnt * stored + /* ats */
- sp->timecnt + /* types */
- sp->typecnt * 6 + /* ttinfos */
- sp->charcnt + /* chars */
- sp->leapcnt * (stored + 4) + /* lsinfos */
- ttisstdcnt + /* ttisstds */
- ttisgmtcnt) /* ttisgmts */
- goto oops;
- for (i = 0; i < sp->timecnt; ++i) {
- sp->ats[i] = (time_t)((stored == 4) ?
- detzcode(p) : detzcode64(p));
- p += stored;
- }
- for (i = 0; i < sp->timecnt; ++i) {
- sp->types[i] = (unsigned char) *p++;
- if (sp->types[i] >= sp->typecnt)
- goto oops;
- }
- for (i = 0; i < sp->typecnt; ++i) {
- 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)
- goto oops;
- ttisp->tt_abbrind = (unsigned char) *p++;
- if (ttisp->tt_abbrind < 0 ||
- ttisp->tt_abbrind > sp->charcnt)
- goto oops;
- }
- for (i = 0; i < sp->charcnt; ++i)
- sp->chars[i] = *p++;
- sp->chars[i] = '\0'; /* ensure '\0' at end */
- for (i = 0; i < sp->leapcnt; ++i) {
- struct lsinfo * lsisp;
-
- lsisp = &sp->lsis[i];
- lsisp->ls_trans = (time_t)((stored == 4) ?
-    detzcode(p) : detzcode64(p));
- p += stored;
- lsisp->ls_corr = detzcode(p);
- p += 4;
- }
- for (i = 0; i < sp->typecnt; ++i) {
- 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)
- goto oops;
- }
- }
- for (i = 0; i < sp->typecnt; ++i) {
- 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)
- goto oops;
- }
- }
- /*
- ** Out-of-sort ats should mean we're running on a
- ** signed time_t system but using a data file with
- ** unsigned values (or vice versa).
- */
- for (i = 0; i < sp->timecnt - 2; ++i)
- if (sp->ats[i] > sp->ats[i + 1]) {
- ++i;
- if (TYPE_SIGNED(time_t)) {
- /*
- ** Ignore the end (easy).
- */
- sp->timecnt = i;
- } else {
- /*
- ** Ignore the beginning (harder).
- */
- int j;
-
- for (j = 0; j + i < sp->timecnt; ++j) {
- sp->ats[j] = sp->ats[j + i];
- sp->types[j] = sp->types[j + i];
- }
- sp->timecnt = j;
- }
- break;
- }
- /*
- ** If this is an old file, we're done.
- */
- if (up->tzhead.tzh_version[0] == '\0')
- break;
- nread -= p - up->buf;
- for (i = 0; i < nread; ++i)
- up->buf[i] = p[i];
- /*
- ** If this is a narrow integer time_t system, we're done.
- */
- if (stored >= (int) sizeof(time_t)
-/* CONSTCOND */
- && TYPE_INTEGRAL(time_t))
- break;
- }
- if (doextend && nread > 2 &&
- up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
- sp->typecnt + 2 <= TZ_MAX_TYPES) {
- struct __state ts;
- int result;
-
- up->buf[nread - 1] = '\0';
- result = tzparse(&ts, &up->buf[1], FALSE);
- if (result == 0 && ts.typecnt == 2 &&
- sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
- for (i = 0; i < 2; ++i)
- ts.ttis[i].tt_abbrind +=
- sp->charcnt;
- for (i = 0; i < ts.charcnt; ++i)
- sp->chars[sp->charcnt++] =
- ts.chars[i];
- i = 0;
- while (i < ts.timecnt &&
- ts.ats[i] <=
- sp->ats[sp->timecnt - 1])
- ++i;
- while (i < ts.timecnt &&
-    sp->timecnt < TZ_MAX_TIMES) {
- sp->ats[sp->timecnt] =
- ts.ats[i];
- sp->types[sp->timecnt] =
- sp->typecnt +
- ts.types[i];
- ++sp->timecnt;
- ++i;
- }
- sp->ttis[sp->typecnt++] = ts.ttis[0];
- sp->ttis[sp->typecnt++] = ts.ttis[1];
- }
- }
- if (sp->timecnt > 1) {
- for (i = 1; i < sp->timecnt; ++i)
- if (typesequiv(sp, sp->types[i], sp->types[0]) &&
- differ_by_repeat(sp->ats[i], sp->ats[0])) {
- sp->goback = TRUE;
- break;
- }
- for (i = sp->timecnt - 2; i >= 0; --i)
- if (typesequiv(sp, sp->types[sp->timecnt - 1],
- sp->types[i]) &&
- differ_by_repeat(sp->ats[sp->timecnt - 1],
- sp->ats[i])) {
- sp->goahead = TRUE;
- break;
- }
- }
- free(up);
- /*
- ** Get latest zone offsets into tzinfo (for newlib). . .
- */
- if (sp == lclptr)
-  {
-    for (i = 0; i < sp->timecnt; ++i)
-      {
- const struct ttinfo *const ttisp = &sp->ttis[sp->types[i]];
-
- __gettzinfo ()->__tzrule[ttisp->tt_isdst].offset
-    = -ttisp->tt_gmtoff;
-      }
-  }
- return 0;
-oops:
- free(up);
- return -1;
-}
-
-static int
-typesequiv(const timezone_t sp, const int a, const int b)
-{
- int result;
-
- if (sp == NULL ||
- a < 0 || a >= sp->typecnt ||
- b < 0 || b >= sp->typecnt)
- result = FALSE;
- else {
- const struct ttinfo * ap = &sp->ttis[a];
- const struct ttinfo * bp = &sp->ttis[b];
- result = ap->tt_gmtoff == bp->tt_gmtoff &&
- ap->tt_isdst == bp->tt_isdst &&
- ap->tt_ttisstd == bp->tt_ttisstd &&
- ap->tt_ttisgmt == bp->tt_ttisgmt &&
- strcmp(&sp->chars[ap->tt_abbrind],
- &sp->chars[bp->tt_abbrind]) == 0;
- }
- return result;
-}
-
-static const int mon_lengths[2][MONSPERYEAR] = {
- { 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(const char *strp)
-{
- char c;
-
- while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
- c != '+')
- ++strp;
- return strp;
-}
-
-/*
-** Given a pointer into an extended time zone string, scan until the ending
-** delimiter of the zone name is located. Return a pointer to the delimiter.
-**
-** As with getzname above, the legal character set is actually quite
-** restricted, with other characters producing undefined results.
-** We don't do any checking here; checking is done later in common-case code.
-*/
-
-static const char *
-getqzname(const char *strp, const int delim)
-{
- int c;
-
- while ((c = *strp) != '\0' && c != delim)
- ++strp;
- return strp;
-}
-
-/*
-** Given a pointer into a time zone string, extract a number from that string.
-** Check that the number is within a specified range; if it is not, return
-** NULL.
-** Otherwise, return a pointer to the first character not part of the number.
-*/
-
-static const char *
-getnum(const char *strp, int *const nump, const int min, const int max)
-{
- char c;
- int num;
-
- if (strp == NULL || !is_digit(c = *strp)) {
- errno = EINVAL;
- return NULL;
- }
- num = 0;
- do {
- num = num * 10 + (c - '0');
- if (num > max) {
- errno = EOVERFLOW;
- return NULL; /* illegal value */
- }
- c = *++strp;
- } while (is_digit(c));
- if (num < min) {
- errno = EINVAL;
- 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(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(const char *strp, long *const offsetp)
-{
- 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(const char *strp, 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(const time_t janfirst, const int year, const struct rule *const rulep,
-    const long offset)
-{
- int leapyear;
- time_t value;
- 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 = (time_t)(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 = (time_t)(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 += (time_t)(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 += (time_t)(d * SECSPERDAY);
- break;
- }
-
- /*
- ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
- ** question. To get the Epoch-relative time of the specified local
- ** time on that day, add the transition time and the current offset
- ** from UTC.
- */
- return (time_t)(value + rulep->r_time + offset);
-}
-
-/*
-** Given a POSIX section 8-style TZ string, fill in the rule tables as
-** appropriate.
-*/
-
-static int
-tzparse(timezone_t sp, const char *name, const int lastditch)
-{
- const char * stdname;
- const char * dstname;
- size_t stdlen;
- size_t dstlen;
- long stdoffset;
- long dstoffset;
- time_t * atp;
- unsigned char * typep;
- char * cp;
- int load_result;
-
- INITIALIZE(dstname);
- stdname = name;
- if (lastditch) {
- stdlen = strlen(name); /* length of standard zone name */
- name += stdlen;
- if (stdlen >= sizeof sp->chars)
- stdlen = (sizeof sp->chars) - 1;
- stdoffset = 0;
- } else {
- if (*name == '<') {
- name++;
- stdname = name;
- name = getqzname(name, '>');
- if (*name != '>')
- return (-1);
- stdlen = name - stdname;
- name++;
- } else {
- name = getzname(name);
- stdlen = name - stdname;
- }
- if (*name == '\0')
- return -1;
- name = getoffset(name, &stdoffset);
- if (name == NULL)
- return -1;
- }
- load_result = tzload(sp, TZDEFRULES, FALSE);
- if (load_result != 0)
- sp->leapcnt = 0; /* so, we're off a little */
- if (*name != '\0') {
- if (*name == '<') {
- dstname = ++name;
- name = getqzname(name, '>');
- if (*name != '>')
- return -1;
- dstlen = name - dstname;
- name++;
- } else {
- dstname = name;
- name = getzname(name);
- dstlen = name - dstname; /* length of DST zone name */
- }
- if (*name != '\0' && *name != ',' && *name != ';') {
- name = getoffset(name, &dstoffset);
- if (name == NULL)
- return -1;
- } else dstoffset = stdoffset - SECSPERHOUR;
- if (*name == '\0' && load_result != 0)
- name = TZDEFRULESTRING;
- if (*name == ',' || *name == ';') {
- struct rule start;
- struct rule end;
- int year;
- 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 forward.
- */
- memset(sp->ttis, 0, sizeof(sp->ttis));
- sp->ttis[0].tt_gmtoff = -dstoffset;
- sp->ttis[0].tt_isdst = 1;
- sp->ttis[0].tt_abbrind = (int)(stdlen + 1);
- sp->ttis[1].tt_gmtoff = -stdoffset;
- sp->ttis[1].tt_isdst = 0;
- sp->ttis[1].tt_abbrind = 0;
- atp = sp->ats;
- typep = sp->types;
- janfirst = 0;
- sp->timecnt = 0;
- for (year = EPOCH_YEAR;
-    sp->timecnt + 2 <= TZ_MAX_TIMES;
-    ++year) {
- time_t newfirst;
-
- starttime = transtime(janfirst, year, &start,
- stdoffset);
- endtime = transtime(janfirst, year, &end,
- dstoffset);
- if (starttime > endtime) {
- *atp++ = endtime;
- *typep++ = 1; /* DST ends */
- *atp++ = starttime;
- *typep++ = 0; /* DST begins */
- } else {
- *atp++ = starttime;
- *typep++ = 0; /* DST begins */
- *atp++ = endtime;
- *typep++ = 1; /* DST ends */
- }
- sp->timecnt += 2;
- newfirst = janfirst;
- newfirst += (time_t)
-    (year_lengths[isleap(year)] * SECSPERDAY);
- if (newfirst <= janfirst)
- break;
- janfirst = newfirst;
- }
- /*
- ** Get zone offsets into tzinfo (for newlib). . .
- */
- if (sp == lclptr)
-  {
-    __gettzinfo ()->__tzrule[0].offset
-    = -sp->ttis[1].tt_gmtoff;
-    __gettzinfo ()->__tzrule[1].offset
-    = -sp->ttis[0].tt_gmtoff;
-  }
- } else {
- long theirstdoffset;
- long theirdstoffset;
- long theiroffset;
- int isdst;
- int i;
- 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] += (time_t)
-    (dstoffset - theirdstoffset);
- } else {
- sp->ats[i] += (time_t)
-    (stdoffset - theirstdoffset);
- }
- }
- theiroffset = -sp->ttis[j].tt_gmtoff;
- if (!sp->ttis[j].tt_isdst)
- theirstdoffset = theiroffset;
- else theirdstoffset = theiroffset;
- }
- /*
- ** Finally, fill in ttis.
- ** ttisstd and ttisgmt need not be handled
- */
- memset(sp->ttis, 0, sizeof(sp->ttis));
- sp->ttis[0].tt_gmtoff = -stdoffset;
- sp->ttis[0].tt_isdst = FALSE;
- sp->ttis[0].tt_abbrind = 0;
- sp->ttis[1].tt_gmtoff = -dstoffset;
- sp->ttis[1].tt_isdst = TRUE;
- sp->ttis[1].tt_abbrind = (int)(stdlen + 1);
- sp->typecnt = 2;
- /*
- ** Get zone offsets into tzinfo (for newlib). . .
- */
- if (sp == lclptr)
-  {
-    __gettzinfo ()->__tzrule[0].offset
-    = -sp->ttis[0].tt_gmtoff;
-    __gettzinfo ()->__tzrule[1].offset
-    = -sp->ttis[1].tt_gmtoff;
-  }
- }
- } else {
- dstlen = 0;
- sp->typecnt = 1; /* only standard time */
- sp->timecnt = 0;
- memset(sp->ttis, 0, sizeof(sp->ttis));
- sp->ttis[0].tt_gmtoff = -stdoffset;
- sp->ttis[0].tt_isdst = 0;
- sp->ttis[0].tt_abbrind = 0;
- /*
- ** Get zone offsets into tzinfo (for newlib). . .
- */
- if (sp == lclptr)
-  {
-    __gettzinfo ()->__tzrule[0].offset = -sp->ttis[0].tt_gmtoff;
-    __gettzinfo ()->__tzrule[1].offset = -sp->ttis[0].tt_gmtoff;
-  }
- }
- sp->charcnt = (int)(stdlen + 1);
- if (dstlen != 0)
- sp->charcnt += (int)(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(timezone_t sp)
-{
- if (tzload(sp, gmt, TRUE) != 0)
- (void) tzparse(sp, gmt, 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 (void)
-{
- if (lcl_is_set == lcl_setting)
- return;
- lcl_is_set = lcl_setting;
-
- if (lclptr == NULL) {
- save_errno save;
- lclptr = (timezone_t) calloc(1, sizeof *lclptr);
- if (lclptr == NULL) {
- settzname(); /* all we can do */
- return;
- }
- }
-#if defined (__CYGWIN__)
- {
-    TIME_ZONE_INFORMATION tz;
-    char buf[BUFSIZ];
-    char *cp, *dst;
-    wchar_t *src;
-    div_t d;
-    GetTimeZoneInformation(&tz);
-    dst = cp = buf;
-    for (src = tz.StandardName; *src; src++)
-      if (*src >= L'A' && *src <= L'Z')
- *dst++ = *src;
-    if ((dst - cp) < 3)
-      {
- /* In non-english Windows, converted tz.StandardName
-   may not contain a valid standard timezone name. */
- strcpy(cp, wildabbr);
- cp += strlen(wildabbr);
-      }
-    else
-      cp = dst;
-    d = div(tz.Bias+tz.StandardBias, 60);
-    sprintf(cp, "%d", d.quot);
-    if (d.rem)
- sprintf(cp=strchr(cp, 0), ":%d", abs(d.rem));
-    if(tz.StandardDate.wMonth) {
- cp = strchr(cp, 0);
- dst = cp;
- for (src = tz.DaylightName; *src; src++)
-  if (*src >= L'A' && *src <= L'Z')
-    *dst++ = *src;
- if ((dst - cp) < 3)
-  {
-    /* In non-english Windows, converted tz.DaylightName
-       may not contain a valid daylight timezone name. */
-    strcpy(cp, wildabbr);
-    cp += strlen(wildabbr);
-  }
- else
-  cp = dst;
- d = div(tz.Bias+tz.DaylightBias, 60);
- sprintf(cp, "%d", d.quot);
- if (d.rem)
-    sprintf(cp=strchr(cp, 0), ":%d", abs(d.rem));
- cp = strchr(cp, 0);
- sprintf(cp=strchr(cp, 0), ",M%d.%d.%d/%d",
-    tz.DaylightDate.wMonth,
-    tz.DaylightDate.wDay,
-    tz.DaylightDate.wDayOfWeek,
-    tz.DaylightDate.wHour);
- if (tz.DaylightDate.wMinute || tz.DaylightDate.wSecond)
-    sprintf(cp=strchr(cp, 0), ":%d", tz.DaylightDate.wMinute);
- if (tz.DaylightDate.wSecond)
-    sprintf(cp=strchr(cp, 0), ":%d", tz.DaylightDate.wSecond);
- cp = strchr(cp, 0);
- sprintf(cp=strchr(cp, 0), ",M%d.%d.%d/%d",
-    tz.StandardDate.wMonth,
-    tz.StandardDate.wDay,
-    tz.StandardDate.wDayOfWeek,
-    tz.StandardDate.wHour);
- if (tz.StandardDate.wMinute || tz.StandardDate.wSecond)
-    sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wMinute);
- if (tz.StandardDate.wSecond)
-    sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wSecond);
-    }
-    /* printf("TZ deduced as `%s'\n", buf); */
-    if (tzparse(lclptr, buf, FALSE) == 0) {
- settzname();
- lcl_is_set = lcl_from_default;
- strlcpy(lcl_TZname, buf, sizeof (lcl_TZname));
-#if 0
- /* Huh?  POSIX doesn't mention anywhere that tzset should
-   set $TZ.  That's not right. */
- setenv("TZ", lcl_TZname, 1);
-#endif
- return;
-    }
- }
-#endif
- if (tzload(lclptr, NULL, TRUE) != 0)
- gmtload(lclptr);
- settzname();
-}
-
-static NO_COPY muto tzset_guard;
-
-#ifdef __CYGWIN__
-extern "C"
-#else
-#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 */
-#endif
-void
-tzset_unlocked(void)
-{
- const char * name;
-
- name = getenv("TZ");
- if (name == NULL) {
- if (lcl_is_set != lcl_from_default)
- tzsetwall();
- return;
- }
-
- if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)
- return;
- lcl_is_set = (strlen(name) < sizeof (lcl_TZname)) ? lcl_from_environment : lcl_unset;
- if (lcl_is_set != lcl_unset)
- (void)strlcpy(lcl_TZname, name, sizeof (lcl_TZname));
-
- if (lclptr == NULL) {
- save_errno save;
- lclptr = (timezone_t) calloc(1, sizeof *lclptr);
- if (lclptr == NULL) {
- settzname(); /* all we can do */
- return;
- }
- }
- 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) strlcpy(lclptr->chars, gmt, sizeof(lclptr->chars));
- } else if (tzload(lclptr, name, TRUE) != 0)
- if (name[0] == ':' || tzparse(lclptr, name, FALSE) != 0)
- (void) gmtload(lclptr);
- settzname();
-}
-
-EXPORT_ALIAS (tzset_unlocked, _tzset_unlocked)
-
-extern "C" void
-tzset(void)
-{
- tzset_guard.init ("tzset_guard")->acquire ();
- tzset_unlocked();
- tzset_guard.release ();
-}
-
-/*
-** The easy way to behave "as if no library function calls" localtime
-** is to not call it--so we drop its guts into "localsub", which can be
-** freely called. (And no, the PANS doesn't require the above behavior--
-** but it *is* desirable.)
-**
-** The unused offset argument is for the benefit of mktime variants.
-*/
-
-/*ARGSUSED*/
-static struct tm *
-localsub(const timezone_t sp, const time_t * const timep, const long offset,
-    struct tm *const tmp)
-{
- const struct ttinfo * ttisp;
- int i;
- struct tm * result;
- const time_t t = *timep;
-
- if ((sp->goback && t < sp->ats[0]) ||
- (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
- time_t newt = t;
- time_t seconds;
- time_t tcycles;
- int_fast64_t icycles;
-
- if (t < sp->ats[0])
- seconds = sp->ats[0] - t;
- else seconds = t - sp->ats[sp->timecnt - 1];
- --seconds;
- tcycles = (time_t)
-    (seconds / YEARSPERREPEAT / AVGSECSPERYEAR);
- ++tcycles;
- icycles = tcycles;
- if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
- return NULL;
- seconds = (time_t) icycles;
- seconds *= YEARSPERREPEAT;
- seconds *= AVGSECSPERYEAR;
- if (t < sp->ats[0])
- newt += seconds;
- else newt -= seconds;
- if (newt < sp->ats[0] ||
- newt > sp->ats[sp->timecnt - 1])
- return NULL; /* "cannot happen" */
- result = localsub(sp, &newt, offset, tmp);
- if (result == tmp) {
- time_t newy;
-
- newy = tmp->tm_year;
- if (t < sp->ats[0])
- newy -= (time_t)icycles * YEARSPERREPEAT;
- else newy += (time_t)icycles * YEARSPERREPEAT;
- tmp->tm_year = (int)newy;
- if (tmp->tm_year != newy)
- return NULL;
- }
- return result;
- }
- if (sp->timecnt == 0 || t < sp->ats[0]) {
- i = 0;
- while (sp->ttis[i].tt_isdst)
- if (++i >= sp->typecnt) {
- i = 0;
- break;
- }
- } else {
- int lo = 1;
- int hi = sp->timecnt;
-
- while (lo < hi) {
- int mid = (lo + hi) / 2;
-
- if (t < sp->ats[mid])
- hi = mid;
- else lo = mid + 1;
- }
- i = (int) sp->types[lo - 1];
- }
- ttisp = &sp->ttis[i];
- /*
- ** 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);
- */
- result = timesub(sp, &t, ttisp->tt_gmtoff, tmp);
- tmp->tm_isdst = ttisp->tt_isdst;
- if (sp == lclptr)
- tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
-#ifdef TM_ZONE
- if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
-  tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
-#endif /* defined TM_ZONE */
- return result;
-}
-
-/*
-** Re-entrant version of localtime.
-*/
-extern "C" struct tm *
-localtime_r(const time_t *__restrict timep, struct tm *__restrict tmp)
-{
- tzset_guard.init ("tzset_guard")->acquire ();
- tzset_unlocked();
- tmp = localsub(lclptr, timep, 0L, tmp);
- tzset_guard.release ();
- if (tmp == NULL)
- errno = EOVERFLOW;
- return tmp;
-}
-
-extern "C" struct tm *
-localtime(const time_t *const timep)
-{
- return localtime_r(timep, &tm);
-}
-
-/*
-** gmtsub is to gmtime as localsub is to localtime.
-*/
-static NO_COPY muto gmt_guard;
-
-static struct tm *
-gmtsub(const timezone_t sp, const time_t *const timep, const long offset,
-    struct tm *tmp)
-{
- struct tm * result;
-
- gmt_guard.init ("gmt_guard")->acquire ();
- if (!gmt_is_set) {
- save_errno save;
- gmt_is_set = TRUE;
- gmtptr = (timezone_t) calloc(1, sizeof *gmtptr);
- if (gmtptr != NULL)
- gmtload(gmtptr);
- }
- gmt_guard.release ();
- result = timesub(gmtptr, timep, offset, 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 (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
-  {
-    if (offset != 0)
-    tmp->TM_ZONE = wildabbr;
-    else {
-    if (gmtptr == NULL)
-    tmp->TM_ZONE = gmt;
-    else tmp->TM_ZONE = gmtptr->chars;
-    }
-  }
-#endif /* defined TM_ZONE */
- return result;
-}
-
-extern "C" struct tm *
-gmtime(const time_t *const timep)
-{
- struct tm *tmp = gmtsub(NULL, timep, 0L, &tm);
-
- if (tmp == NULL)
- errno = EOVERFLOW;
-
- return tmp;
-}
-
-/*
-** Re-entrant version of gmtime.
-*/
-
-extern "C" struct tm *
-gmtime_r(const time_t *__restrict const timep, struct tm *__restrict tmp)
-{
- tmp = gmtsub(NULL, timep, 0L, tmp);
-
- if (tmp == NULL)
- errno = EOVERFLOW;
-
- return tmp;
-}
-
-#ifdef STD_INSPIRED
-
-extern "C" struct tm *
-offtime(const time_t *const timep, long offset)
-{
- struct tm *tmp = gmtsub(NULL, timep, offset, &tm);
-
- if (tmp == NULL)
- errno = EOVERFLOW;
-
- return tmp;
-}
-
-#endif /* defined STD_INSPIRED */
-
-/*
-** Return the number of leap years through the end of the given year
-** where, to make the math easy, the answer for year zero is defined as zero.
-*/
-
-static int
-leaps_thru_end_of(const int y)
-{
- return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
- -(leaps_thru_end_of(-(y + 1)) + 1);
-}
-
-static struct tm *
-timesub(const timezone_t sp, const time_t *const timep, const long offset,
-    struct tm *const tmp)
-{
- const struct lsinfo * lp;
- time_t tdays;
- int idays; /* unsigned would be so 2003 */
- long rem;
- int y;
- const int * ip;
- long corr;
- int hit;
- int i;
-
- corr = 0;
- hit = 0;
- i = (sp == NULL) ? 0 : sp->leapcnt;
- 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;
- }
- }
- y = EPOCH_YEAR;
- tdays = (time_t)(*timep / SECSPERDAY);
- rem = (long) (*timep - tdays * SECSPERDAY);
- while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
- int newy;
- time_t tdelta;
- int idelta;
- int leapdays;
-
- tdelta = tdays / DAYSPERLYEAR;
- idelta = (int) tdelta;
- if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
- return NULL;
- if (idelta == 0)
- idelta = (tdays < 0) ? -1 : 1;
- newy = y;
- if (increment_overflow(&newy, idelta))
- return NULL;
- leapdays = leaps_thru_end_of(newy - 1) -
- leaps_thru_end_of(y - 1);
- tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
- tdays -= leapdays;
- y = newy;
- }
- {
- long seconds;
-
- seconds = tdays * SECSPERDAY + 0.5;
- tdays = (time_t)(seconds / SECSPERDAY);
- rem += (long) (seconds - tdays * SECSPERDAY);
- }
- /*
- ** Given the range, we can now fearlessly cast...
- */
- idays = (int) tdays;
- rem += offset - corr;
- while (rem < 0) {
- rem += SECSPERDAY;
- --idays;
- }
- while (rem >= SECSPERDAY) {
- rem -= SECSPERDAY;
- ++idays;
- }
- while (idays < 0) {
- if (increment_overflow(&y, -1))
- return NULL;
- idays += year_lengths[isleap(y)];
- }
- while (idays >= year_lengths[isleap(y)]) {
- idays -= year_lengths[isleap(y)];
- if (increment_overflow(&y, 1))
- return NULL;
- }
- tmp->tm_year = y;
- if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
- return NULL;
- tmp->tm_yday = idays;
- /*
- ** The "extra" mods below avoid overflow problems.
- */
- tmp->tm_wday = EPOCH_WDAY +
- ((y - EPOCH_YEAR) % DAYSPERWEEK) *
- (DAYSPERNYEAR % DAYSPERWEEK) +
- leaps_thru_end_of(y - 1) -
- leaps_thru_end_of(EPOCH_YEAR - 1) +
- idays;
- tmp->tm_wday %= DAYSPERWEEK;
- if (tmp->tm_wday < 0)
- tmp->tm_wday += DAYSPERWEEK;
- tmp->tm_hour = (int) (rem / SECSPERHOUR);
- rem %= SECSPERHOUR;
- tmp->tm_min = (int) (rem / SECSPERMIN);
- /*
- ** A positive leap second requires a special
- ** representation. This uses "... ??:59:60" et seq.
- */
- tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
- ip = mon_lengths[isleap(y)];
- for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
- idays -= ip[tmp->tm_mon];
- tmp->tm_mday = (int) (idays + 1);
- tmp->tm_isdst = 0;
-#ifdef TM_GMTOFF
- if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
-  tmp->TM_GMTOFF = offset;
-#endif /* defined TM_GMTOFF */
- return tmp;
-}
-
-extern "C" char *
-ctime(const time_t *const timep)
-{
-/*
-** Section 4.12.3.2 of X3.159-1989 requires that
-** The ctime function converts the calendar time pointed to by timer
-** to local time in the form of a string. It is equivalent to
-** asctime(localtime(timer))
-*/
- struct tm *rtm = localtime(timep);
- if (rtm == NULL)
- return NULL;
- return asctime(rtm);
-}
-
-extern "C" char *
-ctime_r(const time_t *const timep, char *buf)
-{
- struct tm mytm, *rtm;
-
- rtm = localtime_r(timep, &mytm);
- if (rtm == NULL)
- return NULL;
- return asctime_r(rtm, buf);
-}
-
-/*
-** Adapted from code provided by Robert Elz, who writes:
-** The "best" way to do mktime I think is based on an idea of Bob
-** Kridle's (so its said...) from a long time ago.
-** 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 ((time_t)-1)
-#endif /* !defined WRONG */
-
-/*
-** Simplified normalize logic courtesy Paul Eggert.
-*/
-
-static int
-increment_overflow(int *const ip, int j)
-{
- int i = *ip;
-
- /*
- ** If i >= 0 there can only be overflow if i + j > INT_MAX
- ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
- ** If i < 0 there can only be overflow if i + j < INT_MIN
- ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
- */
- if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i))
- return TRUE;
- *ip += j;
- return FALSE;
-}
-
-static int
-long_increment_overflow(long *const lp, int m)
-{
- long l = *lp;
-
- if ((l >= 0) ? (m > LONG_MAX - l) : (m < LONG_MIN - l))
- return TRUE;
- *lp += m;
- return FALSE;
-}
-
-static int
-normalize_overflow(int *const tensptr, int *const unitsptr, const int base)
-{
- int tensdelta;
-
- tensdelta = (*unitsptr >= 0) ?
- (*unitsptr / base) :
- (-1 - (-1 - *unitsptr) / base);
- *unitsptr -= tensdelta * base;
- return increment_overflow(tensptr, tensdelta);
-}
-
-static int
-long_normalize_overflow(long *const tensptr, int *const unitsptr,
-    const int base)
-{
- int tensdelta;
-
- tensdelta = (*unitsptr >= 0) ?
- (*unitsptr / base) :
- (-1 - (-1 - *unitsptr) / base);
- *unitsptr -= tensdelta * base;
- return long_increment_overflow(tensptr, tensdelta);
-}
-
-static int
-tmcomp(const struct tm *const atmp, const struct tm *const btmp)
-{
- int result;
-
- if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
- (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
- (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(const timezone_t sp, struct tm *const tmp, subfun_t funcp,
-    const long offset, int *const okayp, const int do_norm_secs)
-{
- int dir;
- int i, j;
- int saved_seconds;
- long li;
- time_t lo;
- time_t hi;
-#ifdef NO_ERROR_IN_DST_GAP
- time_t ilo;
-#endif
- long y;
- time_t newt;
- time_t t;
- struct tm yourtm, mytm;
-
- *okayp = FALSE;
- yourtm = *tmp;
-#ifdef NO_ERROR_IN_DST_GAP
-again:
-#endif
- if (do_norm_secs) {
- if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
-    SECSPERMIN))
- goto overflow;
- }
- if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
- goto overflow;
- if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
- goto overflow;
- y = yourtm.tm_year;
- if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
- goto overflow;
- /*
- ** Turn y into an actual year number for now.
- ** It is converted back to an offset from TM_YEAR_BASE later.
- */
- if (long_increment_overflow(&y, TM_YEAR_BASE))
- goto overflow;
- while (yourtm.tm_mday <= 0) {
- if (long_increment_overflow(&y, -1))
- goto overflow;
- li = y + (1 < yourtm.tm_mon);
- yourtm.tm_mday += year_lengths[isleap(li)];
- }
- while (yourtm.tm_mday > DAYSPERLYEAR) {
- li = y + (1 < yourtm.tm_mon);
- yourtm.tm_mday -= year_lengths[isleap(li)];
- if (long_increment_overflow(&y, 1))
- goto overflow;
- }
- for ( ; ; ) {
- i = mon_lengths[isleap(y)][yourtm.tm_mon];
- if (yourtm.tm_mday <= i)
- break;
- yourtm.tm_mday -= i;
- if (++yourtm.tm_mon >= MONSPERYEAR) {
- yourtm.tm_mon = 0;
- if (long_increment_overflow(&y, 1))
- goto overflow;
- }
- }
- if (long_increment_overflow(&y, -TM_YEAR_BASE))
- goto overflow;
- yourtm.tm_year = (int)y;
- if (yourtm.tm_year != y)
- goto overflow;
- if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
- saved_seconds = 0;
- else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
- /*
- ** We can't set tm_sec to 0, because that might push the
- ** time below the minimum representable time.
- ** 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))
- goto overflow;
- saved_seconds = yourtm.tm_sec;
- yourtm.tm_sec = SECSPERMIN - 1;
- } else {
- saved_seconds = yourtm.tm_sec;
- yourtm.tm_sec = 0;
- }
- /*
- ** Do a binary search (this works whatever time_t's type is).
- */
- /* LINTED const not */
- if (!TYPE_SIGNED(time_t)) {
- lo = 0;
- hi = lo - 1;
- /* LINTED const not */
- } else {
- lo = 1;
- for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
- lo *= 2;
- hi = -(lo + 1);
- }
-#ifdef NO_ERROR_IN_DST_GAP
- ilo = lo;
-#endif
- for ( ; ; ) {
- t = lo / 2 + hi / 2;
- if (t < lo)
- t = lo;
- else if (t > hi)
- t = hi;
- if ((*funcp)(sp, &t, offset, &mytm) == NULL) {
- /*
- ** Assume that t is too extreme to be represented in
- ** a struct tm; arrange things so that it is less
- ** extreme on the next pass.
- */
- dir = (t > 0) ? 1 : -1;
- } else dir = tmcomp(&mytm, &yourtm);
- if (dir != 0) {
- if (t == lo) {
- ++t;
- if (t <= lo)
- goto overflow;
- ++lo;
- } else if (t == hi) {
- --t;
- if (t >= hi)
- goto overflow;
- --hi;
- }
-#ifdef NO_ERROR_IN_DST_GAP
- if (ilo != lo && lo - 1 == hi && yourtm.tm_isdst < 0 &&
-    do_norm_secs) {
- for (i = sp->typecnt - 1; i >= 0; --i) {
- for (j = sp->typecnt - 1; j >= 0; --j) {
- time_t off;
- if (sp->ttis[j].tt_isdst ==
-    sp->ttis[i].tt_isdst)
- continue;
- off = sp->ttis[j].tt_gmtoff -
-    sp->ttis[i].tt_gmtoff;
- yourtm.tm_sec += off < 0 ?
-    -off : off;
- goto again;
- }
- }
- }
-#endif
- if (lo > hi)
- goto invalid;
- if (dir > 0)
- hi = t;
- else lo = t;
- continue;
- }
- if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
- break;
- /*
- ** Right time, wrong type.
- ** Hunt for right time, right type.
- ** It's okay to guess wrong since the guess
- ** gets checked.
- */
- if (sp == NULL)
- goto invalid;
- for (i = sp->typecnt - 1; i >= 0; --i) {
- if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
- continue;
- for (j = sp->typecnt - 1; j >= 0; --j) {
- if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
- continue;
- newt = (time_t)(t + sp->ttis[j].tt_gmtoff -
-    sp->ttis[i].tt_gmtoff);
- if ((*funcp)(sp, &newt, offset, &mytm) == NULL)
- continue;
- if (tmcomp(&mytm, &yourtm) != 0)
- continue;
- if (mytm.tm_isdst != yourtm.tm_isdst)
- continue;
- /*
- ** We have a match.
- */
- t = newt;
- goto label;
- }
- }
- goto invalid;
- }
-label:
- newt = t + saved_seconds;
- if ((newt < t) != (saved_seconds < 0))
- goto overflow;
- t = newt;
- if ((*funcp)(sp, &t, offset, tmp)) {
- *okayp = TRUE;
- return t;
- }
-overflow:
- errno = EOVERFLOW;
- return WRONG;
-invalid:
- errno = EINVAL;
- return WRONG;
-}
-
-static time_t
-time2(const timezone_t sp, struct tm *const tmp, subfun_t funcp,
-    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(sp, tmp, funcp, offset, okayp, FALSE);
- return *okayp ? t : time2sub(sp, tmp, funcp, offset, okayp, TRUE);
-}
-
-static time_t
-time1(const timezone_t sp, struct tm *const tmp, subfun_t funcp,
-    const long offset)
-{
- time_t t;
- int samei, otheri;
- int sameind, otherind;
- int i;
- int nseen;
- int seen[TZ_MAX_TYPES];
- int types[TZ_MAX_TYPES];
- int okay;
-
- if (tmp == NULL) {
- errno = EINVAL;
- return WRONG;
- }
- if (tmp->tm_isdst > 1)
- tmp->tm_isdst = 1;
- t = time2(sp, tmp, funcp, offset, &okay);
-#ifdef PCTS
- /*
- ** PCTS code courtesy Grant Sullivan.
- */
- 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.
- */
- if (sp == NULL) {
- errno = EINVAL;
- return WRONG;
- }
- 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 += (int)(sp->ttis[otheri].tt_gmtoff -
- sp->ttis[samei].tt_gmtoff);
- tmp->tm_isdst = !tmp->tm_isdst;
- t = time2(sp, tmp, funcp, offset, &okay);
- if (okay)
- return t;
- tmp->tm_sec -= (int)(sp->ttis[otheri].tt_gmtoff -
- sp->ttis[samei].tt_gmtoff);
- tmp->tm_isdst = !tmp->tm_isdst;
- }
- }
- errno = EOVERFLOW;
- return WRONG;
-}
-
-extern "C" time_t
-mktime(struct tm *const tmp)
-{
- time_t result;
-
- tzset_guard.init ("tzset_guard")->acquire ();
- tzset_unlocked();
- result = time1(lclptr, tmp, localsub, 0L);
- tzset_guard.release ();
- return result;
-}
-
-#ifdef STD_INSPIRED
-
-extern "C" time_t
-timelocal(struct tm *const tmp)
-{
- if (tmp != NULL)
- tmp->tm_isdst = -1; /* in case it wasn't initialized */
- return mktime(tmp);
-}
-
-extern "C" time_t
-timegm(struct tm *const tmp)
-{
- time_t t;
-
- if (tmp != NULL)
- tmp->tm_isdst = 0;
- t = time1(gmtptr, tmp, gmtsub, 0L);
- return t;
-}
-
-extern "C" time_t
-timeoff(struct tm *const tmp, const long offset)
-{
- time_t t;
-
- if (tmp != NULL)
- tmp->tm_isdst = 0;
- t = time1(gmtptr, tmp, gmtsub, offset);
- return t;
-}
-
-#endif /* defined STD_INSPIRED */
-
-#ifdef CMUCS
-
-/*
-** The following is supplied for compatibility with
-** previous versions of the CMUCS runtime library.
-*/
-
-extern "C" long
-gtime(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(const timezone_t sp, time_t *timep)
-{
- struct lsinfo * lp;
- int i;
-
- i = sp->leapcnt;
- while (--i >= 0) {
- lp = &sp->lsis[i];
- if (*timep >= lp->ls_trans)
- return lp->ls_corr;
- }
- return 0;
-}
-
-extern "C" time_t
-time2posix(time_t t)
-{
- time_t result;
- tzset_guard.init ("tzset_guard")->acquire ();
- tzset_unlocked();
- result = t - leapcorr(lclptr, &t);
- tzset_guard.release ();
- return (result);
-}
-
-extern "C" time_t
-posix2time(time_t t)
-{
- time_t x;
- time_t y;
-
- tzset_guard.init ("tzset_guard")->acquire ();
- tzset_unlocked();
- /*
- ** 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 = (time_t)(t + leapcorr(lclptr, &t));
- y = (time_t)(x - leapcorr(lclptr, &x));
- if (y < t) {
- do {
- x++;
- y = (time_t)(x - leapcorr(lclptr, &x));
- } while (y < t);
- if (t != y) {
- return x - 1;
- }
- } else if (y > t) {
- do {
- --x;
- y = (time_t)(x - leapcorr(lclptr, &x));
- } while (y > t);
- if (t != y) {
- return x + 1;
- }
- }
- tzset_guard.release ();
- return x;
-}
-
-#endif /* defined STD_INSPIRED */
-
-extern "C" long
-__cygwin_gettzoffset (const struct tm *tmp)
-{
-#ifdef TM_GMTOFF
-  if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
-    return tmp->TM_GMTOFF;
-#endif /* defined TM_GMTOFF */
-  __tzinfo_type *tz = __gettzinfo ();
-  /* The sign of this is exactly opposite the envvar TZ.  We
-     could directly use the global _timezone for tm_isdst==0,
-     but have to use __tzrule for daylight savings.  */
-  long offset = -tz->__tzrule[tmp->tm_isdst > 0].offset;
-  return offset;
-}
-
-extern "C" const char *
-__cygwin_gettzname (const struct tm *tmp)
-{
-#ifdef TM_ZONE
-  if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
-    return tmp->TM_ZONE;
-#endif
-  return _tzname[tmp->tm_isdst > 0];
-}
diff --git a/winsup/cygwin/tz_posixrules.h b/winsup/cygwin/tz_posixrules.h
deleted file mode 100644
index dea668e2f..000000000
--- a/winsup/cygwin/tz_posixrules.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* generated with bin2h from zoneinfo/posixrules */
-
-static NO_COPY unsigned char _posixrules_data[] = {
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,
-0,1,16,0,0,0,2,0,0,0,8,0,151,254,240,1,135,225,224,2,119,224,240,3,112,254,96,4,96,253,112,5,80,
-224,96,6,64,223,112,7,48,194,96,7,141,25,112,9,16,164,96,9,173,148,240,10,240,134,96,11,224,133,112,12,217,162,
-224,13,192,103,112,14,185,132,224,15,169,131,240,16,153,102,224,17,137,101,240,18,121,72,224,19,105,71,240,20,89,42,224,
-21,73,41,240,22,57,12,224,23,41,11,240,24,34,41,96,25,8,237,240,26,2,11,96,26,242,10,112,27,225,237,96,28,
-209,236,112,29,193,207,96,30,177,206,112,31,161,177,96,32,118,0,240,33,129,147,96,34,85,226,240,35,106,175,224,36,53,
-196,240,37,74,145,224,38,21,166,240,39,42,115,224,39,254,195,112,41,10,85,224,41,222,165,112,42,234,55,224,43,190,135,
-112,44,211,84,96,45,158,105,112,46,179,54,96,47,126,75,112,48,147,24,96,49,103,103,240,50,114,250,96,51,71,73,240,
-52,82,220,96,53,39,43,240,54,50,190,96,55,7,13,240,56,27,218,224,56,230,239,240,57,251,188,224,58,198,209,240,59,
-219,158,224,60,175,238,112,61,187,128,224,62,143,208,112,63,155,98,224,64,111,178,112,65,132,127,96,66,79,148,112,67,100,
-97,96,68,47,118,112,69,68,67,96,70,15,88,112,71,36,37,96,71,248,116,240,73,4,7,96,73,216,86,240,74,227,233,
-96,75,184,56,240,76,205,5,224,77,152,26,240,78,172,231,224,79,119,252,240,80,140,201,224,81,97,25,112,82,108,171,224,
-83,64,251,112,84,76,141,224,85,32,221,112,86,44,111,224,87,0,191,112,88,21,140,96,88,224,161,112,89,245,110,96,90,
-192,131,112,91,213,80,96,92,169,159,240,93,181,50,96,94,137,129,240,95,149,20,96,96,105,99,240,97,126,48,224,98,73,
-69,240,99,94,18,224,100,41,39,240,101,61,244,224,102,18,68,112,103,29,214,224,103,242,38,112,104,253,184,224,105,210,8,
-112,106,221,154,224,107,177,234,112,108,198,183,96,109,145,204,112,110,166,153,96,111,113,174,112,112,134,123,96,113,90,202,240,
-114,102,93,96,115,58,172,240,116,70,63,96,117,26,142,240,118,47,91,224,118,250,112,240,120,15,61,224,120,218,82,240,121,
-239,31,224,122,186,52,240,123,207,1,224,124,163,81,112,125,174,227,224,126,131,51,112,127,142,197,224,128,99,21,112,129,119,
-226,96,130,66,247,112,131,87,196,96,132,34,217,112,133,55,166,96,134,11,245,240,135,23,136,96,135,235,215,240,136,247,106,
-96,137,203,185,240,138,215,76,96,139,171,155,240,140,192,104,224,141,139,125,240,142,160,74,224,143,107,95,240,144,128,44,224,
-145,84,124,112,146,96,14,224,147,52,94,112,148,63,240,224,149,20,64,112,150,41,13,96,150,244,34,112,152,8,239,96,152,
-212,4,112,153,232,209,96,154,189,32,240,155,200,179,96,156,157,2,240,157,168,149,96,158,124,228,240,159,136,119,96,160,92,
-198,240,161,113,147,224,162,60,168,240,163,81,117,224,164,28,138,240,165,49,87,224,166,5,167,112,167,17,57,224,167,229,137,
-112,168,241,27,224,169,197,107,112,170,218,56,96,171,165,77,112,172,186,26,96,173,133,47,112,174,153,252,96,175,101,17,112,
-176,121,222,96,177,78,45,240,178,89,192,96,179,46,15,240,180,57,162,96,181,13,241,240,182,34,190,224,182,237,211,240,184,
-2,160,224,184,205,181,240,185,226,130,224,186,182,210,112,187,194,100,224,188,150,180,112,189,162,70,224,190,118,150,112,191,130,
-40,224,192,86,120,112,193,107,69,96,194,54,90,112,195,75,39,96,196,22,60,112,197,43,9,96,197,255,88,240,199,10,235,
-96,199,223,58,240,200,234,205,96,201,191,28,240,202,211,233,224,203,158,254,240,204,179,203,224,205,126,224,240,206,147,173,224,
-207,103,253,112,208,115,143,224,209,71,223,112,210,83,113,224,211,39,193,112,212,51,83,224,213,7,163,112,214,28,112,96,214,
-231,133,112,215,252,82,96,216,199,103,112,217,220,52,96,218,176,131,240,219,188,22,96,220,144,101,240,221,155,248,96,222,112,
-71,240,223,133,20,224,224,80,41,240,225,100,246,224,226,48,11,240,227,68,216,224,228,15,237,240,229,36,186,224,229,249,10,
-112,231,4,156,224,231,216,236,112,232,228,126,224,233,184,206,112,234,205,155,96,235,152,176,112,236,173,125,96,237,120,146,112,
-238,141,95,96,239,97,174,240,240,109,65,96,241,65,144,240,242,77,35,96,243,33,114,240,244,45,5,96,245,1,84,240,246,
-22,33,224,246,225,54,240,247,246,3,224,248,193,24,240,249,213,229,224,250,160,250,240,251,181,199,224,252,138,23,112,253,149,
-169,224,254,105,249,112,255,117,139,224,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,
-1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
-0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,
-1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
-0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,
-1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
-0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,
-1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
-0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,255,255,199,192,1,0,255,255,185,176,0,4,69,68,84,
-0,69,83,84,0,0,0
-};
--
2.21.0

Reply | Threaded
Open this post in threaded view
|

[PATCH 2/3 v3] Cygwin: tzcode resync: imports

Mark Geisert
In reply to this post by Mark Geisert
Import most recent NetBSD localtime.c, private.h, and tzfile.h.  An
empty namespace.h suffices for Cygwin.

---
 winsup/cygwin/tzcode/localtime.c | 2493 ++++++++++++++++++++++++++++++
 winsup/cygwin/tzcode/namespace.h |    0
 winsup/cygwin/tzcode/private.h   |  795 ++++++++++
 winsup/cygwin/tzcode/tzfile.h    |  174 +++
 4 files changed, 3462 insertions(+)
 create mode 100644 winsup/cygwin/tzcode/localtime.c
 create mode 100644 winsup/cygwin/tzcode/namespace.h
 create mode 100644 winsup/cygwin/tzcode/private.h
 create mode 100644 winsup/cygwin/tzcode/tzfile.h

diff --git a/winsup/cygwin/tzcode/localtime.c b/winsup/cygwin/tzcode/localtime.c
new file mode 100644
index 000000000..a4d02a4c7
--- /dev/null
+++ b/winsup/cygwin/tzcode/localtime.c
@@ -0,0 +1,2493 @@
+/* $NetBSD: localtime.c,v 1.122 2019/07/03 15:50:16 christos Exp $ */
+
+/* Convert timestamp from time_t to struct tm.  */
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char elsieid[] = "@(#)localtime.c 8.17";
+#else
+__RCSID("$NetBSD: localtime.c,v 1.122 2019/07/03 15:50:16 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
+*/
+
+/*LINTLIBRARY*/
+
+#include "namespace.h"
+#include <assert.h>
+#define LOCALTIME_IMPLEMENTATION
+#include "private.h"
+
+#include "tzfile.h"
+#include <fcntl.h>
+
+#if NETBSD_INSPIRED
+# define NETBSD_INSPIRED_EXTERN
+#else
+# define NETBSD_INSPIRED_EXTERN static
+#endif
+
+#if defined(__weak_alias)
+__weak_alias(daylight,_daylight)
+__weak_alias(tzname,_tzname)
+#endif
+
+#ifndef TZ_ABBR_MAX_LEN
+#define TZ_ABBR_MAX_LEN 16
+#endif /* !defined TZ_ABBR_MAX_LEN */
+
+#ifndef TZ_ABBR_CHAR_SET
+#define TZ_ABBR_CHAR_SET \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
+#endif /* !defined TZ_ABBR_CHAR_SET */
+
+#ifndef TZ_ABBR_ERR_CHAR
+#define TZ_ABBR_ERR_CHAR '_'
+#endif /* !defined TZ_ABBR_ERR_CHAR */
+
+/*
+** SunOS 4.1.1 headers lack O_BINARY.
+*/
+
+#ifdef O_BINARY
+#define OPEN_MODE (O_RDONLY | O_BINARY | O_CLOEXEC)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE (O_RDONLY | O_CLOEXEC)
+#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 const char wildabbr[] = WILDABBR;
+
+static const char gmt[] = "GMT";
+
+/*
+** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
+** Default to US rules as of 2017-05-07.
+** POSIX does not specify the default DST rules;
+** for historical reasons, US rules are a common default.
+*/
+#ifndef TZDEFRULESTRING
+#define TZDEFRULESTRING ",M3.2.0,M11.1.0"
+#endif
+
+struct ttinfo { /* time type information */
+ int_fast32_t tt_utoff; /* UT offset in seconds */
+ bool tt_isdst; /* used to set tm_isdst */
+ int tt_desigidx; /* abbreviation list index */
+ bool tt_ttisstd; /* transition is std time */
+ bool tt_ttisut; /* transition is UT */
+};
+
+struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ int_fast64_t ls_corr; /* correction to apply */
+};
+
+#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
+#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 */
+
+#define state __state
+struct state {
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ bool goback;
+ bool goahead;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[/*CONSTCOND*/BIGGEST(BIGGEST(TZ_MAX_CHARS + 1,
+ sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+
+ /* The time type to use for early times or if no transitions.
+   It is always zero for recent tzdb releases.
+   It might be nonzero for data from tzdb 2018e or earlier.  */
+ int defaulttype;
+};
+
+enum r_type {
+  JULIAN_DAY, /* Jn = Julian day */
+  DAY_OF_YEAR, /* n = day of year */
+  MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */
+};
+
+struct rule {
+ enum r_type r_type; /* type of rule */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ int_fast32_t r_time; /* transition time of rule */
+};
+
+static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t,
+ struct tm *);
+static bool increment_overflow(int *, int);
+static bool increment_overflow_time(time_t *, int_fast32_t);
+static bool normalize_overflow32(int_fast32_t *, int *, int);
+static struct tm *timesub(time_t const *, int_fast32_t, struct state const *,
+  struct tm *);
+static bool typesequiv(struct state const *, int, int);
+static bool tzparse(char const *, struct state *, bool);
+
+static timezone_t gmtptr;
+
+#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;
+
+
+#if !defined(__LIBC12_SOURCE__)
+timezone_t __lclptr;
+#ifdef _REENTRANT
+rwlock_t __lcl_lock = RWLOCK_INITIALIZER;
+#endif
+#endif
+
+/*
+** 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 for noting this.
+*/
+
+static struct tm tm;
+
+#if !HAVE_POSIX_DECLS || TZ_TIME_T || defined(__NetBSD__)
+# if !defined(__LIBC12_SOURCE__)
+
+__aconst char * tzname[2] = {
+ (__aconst char *)__UNCONST(wildabbr),
+ (__aconst char *)__UNCONST(wildabbr)
+};
+
+# else
+
+extern __aconst char * tzname[2];
+
+# endif /* __LIBC12_SOURCE__ */
+
+# if USG_COMPAT
+#  if !defined(__LIBC12_SOURCE__)
+long timezone = 0;
+int daylight = 0;
+#  else
+extern int daylight;
+extern long timezone __RENAME(__timezone13);
+#  endif /* __LIBC12_SOURCE__ */
+# endif /* defined USG_COMPAT */
+
+# ifdef ALTZONE
+long altzone = 0;
+# endif /* defined ALTZONE */
+#endif /* !HAVE_POSIX_DECLS */
+
+/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX.  */
+static void
+init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
+{
+ s->tt_utoff = utoff;
+ s->tt_isdst = isdst;
+ s->tt_desigidx = desigidx;
+ s->tt_ttisstd = false;
+ s->tt_ttisut = false;
+}
+
+static int_fast32_t
+detzcode(const char *const codep)
+{
+ int_fast32_t result;
+ int i;
+ int_fast32_t one = 1;
+ int_fast32_t halfmaxval = one << (32 - 2);
+ int_fast32_t maxval = halfmaxval - 1 + halfmaxval;
+ int_fast32_t minval = -1 - maxval;
+
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80) {
+  /* Do two's-complement negation even on non-two's-complement machines.
+     If the result would be minval - 1, return minval.  */
+    result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0;
+    result += minval;
+ }
+ return result;
+}
+
+static int_fast64_t
+detzcode64(const char *const codep)
+{
+ int_fast64_t result;
+ int i;
+ int_fast64_t one = 1;
+ int_fast64_t halfmaxval = one << (64 - 2);
+ int_fast64_t maxval = halfmaxval - 1 + halfmaxval;
+ int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval;
+
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 8; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80) {
+  /* Do two's-complement negation even on non-two's-complement machines.
+     If the result would be minval - 1, return minval.  */
+  result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0;
+  result += minval;
+ }
+ return result;
+}
+
+#include <stdio.h>
+
+const char *
+tzgetname(const timezone_t sp, int isdst)
+{
+ int i;
+ const char *name = NULL;
+ for (i = 0; i < sp->typecnt; ++i) {
+ const struct ttinfo *const ttisp = &sp->ttis[i];
+ if (ttisp->tt_isdst == isdst)
+ name = &sp->chars[ttisp->tt_desigidx];
+ }
+ if (name != NULL)
+ return name;
+ errno = ESRCH;
+ return NULL;
+}
+
+long
+tzgetgmtoff(const timezone_t sp, int isdst)
+{
+ int i;
+ long l = -1;
+ for (i = 0; i < sp->typecnt; ++i) {
+ const struct ttinfo *const ttisp = &sp->ttis[i];
+
+ if (ttisp->tt_isdst == isdst) {
+ l = ttisp->tt_utoff;
+ }
+ }
+ if (l == -1)
+ errno = ESRCH;
+ return l;
+}
+
+static void
+scrub_abbrs(struct state *sp)
+{
+ int i;
+
+ /*
+ ** First, replace bogus characters.
+ */
+ for (i = 0; i < sp->charcnt; ++i)
+ if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL)
+ sp->chars[i] = TZ_ABBR_ERR_CHAR;
+ /*
+ ** Second, truncate long abbreviations.
+ */
+ for (i = 0; i < sp->typecnt; ++i) {
+ const struct ttinfo * const ttisp = &sp->ttis[i];
+ char *cp = &sp->chars[ttisp->tt_desigidx];
+
+ if (strlen(cp) > TZ_ABBR_MAX_LEN &&
+ strcmp(cp, GRANDPARENTED) != 0)
+ *(cp + TZ_ABBR_MAX_LEN) = '\0';
+ }
+}
+
+static void
+update_tzname_etc(const struct state *sp, const struct ttinfo *ttisp)
+{
+#if HAVE_TZNAME
+ tzname[ttisp->tt_isdst] = __UNCONST(&sp->chars[ttisp->tt_desigidx]);
+#endif
+#if USG_COMPAT
+ if (!ttisp->tt_isdst)
+ timezone = - ttisp->tt_utoff;
+#endif
+#ifdef ALTZONE
+ if (ttisp->tt_isdst)
+    altzone = - ttisp->tt_utoff;
+#endif /* defined ALTZONE */
+}
+
+static void
+settzname(void)
+{
+ timezone_t const sp = __lclptr;
+ int i;
+
+#if HAVE_TZNAME
+ tzname[0] = tzname[1] =
+    (__aconst char *) __UNCONST(sp ? wildabbr : gmt);
+#endif
+#if USG_COMPAT
+ daylight = 0;
+ timezone = 0;
+#endif
+#ifdef ALTZONE
+ altzone = 0;
+#endif /* defined ALTZONE */
+ if (sp == NULL) {
+ return;
+ }
+ /*
+ ** And to get the latest time zone abbreviations into tzname. . .
+ */
+ for (i = 0; i < sp->typecnt; ++i)
+ update_tzname_etc(sp, &sp->ttis[i]);
+
+ for (i = 0; i < sp->timecnt; ++i) {
+ const struct ttinfo * const ttisp = &sp->ttis[sp->types[i]];
+ update_tzname_etc(sp, ttisp);
+#if USG_COMPAT
+ if (ttisp->tt_isdst)
+ daylight = 1;
+#endif
+ }
+}
+
+static bool
+differ_by_repeat(const time_t t1, const time_t t0)
+{
+ if (TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+ return 0;
+ return (int_fast64_t)t1 - (int_fast64_t)t0 == SECSPERREPEAT;
+}
+
+union input_buffer {
+ /* The first part of the buffer, interpreted as a header.  */
+ struct tzhead tzhead;
+
+ /* The entire buffer.  */
+ char buf[2 * sizeof(struct tzhead) + 2 * sizeof (struct state)
+  + 4 * TZ_MAX_TIMES];
+};
+
+/* TZDIR with a trailing '/' rather than a trailing '\0'.  */
+static char const tzdirslash[sizeof TZDIR] = TZDIR "/";
+
+/* Local storage needed for 'tzloadbody'.  */
+union local_storage {
+ /* The results of analyzing the file's contents after it is opened.  */
+ struct file_analysis {
+ /* The input buffer.  */
+ union input_buffer u;
+
+ /* A temporary state used for parsing a TZ string in the file.  */
+ struct state st;
+ } u;
+
+ /* The file name to be opened.  */
+ char fullname[/*CONSTCOND*/BIGGEST(sizeof (struct file_analysis),
+    sizeof tzdirslash + 1024)];
+};
+
+/* Load tz data from the file named NAME into *SP.  Read extended
+   format if DOEXTEND.  Use *LSP for temporary storage.  Return 0 on
+   success, an errno value on failure.  */
+static int
+tzloadbody(char const *name, struct state *sp, bool doextend,
+  union local_storage *lsp)
+{
+ int i;
+ int fid;
+ int stored;
+ ssize_t nread;
+ bool doaccess;
+ union input_buffer *up = &lsp->u.u;
+ size_t tzheadsize = sizeof(struct tzhead);
+
+ sp->goback = sp->goahead = false;
+
+ if (! name) {
+ name = TZDEFAULT;
+ if (! name)
+ return EINVAL;
+ }
+
+ if (name[0] == ':')
+ ++name;
+#ifdef SUPPRESS_TZDIR
+ /* Do not prepend TZDIR.  This is intended for specialized
+   applications only, due to its security implications.  */
+ doaccess = true;
+#else
+ doaccess = name[0] == '/';
+#endif
+ if (!doaccess) {
+ char const *dot;
+ size_t namelen = strlen(name);
+ if (sizeof lsp->fullname - sizeof tzdirslash <= namelen)
+ return ENAMETOOLONG;
+
+ /* Create a string "TZDIR/NAME".  Using sprintf here
+   would pull in stdio (and would fail if the
+   resulting string length exceeded INT_MAX!).  */
+ memcpy(lsp->fullname, tzdirslash, sizeof tzdirslash);
+ strcpy(lsp->fullname + sizeof tzdirslash, name);
+
+ /* Set doaccess if NAME contains a ".." file name
+   component, as such a name could read a file outside
+   the TZDIR virtual subtree.  */
+ for (dot = name; (dot = strchr(dot, '.')) != NULL; dot++)
+  if ((dot == name || dot[-1] == '/') && dot[1] == '.'
+      && (dot[2] == '/' || !dot[2])) {
+    doaccess = true;
+    break;
+  }
+
+ name = lsp->fullname;
+ }
+ if (doaccess && access(name, R_OK) != 0)
+ return errno;
+
+ fid = open(name, OPEN_MODE);
+ if (fid < 0)
+ return errno;
+ nread = read(fid, up->buf, sizeof up->buf);
+ if (nread < (ssize_t)tzheadsize) {
+ int err = nread < 0 ? errno : EINVAL;
+ close(fid);
+ return err;
+ }
+ if (close(fid) < 0)
+ return errno;
+ for (stored = 4; stored <= 8; stored *= 2) {
+ int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+ int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
+ int_fast64_t prevtr = 0;
+ int_fast32_t prevcorr = 0;
+ int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+ int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
+ int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
+ int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt);
+ char const *p = up->buf + tzheadsize;
+ /* Although tzfile(5) currently requires typecnt to be nonzero,
+   support future formats that may allow zero typecnt
+   in files that have a TZ string and no transitions.  */
+ if (! (0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
+       && 0 <= typecnt && typecnt < TZ_MAX_TYPES
+       && 0 <= timecnt && timecnt < TZ_MAX_TIMES
+       && 0 <= charcnt && charcnt < TZ_MAX_CHARS
+       && (ttisstdcnt == typecnt || ttisstdcnt == 0)
+       && (ttisutcnt == typecnt || ttisutcnt == 0)))
+  return EINVAL;
+ if ((size_t)nread
+    < (tzheadsize /* struct tzhead */
+       + timecnt * stored /* ats */
+       + timecnt /* types */
+       + typecnt * 6 /* ttinfos */
+       + charcnt /* chars */
+       + leapcnt * (stored + 4) /* lsinfos */
+       + ttisstdcnt /* ttisstds */
+       + ttisutcnt)) /* ttisuts */
+  return EINVAL;
+ sp->leapcnt = leapcnt;
+ sp->timecnt = timecnt;
+ sp->typecnt = typecnt;
+ sp->charcnt = charcnt;
+
+ /* Read transitions, discarding those out of time_t range.
+   But pretend the last transition before TIME_T_MIN
+   occurred at TIME_T_MIN.  */
+ timecnt = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ int_fast64_t at
+  = stored == 4 ? detzcode(p) : detzcode64(p);
+ sp->types[i] = at <= TIME_T_MAX;
+ if (sp->types[i]) {
+ time_t attime
+    = ((TYPE_SIGNED(time_t) ?
+    at < TIME_T_MIN : at < 0)
+    ? TIME_T_MIN : (time_t)at);
+ if (timecnt && attime <= sp->ats[timecnt - 1]) {
+ if (attime < sp->ats[timecnt - 1])
+ return EINVAL;
+ sp->types[i - 1] = 0;
+ timecnt--;
+ }
+ sp->ats[timecnt++] = attime;
+ }
+ p += stored;
+ }
+
+ timecnt = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ unsigned char typ = *p++;
+ if (sp->typecnt <= typ)
+  return EINVAL;
+ if (sp->types[i])
+ sp->types[timecnt++] = typ;
+ }
+ sp->timecnt = timecnt;
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+ unsigned char isdst, desigidx;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_utoff = detzcode(p);
+ p += 4;
+ isdst = *p++;
+ if (! (isdst < 2))
+ return EINVAL;
+ ttisp->tt_isdst = isdst;
+ desigidx = *p++;
+ if (! (desigidx < sp->charcnt))
+ return EINVAL;
+ ttisp->tt_desigidx = desigidx;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+
+ /* Read leap seconds, discarding those out of time_t range.  */
+ leapcnt = 0;
+ for (i = 0; i < sp->leapcnt; ++i) {
+ int_fast64_t tr = stored == 4 ? detzcode(p) :
+    detzcode64(p);
+ int_fast32_t corr = detzcode(p + stored);
+ p += stored + 4;
+ /* Leap seconds cannot occur before the Epoch.  */
+ if (tr < 0)
+ return EINVAL;
+ if (tr <= TIME_T_MAX) {
+    /* Leap seconds cannot occur more than once per UTC month,
+       and UTC months are at least 28 days long (minus 1
+       second for a negative leap second).  Each leap second's
+       correction must differ from the previous one's by 1
+       second.  */
+ if (tr - prevtr < 28 * SECSPERDAY - 1
+    || (corr != prevcorr - 1
+    && corr != prevcorr + 1))
+  return EINVAL;
+
+ sp->lsis[leapcnt].ls_trans =
+    (time_t)(prevtr = tr);
+ sp->lsis[leapcnt].ls_corr = prevcorr = corr;
+ leapcnt++;
+ }
+ }
+ sp->leapcnt = leapcnt;
+
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = false;
+ else {
+ if (*p != true && *p != false)
+  return EINVAL;
+ ttisp->tt_ttisstd = *p++;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisutcnt == 0)
+ ttisp->tt_ttisut = false;
+ else {
+ if (*p != true && *p != false)
+ return EINVAL;
+ ttisp->tt_ttisut = *p++;
+ }
+ }
+ /*
+ ** If this is an old file, we're done.
+ */
+ if (up->tzhead.tzh_version[0] == '\0')
+ break;
+ nread -= p - up->buf;
+ memmove(up->buf, p, (size_t)nread);
+ }
+ if (doextend && nread > 2 &&
+ up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
+ sp->typecnt + 2 <= TZ_MAX_TYPES) {
+ struct state *ts = &lsp->u.st;
+
+ up->buf[nread - 1] = '\0';
+ if (tzparse(&up->buf[1], ts, false)) {
+
+  /* Attempt to reuse existing abbreviations.
+     Without this, America/Anchorage would be right on
+     the edge after 2037 when TZ_MAX_CHARS is 50, as
+     sp->charcnt equals 40 (for LMT AST AWT APT AHST
+     AHDT YST AKDT AKST) and ts->charcnt equals 10
+     (for AKST AKDT).  Reusing means sp->charcnt can
+     stay 40 in this example.  */
+  int gotabbr = 0;
+  int charcnt = sp->charcnt;
+  for (i = 0; i < ts->typecnt; i++) {
+    char *tsabbr = ts->chars + ts->ttis[i].tt_desigidx;
+    int j;
+    for (j = 0; j < charcnt; j++)
+      if (strcmp(sp->chars + j, tsabbr) == 0) {
+ ts->ttis[i].tt_desigidx = j;
+ gotabbr++;
+ break;
+      }
+    if (! (j < charcnt)) {
+      size_t tsabbrlen = strlen(tsabbr);
+      if (j + tsabbrlen < TZ_MAX_CHARS) {
+ strcpy(sp->chars + j, tsabbr);
+ charcnt = (int_fast32_t)(j + tsabbrlen + 1);
+ ts->ttis[i].tt_desigidx = j;
+ gotabbr++;
+      }
+    }
+  }
+  if (gotabbr == ts->typecnt) {
+    sp->charcnt = charcnt;
+
+    /* Ignore any trailing, no-op transitions generated
+       by zic as they don't help here and can run afoul
+       of bugs in zic 2016j or earlier.  */
+    while (1 < sp->timecnt
+   && (sp->types[sp->timecnt - 1]
+       == sp->types[sp->timecnt - 2]))
+      sp->timecnt--;
+
+    for (i = 0; i < ts->timecnt; i++)
+      if (sp->timecnt == 0
+  || sp->ats[sp->timecnt - 1] < ts->ats[i])
+ break;
+    while (i < ts->timecnt
+   && sp->timecnt < TZ_MAX_TIMES) {
+      sp->ats[sp->timecnt] = ts->ats[i];
+      sp->types[sp->timecnt] = (sp->typecnt
+ + ts->types[i]);
+      sp->timecnt++;
+      i++;
+    }
+    for (i = 0; i < ts->typecnt; i++)
+      sp->ttis[sp->typecnt++] = ts->ttis[i];
+  }
+ }
+ }
+ if (sp->typecnt == 0)
+  return EINVAL;
+ if (sp->timecnt > 1) {
+ for (i = 1; i < sp->timecnt; ++i)
+ if (typesequiv(sp, sp->types[i], sp->types[0]) &&
+ differ_by_repeat(sp->ats[i], sp->ats[0])) {
+ sp->goback = true;
+ break;
+ }
+ for (i = sp->timecnt - 2; i >= 0; --i)
+ if (typesequiv(sp, sp->types[sp->timecnt - 1],
+ sp->types[i]) &&
+ differ_by_repeat(sp->ats[sp->timecnt - 1],
+ sp->ats[i])) {
+ sp->goahead = true;
+ break;
+ }
+ }
+
+ /* Infer sp->defaulttype from the data.  Although this default
+   type is always zero for data from recent tzdb releases,
+   things are trickier for data from tzdb 2018e or earlier.
+
+   The first set of heuristics work around bugs in 32-bit data
+   generated by tzdb 2013c or earlier.  The workaround is for
+   zones like Australia/Macquarie where timestamps before the
+   first transition have a time type that is not the earliest
+   standard-time type.  See:
+   https://mm.icann.org/pipermail/tz/2013-May/019368.html */
+ /*
+ ** If type 0 is unused in transitions,
+ ** it's the type to use for early times.
+ */
+ for (i = 0; i < sp->timecnt; ++i)
+ if (sp->types[i] == 0)
+ break;
+ i = i < sp->timecnt ? -1 : 0;
+ /*
+ ** Absent the above,
+ ** if there are transition times
+ ** and the first transition is to a daylight time
+ ** find the standard type less than and closest to
+ ** the type of the first transition.
+ */
+ if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) {
+ i = sp->types[0];
+ while (--i >= 0)
+ if (!sp->ttis[i].tt_isdst)
+ break;
+ }
+ /* The next heuristics are for data generated by tzdb 2018e or
+   earlier, for zones like EST5EDT where the first transition
+   is to DST.  */
+ /*
+ ** If no result yet, find the first standard type.
+ ** If there is none, punt to type zero.
+ */
+ if (i < 0) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ }
+ /* A simple 'sp->defaulttype = 0;' would suffice here if we
+   didn't have to worry about 2018e-or-earlier data.  Even
+   simpler would be to remove the defaulttype member and just
+   use 0 in its place.  */
+ sp->defaulttype = i;
+
+ return 0;
+}
+
+/* Load tz data from the file named NAME into *SP.  Read extended
+   format if DOEXTEND.  Return 0 on success, an errno value on failure.  */
+static int
+tzload(char const *name, struct state *sp, bool doextend)
+{
+ union local_storage *lsp = malloc(sizeof *lsp);
+ if (!lsp)
+ return errno;
+ else {
+ int err = tzloadbody(name, sp, doextend, lsp);
+ free(lsp);
+ return err;
+ }
+}
+
+static bool
+typesequiv(const struct state *sp, int a, int b)
+{
+ bool result;
+
+ if (sp == NULL ||
+ a < 0 || a >= sp->typecnt ||
+ b < 0 || b >= sp->typecnt)
+ result = false;
+ else {
+ const struct ttinfo * ap = &sp->ttis[a];
+ const struct ttinfo * bp = &sp->ttis[b];
+ result = (ap->tt_utoff == bp->tt_utoff
+  && ap->tt_isdst == bp->tt_isdst
+  && ap->tt_ttisstd == bp->tt_ttisstd
+  && ap->tt_ttisut == bp->tt_ttisut
+  && (strcmp(&sp->chars[ap->tt_desigidx],
+     &sp->chars[bp->tt_desigidx])
+      == 0));
+ }
+ return result;
+}
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*
+** Given a pointer into a timezone string, scan until a character that is not
+** a valid character in a time zone abbreviation is found.
+** Return a pointer to that character.
+*/
+
+static ATTRIBUTE_PURE const char *
+getzname(const char *strp)
+{
+ char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
+}
+
+/*
+** Given a pointer into an extended timezone string, scan until the ending
+** delimiter of the time zone abbreviation is located.
+** Return a pointer to the delimiter.
+**
+** As with getzname above, the legal character set is actually quite
+** restricted, with other characters producing undefined results.
+** We don't do any checking here; checking is done later in common-case code.
+*/
+
+static ATTRIBUTE_PURE const char *
+getqzname(const char *strp, const int delim)
+{
+ int c;
+
+ while ((c = *strp) != '\0' && c != delim)
+ ++strp;
+ return strp;
+}
+
+/*
+** Given a pointer into a timezone string, extract a number from that string.
+** Check that the number is within a specified range; if it is not, return
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *
+getnum(const char *strp, int *const nump, const int min, const int max)
+{
+ char c;
+ int num;
+
+ if (strp == NULL || !is_digit(c = *strp)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ num = 0;
+ do {
+ num = num * 10 + (c - '0');
+ if (num > max) {
+ errno = EOVERFLOW;
+ return NULL; /* illegal value */
+ }
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min) {
+ errno = EINVAL;
+ return NULL; /* illegal value */
+ }
+ *nump = num;
+ return strp;
+}
+
+/*
+** Given a pointer into a timezone 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(const char *strp, int_fast32_t *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 * (int_fast32_t) SECSPERHOUR;
+ if (*strp == ':') {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':') {
+ ++strp;
+ /* '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 timezone 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(const char *strp, int_fast32_t *const offsetp)
+{
+ bool neg = false;
+
+ if (*strp == '-') {
+ neg = true;
+ ++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 timezone string, extract a rule in the form
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *
+getrule(const char *strp, 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 = getoffset(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*
+** Given a year, a rule, and the offset from UT at the time that rule takes
+** effect, calculate the year-relative time that rule takes effect.
+*/
+
+static int_fast32_t
+transtime(const int year, const struct rule *const rulep,
+  const int_fast32_t offset)
+{
+ bool leapyear;
+ int_fast32_t value;
+ 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 = (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 = rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+ /*
+ ** Mm.n.d - nth "dth day" of month m.
+ */
+
+ /*
+ ** 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;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[leapyear][i] * SECSPERDAY;
+ break;
+ }
+
+ /*
+ ** "value" is the year-relative time of 00:00:00 UT on the day in
+ ** question. To get the year-relative time of the specified local
+ ** time on that day, add the transition time and the current offset
+ ** from UT.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static bool
+tzparse(const char *name, struct state *sp, bool lastditch)
+{
+ const char * stdname;
+ const char * dstname;
+ size_t stdlen;
+ size_t dstlen;
+ size_t charcnt;
+ int_fast32_t stdoffset;
+ int_fast32_t dstoffset;
+ char * cp;
+ bool load_ok;
+
+ dstname = NULL; /* XXX gcc */
+ stdname = name;
+ if (lastditch) {
+ stdlen = sizeof gmt - 1;
+ name += stdlen;
+ stdoffset = 0;
+ } else {
+ if (*name == '<') {
+ name++;
+ stdname = name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+  return false;
+ stdlen = name - stdname;
+ name++;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ }
+ if (!stdlen)
+ return false;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return false;
+ }
+ charcnt = stdlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
+ load_ok = tzload(TZDEFRULES, sp, false) == 0;
+ if (!load_ok)
+ sp->leapcnt = 0; /* so, we're off a little */
+ if (*name != '\0') {
+ if (*name == '<') {
+ dstname = ++name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return false;
+ dstlen = name - dstname;
+ name++;
+ } else {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST abbr. */
+ }
+ if (!dstlen)
+  return false;
+ charcnt += dstlen + 1;
+ if (sizeof sp->chars < charcnt)
+  return false;
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+  return false;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == '\0' && !load_ok)
+ name = TZDEFRULESTRING;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ int year;
+ int yearlim;
+ int timecnt;
+ time_t janfirst;
+ int_fast32_t janoffset = 0;
+ int yearbeg;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return false;
+ if (*name++ != ',')
+ return false;
+ if ((name = getrule(name, &end)) == NULL)
+ return false;
+ if (*name != '\0')
+ return false;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR forward.
+ */
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true,
+    (int)(stdlen + 1));
+ sp->defaulttype = 0;
+ timecnt = 0;
+ janfirst = 0;
+ yearbeg = EPOCH_YEAR;
+
+ do {
+  int_fast32_t yearsecs
+    = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
+  yearbeg--;
+  if (increment_overflow_time(&janfirst, -yearsecs)) {
+    janoffset = -yearsecs;
+    break;
+  }
+ } while (EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg);
+
+ yearlim = yearbeg + YEARSPERREPEAT + 1;
+ for (year = yearbeg; year < yearlim; year++) {
+ int_fast32_t
+  starttime = transtime(year, &start, stdoffset),
+  endtime = transtime(year, &end, dstoffset);
+ int_fast32_t
+  yearsecs = (year_lengths[isleap(year)]
+      * SECSPERDAY);
+ bool reversed = endtime < starttime;
+ if (reversed) {
+ int_fast32_t swap = starttime;
+ starttime = endtime;
+ endtime = swap;
+ }
+ if (reversed
+    || (starttime < endtime
+ && (endtime - starttime
+    < (yearsecs
+       + (stdoffset - dstoffset))))) {
+ if (TZ_MAX_TIMES - 2 < timecnt)
+ break;
+ sp->ats[timecnt] = janfirst;
+ if (! increment_overflow_time
+    (&sp->ats[timecnt],
+     janoffset + starttime))
+  sp->types[timecnt++] = !reversed;
+ sp->ats[timecnt] = janfirst;
+ if (! increment_overflow_time
+    (&sp->ats[timecnt],
+     janoffset + endtime)) {
+  sp->types[timecnt++] = reversed;
+  yearlim = year + YEARSPERREPEAT + 1;
+ }
+ }
+ if (increment_overflow_time
+    (&janfirst, janoffset + yearsecs))
+ break;
+ janoffset = 0;
+ }
+ sp->timecnt = timecnt;
+ if (! timecnt) {
+ sp->ttis[0] = sp->ttis[1];
+ sp->typecnt = 1; /* Perpetual DST.  */
+ } else if (YEARSPERREPEAT < year - yearbeg)
+ sp->goback = sp->goahead = true;
+ } else {
+ int_fast32_t theirstdoffset;
+ int_fast32_t theirdstoffset;
+ int_fast32_t theiroffset;
+ bool isdst;
+ int i;
+ int j;
+
+ if (*name != '\0')
+ return false;
+ /*
+ ** 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_utoff;
+ 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_utoff;
+ 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_ttisut) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If daylight saving time is in
+ ** effect, and the transition time was
+ ** not specified as standard time, add
+ ** the daylight saving 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] += (time_t)
+    (dstoffset - theirdstoffset);
+ } else {
+ sp->ats[i] += (time_t)
+    (stdoffset - theirstdoffset);
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_utoff;
+ if (sp->ttis[j].tt_isdst)
+ theirstdoffset = theiroffset;
+ else theirdstoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ */
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true,
+    (int)(stdlen + 1));
+ sp->typecnt = 2;
+ sp->defaulttype = 0;
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], 0, false, 0);
+ sp->defaulttype = 0;
+ }
+ sp->charcnt = (int)charcnt;
+ cp = sp->chars;
+ (void) memcpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ (void) memcpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return true;
+}
+
+static void
+gmtload(struct state *const sp)
+{
+ if (tzload(gmt, sp, true) != 0)
+ (void) tzparse(gmt, sp, true);
+}
+
+static int
+zoneinit(struct state *sp, char const *name)
+{
+ if (name && ! name[0]) {
+ /*
+ ** User wants it fast rather than right.
+ */
+ sp->leapcnt = 0; /* so, we're off a little */
+ sp->timecnt = 0;
+ sp->typecnt = 1;
+ sp->charcnt = 0;
+ sp->goback = sp->goahead = false;
+ init_ttinfo(&sp->ttis[0], 0, false, 0);
+ strcpy(sp->chars, gmt);
+ sp->defaulttype = 0;
+ return 0;
+ } else {
+ int err = tzload(name, sp, true);
+ if (err != 0 && name && name[0] != ':' &&
+    tzparse(name, sp, false))
+ err = 0;
+ if (err == 0)
+ scrub_abbrs(sp);
+ return err;
+ }
+}
+
+static void
+tzsetlcl(char const *name)
+{
+ struct state *sp = __lclptr;
+ int lcl = name ? strlen(name) < sizeof lcl_TZname : -1;
+ if (lcl < 0 ? lcl_is_set < 0
+    : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0)
+ return;
+
+ if (! sp)
+ __lclptr = sp = malloc(sizeof *__lclptr);
+ if (sp) {
+ if (zoneinit(sp, name) != 0)
+ zoneinit(sp, "");
+ if (0 < lcl)
+ strcpy(lcl_TZname, name);
+ }
+ settzname();
+ lcl_is_set = lcl;
+}
+
+#ifdef STD_INSPIRED
+void
+tzsetwall(void)
+{
+ rwlock_wrlock(&__lcl_lock);
+ tzsetlcl(NULL);
+ rwlock_unlock(&__lcl_lock);
+}
+#endif
+
+void
+tzset_unlocked(void)
+{
+ tzsetlcl(getenv("TZ"));
+}
+
+void
+tzset(void)
+{
+ rwlock_wrlock(&__lcl_lock);
+ tzset_unlocked();
+ rwlock_unlock(&__lcl_lock);
+}
+
+static void
+gmtcheck(void)
+{
+ static bool gmt_is_set;
+ rwlock_wrlock(&__lcl_lock);
+ if (! gmt_is_set) {
+ gmtptr = malloc(sizeof *gmtptr);
+ if (gmtptr)
+ gmtload(gmtptr);
+ gmt_is_set = true;
+ }
+ rwlock_unlock(&__lcl_lock);
+}
+
+#if NETBSD_INSPIRED
+
+timezone_t
+tzalloc(const char *name)
+{
+ timezone_t sp = malloc(sizeof *sp);
+ if (sp) {
+ int err = zoneinit(sp, name);
+ if (err != 0) {
+ free(sp);
+ errno = err;
+ return NULL;
+ }
+ }
+ return sp;
+}
+
+void
+tzfree(timezone_t sp)
+{
+ free(sp);
+}
+
+/*
+** NetBSD 6.1.4 has ctime_rz, but omit it because POSIX says ctime and
+** ctime_r are obsolescent and have potential security problems that
+** ctime_rz would share.  Callers can instead use localtime_rz + strftime.
+**
+** NetBSD 6.1.4 has tzgetname, but omit it because it doesn't work
+** in zones with three or more time zone abbreviations.
+** Callers can instead use localtime_rz + strftime.
+*/
+
+#endif
+
+/*
+** 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.)
+**
+** If successful and SETNAME is nonzero,
+** set the applicable parts of tzname, timezone and altzone;
+** however, it's OK to omit this step if the timezone is POSIX-compatible,
+** since in that case tzset should have already done this step correctly.
+** SETNAME's type is intfast32_t for compatibility with gmtsub,
+** but it is actually a boolean and its value should be 0 or 1.
+*/
+
+/*ARGSUSED*/
+static struct tm *
+localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
+ struct tm *const tmp)
+{
+ const struct ttinfo * ttisp;
+ int i;
+ struct tm * result;
+ const time_t t = *timep;
+
+ if (sp == NULL) {
+ /* Don't bother to set tzname etc.; tzset has already done it.  */
+ return gmtsub(gmtptr, timep, 0, tmp);
+ }
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ time_t newt = t;
+ time_t seconds;
+ time_t years;
+
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+ years = (time_t)((seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT);
+ seconds = (time_t)(years * AVGSECSPERYEAR);
+ if (t < sp->ats[0])
+ newt += seconds;
+ else newt -= seconds;
+ if (newt < sp->ats[0] ||
+ newt > sp->ats[sp->timecnt - 1]) {
+ errno = EINVAL;
+ return NULL; /* "cannot happen" */
+ }
+ result = localsub(sp, &newt, setname, tmp);
+ if (result) {
+ int_fast64_t newy;
+
+ newy = result->tm_year;
+ if (t < sp->ats[0])
+ newy -= years;
+ else newy += years;
+ if (! (INT_MIN <= newy && newy <= INT_MAX)) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+ result->tm_year = (int)newy;
+ }
+ return result;
+ }
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ i = sp->defaulttype;
+ } else {
+ int lo = 1;
+ int hi = sp->timecnt;
+
+ while (lo < hi) {
+ int mid = (lo + hi) / 2;
+
+ if (t < sp->ats[mid])
+ hi = mid;
+ else lo = mid + 1;
+ }
+ i = (int) sp->types[lo - 1];
+ }
+ ttisp = &sp->ttis[i];
+ /*
+ ** To get (wrong) behavior that's compatible with System V Release 2.0
+ ** you'd replace the statement below with
+ ** t += ttisp->tt_utoff;
+ ** timesub(&t, 0L, sp, tmp);
+ */
+ result = timesub(&t, ttisp->tt_utoff, sp, tmp);
+ if (result) {
+ result->tm_isdst = ttisp->tt_isdst;
+#ifdef TM_ZONE
+ result->TM_ZONE = __UNCONST(&sp->chars[ttisp->tt_desigidx]);
+#endif /* defined TM_ZONE */
+ if (setname)
+ update_tzname_etc(sp, ttisp);
+ }
+ return result;
+}
+
+#if NETBSD_INSPIRED
+
+struct tm *
+localtime_rz(timezone_t sp, time_t const *timep, struct tm *tmp)
+{
+ return localsub(sp, timep, 0, tmp);
+}
+
+#endif
+
+static struct tm *
+localtime_tzset(time_t const *timep, struct tm *tmp, bool setname)
+{
+ rwlock_wrlock(&__lcl_lock);
+ if (setname || !lcl_is_set)
+ tzset_unlocked();
+ tmp = localsub(__lclptr, timep, setname, tmp);
+ rwlock_unlock(&__lcl_lock);
+ return tmp;
+}
+
+struct tm *
+localtime(const time_t *timep)
+{
+ return localtime_tzset(timep, &tm, true);
+}
+
+struct tm *
+localtime_r(const time_t * __restrict timep, struct tm *tmp)
+{
+ return localtime_tzset(timep, tmp, true);
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static struct tm *
+gmtsub(struct state const *sp, const time_t *timep, int_fast32_t offset,
+       struct tm *tmp)
+{
+ struct tm * result;
+
+ result = timesub(timep, offset, gmtptr, tmp);
+#ifdef TM_ZONE
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "+xx" or "-xx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ if (result)
+ result->TM_ZONE = offset ? __UNCONST(wildabbr) : gmtptr ?
+    gmtptr->chars : __UNCONST(gmt);
+#endif /* defined TM_ZONE */
+ return result;
+}
+
+
+/*
+** Re-entrant version of gmtime.
+*/
+
+struct tm *
+gmtime_r(const time_t *timep, struct tm *tmp)
+{
+ gmtcheck();
+ return gmtsub(NULL, timep, 0, tmp);
+}
+
+struct tm *
+gmtime(const time_t *timep)
+{
+ return gmtime_r(timep, &tm);
+}
+#ifdef STD_INSPIRED
+
+struct tm *
+offtime(const time_t *timep, long offset)
+{
+ gmtcheck();
+ return gmtsub(gmtptr, timep, (int_fast32_t)offset, &tm);
+}
+
+struct tm *
+offtime_r(const time_t *timep, long offset, struct tm *tmp)
+{
+ gmtcheck();
+ return gmtsub(NULL, timep, (int_fast32_t)offset, tmp);
+}
+
+#endif /* defined STD_INSPIRED */
+
+#if TZ_TIME_T
+
+# if USG_COMPAT
+#  define daylight 0
+#  define timezone 0
+# endif
+# ifndef ALTZONE
+#  define altzone 0
+# endif
+
+/* Convert from the underlying system's time_t to the ersatz time_tz,
+   which is called 'time_t' in this file.  Typically, this merely
+   converts the time's integer width.  On some platforms, the system
+   time is local time not UT, or uses some epoch other than the POSIX
+   epoch.
+
+   Although this code appears to define a function named 'time' that
+   returns time_t, the macros in private.h cause this code to actually
+   define a function named 'tz_time' that returns tz_time_t.  The call
+   to sys_time invokes the underlying system's 'time' function.  */
+
+time_t
+time(time_t *p)
+{
+  time_t r = sys_time(0);
+  if (r != (time_t) -1) {
+    int_fast32_t offset = EPOCH_LOCAL ? (daylight ? timezone : altzone) : 0;
+    if (increment_overflow32(&offset, -EPOCH_OFFSET)
+ || increment_overflow_time (&r, offset)) {
+      errno = EOVERFLOW;
+      r = -1;
+    }
+  }
+  if (p)
+    *p = r;
+  return r;
+}
+#endif
+
+/*
+** Return the number of leap years through the end of the given year
+** where, to make the math easy, the answer for year zero is defined as zero.
+*/
+static int
+leaps_thru_end_of_nonneg(int y)
+{
+ return y / 4 - y / 100 + y / 400;
+}
+
+static int ATTRIBUTE_PURE
+leaps_thru_end_of(const int y)
+{
+ return (y < 0
+ ? -1 - leaps_thru_end_of_nonneg(-1 - y)
+ : leaps_thru_end_of_nonneg(y));
+}
+
+static struct tm *
+timesub(const time_t *timep, int_fast32_t offset,
+    const struct state *sp, struct tm *tmp)
+{
+ const struct lsinfo * lp;
+ time_t tdays;
+ int idays; /* unsigned would be so 2003 */
+ int_fast64_t rem;
+ int y;
+ const int * ip;
+ int_fast64_t corr;
+ int hit;
+ int i;
+
+ corr = 0;
+ hit = false;
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans) {
+ corr = lp->ls_corr;
+ hit = (*timep == lp->ls_trans
+       && (i == 0 ? 0 : lp[-1].ls_corr) < corr);
+ break;
+ }
+ }
+ y = EPOCH_YEAR;
+ tdays = (time_t)(*timep / SECSPERDAY);
+ rem = *timep % SECSPERDAY;
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
+ int newy;
+ time_t tdelta;
+ int idelta;
+ int leapdays;
+
+ tdelta = tdays / DAYSPERLYEAR;
+ if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta)
+       && tdelta <= INT_MAX))
+ goto out_of_range;
+ _DIAGASSERT(__type_fit(int, tdelta));
+ idelta = (int)tdelta;
+ if (idelta == 0)
+ idelta = (tdays < 0) ? -1 : 1;
+ newy = y;
+ if (increment_overflow(&newy, idelta))
+ goto out_of_range;
+ leapdays = leaps_thru_end_of(newy - 1) -
+ leaps_thru_end_of(y - 1);
+ tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
+ tdays -= leapdays;
+ y = newy;
+ }
+ /*
+ ** Given the range, we can now fearlessly cast...
+ */
+ idays = (int) tdays;
+ rem += offset - corr;
+ while (rem < 0) {
+ rem += SECSPERDAY;
+ --idays;
+ }
+ while (rem >= SECSPERDAY) {
+ rem -= SECSPERDAY;
+ ++idays;
+ }
+ while (idays < 0) {
+ if (increment_overflow(&y, -1))
+ goto out_of_range;
+ idays += year_lengths[isleap(y)];
+ }
+ while (idays >= year_lengths[isleap(y)]) {
+ idays -= year_lengths[isleap(y)];
+ if (increment_overflow(&y, 1))
+ goto out_of_range;
+ }
+ tmp->tm_year = y;
+ if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+ goto out_of_range;
+ tmp->tm_yday = idays;
+ /*
+ ** The "extra" mods below avoid overflow problems.
+ */
+ tmp->tm_wday = EPOCH_WDAY +
+ ((y - EPOCH_YEAR) % DAYSPERWEEK) *
+ (DAYSPERNYEAR % DAYSPERWEEK) +
+ leaps_thru_end_of(y - 1) -
+ leaps_thru_end_of(EPOCH_YEAR - 1) +
+ idays;
+ tmp->tm_wday %= DAYSPERWEEK;
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ tmp->tm_hour = (int) (rem / SECSPERHOUR);
+ rem %= SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+ /*
+ ** A positive leap second requires a special
+ ** representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ ip = mon_lengths[isleap(y)];
+ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
+ idays -= ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (idays + 1);
+ tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+ tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+ return tmp;
+out_of_range:
+ errno = EOVERFLOW;
+ return NULL;
+}
+
+char *
+ctime(const time_t *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))
+*/
+ struct tm *tmp = localtime(timep);
+ return tmp ? asctime(tmp) : NULL;
+}
+
+char *
+ctime_r(const time_t *timep, char *buf)
+{
+ struct tm mytm;
+ struct tm *tmp = localtime_r(timep, &mytm);
+ return tmp ? asctime_r(tmp, buf) : NULL;
+}
+
+char *
+ctime_rz(const timezone_t sp, const time_t * timep, char *buf)
+{
+ struct tm mytm, *rtm;
+
+ rtm = localtime_rz(sp, timep, &mytm);
+ if (rtm == NULL)
+ return NULL;
+ return asctime_r(rtm, buf);
+}
+
+/*
+** Adapted from code provided by Robert Elz, who writes:
+** The "best" way to do mktime I think is based on an idea of Bob
+** Kridle's (so its said...) from a long time ago.
+** 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 ((time_t)-1)
+#endif /* !defined WRONG */
+
+/*
+** Normalize logic courtesy Paul Eggert.
+*/
+
+static bool
+increment_overflow(int *ip, int j)
+{
+ int const i = *ip;
+
+ /*
+ ** If i >= 0 there can only be overflow if i + j > INT_MAX
+ ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
+ ** If i < 0 there can only be overflow if i + j < INT_MIN
+ ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
+ */
+ if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i))
+ return true;
+ *ip += j;
+ return false;
+}
+
+static bool
+increment_overflow32(int_fast32_t *const lp, int const m)
+{
+ int_fast32_t const l = *lp;
+
+ if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l))
+ return true;
+ *lp += m;
+ return false;
+}
+
+static bool
+increment_overflow_time(time_t *tp, int_fast32_t j)
+{
+ /*
+ ** This is like
+ ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
+ ** except that it does the right thing even if *tp + j would overflow.
+ */
+ if (! (j < 0
+       ? (TYPE_SIGNED(time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp)
+       : *tp <= TIME_T_MAX - j))
+ return true;
+ *tp += j;
+ return false;
+}
+
+static bool
+normalize_overflow(int *const tensptr, int *const unitsptr, const int base)
+{
+ int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static bool
+normalize_overflow32(int_fast32_t *tensptr, int *unitsptr, int base)
+{
+ int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow32(tensptr, tensdelta);
+}
+
+static int
+tmcomp(const struct tm *const atmp,
+       const struct tm *const btmp)
+{
+ int result;
+
+ if (atmp->tm_year != btmp->tm_year)
+ return atmp->tm_year < btmp->tm_year ? -1 : 1;
+ if ((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(struct tm *const tmp,
+ struct tm *(*funcp)(struct state const *, time_t const *,
+     int_fast32_t, struct tm *),
+ struct state const *sp,
+ const int_fast32_t offset,
+ bool *okayp,
+ bool do_norm_secs)
+{
+ int dir;
+ int i, j;
+ int saved_seconds;
+ int_fast32_t li;
+ time_t lo;
+ time_t hi;
+#ifdef NO_ERROR_IN_DST_GAP
+ time_t ilo;
+#endif
+ int_fast32_t y;
+ time_t newt;
+ time_t t;
+ struct tm yourtm, mytm;
+
+ *okayp = false;
+ yourtm = *tmp;
+#ifdef NO_ERROR_IN_DST_GAP
+again:
+#endif
+ if (do_norm_secs) {
+ if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+    SECSPERMIN))
+ goto out_of_range;
+ }
+ if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+ goto out_of_range;
+ if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+ goto out_of_range;
+ y = yourtm.tm_year;
+ if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR))
+ goto out_of_range;
+ /*
+ ** Turn y into an actual year number for now.
+ ** It is converted back to an offset from TM_YEAR_BASE later.
+ */
+ if (increment_overflow32(&y, TM_YEAR_BASE))
+ goto out_of_range;
+ while (yourtm.tm_mday <= 0) {
+ if (increment_overflow32(&y, -1))
+ goto out_of_range;
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(li)];
+ }
+ while (yourtm.tm_mday > DAYSPERLYEAR) {
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday -= year_lengths[isleap(li)];
+ if (increment_overflow32(&y, 1))
+ goto out_of_range;
+ }
+ for ( ; ; ) {
+ i = mon_lengths[isleap(y)][yourtm.tm_mon];
+ if (yourtm.tm_mday <= i)
+ break;
+ yourtm.tm_mday -= i;
+ if (++yourtm.tm_mon >= MONSPERYEAR) {
+ yourtm.tm_mon = 0;
+ if (increment_overflow32(&y, 1))
+ goto out_of_range;
+ }
+ }
+ if (increment_overflow32(&y, -TM_YEAR_BASE))
+ goto out_of_range;
+ if (! (INT_MIN <= y && y <= INT_MAX))
+ goto out_of_range;
+ yourtm.tm_year = (int)y;
+ if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
+ saved_seconds = 0;
+ else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+ /*
+ ** We can't set tm_sec to 0, because that might push the
+ ** time below the minimum representable time.
+ ** Set tm_sec to 59 instead.
+ ** This assumes that the minimum representable time is
+ ** not in the same minute that a leap second was deleted from,
+ ** which is a safer assumption than using 58 would be.
+ */
+ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+ goto out_of_range;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = SECSPERMIN - 1;
+ } else {
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ }
+ /*
+ ** Do a binary search (this works whatever time_t's type is).
+ */
+ lo = TIME_T_MIN;
+ hi = TIME_T_MAX;
+#ifdef NO_ERROR_IN_DST_GAP
+ ilo = lo;
+#endif
+ for ( ; ; ) {
+ t = lo / 2 + hi / 2;
+ if (t < lo)
+ t = lo;
+ else if (t > hi)
+ t = hi;
+ if (! funcp(sp, &t, offset, &mytm)) {
+ /*
+ ** Assume that t is too extreme to be represented in
+ ** a struct tm; arrange things so that it is less
+ ** extreme on the next pass.
+ */
+ dir = (t > 0) ? 1 : -1;
+ } else dir = tmcomp(&mytm, &yourtm);
+ if (dir != 0) {
+ if (t == lo) {
+ if (t == TIME_T_MAX)
+ goto out_of_range;
+ ++t;
+ ++lo;
+ } else if (t == hi) {
+ if (t == TIME_T_MIN)
+ goto out_of_range;
+ --t;
+ --hi;
+ }
+#ifdef NO_ERROR_IN_DST_GAP
+ if (ilo != lo && lo - 1 == hi && yourtm.tm_isdst < 0 &&
+    do_norm_secs) {
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ time_t off;
+ if (sp->ttis[j].tt_isdst ==
+    sp->ttis[i].tt_isdst)
+ continue;
+ off = sp->ttis[j].tt_utoff -
+    sp->ttis[i].tt_utoff;
+ yourtm.tm_sec += off < 0 ?
+    -off : off;
+ goto again;
+ }
+ }
+ }
+#endif
+ if (lo > hi)
+ goto invalid;
+ if (dir > 0)
+ hi = t;
+ else lo = t;
+ continue;
+ }
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ if (mytm.TM_GMTOFF != yourtm.TM_GMTOFF
+    && (yourtm.TM_GMTOFF < 0
+ ? (-SECSPERDAY <= yourtm.TM_GMTOFF
+   && (mytm.TM_GMTOFF <=
+       (/*CONSTCOND*/SMALLEST (INT_FAST32_MAX, LONG_MAX)
+ + yourtm.TM_GMTOFF)))
+ : (yourtm.TM_GMTOFF <= SECSPERDAY
+   && ((/*CONSTCOND*/BIGGEST (INT_FAST32_MIN, LONG_MIN)
+ + yourtm.TM_GMTOFF)
+       <= mytm.TM_GMTOFF)))) {
+  /* MYTM matches YOURTM except with the wrong UT offset.
+     YOURTM.TM_GMTOFF is plausible, so try it instead.
+     It's OK if YOURTM.TM_GMTOFF contains uninitialized data,
+     since the guess gets checked.  */
+  time_t altt = t;
+  int_fast32_t diff = (int_fast32_t)
+      (mytm.TM_GMTOFF - yourtm.TM_GMTOFF);
+  if (!increment_overflow_time(&altt, diff)) {
+    struct tm alttm;
+    if (! funcp(sp, &altt, offset, &alttm)
+ && alttm.tm_isdst == mytm.tm_isdst
+ && alttm.TM_GMTOFF == yourtm.TM_GMTOFF
+ && tmcomp(&alttm, &yourtm)) {
+      t = altt;
+      mytm = alttm;
+    }
+  }
+ }
+#endif
+ 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.
+ */
+ if (sp == NULL)
+ goto invalid;
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+ continue;
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+ continue;
+ newt = (time_t)(t + sp->ttis[j].tt_utoff -
+    sp->ttis[i].tt_utoff);
+ if (! funcp(sp, &newt, offset, &mytm))
+ continue;
+ if (tmcomp(&mytm, &yourtm) != 0)
+ continue;
+ if (mytm.tm_isdst != yourtm.tm_isdst)
+ continue;
+ /*
+ ** We have a match.
+ */
+ t = newt;
+ goto label;
+ }
+ }
+ goto invalid;
+ }
+label:
+ newt = t + saved_seconds;
+ if ((newt < t) != (saved_seconds < 0))
+ goto out_of_range;
+ t = newt;
+ if (funcp(sp, &t, offset, tmp)) {
+ *okayp = true;
+ return t;
+ }
+out_of_range:
+ errno = EOVERFLOW;
+ return WRONG;
+invalid:
+ errno = EINVAL;
+ return WRONG;
+}
+
+static time_t
+time2(struct tm * const tmp,
+      struct tm *(*funcp)(struct state const *, time_t const *,
+  int_fast32_t, struct tm *),
+      struct state const *sp,
+      const int_fast32_t offset,
+      bool *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, sp, offset, okayp, false);
+ return *okayp ? t : time2sub(tmp, funcp, sp, offset, okayp, true);
+}
+
+static time_t
+time1(struct tm *const tmp,
+      struct tm *(*funcp) (struct state const *, time_t const *,
+   int_fast32_t, struct tm *),
+      struct state const *sp,
+      const int_fast32_t offset)
+{
+ time_t t;
+ int samei, otheri;
+ int sameind, otherind;
+ int i;
+ int nseen;
+ int save_errno;
+ char seen[TZ_MAX_TYPES];
+ unsigned char types[TZ_MAX_TYPES];
+ bool okay;
+
+ if (tmp == NULL) {
+ errno = EINVAL;
+ return WRONG;
+ }
+ if (tmp->tm_isdst > 1)
+ tmp->tm_isdst = 1;
+ save_errno = errno;
+ t = time2(tmp, funcp, sp, offset, &okay);
+ if (okay) {
+ errno = save_errno;
+ return t;
+ }
+ if (tmp->tm_isdst < 0)
+#ifdef PCTS
+ /*
+ ** POSIX Conformance Test Suite code courtesy Grant Sullivan.
+ */
+ tmp->tm_isdst = 0; /* reset to std and try again */
+#else
+ 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.
+ */
+ if (sp == NULL) {
+ errno = EINVAL;
+ return WRONG;
+ }
+ 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 += (int)(sp->ttis[otheri].tt_utoff -
+ sp->ttis[samei].tt_utoff);
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, sp, offset, &okay);
+ if (okay) {
+ errno = save_errno;
+ return t;
+ }
+ tmp->tm_sec -= (int)(sp->ttis[otheri].tt_utoff -
+ sp->ttis[samei].tt_utoff);
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ errno = EOVERFLOW;
+ return WRONG;
+}
+
+static time_t
+mktime_tzname(timezone_t sp, struct tm *tmp, bool setname)
+{
+ if (sp)
+ return time1(tmp, localsub, sp, setname);
+ else {
+ gmtcheck();
+ return time1(tmp, gmtsub, gmtptr, 0);
+ }
+}
+
+#if NETBSD_INSPIRED
+
+time_t
+mktime_z(timezone_t sp, struct tm *const tmp)
+{
+ return mktime_tzname(sp, tmp, false);
+}
+
+#endif
+
+time_t
+mktime(struct tm *tmp)
+{
+ time_t t;
+
+ rwlock_wrlock(&__lcl_lock);
+ tzset_unlocked();
+ t = mktime_tzname(__lclptr, tmp, true);
+ rwlock_unlock(&__lcl_lock);
+ return t;
+}
+
+#ifdef STD_INSPIRED
+
+time_t
+timelocal_z(const timezone_t sp, struct tm *const tmp)
+{
+ if (tmp != NULL)
+ tmp->tm_isdst = -1; /* in case it wasn't initialized */
+ return mktime_z(sp, tmp);
+}
+
+time_t
+timelocal(struct tm *tmp)
+{
+ if (tmp != NULL)
+ tmp->tm_isdst = -1; /* in case it wasn't initialized */
+ return mktime(tmp);
+}
+
+time_t
+timegm(struct tm *tmp)
+{
+
+ return timeoff(tmp, 0);
+}
+
+time_t
+timeoff(struct tm *tmp, long offset)
+{
+ if (tmp)
+ tmp->tm_isdst = 0;
+ gmtcheck();
+ return time1(tmp, gmtsub, gmtptr, (int_fast32_t)offset);
+}
+
+#endif /* defined STD_INSPIRED */
+
+/*
+** XXX--is the below the right way to conditionalize??
+*/
+
+#ifdef STD_INSPIRED
+
+/*
+** IEEE Std 1003.1 (POSIX) says 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 int_fast64_t
+leapcorr(const timezone_t sp, time_t t)
+{
+ struct lsinfo const * lp;
+ int i;
+
+ i = sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (t >= lp->ls_trans)
+ return lp->ls_corr;
+ }
+ return 0;
+}
+
+NETBSD_INSPIRED_EXTERN time_t
+time2posix_z(timezone_t sp, time_t t)
+{
+ return (time_t)(t - leapcorr(sp, t));
+}
+
+time_t
+time2posix(time_t t)
+{
+ rwlock_wrlock(&__lcl_lock);
+ if (!lcl_is_set)
+ tzset_unlocked();
+ if (__lclptr)
+ t = (time_t)(t - leapcorr(__lclptr, t));
+ rwlock_unlock(&__lcl_lock);
+ return t;
+}
+
+NETBSD_INSPIRED_EXTERN time_t
+posix2time_z(timezone_t sp, time_t t)
+{
+ time_t x;
+ time_t y;
+
+ /*
+ ** 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 = (time_t)(t + leapcorr(sp, t));
+ y = (time_t)(x - leapcorr(sp, x));
+ if (y < t) {
+ do {
+ x++;
+ y = (time_t)(x - leapcorr(sp, x));
+ } while (y < t);
+ x -= y != t;
+ } else if (y > t) {
+ do {
+ --x;
+ y = (time_t)(x - leapcorr(sp, x));
+ } while (y > t);
+ x += y != t;
+ }
+ return x;
+}
+
+time_t
+posix2time(time_t t)
+{
+ rwlock_wrlock(&__lcl_lock);
+ if (!lcl_is_set)
+ tzset_unlocked();
+ if (__lclptr)
+ t = posix2time_z(__lclptr, t);
+ rwlock_unlock(&__lcl_lock);
+ return t;
+}
+
+#endif /* defined STD_INSPIRED */
diff --git a/winsup/cygwin/tzcode/namespace.h b/winsup/cygwin/tzcode/namespace.h
new file mode 100644
index 000000000..e69de29bb
diff --git a/winsup/cygwin/tzcode/private.h b/winsup/cygwin/tzcode/private.h
new file mode 100644
index 000000000..830750ad5
--- /dev/null
+++ b/winsup/cygwin/tzcode/private.h
@@ -0,0 +1,795 @@
+/* Private header for tzdb code.  */
+
+/* $NetBSD: private.h,v 1.55 2019/04/04 22:03:23 christos Exp $ */
+
+#ifndef PRIVATE_H
+#define PRIVATE_H
+
+/* NetBSD defaults */
+#define TM_GMTOFF tm_gmtoff
+#define TM_ZONE tm_zone
+#define STD_INSPIRED 1
+#define HAVE_LONG_DOUBLE 1
+
+/* For when we build zic as a host tool. */
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** zdump has been made independent of the rest of the time
+** conversion package to increase confidence in the verification it provides.
+** You can use zdump to help in verifying other implementations.
+** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
+*/
+#ifndef USE_LTZ
+# define USE_LTZ 1
+#endif
+
+/* This string was in the Factory zone through version 2016f.  */
+#define GRANDPARENTED "Local time zone must be set--see zic manual page"
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'.
+*/
+
+#ifndef HAVE_DECL_ASCTIME_R
+#define HAVE_DECL_ASCTIME_R 1
+#endif
+
+#if !defined HAVE_GENERIC && defined __has_extension
+# if __has_extension(c_generic_selections)
+#  define HAVE_GENERIC 1
+# else
+#  define HAVE_GENERIC 0
+# endif
+#endif
+/* _Generic is buggy in pre-4.9 GCC.  */
+#if !defined HAVE_GENERIC && defined __GNUC__
+# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
+#endif
+#ifndef HAVE_GENERIC
+# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
+#endif
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT 0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+#define HAVE_INCOMPATIBLE_CTIME_R 0
+#endif
+
+#ifndef HAVE_LINK
+#define HAVE_LINK 1
+#endif /* !defined HAVE_LINK */
+
+#ifndef HAVE_POSIX_DECLS
+#define HAVE_POSIX_DECLS 1
+#endif
+
+#ifndef HAVE_STDBOOL_H
+#define HAVE_STDBOOL_H (199901 <= __STDC_VERSION__)
+#endif
+
+#ifndef HAVE_STRDUP
+#define HAVE_STRDUP 1
+#endif
+
+#ifndef HAVE_STRTOLL
+#define HAVE_STRTOLL 1
+#endif
+
+#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 1
+#endif /* !defined HAVE_UTMPX_H */
+
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#define asctime_r _incompatible_asctime_r
+#define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems.  */
+#define _GNU_SOURCE 1
+/* Fix asctime_r on Solaris 11.  */
+#define _POSIX_PTHREAD_SEMANTICS 1
+/* Enable strtoimax on pre-C99 Solaris 11.  */
+#define __EXTENSIONS__ 1
+
+/* To avoid having 'stat' fail unnecessarily with errno == EOVERFLOW,
+   enable large files on GNUish systems ...  */
+#ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+#endif
+/* ... and on AIX ...  */
+#define _LARGE_FILES 1
+/* ... and enable large inode numbers on Mac OS X 10.5 and later.  */
+#define _DARWIN_USE_64_BIT_INODE 1
+
+/*
+** Nested includes
+*/
+
+#ifndef __NetBSD__
+/* Avoid clashes with NetBSD by renaming NetBSD's declarations.  */
+#define localtime_rz sys_localtime_rz
+#define mktime_z sys_mktime_z
+#define posix2time_z sys_posix2time_z
+#define time2posix_z sys_time2posix_z
+#define timezone_t sys_timezone_t
+#define tzalloc sys_tzalloc
+#define tzfree sys_tzfree
+#include <time.h>
+#undef localtime_rz
+#undef mktime_z
+#undef posix2time_z
+#undef time2posix_z
+#undef timezone_t
+#undef tzalloc
+#undef tzfree
+#else
+#include "time.h"
+#endif
+
+#include <sys/types.h> /* for time_t */
+#include <string.h>
+#include <limits.h> /* for CHAR_BIT et al. */
+#include <stdlib.h>
+
+#include <errno.h>
+
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+#ifndef ENOTSUP
+# define ENOTSUP EINVAL
+#endif
+#ifndef EOVERFLOW
+# define EOVERFLOW EINVAL
+#endif
+
+#if HAVE_GETTEXT
+#include <libintl.h>
+#endif /* HAVE_GETTEXT */
+
+#if HAVE_UNISTD_H
+#include <unistd.h> /* for R_OK, and other POSIX goodness */
+#endif /* HAVE_UNISTD_H */
+
+#ifndef HAVE_STRFTIME_L
+# if _POSIX_VERSION < 200809
+#  define HAVE_STRFTIME_L 0
+# else
+#  define HAVE_STRFTIME_L 1
+# endif
+#endif
+
+#ifndef USG_COMPAT
+# ifndef _XOPEN_VERSION
+#  define USG_COMPAT 0
+# else
+#  define USG_COMPAT 1
+# endif
+#endif
+
+#ifndef HAVE_TZNAME
+# if _POSIX_VERSION < 198808 && !USG_COMPAT
+#  define HAVE_TZNAME 0
+# else
+#  define HAVE_TZNAME 1
+# endif
+#endif
+
+#ifndef R_OK
+#define R_OK 4
+#endif /* !defined R_OK */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/*
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__ and INTMAX_MAX's values depend on
+** previously-included files.  glibc 2.1 and Solaris 10 and later have
+** stdint.h, even with pre-C99 compilers.
+*/
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H \
+   (199901 <= __STDC_VERSION__ \
+    || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
+    || __CYGWIN__ || INTMAX_MAX)
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif /* !HAVE_STDINT_H */
+
+#ifndef HAVE_INTTYPES_H
+# define HAVE_INTTYPES_H HAVE_STDINT_H
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX.  */
+#ifdef __LONG_LONG_MAX__
+# ifndef LLONG_MAX
+#  define LLONG_MAX __LONG_LONG_MAX__
+# endif
+# ifndef LLONG_MIN
+#  define LLONG_MIN (-1 - LLONG_MAX)
+# endif
+#endif
+
+#ifndef INT_FAST64_MAX
+# ifdef LLONG_MAX
+typedef long long int_fast64_t;
+#  define INT_FAST64_MIN LLONG_MIN
+#  define INT_FAST64_MAX LLONG_MAX
+# else
+#  if LONG_MAX >> 31 < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#  endif
+typedef long int_fast64_t;
+#  define INT_FAST64_MIN LONG_MIN
+#  define INT_FAST64_MAX LONG_MAX
+# endif
+#endif
+
+#ifndef PRIdFAST64
+# if INT_FAST64_MAX == LLONG_MAX
+#  define PRIdFAST64 "lld"
+# else
+#  define PRIdFAST64 "ld"
+# endif
+#endif
+
+#ifndef SCNdFAST64
+# define SCNdFAST64 PRIdFAST64
+#endif
+
+#ifndef INT_FAST32_MAX
+# if INT_MAX >> 31 == 0
+typedef long int_fast32_t;
+#  define INT_FAST32_MAX LONG_MAX
+#  define INT_FAST32_MIN LONG_MIN
+# else
+typedef int int_fast32_t;
+#  define INT_FAST32_MAX INT_MAX
+#  define INT_FAST32_MIN INT_MIN
+# endif
+#endif
+
+#ifndef INTMAX_MAX
+# ifdef LLONG_MAX
+typedef long long intmax_t;
+#  if HAVE_STRTOLL
+#   define strtoimax strtoll
+#  endif
+#  define INTMAX_MAX LLONG_MAX
+#  define INTMAX_MIN LLONG_MIN
+# else
+typedef long intmax_t;
+#  define INTMAX_MAX LONG_MAX
+#  define INTMAX_MIN LONG_MIN
+# endif
+# ifndef strtoimax
+#  define strtoimax strtol
+# endif
+#endif
+
+#ifndef PRIdMAX
+# if INTMAX_MAX == LLONG_MAX
+#  define PRIdMAX "lld"
+# else
+#  define PRIdMAX "ld"
+# endif
+#endif
+
+#ifndef UINT_FAST64_MAX
+# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+typedef unsigned long long uint_fast64_t;
+# else
+#  if ULONG_MAX >> 31 >> 1 < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#  endif
+typedef unsigned long uint_fast64_t;
+# endif
+#endif
+
+#ifndef UINTMAX_MAX
+# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+typedef unsigned long long uintmax_t;
+# else
+typedef unsigned long uintmax_t;
+# endif
+#endif
+
+#ifndef PRIuMAX
+# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+#  define PRIuMAX "llu"
+# else
+#  define PRIuMAX "lu"
+# endif
+#endif
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif /* !defined INT32_MAX */
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - INT32_MAX)
+#endif /* !defined INT32_MIN */
+
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t) -1)
+#endif
+
+#if 3 <= __GNUC__
+# define ATTRIBUTE_CONST __attribute__ ((__const__))
+# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
+# define ATTRIBUTE_PURE __attribute__ ((__pure__))
+# define ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec))
+#else
+# define ATTRIBUTE_CONST /* empty */
+# define ATTRIBUTE_MALLOC /* empty */
+# define ATTRIBUTE_PURE /* empty */
+# define ATTRIBUTE_FORMAT(spec) /* empty */
+#endif
+
+#if !defined _Noreturn && __STDC_VERSION__ < 201112
+# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
+#  define _Noreturn __attribute__ ((__noreturn__))
+# else
+#  define _Noreturn
+# endif
+#endif
+
+#if __STDC_VERSION__ < 199901 && !defined restrict
+# define restrict /* empty */
+#endif
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+#ifndef EPOCH_LOCAL
+# define EPOCH_LOCAL 0
+#endif
+#ifndef EPOCH_OFFSET
+# define EPOCH_OFFSET 0
+#endif
+#ifndef RESERVE_STD_EXT_IDS
+# define RESERVE_STD_EXT_IDS 0
+#endif
+
+/* If standard C identifiers with external linkage (e.g., localtime)
+   are reserved and are not already being renamed anyway, rename them
+   as if compiling with '-Dtime_tz=time_t'.  */
+#if !defined time_tz && RESERVE_STD_EXT_IDS && USE_LTZ
+# define time_tz time_t
+#endif
+
+/*
+** Compile with -Dtime_tz=T to build the tz package with a private
+** time_t type equivalent to T rather than the system-supplied time_t.
+** This debugging feature can test unusual design decisions
+** (e.g., time_t wider than 'long', or unsigned time_t) even on
+** typical platforms.
+*/
+#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
+# define TZ_TIME_T 1
+#else
+# define TZ_TIME_T 0
+#endif
+
+#if TZ_TIME_T
+# ifdef LOCALTIME_IMPLEMENTATION
+static time_t sys_time(time_t *x) { return time(x); }
+# endif
+
+typedef time_tz tz_time_t;
+
+# undef  ctime
+# define ctime tz_ctime
+# undef  ctime_r
+# define ctime_r tz_ctime_r
+# undef  difftime
+# define difftime tz_difftime
+# undef  gmtime
+# define gmtime tz_gmtime
+# undef  gmtime_r
+# define gmtime_r tz_gmtime_r
+# undef  localtime
+# define localtime tz_localtime
+# undef  localtime_r
+# define localtime_r tz_localtime_r
+# undef  localtime_rz
+# define localtime_rz tz_localtime_rz
+# undef  mktime
+# define mktime tz_mktime
+# undef  mktime_z
+# define mktime_z tz_mktime_z
+# undef  offtime
+# define offtime tz_offtime
+# undef  posix2time
+# define posix2time tz_posix2time
+# undef  posix2time_z
+# define posix2time_z tz_posix2time_z
+# undef  strftime
+# define strftime tz_strftime
+# undef  time
+# define time tz_time
+# undef  time2posix
+# define time2posix tz_time2posix
+# undef  time2posix_z
+# define time2posix_z tz_time2posix_z
+# undef  time_t
+# define time_t tz_time_t
+# undef  timegm
+# define timegm tz_timegm
+# undef  timelocal
+# define timelocal tz_timelocal
+# undef  timeoff
+# define timeoff tz_timeoff
+# undef  tzalloc
+# define tzalloc tz_tzalloc
+# undef  tzfree
+# define tzfree tz_tzfree
+# undef  tzset
+# define tzset tz_tzset
+# undef  tzsetwall
+# define tzsetwall tz_tzsetwall
+# if HAVE_STRFTIME_L
+#  undef  strftime_l
+#  define strftime_l tz_strftime_l
+# endif
+# if HAVE_TZNAME
+#  undef  tzname
+#  define tzname tz_tzname
+# endif
+# if USG_COMPAT
+#  undef  daylight
+#  define daylight tz_daylight
+#  undef  timezone
+#  define timezone tz_timezone
+# endif
+# ifdef ALTZONE
+#  undef  altzone
+#  define altzone tz_altzone
+# endif
+
+char *ctime(time_t const *);
+char *ctime_r(time_t const *, char *);
+double difftime(time_t, time_t) ATTRIBUTE_CONST;
+size_t strftime(char *restrict, size_t, char const *restrict,
+ struct tm const *restrict);
+# if HAVE_STRFTIME_L
+size_t strftime_l(char *restrict, size_t, char const *restrict,
+  struct tm const *restrict, locale_t);
+# endif
+struct tm *gmtime(time_t const *);
+struct tm *gmtime_r(time_t const *restrict, struct tm *restrict);
+struct tm *localtime(time_t const *);
+struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
+time_t mktime(struct tm *);
+time_t time(time_t *);
+void tzset(void);
+#endif
+
+#if !HAVE_DECL_ASCTIME_R && !defined asctime_r
+extern char *asctime_r(struct tm const *restrict, char *restrict);
+#endif
+
+#ifndef HAVE_DECL_ENVIRON
+# if defined environ || defined __USE_GNU
+#  define HAVE_DECL_ENVIRON 1
+# else
+#  define HAVE_DECL_ENVIRON 0
+# endif
+#endif
+
+#if !HAVE_DECL_ENVIRON
+extern char **environ;
+#endif
+
+#if TZ_TIME_T || !HAVE_POSIX_DECLS
+# if HAVE_TZNAME
+extern char *tzname[];
+# endif
+# if USG_COMPAT
+extern long timezone;
+extern int daylight;
+# endif
+#endif
+
+#ifdef ALTZONE
+extern long altzone;
+#endif
+
+/*
+** The STD_INSPIRED functions are similar, but most also need
+** declarations if time_tz is defined.
+*/
+
+#ifdef STD_INSPIRED
+# if TZ_TIME_T || !defined tzsetwall
+void tzsetwall(void);
+# endif
+# if TZ_TIME_T || !defined offtime
+struct tm *offtime(time_t const *, long);
+# endif
+# if TZ_TIME_T || !defined timegm
+time_t timegm(struct tm *);
+# endif
+# if TZ_TIME_T || !defined timelocal
+time_t timelocal(struct tm *);
+# endif
+# if TZ_TIME_T || !defined timeoff
+time_t timeoff(struct tm *, long);
+# endif
+# if TZ_TIME_T || !defined time2posix
+time_t time2posix(time_t);
+# endif
+# if TZ_TIME_T || !defined posix2time
+time_t posix2time(time_t);
+# endif
+#endif
+
+/* Infer TM_ZONE on systems where this information is known, but suppress
+   guessing if NO_TM_ZONE is defined.  Similarly for TM_GMTOFF.  */
+#if (defined __GLIBC__ \
+     || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+     || (defined __APPLE__ && defined __MACH__))
+# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
+#  define TM_GMTOFF tm_gmtoff
+# endif
+# if !defined TM_ZONE && !defined NO_TM_ZONE
+#  define TM_ZONE tm_zone
+# endif
+#endif
+
+/*
+** Define functions that are ABI compatible with NetBSD but have
+** better prototypes.  NetBSD 6.1.4 defines a pointer type timezone_t
+** and labors under the misconception that 'const timezone_t' is a
+** pointer to a constant.  This use of 'const' is ineffective, so it
+** is not done here.  What we call 'struct state' NetBSD calls
+** 'struct __state', but this is a private name so it doesn't matter.
+*/
+#ifndef __NetBSD__
+#if NETBSD_INSPIRED
+typedef struct state *timezone_t;
+struct tm *localtime_rz(timezone_t restrict, time_t const *restrict,
+ struct tm *restrict);
+time_t mktime_z(timezone_t restrict, struct tm *restrict);
+timezone_t tzalloc(char const *);
+void tzfree(timezone_t);
+# ifdef STD_INSPIRED
+#  if TZ_TIME_T || !defined posix2time_z
+time_t posix2time_z(timezone_t __restrict, time_t) ATTRIBUTE_PURE;
+#  endif
+#  if TZ_TIME_T || !defined time2posix_z
+time_t time2posix_z(timezone_t __restrict, time_t) ATTRIBUTE_PURE;
+#  endif
+# endif
+#endif
+#endif
+
+/*
+** Finally, some convenience items.
+*/
+
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# define true 1
+# define false 0
+# define bool int
+#endif
+
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#define TYPE_SIGNED(type) (/*CONSTCOND*/((type) -1) < 0)
+#define TWOS_COMPLEMENT(t) (/*CONSTCOND*/(t) ~ (t) 0 < 0)
+
+/* Max and min values of the integer type T, of which only the bottom
+   B bits are used, and where the highest-order used bit is considered
+   to be a sign bit if T is signed.  */
+#define MAXVAL(t, b) /*LINTED*/ \
+  ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
+ - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
+#define MINVAL(t, b) \
+  ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
+
+/* The extreme time values, assuming no padding.  */
+#define TIME_T_MIN_NO_PADDING MINVAL(time_t, TYPE_BIT(time_t))
+#define TIME_T_MAX_NO_PADDING MAXVAL(time_t, TYPE_BIT(time_t))
+
+/* The extreme time values.  These are macros, not constants, so that
+   any portability problem occur only when compiling .c files that use
+   the macros, which is safer for applications that need only zdump and zic.
+   This implementation assumes no padding if time_t is signed and
+   either the compiler lacks support for _Generic or time_t is not one
+   of the standard signed integer types.  */
+#if HAVE_GENERIC
+# define TIME_T_MIN \
+    _Generic((time_t) 0, \
+     signed char: SCHAR_MIN, short: SHRT_MIN, \
+     int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN, \
+     default: TIME_T_MIN_NO_PADDING)
+# define TIME_T_MAX \
+    (TYPE_SIGNED(time_t) \
+     ? _Generic((time_t) 0, \
+ signed char: SCHAR_MAX, short: SHRT_MAX, \
+ int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
+ default: TIME_T_MAX_NO_PADDING)    \
+     : (time_t) -1)
+#else
+# define TIME_T_MIN TIME_T_MIN_NO_PADDING
+# define TIME_T_MAX TIME_T_MAX_NO_PADDING
+#endif
+
+/*
+** 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))
+
+/*
+** INITIALIZE(x)
+*/
+
+#if defined(__GNUC__) || defined(__lint__)
+# define INITIALIZE(x) ((x) = 0)
+#else
+# define INITIALIZE(x)
+#endif
+
+#ifndef UNINIT_TRAP
+# define UNINIT_TRAP 0
+#endif
+
+/*
+** 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.
+*/
+
+#if HAVE_GETTEXT
+#define _(msgid) gettext(msgid)
+#else /* !HAVE_GETTEXT */
+#define _(msgid) msgid
+#endif /* !HAVE_GETTEXT */
+
+#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
+# define TZ_DOMAIN "tz"
+#endif
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#undef asctime_r
+#undef ctime_r
+char *asctime_r(struct tm const *, char *);
+char *ctime_r(time_t const *, char *);
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/* Handy macros that are independent of tzfile implementation.  */
+
+#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((int_fast32_t) 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
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+** isleap(y) == isleap(y % 400)
+** and so
+** isleap(a + b) == isleap((a + b) % 400)
+** or
+** isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not by C99 or later).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+
+#define AVGSECSPERYEAR 31556952L
+#define SECSPERREPEAT \
+  ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
+
+#ifdef _LIBC
+#include "reentrant.h"
+extern struct __state *__lclptr;
+#if defined(__LIBC12_SOURCE__)
+#define tzset_unlocked __tzset_unlocked
+#else
+#define tzset_unlocked __tzset_unlocked50
+#endif
+
+void tzset_unlocked(void);
+#ifdef _REENTRANT
+extern rwlock_t __lcl_lock;
+#endif
+#endif
+
+#endif /* !defined PRIVATE_H */
diff --git a/winsup/cygwin/tzcode/tzfile.h b/winsup/cygwin/tzcode/tzfile.h
new file mode 100644
index 000000000..5c7a22239
--- /dev/null
+++ b/winsup/cygwin/tzcode/tzfile.h
@@ -0,0 +1,174 @@
+/* $NetBSD: tzfile.h,v 1.10 2019/07/03 15:49:21 christos Exp $ */
+
+#ifndef _TZFILE_H_
+#define _TZFILE_H_
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR /* Time zone object file directory */
+#define TZDIR "/usr/share/zoneinfo"
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT "/etc/localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES "posixrules"
+#endif /* !defined TZDEFRULES */
+
+
+/* See Internet RFC 8536 for more details about the following format.  */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
+ char tzh_reserved[15]; /* reserved; must be zero */
+ char tzh_ttisutcnt[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 UT 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 1, transition
+** time is standard time, if 0,
+** transition time is local (wall clock)
+** time; if absent, transition times are
+** assumed to be local time
+** tzh_ttisutcnt (char)s indexed by type; if 1, transition
+** time is UT, if 0, transition time is
+** local time; if absent, transition
+** times are assumed to be local time.
+** When this is 1, the corresponding
+** std/wall indicator must also be 1.
+*/
+
+/*
+** If tzh_version is '2' or greater, the above is followed by a second instance
+** of tzhead and a second instance of the data in which each coded transition
+** time uses 8 rather than 4 chars,
+** then a POSIX-TZ-environment-variable-style string for use in handling
+** instants after the last transition time stored in the file
+** (with nothing between the newlines if there is no POSIX representation for
+** such instants).
+**
+** If tz_version is '3' or greater, the above is extended as follows.
+** First, the POSIX TZ string's hour offset may range from -167
+** through 167 as compared to the POSIX-required 0 through 24.
+** Second, its DST start time may be January 1 at 00:00 and its stop
+** time December 31 at 24:00 plus the difference between DST and
+** standard time, indicating DST all year.
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+#define TZ_MAX_TIMES 2000
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+/* This must be at least 17 for Europe/Samara and Europe/Vilnius.  */
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#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 ((int_fast32_t) 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
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+** isleap(y) == isleap(y % 400)
+** and so
+** isleap(a + b) == isleap((a + b) % 400)
+** or
+** isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not C99).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined _TZFILE_H_ */
--
2.21.0

Reply | Threaded
Open this post in threaded view
|

[PATCH 3/3 v3] Cygwin: tzcode resync: details

Mark Geisert
In reply to this post by Mark Geisert
Add tz_posixrules.h with data generated from most recent Cygwin tzdata
package.  Establish localtime.cc as primarily a wrapper around a patched
copy of localtime.c.  See README for more information.

---
 winsup/cygwin/tzcode/README            |  37 ++++
 winsup/cygwin/tzcode/localtime.c.patch |  59 +++++++
 winsup/cygwin/tzcode/localtime.cc      | 159 +++++++++++++++++
 winsup/cygwin/tzcode/tz_posixrules.h   | 231 +++++++++++++++++++++++++
 4 files changed, 486 insertions(+)
 create mode 100644 winsup/cygwin/tzcode/README
 create mode 100644 winsup/cygwin/tzcode/localtime.c.patch
 create mode 100644 winsup/cygwin/tzcode/localtime.cc
 create mode 100644 winsup/cygwin/tzcode/tz_posixrules.h

diff --git a/winsup/cygwin/tzcode/README b/winsup/cygwin/tzcode/README
new file mode 100644
index 000000000..b1a811e22
--- /dev/null
+++ b/winsup/cygwin/tzcode/README
@@ -0,0 +1,37 @@
+/*
+ How the code in this directory is supposed to work...
+ 2020/05/22 Mark Geisert <[hidden email]>
+
+ localtime.cc is the Cygwin-specific module that is compiled into
+ the Cygwin DLL when the latter is built.  It's just a wrapper that
+ #defines a bunch of stuff then #includes localtime.c.
+
+ localtime.c, at any point in time, is a reasonably recent version
+ of /src/lib/libc/time/localtime.c from NetBSD.  The same goes for
+ private.h and tzfile.h.  An empty namespace.h suffices for Cygwin.
+
+ The idea is that in the future, one just needs to bring over newer
+ versions of localtime.c, private.h, and/or tzfile.h from NetBSD as
+ they become available.
+
+ With luck, you can drop those files into this directory and they
+ can be immediately used to build a newer Cygwin DLL that has the
+ newer NetBSD functionality.  Without luck, you'll have to tweak the
+ wrapper localtime.cc.  In the worst case, some other strategy will
+ need to be figured out, such as manually pulling out the parts of
+ the NetBSD code Cygwin needs to build a stand-alone localtime.cc.
+
+ Re tz_posixrules.h: The data elements can be generated from
+ /usr/share/zoneinfo/posixrules in any version of Cygwin's tzdata
+ package.  Instructions are in the comment leading tz_posixrules.h.
+
+ Addendum:
+ Implementation of the strategy above has uncovered a small number
+ of NetBSD-isms in localtime.c that cannot be worked around with
+ preprocessor tricks.  So there is another file localtime.c.patched
+ that holds just these adjustments for Cygwin, and it's this file
+ that localtime.cc #includes.  localtime.c.patched is generated by
+ winsup/cygwin/Makefile[.in] operating with localtime.c.patch.
+
+ ..mark
+*/
diff --git a/winsup/cygwin/tzcode/localtime.c.patch b/winsup/cygwin/tzcode/localtime.c.patch
new file mode 100644
index 000000000..e19a2cd02
--- /dev/null
+++ b/winsup/cygwin/tzcode/localtime.c.patch
@@ -0,0 +1,59 @@
+--- localtime.c 2020-05-16 21:54:00.533111800 -0700
++++ localtime.c.patched 2020-05-22 00:03:30.826646000 -0700
+@@ -413,7 +413,7 @@
+ };
+
+ /* TZDIR with a trailing '/' rather than a trailing '\0'.  */
+-static char const tzdirslash[sizeof TZDIR] = TZDIR "/";
++static char const tzdirslash[sizeof TZDIR + 1] = TZDIR "/";
+
+ /* Local storage needed for 'tzloadbody'.  */
+ union local_storage {
+@@ -473,7 +473,7 @@
+   would pull in stdio (and would fail if the
+   resulting string length exceeded INT_MAX!).  */
+ memcpy(lsp->fullname, tzdirslash, sizeof tzdirslash);
+- strcpy(lsp->fullname + sizeof tzdirslash, name);
++ strcpy(lsp->fullname + sizeof tzdirslash - 1, name);
+
+ /* Set doaccess if NAME contains a ".." file name
+   component, as such a name could read a file outside
+@@ -488,11 +488,11 @@
+ name = lsp->fullname;
+ }
+ if (doaccess && access(name, R_OK) != 0)
+- return errno;
++ goto trydefrules;
+
+ fid = open(name, OPEN_MODE);
+ if (fid < 0)
+- return errno;
++ goto trydefrules;
+ nread = read(fid, up->buf, sizeof up->buf);
+ if (nread < (ssize_t)tzheadsize) {
+ int err = nread < 0 ? errno : EINVAL;
+@@ -501,6 +501,15 @@
+ }
+ if (close(fid) < 0)
+ return errno;
++ if (0) {
++trydefrules:
++ const char *base = strrchr(name, '/');
++ base = base ? base + 1 : name;
++ if (strcmp(base, TZDEFRULES))
++    return errno;
++ nread = sizeof _posixrules_data;
++ memcpy(up->buf, _posixrules_data, nread);
++ }
+ for (stored = 4; stored <= 8; stored *= 2) {
+ int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+ int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
+@@ -1417,6 +1426,8 @@
+ tzsetlcl(char const *name)
+ {
+ struct state *sp = __lclptr;
++ if (! name)
++ name = tzgetwintzi(__UNCONST(wildabbr), (char *) alloca (512));
+ int lcl = name ? strlen(name) < sizeof lcl_TZname : -1;
+ if (lcl < 0 ? lcl_is_set < 0
+    : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0)
diff --git a/winsup/cygwin/tzcode/localtime.cc b/winsup/cygwin/tzcode/localtime.cc
new file mode 100644
index 000000000..c903bf3b9
--- /dev/null
+++ b/winsup/cygwin/tzcode/localtime.cc
@@ -0,0 +1,159 @@
+/* localtime.cc: Wrapper of NetBSD tzcode support for Cygwin. See README file.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "../winsup.h"
+#include "../sync.h"
+#include "../include/cygwin/version.h"
+#include "tz_posixrules.h"
+
+static NO_COPY muto tzset_guard;
+
+// Convert these NetBSD rwlock ops into Cygwin muto ops
+#define rwlock_wrlock(X) tzset_guard.init("tzset_guard")->acquire()
+#define rwlock_unlock(X) tzset_guard.release()
+
+// Set these NetBSD-related option #defines appropriately for Cygwin
+//#define STD_INSPIRED // early-include private.h below does this
+#define lint
+#define HAVE_POSIX_DECLS 0
+#define USG_COMPAT 1
+#define NO_ERROR_IN_DST_GAP
+#define state __state
+
+// Turn a specific known kind of const parameter into non-const
+#define __UNCONST(X) ((char *) (X))
+
+// Turn off these NetBSD audit-related definitions
+#define __aconst
+#define _DIAGASSERT(X)
+
+// Supply this Cygwin-specific function in advance of its use in localtime.c
+static char *
+tzgetwintzi (char *wildabbr, char *outbuf)
+{
+    TIME_ZONE_INFORMATION tzi;
+    char *cp, *dst;
+    wchar_t *wsrc;
+    div_t d;
+
+    GetTimeZoneInformation (&tzi);
+    dst = cp = outbuf;
+    for (wsrc = tzi.StandardName; *wsrc; wsrc++)
+ if (*wsrc >= L'A' && *wsrc <= L'Z')
+    *dst++ = *wsrc;
+    if ((dst - cp) < 3)
+      {
+ /* In non-english Windows, converted tz.StandardName
+   may not contain a valid standard timezone name. */
+ strcpy (cp, wildabbr);
+ cp += strlen (wildabbr);
+      }
+    else
+ cp = dst;
+    d = div (tzi.Bias + tzi.StandardBias, 60);
+    __small_sprintf (cp, "%d", d.quot);
+    if (d.rem)
+ __small_sprintf (cp = strchr (cp, 0), ":%d", abs (d.rem));
+    if (tzi.StandardDate.wMonth)
+      {
+ cp = strchr (cp, 0);
+ dst = cp;
+ for (wsrc = tzi.DaylightName; *wsrc; wsrc++)
+    if (*wsrc >= L'A' && *wsrc <= L'Z')
+ *dst++ = *wsrc;
+ if ((dst - cp) < 3)
+  {
+    /* In non-english Windows, converted tz.DaylightName
+       may not contain a valid daylight timezone name. */
+    strcpy (cp, wildabbr);
+    cp += strlen (wildabbr);
+  }
+ else
+    cp = dst;
+ d = div (tzi.Bias + tzi.DaylightBias, 60);
+ __small_sprintf (cp, "%d", d.quot);
+ if (d.rem)
+    __small_sprintf (cp = strchr (cp, 0), ":%d", abs (d.rem));
+ cp = strchr (cp, 0);
+ __small_sprintf (cp = strchr (cp, 0), ",M%d.%d.%d/%d",
+ tzi.DaylightDate.wMonth,
+ tzi.DaylightDate.wDay,
+ tzi.DaylightDate.wDayOfWeek,
+ tzi.DaylightDate.wHour);
+ if (tzi.DaylightDate.wMinute || tzi.DaylightDate.wSecond)
+    __small_sprintf (cp = strchr (cp, 0), ":%d",
+     tzi.DaylightDate.wMinute);
+ if (tzi.DaylightDate.wSecond)
+    __small_sprintf (cp = strchr (cp, 0), ":%d",
+     tzi.DaylightDate.wSecond);
+ cp = strchr (cp, 0);
+ __small_sprintf (cp = strchr (cp, 0), ",M%d.%d.%d/%d",
+ tzi.StandardDate.wMonth,
+ tzi.StandardDate.wDay,
+ tzi.StandardDate.wDayOfWeek,
+ tzi.StandardDate.wHour);
+ if (tzi.StandardDate.wMinute || tzi.StandardDate.wSecond)
+    __small_sprintf (cp = strchr (cp, 0), ":%d",
+     tzi.StandardDate.wMinute);
+ if (tzi.StandardDate.wSecond)
+    __small_sprintf (cp = strchr (cp, 0), ":%d",
+     tzi.StandardDate.wSecond);
+      }
+    /* __small_printf ("TZ deduced as `%s'\n", outbuf); */
+    return outbuf;
+}
+
+// Get ready to wrap NetBSD's localtime.c
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Pull these in early to catch any small issues before the real test
+#include "private.h"
+#include "tzfile.h"
+
+/* Some NetBSD differences were too difficult to work around..
+   so #include a patched copy of localtime.c rather than the NetBSD original.
+   Here is a list of the patches...
+   (1) fix an erroneous decl of tzdirslash size (flagged by g++)
+   (2) add conditional call to Cygwin's tzgetwintzi() from tzsetlcl()
+   (3) add Cygwin's historical "posixrules" support to tzloadbody()
+*/
+#include "localtime.c.patched"
+
+#ifdef __cplusplus
+}
+#endif
+
+// Don't forget these Cygwin-specific additions from this point to EOF
+EXPORT_ALIAS (tzset_unlocked, _tzset_unlocked)
+
+extern "C" long
+__cygwin_gettzoffset (const struct tm *tmp)
+{
+#ifdef TM_GMTOFF
+    if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
+     return tmp->TM_GMTOFF;
+#endif /* defined TM_GMTOFF */
+    __tzinfo_type *tz = __gettzinfo ();
+    /* The sign of this is exactly opposite the envvar TZ.  We
+       could directly use the global _timezone for tm_isdst==0,
+       but have to use __tzrule for daylight savings.  */
+    long offset = -tz->__tzrule[tmp->tm_isdst > 0].offset;
+    return offset;
+}
+
+extern "C" const char *
+__cygwin_gettzname (const struct tm *tmp)
+{
+#ifdef TM_ZONE
+    if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
+ return tmp->TM_ZONE;
+#endif
+    return _tzname[tmp->tm_isdst > 0];
+}
diff --git a/winsup/cygwin/tzcode/tz_posixrules.h b/winsup/cygwin/tzcode/tz_posixrules.h
new file mode 100644
index 000000000..ebfcd065a
--- /dev/null
+++ b/winsup/cygwin/tzcode/tz_posixrules.h
@@ -0,0 +1,231 @@
+/* tz_posixrules.h
+ *
+ * The data elements were generated with...
+ * od -vt u1 -A n /usr/share/zoneinfo/posixrules |
+ * sed 's/$/,/;s/^ //;s/[0-9] /&,/g;s/ ,/, /g' > elements_file
+ *
+ * The source posixrules file is from the Cygwin tzdata 2020a-1 package.
+ */
+static NO_COPY unsigned char _posixrules_data[] = {
+ 84,  90, 105, 102,  50,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   5,   0,   0,   0,   0,
+  0,   0,   0, 236,   0,   0,   0,   5,   0,   0,   0,  20, 128,   0,   0,   0,
+158, 166,  30, 112, 159, 186, 235,  96, 160, 134,   0, 112, 161, 154, 205,  96,
+162, 101, 226, 112, 163, 131, 233, 224, 164, 106, 174, 112, 165,  53, 167,  96,
+166,  83, 202, 240, 167,  21, 137,  96, 168,  51, 172, 240, 168, 254, 165, 224,
+170,  19, 142, 240, 170, 222, 135, 224, 171, 243, 112, 240, 172, 190, 105, 224,
+173, 211,  82, 240, 174, 158,  75, 224, 175, 179,  52, 240, 176, 126,  45, 224,
+177, 156,  81, 112, 178, 103,  74,  96, 179, 124,  51, 112, 180,  71,  44,  96,
+181,  92,  21, 112, 182,  39,  14,  96, 183,  59, 247, 112, 184,   6, 240,  96,
+185,  27, 217, 112, 185, 230, 210,  96, 187,   4, 245, 240, 187, 198, 180,  96,
+188, 228, 215, 240, 189, 175, 208, 224, 190, 196, 185, 240, 191, 143, 178, 224,
+192, 164, 155, 240, 193, 111, 148, 224, 194, 132, 125, 240, 195,  79, 118, 224,
+196, 100,  95, 240, 197,  47,  88, 224, 198,  77, 124, 112, 199,  15,  58, 224,
+200,  45,  94, 112, 200, 248,  87,  96, 202,  13,  64, 112, 202, 216,  57,  96,
+203, 136, 240, 112, 210,  35, 244, 112, 210,  96, 251, 224, 211, 117, 228, 240,
+212,  64, 221, 224, 213,  85, 198, 240, 214,  32, 191, 224, 215,  53, 168, 240,
+216,   0, 161, 224, 217,  21, 138, 240, 217, 224, 131, 224, 218, 254, 167, 112,
+219, 192, 101, 224, 220, 222, 137, 112, 221, 169, 130,  96, 222, 190, 107, 112,
+223, 137, 100,  96, 224, 158,  77, 112, 225, 105,  70,  96, 226, 126,  47, 112,
+227,  73,  40,  96, 228,  94,  17, 112, 229,  87,  46, 224, 230,  71,  45, 240,
+231,  55,  16, 224, 232,  39,  15, 240, 233,  22, 242, 224, 234,   6, 241, 240,
+234, 246, 212, 224, 235, 230, 211, 240, 236, 214, 182, 224, 237, 198, 181, 240,
+238, 191, 211,  96, 239, 175, 210, 112, 240, 159, 181,  96, 241, 143, 180, 112,
+242, 127, 151,  96, 243, 111, 150, 112, 244,  95, 121,  96, 245,  79, 120, 112,
+246,  63,  91,  96, 247,  47,  90, 112, 248,  40, 119, 224, 249,  15,  60, 112,
+250,   8,  89, 224, 250, 248,  88, 240, 251, 232,  59, 224, 252, 216,  58, 240,
+253, 200,  29, 224, 254, 184,  28, 240, 255, 167, 255, 224,   0, 151, 254, 240,
+  1, 135, 225, 224,   2, 119, 224, 240,   3, 112, 254,  96,   4,  96, 253, 112,
+  5,  80, 224,  96,   6,  64, 223, 112,   7,  48, 194,  96,   7, 141,  25, 112,
+  9,  16, 164,  96,   9, 173, 148, 240,  10, 240, 134,  96,  11, 224, 133, 112,
+ 12, 217, 162, 224,  13, 192, 103, 112,  14, 185, 132, 224,  15, 169, 131, 240,
+ 16, 153, 102, 224,  17, 137, 101, 240,  18, 121,  72, 224,  19, 105,  71, 240,
+ 20,  89,  42, 224,  21,  73,  41, 240,  22,  57,  12, 224,  23,  41,  11, 240,
+ 24,  34,  41,  96,  25,   8, 237, 240,  26,   2,  11,  96,  26, 242,  10, 112,
+ 27, 225, 237,  96,  28, 209, 236, 112,  29, 193, 207,  96,  30, 177, 206, 112,
+ 31, 161, 177,  96,  32, 118,   0, 240,  33, 129, 147,  96,  34,  85, 226, 240,
+ 35, 106, 175, 224,  36,  53, 196, 240,  37,  74, 145, 224,  38,  21, 166, 240,
+ 39,  42, 115, 224,  39, 254, 195, 112,  41,  10,  85, 224,  41, 222, 165, 112,
+ 42, 234,  55, 224,  43, 190, 135, 112,  44, 211,  84,  96,  45, 158, 105, 112,
+ 46, 179,  54,  96,  47, 126,  75, 112,  48, 147,  24,  96,  49, 103, 103, 240,
+ 50, 114, 250,  96,  51,  71,  73, 240,  52,  82, 220,  96,  53,  39,  43, 240,
+ 54,  50, 190,  96,  55,   7,  13, 240,  56,  27, 218, 224,  56, 230, 239, 240,
+ 57, 251, 188, 224,  58, 198, 209, 240,  59, 219, 158, 224,  60, 175, 238, 112,
+ 61, 187, 128, 224,  62, 143, 208, 112,  63, 155,  98, 224,  64, 111, 178, 112,
+ 65, 132, 127,  96,  66,  79, 148, 112,  67, 100,  97,  96,  68,  47, 118, 112,
+ 69,  68,  67,  96,  69, 243, 168, 240,  71,  45,  95, 224,  71, 211, 138, 240,
+ 73,  13,  65, 224,  73, 179, 108, 240,  74, 237,  35, 224,  75, 156, 137, 112,
+ 76, 214,  64,  96,  77, 124, 107, 112,  78, 182,  34,  96,  79,  92,  77, 112,
+ 80, 150,   4,  96,  81,  60,  47, 112,  82, 117, 230,  96,  83,  28,  17, 112,
+ 84,  85, 200,  96,  84, 251, 243, 112,  86,  53, 170,  96,  86, 229,  15, 240,
+ 88,  30, 198, 224,  88, 196, 241, 240,  89, 254, 168, 224,  90, 164, 211, 240,
+ 91, 222, 138, 224,  92, 132, 181, 240,  93, 190, 108, 224,  94, 100, 151, 240,
+ 95, 158,  78, 224,  96,  77, 180, 112,  97, 135, 107,  96,  98,  45, 150, 112,
+ 99, 103,  77,  96, 100,  13, 120, 112, 101,  71,  47,  96, 101, 237,  90, 112,
+103,  39,  17,  96, 103, 205,  60, 112, 105,   6, 243,  96, 105, 173,  30, 112,
+106, 230, 213,  96, 107, 150,  58, 240, 108, 207, 241, 224, 109, 118,  28, 240,
+110, 175, 211, 224, 111,  85, 254, 240, 112, 143, 181, 224, 113,  53, 224, 240,
+114, 111, 151, 224, 115,  21, 194, 240, 116,  79, 121, 224, 116, 254, 223, 112,
+118,  56, 150,  96, 118, 222, 193, 112, 120,  24, 120,  96, 120, 190, 163, 112,
+121, 248,  90,  96, 122, 158, 133, 112, 123, 216,  60,  96, 124, 126, 103, 112,
+125, 184,  30,  96, 126,  94,  73, 112, 127, 152,   0,  96,   2,   1,   2,   1,
+  2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,
+  2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,
+  2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   3,   4,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2, 255, 255, 186, 158,   0,   0, 255, 255,
+199, 192,   1,   4, 255, 255, 185, 176,   0,   8, 255, 255, 199, 192,   1,  12,
+255, 255, 199, 192,   1,  16,  76,  77,  84,   0,  69,  68,  84,   0,  69,  83,
+ 84,   0,  69,  87,  84,   0,  69,  80,  84,   0,   0,   0,   0,   0,   1,   0,
+  0,   0,   0,   1,  84,  90, 105, 102,  50,   0,   0,   0,   0,   0,   0,   0,
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   5,
+  0,   0,   0,   0,   0,   0,   0, 236,   0,   0,   0,   5,   0,   0,   0,  20,
+255, 255, 255, 255,  94,   3, 240, 144, 255, 255, 255, 255, 158, 166,  30, 112,
+255, 255, 255, 255, 159, 186, 235,  96, 255, 255, 255, 255, 160, 134,   0, 112,
+255, 255, 255, 255, 161, 154, 205,  96, 255, 255, 255, 255, 162, 101, 226, 112,
+255, 255, 255, 255, 163, 131, 233, 224, 255, 255, 255, 255, 164, 106, 174, 112,
+255, 255, 255, 255, 165,  53, 167,  96, 255, 255, 255, 255, 166,  83, 202, 240,
+255, 255, 255, 255, 167,  21, 137,  96, 255, 255, 255, 255, 168,  51, 172, 240,
+255, 255, 255, 255, 168, 254, 165, 224, 255, 255, 255, 255, 170,  19, 142, 240,
+255, 255, 255, 255, 170, 222, 135, 224, 255, 255, 255, 255, 171, 243, 112, 240,
+255, 255, 255, 255, 172, 190, 105, 224, 255, 255, 255, 255, 173, 211,  82, 240,
+255, 255, 255, 255, 174, 158,  75, 224, 255, 255, 255, 255, 175, 179,  52, 240,
+255, 255, 255, 255, 176, 126,  45, 224, 255, 255, 255, 255, 177, 156,  81, 112,
+255, 255, 255, 255, 178, 103,  74,  96, 255, 255, 255, 255, 179, 124,  51, 112,
+255, 255, 255, 255, 180,  71,  44,  96, 255, 255, 255, 255, 181,  92,  21, 112,
+255, 255, 255, 255, 182,  39,  14,  96, 255, 255, 255, 255, 183,  59, 247, 112,
+255, 255, 255, 255, 184,   6, 240,  96, 255, 255, 255, 255, 185,  27, 217, 112,
+255, 255, 255, 255, 185, 230, 210,  96, 255, 255, 255, 255, 187,   4, 245, 240,
+255, 255, 255, 255, 187, 198, 180,  96, 255, 255, 255, 255, 188, 228, 215, 240,
+255, 255, 255, 255, 189, 175, 208, 224, 255, 255, 255, 255, 190, 196, 185, 240,
+255, 255, 255, 255, 191, 143, 178, 224, 255, 255, 255, 255, 192, 164, 155, 240,
+255, 255, 255, 255, 193, 111, 148, 224, 255, 255, 255, 255, 194, 132, 125, 240,
+255, 255, 255, 255, 195,  79, 118, 224, 255, 255, 255, 255, 196, 100,  95, 240,
+255, 255, 255, 255, 197,  47,  88, 224, 255, 255, 255, 255, 198,  77, 124, 112,
+255, 255, 255, 255, 199,  15,  58, 224, 255, 255, 255, 255, 200,  45,  94, 112,
+255, 255, 255, 255, 200, 248,  87,  96, 255, 255, 255, 255, 202,  13,  64, 112,
+255, 255, 255, 255, 202, 216,  57,  96, 255, 255, 255, 255, 203, 136, 240, 112,
+255, 255, 255, 255, 210,  35, 244, 112, 255, 255, 255, 255, 210,  96, 251, 224,
+255, 255, 255, 255, 211, 117, 228, 240, 255, 255, 255, 255, 212,  64, 221, 224,
+255, 255, 255, 255, 213,  85, 198, 240, 255, 255, 255, 255, 214,  32, 191, 224,
+255, 255, 255, 255, 215,  53, 168, 240, 255, 255, 255, 255, 216,   0, 161, 224,
+255, 255, 255, 255, 217,  21, 138, 240, 255, 255, 255, 255, 217, 224, 131, 224,
+255, 255, 255, 255, 218, 254, 167, 112, 255, 255, 255, 255, 219, 192, 101, 224,
+255, 255, 255, 255, 220, 222, 137, 112, 255, 255, 255, 255, 221, 169, 130,  96,
+255, 255, 255, 255, 222, 190, 107, 112, 255, 255, 255, 255, 223, 137, 100,  96,
+255, 255, 255, 255, 224, 158,  77, 112, 255, 255, 255, 255, 225, 105,  70,  96,
+255, 255, 255, 255, 226, 126,  47, 112, 255, 255, 255, 255, 227,  73,  40,  96,
+255, 255, 255, 255, 228,  94,  17, 112, 255, 255, 255, 255, 229,  87,  46, 224,
+255, 255, 255, 255, 230,  71,  45, 240, 255, 255, 255, 255, 231,  55,  16, 224,
+255, 255, 255, 255, 232,  39,  15, 240, 255, 255, 255, 255, 233,  22, 242, 224,
+255, 255, 255, 255, 234,   6, 241, 240, 255, 255, 255, 255, 234, 246, 212, 224,
+255, 255, 255, 255, 235, 230, 211, 240, 255, 255, 255, 255, 236, 214, 182, 224,
+255, 255, 255, 255, 237, 198, 181, 240, 255, 255, 255, 255, 238, 191, 211,  96,
+255, 255, 255, 255, 239, 175, 210, 112, 255, 255, 255, 255, 240, 159, 181,  96,
+255, 255, 255, 255, 241, 143, 180, 112, 255, 255, 255, 255, 242, 127, 151,  96,
+255, 255, 255, 255, 243, 111, 150, 112, 255, 255, 255, 255, 244,  95, 121,  96,
+255, 255, 255, 255, 245,  79, 120, 112, 255, 255, 255, 255, 246,  63,  91,  96,
+255, 255, 255, 255, 247,  47,  90, 112, 255, 255, 255, 255, 248,  40, 119, 224,
+255, 255, 255, 255, 249,  15,  60, 112, 255, 255, 255, 255, 250,   8,  89, 224,
+255, 255, 255, 255, 250, 248,  88, 240, 255, 255, 255, 255, 251, 232,  59, 224,
+255, 255, 255, 255, 252, 216,  58, 240, 255, 255, 255, 255, 253, 200,  29, 224,
+255, 255, 255, 255, 254, 184,  28, 240, 255, 255, 255, 255, 255, 167, 255, 224,
+  0,   0,   0,   0,   0, 151, 254, 240,   0,   0,   0,   0,   1, 135, 225, 224,
+  0,   0,   0,   0,   2, 119, 224, 240,   0,   0,   0,   0,   3, 112, 254,  96,
+  0,   0,   0,   0,   4,  96, 253, 112,   0,   0,   0,   0,   5,  80, 224,  96,
+  0,   0,   0,   0,   6,  64, 223, 112,   0,   0,   0,   0,   7,  48, 194,  96,
+  0,   0,   0,   0,   7, 141,  25, 112,   0,   0,   0,   0,   9,  16, 164,  96,
+  0,   0,   0,   0,   9, 173, 148, 240,   0,   0,   0,   0,  10, 240, 134,  96,
+  0,   0,   0,   0,  11, 224, 133, 112,   0,   0,   0,   0,  12, 217, 162, 224,
+  0,   0,   0,   0,  13, 192, 103, 112,   0,   0,   0,   0,  14, 185, 132, 224,
+  0,   0,   0,   0,  15, 169, 131, 240,   0,   0,   0,   0,  16, 153, 102, 224,
+  0,   0,   0,   0,  17, 137, 101, 240,   0,   0,   0,   0,  18, 121,  72, 224,
+  0,   0,   0,   0,  19, 105,  71, 240,   0,   0,   0,   0,  20,  89,  42, 224,
+  0,   0,   0,   0,  21,  73,  41, 240,   0,   0,   0,   0,  22,  57,  12, 224,
+  0,   0,   0,   0,  23,  41,  11, 240,   0,   0,   0,   0,  24,  34,  41,  96,
+  0,   0,   0,   0,  25,   8, 237, 240,   0,   0,   0,   0,  26,   2,  11,  96,
+  0,   0,   0,   0,  26, 242,  10, 112,   0,   0,   0,   0,  27, 225, 237,  96,
+  0,   0,   0,   0,  28, 209, 236, 112,   0,   0,   0,   0,  29, 193, 207,  96,
+  0,   0,   0,   0,  30, 177, 206, 112,   0,   0,   0,   0,  31, 161, 177,  96,
+  0,   0,   0,   0,  32, 118,   0, 240,   0,   0,   0,   0,  33, 129, 147,  96,
+  0,   0,   0,   0,  34,  85, 226, 240,   0,   0,   0,   0,  35, 106, 175, 224,
+  0,   0,   0,   0,  36,  53, 196, 240,   0,   0,   0,   0,  37,  74, 145, 224,
+  0,   0,   0,   0,  38,  21, 166, 240,   0,   0,   0,   0,  39,  42, 115, 224,
+  0,   0,   0,   0,  39, 254, 195, 112,   0,   0,   0,   0,  41,  10,  85, 224,
+  0,   0,   0,   0,  41, 222, 165, 112,   0,   0,   0,   0,  42, 234,  55, 224,
+  0,   0,   0,   0,  43, 190, 135, 112,   0,   0,   0,   0,  44, 211,  84,  96,
+  0,   0,   0,   0,  45, 158, 105, 112,   0,   0,   0,   0,  46, 179,  54,  96,
+  0,   0,   0,   0,  47, 126,  75, 112,   0,   0,   0,   0,  48, 147,  24,  96,
+  0,   0,   0,   0,  49, 103, 103, 240,   0,   0,   0,   0,  50, 114, 250,  96,
+  0,   0,   0,   0,  51,  71,  73, 240,   0,   0,   0,   0,  52,  82, 220,  96,
+  0,   0,   0,   0,  53,  39,  43, 240,   0,   0,   0,   0,  54,  50, 190,  96,
+  0,   0,   0,   0,  55,   7,  13, 240,   0,   0,   0,   0,  56,  27, 218, 224,
+  0,   0,   0,   0,  56, 230, 239, 240,   0,   0,   0,   0,  57, 251, 188, 224,
+  0,   0,   0,   0,  58, 198, 209, 240,   0,   0,   0,   0,  59, 219, 158, 224,
+  0,   0,   0,   0,  60, 175, 238, 112,   0,   0,   0,   0,  61, 187, 128, 224,
+  0,   0,   0,   0,  62, 143, 208, 112,   0,   0,   0,   0,  63, 155,  98, 224,
+  0,   0,   0,   0,  64, 111, 178, 112,   0,   0,   0,   0,  65, 132, 127,  96,
+  0,   0,   0,   0,  66,  79, 148, 112,   0,   0,   0,   0,  67, 100,  97,  96,
+  0,   0,   0,   0,  68,  47, 118, 112,   0,   0,   0,   0,  69,  68,  67,  96,
+  0,   0,   0,   0,  69, 243, 168, 240,   0,   0,   0,   0,  71,  45,  95, 224,
+  0,   0,   0,   0,  71, 211, 138, 240,   0,   0,   0,   0,  73,  13,  65, 224,
+  0,   0,   0,   0,  73, 179, 108, 240,   0,   0,   0,   0,  74, 237,  35, 224,
+  0,   0,   0,   0,  75, 156, 137, 112,   0,   0,   0,   0,  76, 214,  64,  96,
+  0,   0,   0,   0,  77, 124, 107, 112,   0,   0,   0,   0,  78, 182,  34,  96,
+  0,   0,   0,   0,  79,  92,  77, 112,   0,   0,   0,   0,  80, 150,   4,  96,
+  0,   0,   0,   0,  81,  60,  47, 112,   0,   0,   0,   0,  82, 117, 230,  96,
+  0,   0,   0,   0,  83,  28,  17, 112,   0,   0,   0,   0,  84,  85, 200,  96,
+  0,   0,   0,   0,  84, 251, 243, 112,   0,   0,   0,   0,  86,  53, 170,  96,
+  0,   0,   0,   0,  86, 229,  15, 240,   0,   0,   0,   0,  88,  30, 198, 224,
+  0,   0,   0,   0,  88, 196, 241, 240,   0,   0,   0,   0,  89, 254, 168, 224,
+  0,   0,   0,   0,  90, 164, 211, 240,   0,   0,   0,   0,  91, 222, 138, 224,
+  0,   0,   0,   0,  92, 132, 181, 240,   0,   0,   0,   0,  93, 190, 108, 224,
+  0,   0,   0,   0,  94, 100, 151, 240,   0,   0,   0,   0,  95, 158,  78, 224,
+  0,   0,   0,   0,  96,  77, 180, 112,   0,   0,   0,   0,  97, 135, 107,  96,
+  0,   0,   0,   0,  98,  45, 150, 112,   0,   0,   0,   0,  99, 103,  77,  96,
+  0,   0,   0,   0, 100,  13, 120, 112,   0,   0,   0,   0, 101,  71,  47,  96,
+  0,   0,   0,   0, 101, 237,  90, 112,   0,   0,   0,   0, 103,  39,  17,  96,
+  0,   0,   0,   0, 103, 205,  60, 112,   0,   0,   0,   0, 105,   6, 243,  96,
+  0,   0,   0,   0, 105, 173,  30, 112,   0,   0,   0,   0, 106, 230, 213,  96,
+  0,   0,   0,   0, 107, 150,  58, 240,   0,   0,   0,   0, 108, 207, 241, 224,
+  0,   0,   0,   0, 109, 118,  28, 240,   0,   0,   0,   0, 110, 175, 211, 224,
+  0,   0,   0,   0, 111,  85, 254, 240,   0,   0,   0,   0, 112, 143, 181, 224,
+  0,   0,   0,   0, 113,  53, 224, 240,   0,   0,   0,   0, 114, 111, 151, 224,
+  0,   0,   0,   0, 115,  21, 194, 240,   0,   0,   0,   0, 116,  79, 121, 224,
+  0,   0,   0,   0, 116, 254, 223, 112,   0,   0,   0,   0, 118,  56, 150,  96,
+  0,   0,   0,   0, 118, 222, 193, 112,   0,   0,   0,   0, 120,  24, 120,  96,
+  0,   0,   0,   0, 120, 190, 163, 112,   0,   0,   0,   0, 121, 248,  90,  96,
+  0,   0,   0,   0, 122, 158, 133, 112,   0,   0,   0,   0, 123, 216,  60,  96,
+  0,   0,   0,   0, 124, 126, 103, 112,   0,   0,   0,   0, 125, 184,  30,  96,
+  0,   0,   0,   0, 126,  94,  73, 112,   0,   0,   0,   0, 127, 152,   0,  96,
+  2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,
+  2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,
+  2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,
+  2,   3,   4,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2,
+  1,   2,   1,   2,   1,   2,   1,   2,   1,   2,   1,   2, 255, 255, 186, 158,
+  0,   0, 255, 255, 199, 192,   1,   4, 255, 255, 185, 176,   0,   8, 255, 255,
+199, 192,   1,  12, 255, 255, 199, 192,   1,  16,  76,  77,  84,   0,  69,  68,
+ 84,   0,  69,  83,  84,   0,  69,  87,  84,   0,  69,  80,  84,   0,   0,   0,
+  0,   0,   1,   0,   0,   0,   0,   1,  10,  69,  83,  84,  53,  69,  68,  84,
+ 44,  77,  51,  46,  50,  46,  48,  44,  77,  49,  49,  46,  49,  46,  48,  10,
+};
--
2.21.0

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/3 v3] Cygwin: tzcode resync: basics

Corinna Vinschen-2
In reply to this post by Mark Geisert
Hi Mark,

On May 22 02:32, Mark Geisert wrote:

> Modifies winsup/cygwin/Makefile.in to build localtime.o from items in
> new winsup/cygwin/tzcode subdirectory.  Compiler option "-fpermissive"
> is used to accept warnings about missing casts on the return values of
> malloc() calls.  This patch also removes existing localtime.cc and
> tz_posixrules.h from winsup/cygwin as they are superseded by the
> subsequent patches in this set.
> [...]
> @@ -246,6 +246,15 @@ MATH_OFILES:= \
>   tgammal.o \
>   truncl.o
>  
> +TZCODE_OFILES:=localtime.o
> +
> +localtime.o: $(srcdir)/tzcode/localtime.cc $(srcdir)/tzcode/localtime.c.patch
> + (cd $(srcdir)/tzcode && \
> + patch -u -o localtime.c.patched localtime.c localtime.c.patch)
> + $(CXX) ${CXXFLAGS} ${localtime_CFLAGS} \
> + -I$(target_builddir)/winsup/cygwin \
> + -I$(srcdir) -I$(srcdir)/tzcode -c -o $@ $<
> +
This doesn't work well for me.  That rule is the top rule in Makefile.in
now, so just calling `make' doesn't build the DLL anymore, only
localtime.o.  The rule should get moved way down Makefile.in.

What still bugs me is that we get these -fpermissive warnings (albeit
non-fatal) and the fact that we don't get a dependencies file.  On
second thought, there's no good reason to keep localtime.cc a C++ file.
Converting this file to a plain C wrapper drops the C++-specific warning
and thus allows to revert the localtime.o build rule to use ${COMMON_CFLAGS}.

So I took the liberty to tweak your patch a bit.  I created a followup
patchset, which I'd like you to take a look at.

I attached the followup patches to this mail.  Please scrutinize it and
don't hesitate to discuss the changes.  For a start:

- I do not exactly like the name "localtime_wrapper.c" but I don't
  have a better idea.

- muto's are C++-only, so I changed rwlock_wrlock/rwlock_unlock to use
  Windows SRWLocks.  I think this is a good thing and I'm inclined
  to drop the muto datatype entirely in favor of using SRWLocks since
  they are cleaner and langauge-agnostic.


Thanks,
Corinna

--
Corinna Vinschen
Cygwin Maintainer

0001-Cygwin-move-localtime.o-build-rule-to-end-of-file.patch (1K) Download Attachment
0002-Cygwin-rename-localtime.cc-to-localtime_wrapper.c.patch (1K) Download Attachment
0003-Cygwin-convert-localtime_wrapper.c-to-plain-C-source.patch (4K) Download Attachment
0004-.gitignore-add-.patched-to-support-Cygwin-s-new-loca.patch (567 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/3 v3] Cygwin: tzcode resync: basics

Corinna Vinschen-2
Hi Mark,

> On May 22 02:32, Mark Geisert wrote:
On May 25 14:06, Corinna Vinschen wrote:

> > Modifies winsup/cygwin/Makefile.in to build localtime.o from items in
> > new winsup/cygwin/tzcode subdirectory.  Compiler option "-fpermissive"
> > is used to accept warnings about missing casts on the return values of
> > malloc() calls.  This patch also removes existing localtime.cc and
> > tz_posixrules.h from winsup/cygwin as they are superseded by the
> > subsequent patches in this set.
> > [...]
> > @@ -246,6 +246,15 @@ MATH_OFILES:= \
> >   tgammal.o \
> >   truncl.o
> >  
> > +TZCODE_OFILES:=localtime.o
> > +
> > +localtime.o: $(srcdir)/tzcode/localtime.cc $(srcdir)/tzcode/localtime.c.patch
> > + (cd $(srcdir)/tzcode && \
> > + patch -u -o localtime.c.patched localtime.c localtime.c.patch)
> > + $(CXX) ${CXXFLAGS} ${localtime_CFLAGS} \
> > + -I$(target_builddir)/winsup/cygwin \
> > + -I$(srcdir) -I$(srcdir)/tzcode -c -o $@ $<
> > +
>
> This doesn't work well for me.  That rule is the top rule in Makefile.in
> now, so just calling `make' doesn't build the DLL anymore, only
> localtime.o.  The rule should get moved way down Makefile.in.
>
> What still bugs me is that we get these -fpermissive warnings (albeit
> non-fatal) and the fact that we don't get a dependencies file.  On
> second thought, there's no good reason to keep localtime.cc a C++ file.
> Converting this file to a plain C wrapper drops the C++-specific warning
> and thus allows to revert the localtime.o build rule to use ${COMMON_CFLAGS}.
>
> So I took the liberty to tweak your patch a bit.  I created a followup
> patchset, which I'd like you to take a look at.
>
> I attached the followup patches to this mail.  Please scrutinize it and
> don't hesitate to discuss the changes.  For a start:
>
> - I do not exactly like the name "localtime_wrapper.c" but I don't
>   have a better idea.
>
> - muto's are C++-only, so I changed rwlock_wrlock/rwlock_unlock to use
>   Windows SRWLocks.  I think this is a good thing and I'm inclined
>   to drop the muto datatype entirely in favor of using SRWLocks since
>   they are cleaner and langauge-agnostic.
Two changes in my patchset:

- I didn't initialize the SRWLOCK following the books.  Fixed that.

- Rather than creating the patched file in the source dir, I changed
  the Makefile.in rule so that the patched file is created in the build
  dir.  This drops the requirement to tweak .gitignore.  It's also
  cleaner.

- Splitting the build rule for localtime.c.patched from the build rule
  for localtime.o makes sure that the patched file is not regenerated
  every time we build localtime.o.

I attached my patchset again, but only patch 3 and 4 actually changed.


Thanks,
Corinna

0001-Cygwin-move-localtime.o-build-rule-to-end-of-file.patch (1K) Download Attachment
0002-Cygwin-rename-localtime.cc-to-localtime_wrapper.c.patch (1K) Download Attachment
0003-Cygwin-convert-localtime_wrapper.c-to-plain-C-source.patch (4K) Download Attachment
0004-Cygwin-revamp-localtime.o-build-rule.patch (1K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/3 v3] Cygwin: tzcode resync: basics

Mark Geisert
Hi Corinna,

Corinna Vinschen wrote:

> Hi Mark,
>
>> On May 22 02:32, Mark Geisert wrote:
> On May 25 14:06, Corinna Vinschen wrote:
>>> Modifies winsup/cygwin/Makefile.in to build localtime.o from items in
>>> new winsup/cygwin/tzcode subdirectory.  Compiler option "-fpermissive"
>>> is used to accept warnings about missing casts on the return values of
>>> malloc() calls.  This patch also removes existing localtime.cc and
>>> tz_posixrules.h from winsup/cygwin as they are superseded by the
>>> subsequent patches in this set.
>>> [...]
>>> @@ -246,6 +246,15 @@ MATH_OFILES:= \
>>>   tgammal.o \
>>>   truncl.o
>>>  
>>> +TZCODE_OFILES:=localtime.o
>>> +
>>> +localtime.o: $(srcdir)/tzcode/localtime.cc $(srcdir)/tzcode/localtime.c.patch
>>> + (cd $(srcdir)/tzcode && \
>>> + patch -u -o localtime.c.patched localtime.c localtime.c.patch)
>>> + $(CXX) ${CXXFLAGS} ${localtime_CFLAGS} \
>>> + -I$(target_builddir)/winsup/cygwin \
>>> + -I$(srcdir) -I$(srcdir)/tzcode -c -o $@ $<
>>> +
>>
>> This doesn't work well for me.  That rule is the top rule in Makefile.in
>> now, so just calling `make' doesn't build the DLL anymore, only
>> localtime.o.  The rule should get moved way down Makefile.in.

Oops.  My workflow didn't make this apparent to me.  Thanks for the fix.

>> What still bugs me is that we get these -fpermissive warnings (albeit
>> non-fatal) and the fact that we don't get a dependencies file.  On
>> second thought, there's no good reason to keep localtime.cc a C++ file.
>> Converting this file to a plain C wrapper drops the C++-specific warning
>> and thus allows to revert the localtime.o build rule to use ${COMMON_CFLAGS}.
>>
>> So I took the liberty to tweak your patch a bit.  I created a followup
>> patchset, which I'd like you to take a look at.
>>
>> I attached the followup patches to this mail.  Please scrutinize it and
>> don't hesitate to discuss the changes.  For a start:
>>
>> - I do not exactly like the name "localtime_wrapper.c" but I don't
>>    have a better idea.

localtime_cygwin.c?  cyglocaltime.c?  Not much nicer IMO.

>> - muto's are C++-only, so I changed rwlock_wrlock/rwlock_unlock to use
>>    Windows SRWLocks.  I think this is a good thing and I'm inclined
>>    to drop the muto datatype entirely in favor of using SRWLocks since
>>    they are cleaner and langauge-agnostic.
>
> Two changes in my patchset:
>
> - I didn't initialize the SRWLOCK following the books.  Fixed that.
>
> - Rather than creating the patched file in the source dir, I changed
>    the Makefile.in rule so that the patched file is created in the build
>    dir.  This drops the requirement to tweak .gitignore.  It's also
>    cleaner.
>
> - Splitting the build rule for localtime.c.patched from the build rule
>    for localtime.o makes sure that the patched file is not regenerated
>    every time we build localtime.o.
>
> I attached my patchset again, but only patch 3 and 4 actually changed.

All the above are great improvements.  But I would now remove the "// Get ready
to wrap NetBSD's localtime.c" line and blank line following it.  Good to go!
Thank you,

..mark
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/3 v3] Cygwin: tzcode resync: basics

Corinna Vinschen-2
Hi Mark,

On May 26 00:09, Mark Geisert wrote:

> Hi Corinna,
>
> Corinna Vinschen wrote:
> > Hi Mark,
> >
> > > On May 22 02:32, Mark Geisert wrote:
> > On May 25 14:06, Corinna Vinschen wrote:
> > > > Modifies winsup/cygwin/Makefile.in to build localtime.o from items in
> > > > new winsup/cygwin/tzcode subdirectory.  Compiler option "-fpermissive"
> > > > is used to accept warnings about missing casts on the return values of
> > > > malloc() calls.  This patch also removes existing localtime.cc and
> > > > tz_posixrules.h from winsup/cygwin as they are superseded by the
> > > > subsequent patches in this set.
> > > > [...]
> > > > @@ -246,6 +246,15 @@ MATH_OFILES:= \
> > > >   tgammal.o \
> > > >   truncl.o
> > > > +TZCODE_OFILES:=localtime.o
> > > > +
> > > > +localtime.o: $(srcdir)/tzcode/localtime.cc $(srcdir)/tzcode/localtime.c.patch
> > > > + (cd $(srcdir)/tzcode && \
> > > > + patch -u -o localtime.c.patched localtime.c localtime.c.patch)
> > > > + $(CXX) ${CXXFLAGS} ${localtime_CFLAGS} \
> > > > + -I$(target_builddir)/winsup/cygwin \
> > > > + -I$(srcdir) -I$(srcdir)/tzcode -c -o $@ $<
> > > > +
> > >
> > > This doesn't work well for me.  That rule is the top rule in Makefile.in
> > > now, so just calling `make' doesn't build the DLL anymore, only
> > > localtime.o.  The rule should get moved way down Makefile.in.
>
> Oops.  My workflow didn't make this apparent to me.  Thanks for the fix.
>
> > > What still bugs me [...etc...]
> > > I attached the followup patches to this mail.  Please scrutinize it and
> > > don't hesitate to discuss the changes.  For a start:
> > >
> > > - I do not exactly like the name "localtime_wrapper.c" but I don't
> > >    have a better idea.
>
> localtime_cygwin.c?  cyglocaltime.c?  Not much nicer IMO.
>
> > > - muto's are C++-only, so I changed rwlock_wrlock/rwlock_unlock to use
> > >    Windows SRWLocks.  I think this is a good thing and I'm inclined
> > >    to drop the muto datatype entirely in favor of using SRWLocks since
> > >    they are cleaner and langauge-agnostic.
> >
> > Two changes in my patchset:
> >
> > - I didn't initialize the SRWLOCK following the books.  Fixed that.
> >
> > - Rather than creating the patched file in the source dir, I changed
> >    the Makefile.in rule so that the patched file is created in the build
> >    dir.  This drops the requirement to tweak .gitignore.  It's also
> >    cleaner.
> >
> > - Splitting the build rule for localtime.c.patched from the build rule
> >    for localtime.o makes sure that the patched file is not regenerated
> >    every time we build localtime.o.
> >
> > I attached my patchset again, but only patch 3 and 4 actually changed.
>
> All the above are great improvements.  But I would now remove the "// Get
> ready to wrap NetBSD's localtime.c" line and blank line following it.

(Belatedly) done.

> Good to go!

Great!  I added two more tweaks:

- I renamed the generated file from localtime.c.patched to
  localtime.patched.c so the .c suffix remains intact.  Seems
  a bit cleaner to me.  I also added it to the 'clean' rule,
  so that it gets removed at `make clean' time.

- I simplified the #includes in the wrapper file.  The paths to these
  headers are searched anyway, so they don't have to be written out
  explicitely.

> Thank you,

Good job, thank you!


Corinna

--
Corinna Vinschen
Cygwin Maintainer
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/3 v3] Cygwin: tzcode resync: basics

Jon TURNEY
In reply to this post by Mark Geisert
On 22/05/2020 10:32, Mark Geisert wrote:

>
> diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
> index f273ba793..2ac8bcbd8 100644
> --- a/winsup/cygwin/Makefile.in
> +++ b/winsup/cygwin/Makefile.in
> @@ -27,7 +27,7 @@ export CCWRAP_HEADERS:=. ${srcdir}
>   export CCWRAP_SYSTEM_HEADERS:=@cygwin_headers@ @newlib_headers@
>   export CCWRAP_DIRAFTER_HEADERS:=@windows_headers@
>  
> -VPATH+=$(srcdir)/regex $(srcdir)/lib $(srcdir)/libc $(srcdir)/math
> +VPATH+=$(srcdir)/regex $(srcdir)/lib $(srcdir)/libc $(srcdir)/math $(srcdir)/tzcode
>  
>   target_cpu:=@target_cpu@
>   target_alias:=@target_alias@
> @@ -246,6 +246,15 @@ MATH_OFILES:= \
>   tgammal.o \
>   truncl.o
>  
> +TZCODE_OFILES:=localtime.o
> +
> +localtime.o: $(srcdir)/tzcode/localtime.cc $(srcdir)/tzcode/localtime.c.patch
> + (cd $(srcdir)/tzcode && \
> + patch -u -o localtime.c.patched localtime.c localtime.c.patch)
> + $(CXX) ${CXXFLAGS} ${localtime_CFLAGS} \
> + -I$(target_builddir)/winsup/cygwin \
> + -I$(srcdir) -I$(srcdir)/tzcode -c -o $@ $<
> +
>   DLL_OFILES:= \

This adds 'patch' to the build-time requirements of Cygwin, which should
probably be noted in the answer to faq.programming.building-cygwin in
winsup/doc/faq-programming.xml.

(There's also some stuff in there about LSAs requirements which is
stale, I think?)

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/3 v3] Cygwin: tzcode resync: basics

Corinna Vinschen-2
On May 26 16:11, Jon Turney wrote:

> On 22/05/2020 10:32, Mark Geisert wrote:
> >
> > diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
> > index f273ba793..2ac8bcbd8 100644
> > --- a/winsup/cygwin/Makefile.in
> > +++ b/winsup/cygwin/Makefile.in
> > @@ -27,7 +27,7 @@ export CCWRAP_HEADERS:=. ${srcdir}
> >   export CCWRAP_SYSTEM_HEADERS:=@cygwin_headers@ @newlib_headers@
> >   export CCWRAP_DIRAFTER_HEADERS:=@windows_headers@
> > -VPATH+=$(srcdir)/regex $(srcdir)/lib $(srcdir)/libc $(srcdir)/math
> > +VPATH+=$(srcdir)/regex $(srcdir)/lib $(srcdir)/libc $(srcdir)/math $(srcdir)/tzcode
> >   target_cpu:=@target_cpu@
> >   target_alias:=@target_alias@
> > @@ -246,6 +246,15 @@ MATH_OFILES:= \
> >   tgammal.o \
> >   truncl.o
> > +TZCODE_OFILES:=localtime.o
> > +
> > +localtime.o: $(srcdir)/tzcode/localtime.cc $(srcdir)/tzcode/localtime.c.patch
> > + (cd $(srcdir)/tzcode && \
> > + patch -u -o localtime.c.patched localtime.c localtime.c.patch)
> > + $(CXX) ${CXXFLAGS} ${localtime_CFLAGS} \
> > + -I$(target_builddir)/winsup/cygwin \
> > + -I$(srcdir) -I$(srcdir)/tzcode -c -o $@ $<
> > +
> >   DLL_OFILES:= \
>
> This adds 'patch' to the build-time requirements of Cygwin, which should
> probably be noted in the answer to faq.programming.building-cygwin in
> winsup/doc/faq-programming.xml.
>
> (There's also some stuff in there about LSAs requirements which is stale, I
> think?)

I meddled with the text a bit.  Feel free to patch it further if you
dislike it.


Thanks,
Corinna

--
Corinna Vinschen
Cygwin Maintainer