diff -crN -x *~ ../tzcode2001c/Makefile thread-safe-2/Makefile
*** ../tzcode2001c/Makefile	Tue Jun  5 13:48:21 2001
--- thread-safe-2/Makefile	Mon Jun 18 15:49:47 2001
***************
*** 83,88 ****
--- 83,89 ----
  
  # Non-default libraries needed to link.
  # Add -lintl if you want to use `gettext' on Solaris.
+ # Add -lpthread if you're compiling with non-weak POSIX threads in libpthread.
  LDLIBS=
  
  # Add the following to the end of the "CFLAGS=" line as needed.
***************
*** 211,216 ****
--- 212,220 ----
  
  CFLAGS=
  
+ # The threading model for this system.  "dummy" or "posix"
+ THREADMODEL=	dummy
+ 
  # If you want zic's -s option used when installing, uncomment the next line
  # ZFLAGS=	-s
  
***************
*** 242,260 ****
  cc=		cc
  CC=		$(cc) -DTZDIR=\"$(TZDIR)\"
  
! TZCSRCS=	zic.c localtime.c asctime.c scheck.c ialloc.c
! TZCOBJS=	zic.o localtime.o asctime.o scheck.o ialloc.o
! TZDSRCS=	zdump.c localtime.c asctime.c ialloc.c
! TZDOBJS=	zdump.o localtime.o asctime.o ialloc.o
! DATESRCS=	date.c localtime.c logwtmp.c strftime.c asctime.c
! DATEOBJS=	date.o localtime.o logwtmp.o strftime.o asctime.o
! LIBSRCS=	localtime.c asctime.c difftime.c
! LIBOBJS=	localtime.o asctime.o difftime.o
! HEADERS=	tzfile.h private.h
  NONLIBSRCS=	zic.c zdump.c scheck.c ialloc.c
  NEWUCBSRCS=	date.c logwtmp.c strftime.c
! SOURCES=	$(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS) tzselect.ksh
  MANS=		newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
  			tzfile.5 tzselect.8 zic.8 zdump.8
  DOCS=		README Theory $(MANS) date.1 Makefile
  PRIMARY_YDATA=	africa antarctica asia australasia \
--- 246,274 ----
  cc=		cc
  CC=		$(cc) -DTZDIR=\"$(TZDIR)\"
  
! THREADSRC=	tzthread-$(THREADMODEL).c
! THREADOBJ=	tzthread-$(THREADMODEL).o
! THREADHDR=	tzthread-$(THREADMODEL).h
! 
! CFLAGS += -DTZTHREAD_HEADER_H=\"$(THREADHDR)\"
! 
! TZCSRCS=	zic.c localtime.c $(THREADSRC) asctime.c scheck.c ialloc.c
! TZCOBJS=	zic.o localtime.o $(THREADOBJ) asctime.o scheck.o ialloc.o
! TZDSRCS=	zdump.c localtime.c $(THREADSRC) asctime.c ialloc.c
! TZDOBJS=	zdump.o localtime.o $(THREADOBJ) asctime.o ialloc.o
! DATESRCS=	date.c localtime.c $(THREADSRC) logwtmp.c strftime.c asctime.c
! DATEOBJS=	date.o localtime.o $(THREADOBJ) logwtmp.o strftime.o asctime.o
! LIBSRCS=	localtime.c $(THREADSRC) asctime.c difftime.c
! LIBOBJS=	localtime.o $(THREADOBJ) asctime.o difftime.o
! HEADERS=	tzfile.h private.h struct_tzinfo.h tzthread-dummy.h \
! 		tzthread-posix.h tztimeext.h
! VARLIBSRCS=	tzthread-dummy.c tzthread-posix.c
  NONLIBSRCS=	zic.c zdump.c scheck.c ialloc.c
  NEWUCBSRCS=	date.c logwtmp.c strftime.c
! SOURCES=	$(HEADERS) $(LIBSRCS) $(VARLIBSRCS) $(NONLIBSRCS) \
! 		$(NEWUCBSRCS) tzselect.ksh
  MANS=		newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
+ 			tz_prep.3 time_make.3 pthread_settz.3 \
  			tzfile.5 tzselect.8 zic.8 zdump.8
  DOCS=		README Theory $(MANS) date.1 Makefile
  PRIMARY_YDATA=	africa antarctica asia australasia \
***************
*** 353,359 ****
  		ar r ,lib.a logwtmp.o
  		if [ -x /usr/ucb/ranlib -o -x /usr/bin/ranlib ] ; \
  			then ranlib ,lib.a ; fi
! 		$(CC) $(CFLAGS) date.o localtime.o asctime.o strftime.o \
  			$(LDLIBS) -lc ,lib.a -o $@
  		rm -f ,lib.a
  
--- 367,373 ----
  		ar r ,lib.a logwtmp.o
  		if [ -x /usr/ucb/ranlib -o -x /usr/bin/ranlib ] ; \
  			then ranlib ,lib.a ; fi
! 		$(CC) $(CFLAGS) date.o localtime.o $(THREADOBJ) asctime.o strftime.o \
  			$(LDLIBS) -lc ,lib.a -o $@
  		rm -f ,lib.a
  
***************
*** 396,404 ****
  date.o:		private.h
  difftime.o:	private.h
  ialloc.o:	private.h
! localtime.o:	private.h tzfile.h
  scheck.o:	private.h
! strftime.o:	tzfile.h
  zic.o:		private.h tzfile.h
  
  .KEEP_STATE:
--- 410,419 ----
  date.o:		private.h
  difftime.o:	private.h
  ialloc.o:	private.h
! localtime.o:	private.h tzfile.h tztimeext.h struct_tzinfo.h $(THREADHDR)
  scheck.o:	private.h
! strftime.o:	tzfile.h private.h struct_tzinfo.h
  zic.o:		private.h tzfile.h
+ $(THREADOBJ):	private.h tzfile.h struct_tzinfo.h $(THREADHDR)
  
  .KEEP_STATE:
diff -crN -x *~ ../tzcode2001c/asctime.c thread-safe-2/asctime.c
*** ../tzcode2001c/asctime.c	Tue Jun  5 13:49:48 2001
--- thread-safe-2/asctime.c	Mon Jun 18 15:46:04 2001
***************
*** 14,19 ****
--- 14,21 ----
  #include "private.h"
  #include "tzfile.h"
  
+ #include TZTHREAD_HEADER_H
+ 
  /*
  ** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12.
  */
***************
*** 60,74 ****
  asctime(timeptr)
  register const struct tm *	timeptr;
  {
! 	/*
! 	** Big enough for something such as
! 	** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
! 	** (two three-character abbreviations, five strings denoting integers,
! 	** three explicit spaces, two explicit colons, a newline,
! 	** and a trailing ASCII nul).
! 	*/
! 	static char		result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
! 					3 + 2 + 1 + 1];
  
  	return asctime_r(timeptr, result);
  }
--- 62,68 ----
  asctime(timeptr)
  register const struct tm *	timeptr;
  {
! 	char *result = GET_ASCTIME_BUF();
  
  	return asctime_r(timeptr, result);
  }
diff -crN -x *~ ../tzcode2001c/localtime.c thread-safe-2/localtime.c
*** ../tzcode2001c/localtime.c	Tue Jun  5 13:59:55 2001
--- thread-safe-2/localtime.c	Mon Jun 18 16:26:37 2001
***************
*** 21,26 ****
--- 21,32 ----
  #include "tzfile.h"
  #include "fcntl.h"
  
+ #include "struct_tzinfo.h"
+ 
+ #include "tztimeext.h"
+ 
+ #include TZTHREAD_HEADER_H
+ 
  /*
  ** SunOS 4.1.1 headers lack O_BINARY.
  */
***************
*** 70,110 ****
  #define TZDEFRULESTRING ",M4.1.0,M10.5.0"
  #endif /* !defined TZDEFDST */
  
- struct ttinfo {				/* time type information */
- 	long		tt_gmtoff;	/* UTC offset in seconds */
- 	int		tt_isdst;	/* used to set tm_isdst */
- 	int		tt_abbrind;	/* abbreviation list index */
- 	int		tt_ttisstd;	/* TRUE if transition is std time */
- 	int		tt_ttisgmt;	/* TRUE if transition is UTC */
- };
- 
- struct lsinfo {				/* leap second information */
- 	time_t		ls_trans;	/* transition time */
- 	long		ls_corr;	/* correction to apply */
- };
- 
- #define BIGGEST(a, b)	(((a) > (b)) ? (a) : (b))
- 
- #ifdef TZNAME_MAX
- #define MY_TZNAME_MAX	TZNAME_MAX
- #endif /* defined TZNAME_MAX */
- #ifndef TZNAME_MAX
- #define MY_TZNAME_MAX	255
- #endif /* !defined TZNAME_MAX */
- 
- struct state {
- 	int		leapcnt;
- 	int		timecnt;
- 	int		typecnt;
- 	int		charcnt;
- 	time_t		ats[TZ_MAX_TIMES];
- 	unsigned char	types[TZ_MAX_TIMES];
- 	struct ttinfo	ttis[TZ_MAX_TYPES];
- 	char		chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
- 				(2 * (MY_TZNAME_MAX + 1)))];
- 	struct lsinfo	lsis[TZ_MAX_LEAPS];
- };
- 
  struct rule {
  	int		r_type;		/* type of rule--see below */
  	int		r_day;		/* day number of rule */
--- 76,81 ----
***************
*** 128,175 ****
  static const char *	getsecs P((const char * strp, long * secsp));
  static const char *	getoffset P((const char * strp, long * offsetp));
  static const char *	getrule P((const char * strp, struct rule * rulep));
! static void		gmtload P((struct state * sp));
! static void		gmtsub P((const time_t * timep, long offset,
! 				struct tm * tmp));
! static void		localsub P((const time_t * timep, long offset,
! 				struct tm * tmp));
  static int		increment_overflow P((int * number, int delta));
  static int		normalize_overflow P((int * tensptr, int * unitsptr,
  				int base));
! static void		settzname P((void));
! static time_t		time1 P((struct tm * tmp,
! 				void(*funcp) P((const time_t *,
! 				long, struct tm *)),
  				long offset));
! static time_t		time2 P((struct tm *tmp,
! 				void(*funcp) P((const time_t *,
! 				long, struct tm*)),
! 				long offset, int * okayp));
! static time_t		time2sub P((struct tm *tmp,
! 				void(*funcp) P((const time_t *,
! 				long, struct tm*)),
! 				long offset, int * okayp, int do_norm_secs));
  static void		timesub P((const time_t * timep, long offset,
! 				const struct state * sp, struct tm * tmp));
  static int		tmcomp P((const struct tm * atmp,
  				const struct tm * btmp));
  static time_t		transtime P((time_t janfirst, int year,
  				const struct rule * rulep, long offset));
! static int		tzload P((const char * name, struct state * sp));
! static int		tzparse P((const char * name, struct state * sp,
  				int lastditch));
  
- #ifdef ALL_STATE
- static struct state *	lclptr;
- static struct state *	gmtptr;
- #endif /* defined ALL_STATE */
- 
- #ifndef ALL_STATE
- static struct state	lclmem;
- static struct state	gmtmem;
- #define lclptr		(&lclmem)
- #define gmtptr		(&gmtmem)
- #endif /* State Farm */
  
  #ifndef TZ_STRLEN_MAX
  #define TZ_STRLEN_MAX 255
--- 99,145 ----
  static const char *	getsecs P((const char * strp, long * secsp));
  static const char *	getoffset P((const char * strp, long * offsetp));
  static const char *	getrule P((const char * strp, struct rule * rulep));
! static void		gmtload P((struct tzinfo * sp));
! static void		nulltzsub P((const time_t * timep,
! 				long offset, struct tm * tmp));
! static void		tzsub P((const time_t * timep,
! 				const struct tzinfo *tz,
! 				long offset, struct tm * tmp, int));
  static int		increment_overflow P((int * number, int delta));
  static int		normalize_overflow P((int * tensptr, int * unitsptr,
  				int base));
! static void		settzname P((const struct tzinfo * const));
! static int		time1 P((time_t * timep,
! 				struct tm * tmp,
! 				const struct tzinfo * tz,
! 				long offset,
! 				int accomodate_gap));
! static int		time2 P((time_t * timep,
! 				struct tm *tmp,
! 				const struct tzinfo * tz,
  				long offset));
! static int		time2sub P((time_t * timep,
! 				struct tm *tmp,
! 				const struct tzinfo * tz,
! 				long offset, int do_norm_secs));
  static void		timesub P((const time_t * timep, long offset,
! 				const struct tzinfo * sp, struct tm * tmp));
  static int		tmcomp P((const struct tm * atmp,
  				const struct tm * btmp));
  static time_t		transtime P((time_t janfirst, int year,
  				const struct rule * rulep, long offset));
! static int		tzload P((const char * name, struct tzinfo * sp));
! static int		tzparse P((const char * name, struct tzinfo * sp,
  				int lastditch));
+ static void		tzsetwall_basic P((struct tzinfo * const sp));
+ static void		tzset_basic P((struct tzinfo * const sp));
+ #ifndef STD_INSPIRED
+ static void		tzsetwall P((void));
+ #endif
+ #ifdef STD_INSPIRED
+ static long		leapcorr P((time_t *, const struct tzinfo *));
+ #endif
  
  
  #ifndef TZ_STRLEN_MAX
  #define TZ_STRLEN_MAX 255
***************
*** 184,189 ****
--- 154,164 ----
  	wildabbr
  };
  
+ #define MIN_TIME (TYPE_SIGNED(time_t) ? \
+ 	(((time_t) 1) << (TYPE_BIT(time_t) - 1)) : 0)
+ 
+ #define MAX_TIME (~MIN_TIME)
+ 
  /*
  ** Section 4.12.3 of X3.159-1989 requires that
  **	Except for the strftime function, these functions [asctime,
***************
*** 192,199 ****
  ** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
  */
  
- static struct tm	tm;
- 
  #ifdef USG_COMPAT
  time_t			timezone = 0;
  int			daylight = 0;
--- 167,172 ----
***************
*** 217,225 ****
  }
  
  static void
! settzname P((void))
  {
- 	register struct state * const	sp = lclptr;
  	register int			i;
  
  	tzname[0] = wildabbr;
--- 190,198 ----
  }
  
  static void
! settzname(sp)
! const struct tzinfo * const	sp;
  {
  	register int			i;
  
  	tzname[0] = wildabbr;
***************
*** 231,242 ****
  #ifdef ALTZONE
  	altzone = 0;
  #endif /* defined ALTZONE */
- #ifdef ALL_STATE
  	if (sp == NULL) {
  		tzname[0] = tzname[1] = gmt;
  		return;
  	}
- #endif /* defined ALL_STATE */
  	for (i = 0; i < sp->typecnt; ++i) {
  		register const struct ttinfo * const	ttisp = &sp->ttis[i];
  
--- 204,213 ----
***************
*** 269,275 ****
  static int
  tzload(name, sp)
  register const char *		name;
! register struct state * const	sp;
  {
  	register const char *	p;
  	register int		i;
--- 240,246 ----
  static int
  tzload(name, sp)
  register const char *		name;
! register struct tzinfo * const	sp;
  {
  	register const char *	p;
  	register int		i;
***************
*** 698,704 ****
  static int
  tzparse(name, sp, lastditch)
  const char *			name;
! register struct state * const	sp;
  const int			lastditch;
  {
  	const char *			stdname;
--- 669,675 ----
  static int
  tzparse(name, sp, lastditch)
  const char *			name;
! register struct tzinfo * const	sp;
  const int			lastditch;
  {
  	const char *			stdname;
***************
*** 910,921 ****
  
  static void
  gmtload(sp)
! struct state * const	sp;
  {
  	if (tzload(gmt, sp) != 0)
  		(void) tzparse(gmt, sp, TRUE);
  }
  
  #ifndef STD_INSPIRED
  /*
  ** A non-static declaration of tzsetwall in a system header file
--- 881,934 ----
  
  static void
  gmtload(sp)
! struct tzinfo * const	sp;
  {
  	if (tzload(gmt, sp) != 0)
  		(void) tzparse(gmt, sp, TRUE);
  }
  
+ 
+ static void
+ gmtset(sp)
+ struct tzinfo * const sp;
+ {
+ 	/*
+ 	** Fast-path the common-case (no locking needed once gmt is set).
+ 	*/
+ 	if (gmt_is_set) {
+ 		return;
+ 	}
+ 
+ 	LOCK_GMTPTR();
+ 	/*
+ 	** Another thread might have set it, so check gmt_is_set again.
+ 	*/
+ 	if (!gmt_is_set) {
+ 		gmt_is_set = TRUE;
+ 		if (sp != NULL)
+ 			gmtload(sp);
+ 	}
+ 	UNLOCK_GMTPTR();     
+ }
+ 
+ 
+ static void
+ tzsetwall_basic(sp)
+ struct tzinfo * const sp;
+ {
+ 	if (lcl_is_set < 0)
+ 		return;
+ 	lcl_is_set = -1;
+ 
+ 	if (sp == NULL) {
+ 		settzname(NULL);	/* all we can do */
+ 		return;
+ 	}
+ 	if (tzload((char *) NULL, sp) != 0)
+ 		gmtload(sp);
+ 	settzname(sp);
+ }
+ 
  #ifndef STD_INSPIRED
  /*
  ** A non-static declaration of tzsetwall in a system header file
***************
*** 926,957 ****
  void
  tzsetwall P((void))
  {
! 	if (lcl_is_set < 0)
! 		return;
! 	lcl_is_set = -1;
  
! #ifdef ALL_STATE
! 	if (lclptr == NULL) {
! 		lclptr = (struct state *) malloc(sizeof *lclptr);
! 		if (lclptr == NULL) {
! 			settzname();	/* all we can do */
! 			return;
! 		}
  	}
! #endif /* defined ALL_STATE */
! 	if (tzload((char *) NULL, lclptr) != 0)
! 		gmtload(lclptr);
! 	settzname();
  }
  
! void
! tzset P((void))
  {
  	register const char *	name;
  
  	name = getenv("TZ");
  	if (name == NULL) {
! 		tzsetwall();
  		return;
  	}
  
--- 939,967 ----
  void
  tzsetwall P((void))
  {
! 	struct tzinfo * sp;
! 	int process_wide;
  
! 	LOCK_LCLPTR();
! 	sp = GET_LCLPTR(&process_wide);
! 	if (!process_wide) {
! 		UNLOCK_LCLPTR();
! 		return;
  	}
! 
! 	tzsetwall_basic(sp);
! 	UNLOCK_LCLPTR();
  }
  
! static void
! tzset_basic(lclptr)
! struct tzinfo * const lclptr;
  {
  	register const char *	name;
  
  	name = getenv("TZ");
  	if (name == NULL) {
! 		tzsetwall_basic(lclptr);
  		return;
  	}
  
***************
*** 961,975 ****
  	if (lcl_is_set)
  		(void) strcpy(lcl_TZname, name);
  
- #ifdef ALL_STATE
  	if (lclptr == NULL) {
! 		lclptr = (struct state *) malloc(sizeof *lclptr);
! 		if (lclptr == NULL) {
! 			settzname();	/* all we can do */
! 			return;
! 		}
  	}
- #endif /* defined ALL_STATE */
  	if (*name == '\0') {
  		/*
  		** User wants it fast rather than right.
--- 971,980 ----
  	if (lcl_is_set)
  		(void) strcpy(lcl_TZname, name);
  
  	if (lclptr == NULL) {
! 		settzname(NULL);	/* all we can do */
! 		return;
  	}
  	if (*name == '\0') {
  		/*
  		** User wants it fast rather than right.
***************
*** 984,995 ****
  	} else if (tzload(name, lclptr) != 0)
  		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
  			(void) gmtload(lclptr);
! 	settzname();
  }
  
  /*
  ** The easy way to behave "as if no library function calls" localtime
! ** is to not call it--so we drop its guts into "localsub", which can be
  ** freely called.  (And no, the PANS doesn't require the above behavior--
  ** but it *is* desirable.)
  **
--- 989,1041 ----
  	} else if (tzload(name, lclptr) != 0)
  		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
  			(void) gmtload(lclptr);
! 	settzname(lclptr);
! }
! 
! void
! tzset P((void))
! {
! 	struct tzinfo * lclptr;
! 	int process_wide;
! 
! 	LOCK_LCLPTR();
! 	lclptr = GET_LCLPTR(&process_wide);
! 	if (!process_wide) {
! 		UNLOCK_LCLPTR();
! 		return;
! 	}
! 
! 	tzset_basic(lclptr);
! 	UNLOCK_LCLPTR();
! }
! 
! const struct ttinfo *
! _tz_getttype(timep, sp)
! const time_t * const timep;
! const struct tzinfo * sp;
! {
! 	register int			i;
! 	const time_t			t = *timep;
! 
! 	if (sp->timecnt == 0 || t < sp->ats[0]) {
! 		i = 0;
! 		while (sp->ttis[i].tt_isdst)
! 			if (++i >= sp->typecnt) {
! 				i = 0;
! 				break;
! 			}
! 	} else {
! 		for (i = 1; i < sp->timecnt; ++i)
! 			if (t < sp->ats[i])
! 				break;
! 		i = sp->types[i - 1];
! 	}
! 	return &sp->ttis[i];
  }
  
  /*
  ** The easy way to behave "as if no library function calls" localtime
! ** is to not call it--so we drop its guts into "tzsub", which can be
  ** freely called.  (And no, the PANS doesn't require the above behavior--
  ** but it *is* desirable.)
  **
***************
*** 998,1045 ****
  
  /*ARGSUSED*/
  static void
! localsub(timep, offset, tmp)
  const time_t * const	timep;
  const long		offset;
  struct tm * const	tmp;
  {
- 	register struct state *		sp;
  	register const struct ttinfo *	ttisp;
- 	register int			i;
  	const time_t			t = *timep;
  
- 	sp = lclptr;
- #ifdef ALL_STATE
  	if (sp == NULL) {
! 		gmtsub(timep, offset, tmp);
  		return;
  	}
! #endif /* defined ALL_STATE */
! 	if (sp->timecnt == 0 || t < sp->ats[0]) {
! 		i = 0;
! 		while (sp->ttis[i].tt_isdst)
! 			if (++i >= sp->typecnt) {
! 				i = 0;
! 				break;
! 			}
! 	} else {
! 		for (i = 1; i < sp->timecnt; ++i)
! 			if (t < sp->ats[i])
! 				break;
! 		i = sp->types[i - 1];
! 	}
! 	ttisp = &sp->ttis[i];
  	/*
  	** To get (wrong) behavior that's compatible with System V Release 2.0
  	** you'd replace the statement below with
! 	**	t += ttisp->tt_gmtoff;
  	**	timesub(&t, 0L, sp, tmp);
  	*/
! 	timesub(&t, ttisp->tt_gmtoff, sp, tmp);
  	tmp->tm_isdst = ttisp->tt_isdst;
! 	tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
  #ifdef TM_ZONE
! 	tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
  #endif /* defined TM_ZONE */
  }
  
--- 1044,1089 ----
  
  /*ARGSUSED*/
  static void
! tzsub(timep, sp, offset, tmp, alter_tzname)
  const time_t * const	timep;
+ const struct tzinfo * const	sp;
  const long		offset;
  struct tm * const	tmp;
+ int			alter_tzname;
  {
  	register const struct ttinfo *	ttisp;
  	const time_t			t = *timep;
  
  	if (sp == NULL) {
! 		nulltzsub(timep, offset, tmp);
  		return;
  	}
! 	ttisp = _tz_getttype(timep, sp);
  	/*
  	** To get (wrong) behavior that's compatible with System V Release 2.0
  	** you'd replace the statement below with
! 	**	t += ttisp->tt_gmtoff + offset;
  	**	timesub(&t, 0L, sp, tmp);
+ 	**
+ 	** ttisp->tt_gmtoff and offset should never both be non-zero
+ 	** simultaneously.
  	*/
! 	timesub(&t, ttisp->tt_gmtoff + offset, sp, tmp);
  	tmp->tm_isdst = ttisp->tt_isdst;
! 	if (alter_tzname) {
! 		tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
! 	}
  #ifdef TM_ZONE
! 	/*
! 	** Could get fancy here and deliver something such as
! 	** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
! 	** but this is no time for a treasure hunt.
! 	*/
! 	if (offset != 0)
! 		tmp->TM_ZONE = wildabbr;
! 	else {
! 		tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
! 	}
  #endif /* defined TM_ZONE */
  }
  
***************
*** 1047,1055 ****
  localtime(timep)
  const time_t * const	timep;
  {
! 	tzset();
! 	localsub(timep, 0L, &tm);
! 	return &tm;
  }
  
  /*
--- 1091,1114 ----
  localtime(timep)
  const time_t * const	timep;
  {
! 	struct tm * tm;
! 	struct tzinfo * tz;
! 	int process_wide;
! 	tm = GET_TM_BUF();
! 	if (tm == NULL)
! 		return NULL;
! 
! 	LOCK_LCLPTR();
! 	tz = GET_LCLPTR(&process_wide);
! 	if (tz == NULL) {
! 		UNLOCK_LCLPTR();
! 		return NULL;
! 	}
! 	if (process_wide)
! 		tzset_basic(tz);
! 	tzsub(timep, tz, 0L, tm, process_wide);
! 	UNLOCK_LCLPTR();
! 	return tm;
  }
  
  /*
***************
*** 1060,1088 ****
  const time_t * const	timep;
  struct tm *		tm;
  {
! 	localsub(timep, 0L, tm);
  	return tm;
  }
  
  /*
! ** gmtsub is to gmtime as localsub is to localtime.
  */
  
  static void
! gmtsub(timep, offset, tmp)
  const time_t * const	timep;
  const long		offset;
  struct tm * const	tmp;
  {
! 	if (!gmt_is_set) {
! 		gmt_is_set = TRUE;
! #ifdef ALL_STATE
! 		gmtptr = (struct state *) malloc(sizeof *gmtptr);
! 		if (gmtptr != NULL)
! #endif /* defined ALL_STATE */
! 			gmtload(gmtptr);
! 	}
! 	timesub(timep, offset, gmtptr, tmp);
  #ifdef TM_ZONE
  	/*
  	** Could get fancy here and deliver something such as
--- 1119,1150 ----
  const time_t * const	timep;
  struct tm *		tm;
  {
! 	struct tzinfo * tz;
! 	int process_wide;
! 	LOCK_LCLPTR();
! 	tz = GET_LCLPTR(&process_wide);
! 	if (tz == NULL) {
! 		UNLOCK_LCLPTR();
! 		return NULL;
! 	}
! 	if (process_wide)
! 		tzset_basic(tz);
! 	tzsub(timep, tz, 0L, tm, process_wide);
! 	UNLOCK_LCLPTR();
  	return tm;
  }
  
  /*
! ** nulltzsub is a stripped-down version of tzsub used if tz is NULL.
  */
  
  static void
! nulltzsub(timep, offset, tmp)
  const time_t * const	timep;
  const long		offset;
  struct tm * const	tmp;
  {
! 	timesub(timep, offset, NULL, tmp);
  #ifdef TM_ZONE
  	/*
  	** Could get fancy here and deliver something such as
***************
*** 1092,1105 ****
  	if (offset != 0)
  		tmp->TM_ZONE = wildabbr;
  	else {
! #ifdef ALL_STATE
! 		if (gmtptr == NULL)
! 			tmp->TM_ZONE = gmt;
! 		else	tmp->TM_ZONE = gmtptr->chars;
! #endif /* defined ALL_STATE */
! #ifndef ALL_STATE
! 		tmp->TM_ZONE = gmtptr->chars;
! #endif /* State Farm */
  	}
  #endif /* defined TM_ZONE */
  }
--- 1154,1160 ----
  	if (offset != 0)
  		tmp->TM_ZONE = wildabbr;
  	else {
! 		tmp->TM_ZONE = gmt;
  	}
  #endif /* defined TM_ZONE */
  }
***************
*** 1108,1115 ****
  gmtime(timep)
  const time_t * const	timep;
  {
! 	gmtsub(timep, 0L, &tm);
! 	return &tm;
  }
  
  /*
--- 1163,1181 ----
  gmtime(timep)
  const time_t * const	timep;
  {
! 	struct tm * tm;
! 	struct tzinfo * tz;
! 	tm = GET_TM_BUF();
! 	if (tm == NULL)
! 		return NULL;
! 
! 	tz = GET_GMTPTR();
! 	if (tz == NULL)
! 		return NULL;
! 	gmtset(tz);
! 	tzsub(timep, tz, 0L, tm, 0);
! 
! 	return tm;
  }
  
  /*
***************
*** 1120,1126 ****
  const time_t * const	timep;
  struct tm *		tm;
  {
! 	gmtsub(timep, 0L, tm);
  	return tm;
  }
  
--- 1186,1199 ----
  const time_t * const	timep;
  struct tm *		tm;
  {
! 	struct tzinfo * tz;
! 
! 	tz = GET_GMTPTR();
! 	if (tz == NULL)
! 		return NULL;
! 	gmtset(tz);
! 	tzsub(timep, tz, 0L, tm, 0);
! 
  	return tm;
  }
  
***************
*** 1131,1138 ****
  const time_t * const	timep;
  const long		offset;
  {
! 	gmtsub(timep, offset, &tm);
! 	return &tm;
  }
  
  #endif /* defined STD_INSPIRED */
--- 1204,1223 ----
  const time_t * const	timep;
  const long		offset;
  {
! 	struct tm * tm;
! 	struct tzinfo * tz;
! 
! 	tm = GET_TM_BUF();
! 	if (tm == NULL)
! 		return NULL;
! 
! 	tz = GET_GMTPTR();
! 	if (tz == NULL)
! 		return NULL;
! 	gmtset(tz);
! 	tzsub(timep, tz, offset, tm, 0);
! 
! 	return tm;
  }
  
  #endif /* defined STD_INSPIRED */
***************
*** 1141,1147 ****
  timesub(timep, offset, sp, tmp)
  const time_t * const			timep;
  const long				offset;
! register const struct state * const	sp;
  register struct tm * const		tmp;
  {
  	register const struct lsinfo *	lp;
--- 1226,1232 ----
  timesub(timep, offset, sp, tmp)
  const time_t * const			timep;
  const long				offset;
! register const struct tzinfo * const	sp;
  register struct tm * const		tmp;
  {
  	register const struct lsinfo *	lp;
***************
*** 1156,1167 ****
  
  	corr = 0;
  	hit = 0;
- #ifdef ALL_STATE
  	i = (sp == NULL) ? 0 : sp->leapcnt;
- #endif /* defined ALL_STATE */
- #ifndef ALL_STATE
- 	i = sp->leapcnt;
- #endif /* State Farm */
  	while (--i >= 0) {
  		lp = &sp->lsis[i];
  		if (*timep >= lp->ls_trans) {
--- 1241,1247 ----
***************
*** 1322,1336 ****
  	return result;
  }
  
! static time_t
! time2sub(tmp, funcp, offset, okayp, do_norm_secs)
  struct tm * const	tmp;
! void (* const		funcp) P((const time_t*, long, struct tm*));
  const long		offset;
- int * const		okayp;
  const int		do_norm_secs;
  {
- 	register const struct state *	sp;
  	register int			dir;
  	register int			bits;
  	register int			i, j ;
--- 1402,1415 ----
  	return result;
  }
  
! static int
! time2sub(timep, tmp, sp, offset, do_norm_secs)
! time_t *		timep;
  struct tm * const	tmp;
! const struct tzinfo *	sp;
  const long		offset;
  const int		do_norm_secs;
  {
  	register int			dir;
  	register int			bits;
  	register int			i, j ;
***************
*** 1339,1366 ****
  	time_t				t;
  	struct tm			yourtm, mytm;
  
- 	*okayp = FALSE;
  	yourtm = *tmp;
  	if (do_norm_secs) {
  		if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
  			SECSPERMIN))
! 				return WRONG;
  	}
  	if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
! 		return WRONG;
  	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
! 		return WRONG;
  	if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
! 		return WRONG;
  	/*
  	** Turn yourtm.tm_year into an actual year number for now.
  	** It is converted back to an offset from TM_YEAR_BASE later.
  	*/
  	if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
! 		return WRONG;
  	while (yourtm.tm_mday <= 0) {
  		if (increment_overflow(&yourtm.tm_year, -1))
! 			return WRONG;
  		i = yourtm.tm_year + (1 < yourtm.tm_mon);
  		yourtm.tm_mday += year_lengths[isleap(i)];
  	}
--- 1418,1444 ----
  	time_t				t;
  	struct tm			yourtm, mytm;
  
  	yourtm = *tmp;
  	if (do_norm_secs) {
  		if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
  			SECSPERMIN))
! 				return ERANGE;
  	}
  	if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
! 		return ERANGE;
  	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
! 		return ERANGE;
  	if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
! 		return ERANGE;
  	/*
  	** Turn yourtm.tm_year into an actual year number for now.
  	** It is converted back to an offset from TM_YEAR_BASE later.
  	*/
  	if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
! 		return ERANGE;
  	while (yourtm.tm_mday <= 0) {
  		if (increment_overflow(&yourtm.tm_year, -1))
! 			return ERANGE;
  		i = yourtm.tm_year + (1 < yourtm.tm_mon);
  		yourtm.tm_mday += year_lengths[isleap(i)];
  	}
***************
*** 1368,1374 ****
  		i = yourtm.tm_year + (1 < yourtm.tm_mon);
  		yourtm.tm_mday -= year_lengths[isleap(i)];
  		if (increment_overflow(&yourtm.tm_year, 1))
! 			return WRONG;
  	}
  	for ( ; ; ) {
  		i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
--- 1446,1452 ----
  		i = yourtm.tm_year + (1 < yourtm.tm_mon);
  		yourtm.tm_mday -= year_lengths[isleap(i)];
  		if (increment_overflow(&yourtm.tm_year, 1))
! 			return ERANGE;
  	}
  	for ( ; ; ) {
  		i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
***************
*** 1378,1388 ****
  		if (++yourtm.tm_mon >= MONSPERYEAR) {
  			yourtm.tm_mon = 0;
  			if (increment_overflow(&yourtm.tm_year, 1))
! 				return WRONG;
  		}
  	}
  	if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
! 		return WRONG;
  	if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
  		/*
  		** We can't set tm_sec to 0, because that might push the
--- 1456,1466 ----
  		if (++yourtm.tm_mon >= MONSPERYEAR) {
  			yourtm.tm_mon = 0;
  			if (increment_overflow(&yourtm.tm_year, 1))
! 				return ERANGE;
  		}
  	}
  	if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
! 		return ERANGE;
  	if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
  		/*
  		** We can't set tm_sec to 0, because that might push the
***************
*** 1393,1399 ****
  		** which is a safer assumption than using 58 would be.
  		*/
  		if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
! 			return WRONG;
  		saved_seconds = yourtm.tm_sec;
  		yourtm.tm_sec = SECSPERMIN - 1;
  	} else {
--- 1471,1477 ----
  		** which is a safer assumption than using 58 would be.
  		*/
  		if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
! 			return ERANGE;
  		saved_seconds = yourtm.tm_sec;
  		yourtm.tm_sec = SECSPERMIN - 1;
  	} else {
***************
*** 1412,1424 ****
  	*/
  	t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
  	for ( ; ; ) {
! 		(*funcp)(&t, offset, &mytm);
  		dir = tmcomp(&mytm, &yourtm);
  		if (dir != 0) {
! 			if (bits-- < 0)
! 				return WRONG;
! 			if (bits < 0)
! 				--t; /* may be needed if new t is minimal */
  			else if (dir > 0)
  				t -= ((time_t) 1) << bits;
  			else	t += ((time_t) 1) << bits;
--- 1490,1517 ----
  	*/
  	t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
  	for ( ; ; ) {
! 		tzsub(&t, sp, offset, &mytm, 0);
  		dir = tmcomp(&mytm, &yourtm);
  		if (dir != 0) {
! 			if (bits-- < 0) {
! 				/*
! 				** Assume that MAX_TIME and MIN_TIME aren't
! 				** near a leap-forward interval.
! 				** With signed 32-bit time_t, they're in
! 				** January and December respectively,
! 				** so it's unlikely.
! 				*/
! 				if (t == MAX_TIME || t == MIN_TIME)
! 					return ERANGE;
! 				return EINVAL;
! 			}
! 			if (bits < 0) {
! 				/* may be needed if new t is minimal,
! 				** since we started above the median.
! 				*/
! 				if (dir > 0)
! 					--t;
! 			}
  			else if (dir > 0)
  				t -= ((time_t) 1) << bits;
  			else	t += ((time_t) 1) << bits;
***************
*** 1435,1447 ****
  		/*
  		** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
  		*/
- 		sp = (const struct state *)
- 			(((void *) funcp == (void *) localsub) ?
- 			lclptr : gmtptr);
- #ifdef ALL_STATE
  		if (sp == NULL)
! 			return WRONG;
! #endif /* defined ALL_STATE */
  		for (i = sp->typecnt - 1; i >= 0; --i) {
  			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
  				continue;
--- 1528,1535 ----
  		/*
  		** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
  		*/
  		if (sp == NULL)
! 			return EINVAL;
  		for (i = sp->typecnt - 1; i >= 0; --i) {
  			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
  				continue;
***************
*** 1450,1456 ****
  					continue;
  				newt = t + sp->ttis[j].tt_gmtoff -
  					sp->ttis[i].tt_gmtoff;
! 				(*funcp)(&newt, offset, &mytm);
  				if (tmcomp(&mytm, &yourtm) != 0)
  					continue;
  				if (mytm.tm_isdst != yourtm.tm_isdst)
--- 1538,1544 ----
  					continue;
  				newt = t + sp->ttis[j].tt_gmtoff -
  					sp->ttis[i].tt_gmtoff;
! 				tzsub(&newt, sp, offset, &mytm, 0);
  				if (tmcomp(&mytm, &yourtm) != 0)
  					continue;
  				if (mytm.tm_isdst != yourtm.tm_isdst)
***************
*** 1462,1524 ****
  				goto label;
  			}
  		}
! 		return WRONG;
  	}
  label:
  	newt = t + saved_seconds;
  	if ((newt < t) != (saved_seconds < 0))
! 		return WRONG;
  	t = newt;
! 	(*funcp)(&t, offset, tmp);
! 	*okayp = TRUE;
! 	return t;
  }
  
! static time_t
! time2(tmp, funcp, offset, okayp)
  struct tm * const	tmp;
! void (* const		funcp) P((const time_t*, long, struct tm*));
  const long		offset;
- int * const		okayp;
  {
  	time_t	t;
  
  	/*
  	** First try without normalization of seconds
  	** (in case tm_sec contains a value associated with a leap second).
  	** If that fails, try with normalization of seconds.
  	*/
! 	t = time2sub(tmp, funcp, offset, okayp, FALSE);
! 	return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE);
  }
  
! static time_t
! time1(tmp, funcp, offset)
  struct tm * const	tmp;
! void (* const		funcp) P((const time_t *, long, struct tm *));
  const long		offset;
  {
! 	register time_t			t;
! 	register const struct state *	sp;
  	register int			samei, otheri;
! 	int				okay;
  
  	if (tmp->tm_isdst > 1)
  		tmp->tm_isdst = 1;
! 	t = time2(tmp, funcp, offset, &okay);
  #ifdef PCTS
! 	/*
! 	** PCTS code courtesy Grant Sullivan (grant@osf.org).
! 	*/
! 	if (okay)
! 		return t;
! 	if (tmp->tm_isdst < 0)
! 		tmp->tm_isdst = 0;	/* reset to std and try again */
! #endif /* defined PCTS */
! #ifndef PCTS
! 	if (okay || tmp->tm_isdst < 0)
! 		return t;
  #endif /* !defined PCTS */
  	/*
  	** We're supposed to assume that somebody took a time of one type
  	** and did some math on it that yielded a "struct tm" that's bad.
--- 1550,1621 ----
  				goto label;
  			}
  		}
! 		return EINVAL;
  	}
  label:
  	newt = t + saved_seconds;
  	if ((newt < t) != (saved_seconds < 0))
! 		return ERANGE;
  	t = newt;
! 	tzsub(&t, sp, offset, tmp, 0);
! 	*timep = t;
! 	return 0;
  }
  
! static int
! time2(timep, tmp, tz, offset)
! time_t *		timep;
  struct tm * const	tmp;
! const struct tzinfo *	tz;
  const long		offset;
  {
  	time_t	t;
+ 	int err;
  
  	/*
  	** 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.
  	*/
! 	err = time2sub(&t, tmp, tz, offset, FALSE);
! 	if (err == 0) {
! 		*timep = t;
! 		return 0;
! 	}
! 	return time2sub(timep, tmp, tz, offset, TRUE);
  }
  
! static int
! time1(timep, tmp, sp, offset, accomodate_gap)
! time_t *		timep;
  struct tm * const	tmp;
! const struct tzinfo *	sp;
  const long		offset;
+ int			accomodate_gap;
  {
! 	time_t				t;
  	register int			samei, otheri;
! 	int				err;
  
  	if (tmp->tm_isdst > 1)
  		tmp->tm_isdst = 1;
! 	err = time2(&t, tmp, sp, offset);
! 	if (err == 0) {
! 		*timep = t;
! 		return 0;
! 	}
! 	if (tmp->tm_isdst < 0) {
  #ifdef PCTS
! 		/*
! 		** PCTS code courtesy Grant Sullivan (grant@osf.org).
! 		*/
! 		if (accomodate_gap)
! 			/* reset to std and try again */
! 			tmp->tm_isdst = 0;
! 	        else
  #endif /* !defined PCTS */
+ 			return (err != 0 ? err : EINVAL);
+ 	}
  	/*
  	** 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.
***************
*** 1528,1539 ****
  	/*
  	** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
  	*/
- 	sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
- 		lclptr : gmtptr);
- #ifdef ALL_STATE
  	if (sp == NULL)
! 		return WRONG;
! #endif /* defined ALL_STATE */
  	for (samei = sp->typecnt - 1; samei >= 0; --samei) {
  		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
  			continue;
--- 1625,1632 ----
  	/*
  	** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
  	*/
  	if (sp == NULL)
! 		return (err != 0 ? err : EINVAL);
  	for (samei = sp->typecnt - 1; samei >= 0; --samei) {
  		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
  			continue;
***************
*** 1543,1565 ****
  			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
  					sp->ttis[samei].tt_gmtoff;
  			tmp->tm_isdst = !tmp->tm_isdst;
! 			t = time2(tmp, funcp, offset, &okay);
! 			if (okay)
! 				return t;
  			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
  					sp->ttis[samei].tt_gmtoff;
  			tmp->tm_isdst = !tmp->tm_isdst;
  		}
  	}
! 	return WRONG;
  }
  
  time_t
  mktime(tmp)
  struct tm * const	tmp;
  {
! 	tzset();
! 	return time1(tmp, localsub, 0L);
  }
  
  #ifdef STD_INSPIRED
--- 1636,1670 ----
  			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
  					sp->ttis[samei].tt_gmtoff;
  			tmp->tm_isdst = !tmp->tm_isdst;
! 			err = time2(&t, tmp, sp, offset);
! 			if (err == 0) {
! 				*timep = t;
! 				return 0;
! 			}
  			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
  					sp->ttis[samei].tt_gmtoff;
  			tmp->tm_isdst = !tmp->tm_isdst;
  		}
  	}
! 	return (err != 0 ? err : EINVAL);
  }
  
  time_t
  mktime(tmp)
  struct tm * const	tmp;
  {
! 	time_t mktime_return_value;
! 	int err;
! 	struct tzinfo *tz;
! 	int process_wide;
! 
! 	LOCK_LCLPTR();
! 	tz = GET_LCLPTR(&process_wide);
! 	if (process_wide)
! 		tzset_basic(tz);
! 	err = time1(&mktime_return_value, tmp, tz, 0L, 1);
! 	UNLOCK_LCLPTR();
! 	return (err == 0 ? mktime_return_value : WRONG);
  }
  
  #ifdef STD_INSPIRED
***************
*** 1576,1583 ****
  timegm(tmp)
  struct tm * const	tmp;
  {
  	tmp->tm_isdst = 0;
! 	return time1(tmp, gmtsub, 0L);
  }
  
  time_t
--- 1681,1692 ----
  timegm(tmp)
  struct tm * const	tmp;
  {
+ 	time_t timegm_return_value;
+ 	int err;
+ 
  	tmp->tm_isdst = 0;
! 	err = time1(&timegm_return_value, tmp, GET_GMTPTR(), 0L, 1);
! 	return (err == 0 ? timegm_return_value : WRONG);
  }
  
  time_t
***************
*** 1585,1594 ****
  struct tm * const	tmp;
  const long		offset;
  {
  	tmp->tm_isdst = 0;
! 	return time1(tmp, gmtsub, offset);
  }
  
  #endif /* defined STD_INSPIRED */
  
  #ifdef CMUCS
--- 1694,1811 ----
  struct tm * const	tmp;
  const long		offset;
  {
+ 	time_t timeoff_return_value;
+ 	int err;
+ 
  	tmp->tm_isdst = 0;
! 	err = time1(&timeoff_return_value, tmp, GET_GMTPTR(), offset, 1);
! 	return (err == 0 ? timeoff_return_value : WRONG);
! }
! 
! 
! /*
! ** Thread-safety functions
! */
! 
! int
! tz_prep(tz_p, name)
! struct tzinfo** tz_p;
! const char * name;
! {
! 	struct tzinfo * result;
! 
! 	result = (struct tzinfo *) malloc(sizeof(struct tzinfo));
! 	if (result == NULL) {
! 		return errno;
! 	}
! 
! 	if (name != NULL && *name == '\0') {
! 		/* Zero-length string.  Indicates GMT. */
! #ifdef TZCODE_GMT_USAGE
! 		/*
! 		** Traditional tzcode usage is that the zero-length string
! 		** means GMT without leap seconds.
! 		*/
! 		result->leapcnt = 0;		/* so, we're off a little */
! 		result->timecnt = 0;
! 		result->typecnt = 0;
! 		result->ttis[0].tt_isdst = 0;
! 		result->ttis[0].tt_gmtoff = 0;
! 		result->ttis[0].tt_abbrind = 0;
! 		(void) strcpy(result->chars, gmt);
! #else
! 		gmtload(result);
! #endif
! 	}
! 	else {
! 		/*
! 		** 1.  Try to tzload.
! 		** 2.  If name is non-NULL and doesn't begin with :,
! 		**     try to tzparse.
! 		** 3.  Fail ENOENT.
! 		*/
! 		if (tzload(name, result) != 0) {
! 			if (name != NULL && name[0] != ':') {
! 				if (tzparse(name, result, FALSE) != 0) {
! 					goto noentry;
! 				}
! 			}
! 			else {
! 				goto noentry;
! 			}
! 		}
! 	}
! 
! 	*tz_p = result;
! 	return 0;
! 
!  noentry:
! 	free(result);
! 	return ENOENT;
  }
  
+ 
+ void
+ tz_free(tz)
+ struct tzinfo* tz;
+ {
+ 	free(tz);
+ }
+ 
+ 
+ int
+ time_make(timep, tmp, tz)
+ time_t * timep;
+ struct tm *tmp;
+ const struct tzinfo *tz;
+ {
+ 	return time1(timep, tmp, tz, 0L, 0);
+ }
+ 
+ 
+ int time_breakup(tmp, timep, tz)
+ struct tm * tmp;
+ const time_t * timep;
+ const struct tzinfo * tz;
+ {
+ 	tzsub(timep, tz, 0L, tmp, 0);
+ 	return 0;
+ }
+ 
+ 
+ int
+ pthread_settz(tz)
+ const struct tzinfo * tz;
+ {
+ 	if (tz != NULL) {
+ 		return SET_THREAD_LCLPTR(tz);
+ 	}
+ 	else {
+ 		return CLEAR_THREAD_LCLPTR();
+ 	}
+ }
+ 
+ 
  #endif /* defined STD_INSPIRED */
  
  #ifdef CMUCS
***************
*** 1626,1640 ****
  */
  
  static long
! leapcorr(timep)
! time_t *	timep;
  {
! 	register struct state *		sp;
! 	register struct lsinfo *	lp;
  	register int			i;
  
! 	sp = lclptr;
! 	i = sp->leapcnt;
  	while (--i >= 0) {
  		lp = &sp->lsis[i];
  		if (*timep >= lp->ls_trans)
--- 1843,1856 ----
  */
  
  static long
! leapcorr(timep, sp)
! time_t *			timep;
! const struct tzinfo * const	sp;
  {
! 	register const struct lsinfo *	lp;
  	register int			i;
  
! 	i = (sp == NULL ? 0 : sp->leapcnt);
  	while (--i >= 0) {
  		lp = &sp->lsis[i];
  		if (*timep >= lp->ls_trans)
***************
*** 1647,1654 ****
  time2posix(t)
  time_t	t;
  {
! 	tzset();
! 	return t - leapcorr(&t);
  }
  
  time_t
--- 1863,1873 ----
  time2posix(t)
  time_t	t;
  {
! 	const struct tzinfo * sp;
! 
! 	sp = GET_GMTPTR();
! 	gmtset(sp);
! 	return t - leapcorr(&t, sp);
  }
  
  time_t
***************
*** 1657,1683 ****
  {
  	time_t	x;
  	time_t	y;
  
- 	tzset();
  	/*
  	** For a positive leap second hit, the result
  	** is not unique.  For a negative leap second
  	** hit, the corresponding time doesn't exist,
  	** so we return an adjacent second.
  	*/
! 	x = t + leapcorr(&t);
! 	y = x - leapcorr(&x);
  	if (y < t) {
  		do {
  			x++;
! 			y = x - leapcorr(&x);
  		} while (y < t);
  		if (t != y)
  			return x - 1;
  	} else if (y > t) {
  		do {
  			--x;
! 			y = x - leapcorr(&x);
  		} while (y > t);
  		if (t != y)
  			return x + 1;
--- 1876,1905 ----
  {
  	time_t	x;
  	time_t	y;
+ 	const struct tzinfo * sp;
+ 
+ 	sp = GET_GMTPTR();
+ 	gmtset(sp);
  
  	/*
  	** For a positive leap second hit, the result
  	** is not unique.  For a negative leap second
  	** hit, the corresponding time doesn't exist,
  	** so we return an adjacent second.
  	*/
! 	x = t + leapcorr(&t, sp);
! 	y = x - leapcorr(&x, sp);
  	if (y < t) {
  		do {
  			x++;
! 			y = x - leapcorr(&x, sp);
  		} while (y < t);
  		if (t != y)
  			return x - 1;
  	} else if (y > t) {
  		do {
  			--x;
! 			y = x - leapcorr(&x, sp);
  		} while (y > t);
  		if (t != y)
  			return x + 1;
diff -crN -x *~ ../tzcode2001c/private.h thread-safe-2/private.h
*** ../tzcode2001c/private.h	Tue Jun  5 13:48:21 2001
--- thread-safe-2/private.h	Mon Jun 18 15:59:54 2001
***************
*** 206,211 ****
--- 206,215 ----
  void	ifree P((char * pointer));
  char *	scheck P((const char *string, const char *format));
  
+ struct tzinfo;
+ const struct ttinfo * _tz_getttype P((const time_t * const timep,
+ 				      const struct tzinfo * sp));
+ 
  
  /*
  ** Finally, some convenience items.
***************
*** 237,242 ****
--- 241,256 ----
  #define INT_STRLEN_MAXIMUM(type) \
      ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
  #endif /* !defined INT_STRLEN_MAXIMUM */
+ 
+ /*
+ ** Big enough for something such as
+ ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
+ ** (two three-character abbreviations, five strings denoting integers,
+ ** three explicit spaces, two explicit colons, a newline,
+ ** and a trailing ASCII nul).
+ */
+ 
+ #define ASCTIME_BUF_SIZE (3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + 3 + 2 + 1 + 1)
  
  /*
  ** INITIALIZE(x)
diff -crN -x *~ ../tzcode2001c/pthread_settz.3 thread-safe-2/pthread_settz.3
*** ../tzcode2001c/pthread_settz.3	Wed Dec 31 19:00:00 1969
--- thread-safe-2/pthread_settz.3	Mon Jun 18 15:46:08 2001
***************
*** 0 ****
--- 1,100 ----
+ .TH PTHREAD_SETTZ 3
+ .SH NAME
+ pthread_settz \- locally set the time zone for the current POSIX thread
+ .SH SYNOPSIS
+ .nf
+ .B #include <time.h>
+ .B #include <pthread.h>
+ .PP
+ .B int pthread_settz(tz)
+ .B const struct tzinfo *tz;
+ .PP
+ .B cc ... -ltz -lpthread
+ .fi
+ .SH DESCRIPTION
+ .B Pthread_settz()
+ sets the time zone object to be used for time functions called from the
+ current POSIX thread to the time zone object specified by
+ .IR tz ,
+ if it is non-\fBNULL\fR.  If
+ .IR tz
+ is
+ .BR NULL ,
+ the time zone for the current POSIX thread is reset to the global time zone
+ object. The time zone setting for all other threads remains the same.
+ .PP
+ Once
+ .B pthread_settz()
+ has been called with a non-\fBNULL\fR value, all calls to the functions
+ .BR localtime(3) ,
+ .BR localtime_r(3) ,
+ .BR ctime(3) ,
+ .BR ctime_r(3) ,
+ .BR asctime(3) ,
+ .BR asctime_r(3) ,
+ .BR mktime(3) ,
+ and
+ .BR strftime(3)
+ in the thread from which 
+ .B pthread_settz()
+ was called will use the time zone specified by
+ .IR tz ,
+ rather than the process-wide time zone.
+ .PP
+ .B pthread_settz()
+ does not alter the global variable
+ .BR tzname .
+ No function which normally implicitly sets
+ .B tzname
+ will do so while a thread-local time zone is in effect.
+ .PP
+ Calls to 
+ .B tzset(3)
+ or
+ .BR tzsetwall(3) ,
+ while a thread-local time zone is in effect, will alter the global time
+ zone, but will not affect the local time zone.
+ .PP
+ .B pthread_settz()
+ may allocate a thread-specific data key, which may count towards the number
+ of keys
+ .B PTHREAD_KEYS_MAX
+ that can be allocated by
+ .BR pthread_key_create() .
+ .SH RETURN VALUES
+ Upon successful completion,
+ .B pthread_settz()
+ function returns a value of
+ .BR 0 .
+ Otherwise, an error number is returned to indicate an error.
+ .SH ERRORS
+ .B pthread_settz()
+ may fail if:
+ .IP \fBENOMEM\fR
+ Insufficient memory exists to store the time zone information for the
+ thread.
+ .PP
+ .IP \fBEAGAIN\fR
+ The system lacked the necessary resources to associate the time zone
+ information with the thread.
+ .SH SEE ALSO
+ getenv(3),
+ newctime(3),
+ newstrftime(3),
+ newtzset(3),
+ pthread_key_create(3),
+ time(2),
+ time_make(3),
+ tz_prep(3),
+ tzfile(5)
+ .SH NOTES
+ This function allows existing code which uses the ISO C APIs to work
+ correctly, unmodified, in a threaded environment.  However, code which uses
+ use the extended parts of the POSIX APIs (involving global variables, or
+ .BR tzset() )
+ may not work correctly unmodified.
+ .PP
+ No parallel function
+ .B pthread_gettz()
+ is defined.
+ .\" @(#)pthread_settz.3	1.0
diff -crN -x *~ ../tzcode2001c/strftime.c thread-safe-2/strftime.c
*** ../tzcode2001c/strftime.c	Tue Jun  5 13:49:50 2001
--- thread-safe-2/strftime.c	Mon Jun 18 16:11:40 2001
***************
*** 9,14 ****
--- 9,15 ----
  #endif /* !defined lint */
  
  #include "private.h"
+ #include "struct_tzinfo.h"
  
  /*
  ** Copyright (c) 1989 The Regents of the University of California.
***************
*** 37,42 ****
--- 38,47 ----
  #include "fcntl.h"
  #include "locale.h"
  
+ #include "tztimeext.h"
+ 
+ #include TZTHREAD_HEADER_H
+ 
  struct lc_time_T {
  	const char *	mon[MONSPERYEAR];
  	const char *	month[MONSPERYEAR];
***************
*** 108,114 ****
  
  static char *	_add P((const char *, char *, const char *));
  static char *	_conv P((int, const char *, char *, const char *));
! static char *	_fmt P((const char *, const struct tm *, char *, const char *, int *));
  
  size_t strftime P((char *, size_t, const char *, const struct tm *));
  
--- 113,119 ----
  
  static char *	_add P((const char *, char *, const char *));
  static char *	_conv P((int, const char *, char *, const char *));
! static char *	_fmt P((const char *, const struct tm *, char *, const char *, int *, const struct tzinfo *));
  
  size_t strftime P((char *, size_t, const char *, const struct tm *));
  
***************
*** 125,145 ****
  #define IN_ALL	3
  
  size_t
! strftime(s, maxsize, format, t)
  char * const		s;
  const size_t		maxsize;
  const char * const	format;
  const struct tm * const	t;
  {
  	char *	p;
  	int	warn;
  
- 	tzset();
  #ifdef LOCALE_HOME
  	localebuf.mon[0] = 0;
  #endif /* defined LOCALE_HOME */
  	warn = IN_NONE;
! 	p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
  #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
  	if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
  		(void) fprintf(stderr, "\n");
--- 130,151 ----
  #define IN_ALL	3
  
  size_t
! strftime_z(s, maxsize, format, t, tz)
  char * const		s;
  const size_t		maxsize;
  const char * const	format;
  const struct tm * const	t;
+ const struct tzinfo * tz;
  {
  	char *	p;
  	int	warn;
  
  #ifdef LOCALE_HOME
  	localebuf.mon[0] = 0;
  #endif /* defined LOCALE_HOME */
  	warn = IN_NONE;
! 	p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn,
! 		 tz);
  #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
  	if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
  		(void) fprintf(stderr, "\n");
***************
*** 156,174 ****
  		(void) fprintf(stderr, "\n");
  	}
  #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
! 	if (p == s + maxsize)
  		return 0;
  	*p = '\0';
  	return p - s;
  }
  
  static char *
! _fmt(format, t, pt, ptlim, warnp)
  const char *		format;
  const struct tm * const	t;
  char *			pt;
  const char * const	ptlim;
  int *			warnp;
  {
  	for ( ; *format; ++format) {
  		if (*format == '%') {
--- 162,206 ----
  		(void) fprintf(stderr, "\n");
  	}
  #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
! 	if (p >= s + maxsize)
! 		/* XXX */
  		return 0;
  	*p = '\0';
  	return p - s;
  }
  
+ 
+ size_t
+ strftime(s, maxsize, format, t)
+ char * const		s;
+ const size_t		maxsize;
+ const char * const	format;
+ const struct tm * const	t;
+ {
+ 	struct tzinfo * lclptr;
+ 	int dummy;
+ 	size_t ret;
+ 	tzset();
+ 
+ 	LOCK_LCLPTR();
+ 	lclptr = GET_LCLPTR(&dummy);
+ 
+ 	ret = strftime_z(s, maxsize, format, t, lclptr);
+ 	UNLOCK_LCLPTR();
+ 
+ 	if (ret > maxsize)
+ 		ret = 0;
+ 	return ret;
+ }
+ 
  static char *
! _fmt(format, t, pt, ptlim, warnp, tz)
  const char *		format;
  const struct tm * const	t;
  char *			pt;
  const char * const	ptlim;
  int *			warnp;
+ const struct tzinfo *	tz;
  {
  	for ( ; *format; ++format) {
  		if (*format == '%') {
***************
*** 217,223 ****
  				{
  				int warn2 = IN_SOME;
  
! 				pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp);
  				if (warn2 == IN_ALL)
  					warn2 = IN_THIS;
  				if (warn2 > *warnp)
--- 249,255 ----
  				{
  				int warn2 = IN_SOME;
  
! 				pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp, tz);
  				if (warn2 == IN_ALL)
  					warn2 = IN_THIS;
  				if (warn2 > *warnp)
***************
*** 225,231 ****
  				}
  				continue;
  			case 'D':
! 				pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
  				continue;
  			case 'd':
  				pt = _conv(t->tm_mday, "%02d", pt, ptlim);
--- 257,263 ----
  				}
  				continue;
  			case 'D':
! 				pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, tz);
  				continue;
  			case 'd':
  				pt = _conv(t->tm_mday, "%02d", pt, ptlim);
***************
*** 246,252 ****
  				pt = _conv(t->tm_mday, "%2d", pt, ptlim);
  				continue;
  			case 'F':
! 				pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
  				continue;
  			case 'H':
  				pt = _conv(t->tm_hour, "%02d", pt, ptlim);
--- 278,284 ----
  				pt = _conv(t->tm_mday, "%2d", pt, ptlim);
  				continue;
  			case 'F':
! 				pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, tz);
  				continue;
  			case 'H':
  				pt = _conv(t->tm_hour, "%02d", pt, ptlim);
***************
*** 310,319 ****
  					pt, ptlim);
  				continue;
  			case 'R':
! 				pt = _fmt("%H:%M", t, pt, ptlim, warnp);
  				continue;
  			case 'r':
! 				pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
  				continue;
  			case 'S':
  				pt = _conv(t->tm_sec, "%02d", pt, ptlim);
--- 342,351 ----
  					pt, ptlim);
  				continue;
  			case 'R':
! 				pt = _fmt("%H:%M", t, pt, ptlim, warnp, tz);
  				continue;
  			case 'r':
! 				pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp, tz);
  				continue;
  			case 'S':
  				pt = _conv(t->tm_sec, "%02d", pt, ptlim);
***************
*** 336,342 ****
  				}
  				continue;
  			case 'T':
! 				pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
  				continue;
  			case 't':
  				pt = _add("\t", pt, ptlim);
--- 368,374 ----
  				}
  				continue;
  			case 'T':
! 				pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, tz);
  				continue;
  			case 't':
  				pt = _add("\t", pt, ptlim);
***************
*** 449,455 ****
  				** "date as dd-bbb-YYYY"
  				** (ado, 1993-05-24)
  				*/
! 				pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
  				continue;
  			case 'W':
  				pt = _conv((t->tm_yday + DAYSPERWEEK -
--- 481,487 ----
  				** "date as dd-bbb-YYYY"
  				** (ado, 1993-05-24)
  				*/
! 				pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, tz);
  				continue;
  			case 'W':
  				pt = _conv((t->tm_yday + DAYSPERWEEK -
***************
*** 462,474 ****
  				pt = _conv(t->tm_wday, "%d", pt, ptlim);
  				continue;
  			case 'X':
! 				pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
  				continue;
  			case 'x':
  				{
  				int	warn2 = IN_SOME;
  
! 				pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
  				if (warn2 == IN_ALL)
  					warn2 = IN_THIS;
  				if (warn2 > *warnp)
--- 494,506 ----
  				pt = _conv(t->tm_wday, "%d", pt, ptlim);
  				continue;
  			case 'X':
! 				pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp, tz);
  				continue;
  			case 'x':
  				{
  				int	warn2 = IN_SOME;
  
! 				pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2, tz);
  				if (warn2 == IN_ALL)
  					warn2 = IN_THIS;
  				if (warn2 > *warnp)
***************
*** 490,498 ****
  					pt = _add(t->TM_ZONE, pt, ptlim);
  				else
  #endif /* defined TM_ZONE */
! 				if (t->tm_isdst >= 0)
! 					pt = _add(tzname[t->tm_isdst != 0],
  						pt, ptlim);
  				/*
  				** C99 says that %Z must be replaced by the
  				** empty string if the time zone is not
--- 522,539 ----
  					pt = _add(t->TM_ZONE, pt, ptlim);
  				else
  #endif /* defined TM_ZONE */
! 				if (t->tm_isdst >= 0) {
! 					time_t tval;
! 					struct tm tcopy = *t;
! 					const struct ttinfo *	ttisp;
! 
! 				if (time_make(&tval, &tcopy, tz) != 0)
! 					continue;
! 				ttisp = _tz_getttype(&tval, tz);
! 
! 				pt = _add(&tz->chars[ttisp->tt_abbrind],
  						pt, ptlim);
+ 				}
  				/*
  				** C99 says that %Z must be replaced by the
  				** empty string if the time zone is not
***************
*** 514,545 ****
  				** be computed by looking only at
  				** tm_isdst.  This requirement is
  				** incorrect, since it means the code
! 				** must rely on magic (in this case
! 				** altzone and timezone), and the
  				** magic might not have the correct
! 				** offset.  Doing things correctly is
! 				** tricky and requires disobeying C99;
! 				** see GNU C strftime for details.
! 				** For now, punt and conform to the
! 				** standard, even though it's incorrect.
! 				**
! 				** C99 says that %z must be replaced by the
! 				** empty string if the time zone is not
! 				** determinable, so output nothing if the
! 				** appropriate variables are not available.
  				*/
! 				if (t->tm_isdst == 0)
! #ifdef USG_COMPAT
! 					diff = -timezone;
! #else /* defined USG_COMPAT */
! 					continue;
! #endif /* !defined USG_COMPAT */
! 				else
! #ifdef ALTZONE
! 					diff = -altzone;
! #else /* !defined ALTZONE */
! 					continue;
! #endif /* !defined ALTZONE */
  #endif /* !defined TM_GMTOFF */
  				if (diff < 0) {
  					sign = "-";
--- 555,579 ----
  				** be computed by looking only at
  				** tm_isdst.  This requirement is
  				** incorrect, since it means the code
! 				** must rely on magic, and the
  				** magic might not have the correct
! 				** offset.
! 				** We perform hopefully-correct magic by
! 				** peeking inside the struct tzinfo
! 				** structure, in an encapsulation
! 				** violation, to find the appropriate
! 				** transition type.
  				*/
! 				{
! 					time_t tval;
! 					struct tm tcopy = *t;
! 					const struct ttinfo *	ttisp;
! 				  
! 					if (time_make(&tval, &tcopy, tz) != 0)
! 						continue;
! 					ttisp = _tz_getttype(&tval, tz);
! 					diff = ttisp->tt_gmtoff;
! 				}
  #endif /* !defined TM_GMTOFF */
  				if (diff < 0) {
  					sign = "-";
***************
*** 553,559 ****
  				continue;
  			case '+':
  				pt = _fmt(Locale->date_fmt, t, pt, ptlim,
! 					warnp);
  				continue;
  			case '%':
  			/*
--- 587,593 ----
  				continue;
  			case '+':
  				pt = _fmt(Locale->date_fmt, t, pt, ptlim,
! 					warnp, tz);
  				continue;
  			case '%':
  			/*
***************
*** 603,609 ****
  	static const char	locale_home[] = LOCALE_HOME;
  	static const char	lc_time[] = "LC_TIME";
  	static char *		locale_buf;
- 	static char		locale_buf_C[] = "C";
  
  	int			fd;
  	int			oldsun;	/* "...ain't got nothin' to do..." */
--- 637,642 ----
***************
*** 664,671 ****
  		goto bad_locale;
  	bufsize = namesize + st.st_size;
  	locale_buf = NULL;
! 	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
! 		malloc(bufsize) : realloc(lbuf, bufsize);
  	if (lbuf == NULL)
  		goto bad_locale;
  	(void) strcpy(lbuf, name);
--- 697,703 ----
  		goto bad_locale;
  	bufsize = namesize + st.st_size;
  	locale_buf = NULL;
! 	lbuf = (lbuf == NULL) ?	 malloc(bufsize) : realloc(lbuf, bufsize);
  	if (lbuf == NULL)
  		goto bad_locale;
  	(void) strcpy(lbuf, name);
***************
*** 714,720 ****
  	(void) close(fd);
  no_locale:
  	localebuf = C_time_locale;
! 	locale_buf = locale_buf_C;
  	return &localebuf;
  }
  #endif /* defined LOCALE_HOME */
--- 746,752 ----
  	(void) close(fd);
  no_locale:
  	localebuf = C_time_locale;
! 	locale_buf = NULL;
  	return &localebuf;
  }
  #endif /* defined LOCALE_HOME */
diff -crN -x *~ ../tzcode2001c/struct_tzinfo.h thread-safe-2/struct_tzinfo.h
*** ../tzcode2001c/struct_tzinfo.h	Wed Dec 31 19:00:00 1969
--- thread-safe-2/struct_tzinfo.h	Mon Jun 18 15:46:07 2001
***************
*** 0 ****
--- 1,58 ----
+ #ifndef STRUCT_TIMEZONE_H
+ 
+ #define STRUCT_TIMEZONE_H
+ 
+ #include "private.h"
+ #include "tzfile.h"
+ 
+ /*
+ ** This file is in the public domain, so clarified as of
+ ** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+ */
+ 
+ /*
+ ** This header is for use ONLY with the time conversion code.
+ ** There is no guarantee that it will remain unchanged,
+ ** or that it will remain at all.
+ ** Do NOT copy it to any system include directory.
+ ** Thank you!
+ */
+ 
+ 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 tzinfo {
+ 	int		leapcnt;
+ 	int		timecnt;
+ 	int		typecnt;
+ 	int		charcnt;
+ 	time_t		ats[TZ_MAX_TIMES];
+ 	unsigned char	types[TZ_MAX_TIMES];
+ 	struct ttinfo	ttis[TZ_MAX_TYPES];
+ 	char		chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof "GMT"),
+ 				(2 * (MY_TZNAME_MAX + 1)))];
+ 	struct lsinfo	lsis[TZ_MAX_LEAPS];
+ };
+ 
+ 
+ 
+ #endif /* !defined STRUCT_TIMEZONE_H */
diff -crN -x *~ ../tzcode2001c/time_make.3 thread-safe-2/time_make.3
*** ../tzcode2001c/time_make.3	Wed Dec 31 19:00:00 1969
--- thread-safe-2/time_make.3	Mon Jun 18 15:46:08 2001
***************
*** 0 ****
--- 1,277 ----
+ .TH TIME_MAKE 3
+ .SH NAME
+ time_make, time_breakup, strftime_z \- manipulate time-zone-dependent
+ time conversions in a thread-safe manner
+ .SH SYNOPSIS
+ .nf
+ .B #include <time.h>
+ .B #include <sys/types.h>
+ .PP
+ .B int time_make(clock, tm, tz)
+ .B time_t *clock;
+ .B struct tm *tm;
+ .B const struct tzinfo *tz;
+ .PP
+ .B int time_breakup(result, clock, tz)
+ .B struct tm *result;
+ .B const time_t *clock;
+ .B const struct tzinfo *tz;
+ .PP
+ .B size_t strftime_z(buf, maxsize, format, timeptr, tz)
+ .B char * restrict buf;
+ .B size_t maxsize;
+ .B const char * restrict format;
+ .B const struct tm * restrict timeptr;
+ .B const struct tzinfo * restrict tz;
+ .PP
+ .B cc ... -ltz
+ .fi
+ .SH DESCRIPTION
+ .B Time_make()
+ interprets the broken-down time in
+ .I *tm
+ as a local time in the timezone specified in
+ .IR *tz,
+ and writes the corresponding value into
+ .IR *clock,
+ using the same encoding as that of the values returned by the
+ .I time function.
+ .PP
+ The original values of the
+ .B tm_wday
+ and
+ .B tm_yday
+ components of
+ .I *tm
+ are ignored, and the original values of the other components are not
+ restricted to their normal ranges.  (A positive or zero value for
+ .B tm_isdst
+ causes
+ .B time_make()
+ to presume initially that summer time (for example, Daylight Saving Time) is
+ or is not in effect for the specified time, respectively.  A negative value
+ for
+ .B tm_isdst
+ causes the
+ .B time_make() 
+ function to attempt to divine whether summer time is in effect for the
+ specified time.)
+ .PP
+ On successful completion, the values of the
+ .B tm_wday
+ and
+ .B tm_yday
+ components of
+ .I *tm
+ are set appropriately, and the other components are set to represent the
+ specified calendar time, but with their values forced to their normal
+ ranges; the final value of
+ .B tm_mday
+ is not set until
+ .B tm_mon
+ and
+ .B tm_year
+ are determined.
+ .PP
+ .B Time_breakup()
+ converts the time value in
+ .I *clock
+ to a broken-out time for the value, which it places in
+ .IR *result ,
+ after adjusting for the time zone specified in
+ .IR *tz .
+ .PP
+ .B Strftime_z()
+ formats the information from
+ .I timeptr
+ into the buffer
+ .I buf
+ according to the string pointed to by
+ .IR format .
+ The 
+ .I format
+ string follows the same specification as for the function strftime(3),
+ except that the values for the conversion specifiers
+ .B %Z
+ and
+ .B %z
+ are obtained from the time zone
+ .IR tz .
+ If
+ .B strftime_z()
+ is passed a
+ .B struct tm
+ created by a call to
+ .B time_make()
+ or
+ .B time_breakup()
+ which was given a different time zone object than
+ .IR tz ,
+ or if it is passed a
+ .B struct tm
+ created by
+ .BR localtime() ,
+ .BR localtime_r() ,
+ .BR gmtime() ,
+ .BR gmtime_r() ,
+ .BR mktime() , 
+ or
+ .BR timegm() ,
+ the behavior of the
+ .B %Z
+ and 
+ .B %z
+ conversion specifiers is undefined.
+ .SH RETURN VALUES
+ The
+ .B time_make()
+ function returns a value of
+ .B 0
+ on sucessful completion, sets
+ .IR *clock ,
+ and normalizes
+ .IR *tm .
+ On failure, it returns an error number, and leaves 
+ .I *clock
+ and
+ .I *tm
+ in an indeterminate state.
+ .PP
+ The 
+ .B time_breakup()
+ function returns
+ .BR 0 ,
+ and fills in
+ .IR *result ,
+ always.
+ .PP
+ On successful completion,
+ .B strftime_z()
+ fills in
+ .I buf
+ and returns the number of bytes converted.  On failure,
+ .B strftime_z()
+ returns the number of bytes of buffer that would be required to fully
+ perform the conversion (including the terminating
+ .BR NUL ),
+ and leaves 
+ .I buf
+ in an indeterminate state.
+ .SH ERRORS
+ .B time_make()
+ shall fail if:
+ .IP \fBERANGE\fR
+ .I *tm
+ does not represent a time representable by a time_t value.
+ .PP
+ .B time_make()
+ may fail if:
+ .IP \fBEINVAL\fR
+ .I *tm
+ does not represent a possible time in the timezone
+ .IR *tz .
+ (For instance, it indicates a time occuring during a leap-forward interval.)
+ .PP
+ No error status codes are defined for
+ .BR time_breakup() .
+ .PP
+ .B Strftime_z()
+ does not set or return error statuses.
+ .SH SEE ALSO
+ getenv(3),
+ newctime(3),
+ newstrftime(3),
+ newtzset(3),
+ pthread_settz(3),
+ time(2),
+ tz_prep(3),
+ tzfile(5)
+ .SH NOTES
+ .B time_make()
+ is a generalization of the ISO C function 
+ .B mktime(3)
+ and the BSD/tzcode function
+ .BR timegm(3) .
+ .B time_breakup()
+ is a generalization of the ISO C functions
+ .B localtime(3)
+ and
+ .BR gmtime(3) ,
+ and the POSIX functions
+ .B localtime_r(3)
+ and
+ .BR gmtime_r(3) .
+ .B strftime_z()
+ is a generalization of the ISO C function
+ .BR strftime(3) .
+ .PP
+ Depending on how
+ .B tzcode
+ was compiled,
+ .B time_make()
+ may handle impossible time values (e.g., values that fall during a
+ leap-forward interval) by failing and returning
+ .BR EINVAL ,
+ or by normalizing 
+ .I *tm
+ to a nearby valid time.
+ .PP
+ The return value of 
+ .B strftime_z()
+ on error has changed from that of 
+ .BR strftime() .
+ .B Strftime()
+ returns
+ .B 0
+ if 
+ .I buf
+ is not large enough. 
+ .BR Strftime_z() ,
+ instead, follows the example of ISO C 99's 
+ .BR snprintf(3) ,
+ by returning the number of bytes that would be required to fully convert the
+ format string.  This allows an appropriately-sized buffer to be allocated in
+ one step, rather than requiring a binary search.  Error returns can be
+ easily detected by checking (\fIret\fR <= \fImaxbuf\fR).
+ .PP
+ The names and calling conventions of 
+ .B time_make()
+ and 
+ .B time_breakup()
+ are inspired by Markus Kuhn's proposed
+ .B xtime_make()
+ and
+ .B xtime_breakup()
+ functions.  The
+ .B xtime_*
+ functions represent time using
+ .BR "struct xtime" ,
+ which is a much more sophisticated and better-defined representation of time
+ than
+ .BR time_t .
+ The
+ .B time_*
+ functions are designed to be less ambitious while still leaving room
+ for these future improvements.
+ .PP
+ Following Kuhn, 
+ .B time_make()
+ corrects a flaw of
+ .B mktime() 
+ and
+ .BR timegm() .
+ Those functions use the value
+ .B (time_t)-1
+ to represent an error return status.  However, this value can also be a
+ correct translation of a 
+ .B struct tm
+ representing the time "December 31, 1969, 23:59:59 GMT" (assuming POSIX 
+ .B time_t 
+ values),
+ and these cases are not distinguished.  Instead, 
+ .B time_make()
+ uses an out-of-band method to indicate error conditions, leaving
+ the entire
+ .B time_t
+ space free to represent valid values.
+ .\" @(#)time_make.3	1.0
diff -crN -x *~ ../tzcode2001c/tz_prep.3 thread-safe-2/tz_prep.3
*** ../tzcode2001c/tz_prep.3	Wed Dec 31 19:00:00 1969
--- thread-safe-2/tz_prep.3	Mon Jun 18 15:46:08 2001
***************
*** 0 ****
--- 1,120 ----
+ .TH TZ_PREP 3
+ .SH NAME
+ tz_prep, tz_free \- create and destroy time zone objects
+ .SH SYNOPSIS
+ .nf
+ .B #include <time.h>
+ .B #include <sys/types.h>
+ .PP
+ .B int tz_prep(tz, tzstring)
+ .B struct tzinfo** tz;
+ .B const char *tzstring;
+ .PP
+ .B void tz_free()
+ .B struct tzinfo* tzobj;
+ .PP
+ .B cc ... -ltz
+ .fi
+ .SH DESCRIPTION
+ The
+ .B tz_prep()
+ function creates a new time zone object, corresponding to the
+ given time zone name
+ .IR tzstring .
+ .I tzstring
+ is a pointer to a string, or 
+ .BR NULL .
+ .PP
+ If
+ .I tzstring
+ is
+ .BR NULL ,
+ the returned time zone will represent the system's best approximation of its
+ current wall clock time.  If
+ .I tzstring
+ is the zero-length string \fB""\fR, the returned time zone will represent the
+ system's best approximation of Coordinated Universal Time (UTC).
+ .PP
+ All other values of
+ .I tzstring
+ are interpreted in the same manner as the
+ .B TZ
+ environment variable, as specified in environ(7).
+ .PP
+ The
+ .B tz_free()
+ function frees a time zone object allocated by
+ .BR tz_prep() .
+ .SH RETURN VALUES
+ On successful completion,
+ .B tz_prep()
+ returns a value of
+ .BR 0
+ and fills in 
+ .IR *tz .
+ On failure, an error number is returned and no resources are allocated.
+ .PP
+ The
+ .B tz_free()
+ function returns no value.
+ .SH ERRORS
+ The
+ .B tz_prep()
+ function will fail if:
+ .IP \fBENOMEM\fR
+ Not enough memory is available to create the time zone object.
+ .IP \fBENOENT\fR
+ No known time zone corresponds to tzname.
+ .PP
+ The
+ .B tz_prep()
+ function may fail if there is a problem with the system time
+ zone database which caused retrieval of the time zone information to fail
+ unexpectedly.  In this case any appropriate error number may be returned.
+ .PP
+ The
+ .B tz_free()
+ function does not return any errors.
+ .SH FILES
+ .ta \w'/usr/local/etc/zoneinfo/posixrules\0\0'u
+ /usr/local/etc/zoneinfo	time zone information directory
+ .br
+ /usr/local/etc/zoneinfo/localtime	local time zone file
+ .br
+ /usr/local/etc/zoneinfo/posixrules	used with POSIX-style TZ's
+ .br
+ /usr/local/etc/zoneinfo/GMT	for UTC leap seconds
+ .sp
+ If
+ .B /usr/local/etc/zoneinfo/GMT
+ is absent,
+ UTC leap seconds are loaded from
+ .BR /usr/local/etc/zoneinfo/posixrules .
+ .SH SEE ALSO
+ environ(7),
+ getenv(3),
+ newlocaltime(3),
+ newstrftime(3),
+ newtzset(3),
+ pthread_settz(3),
+ time(2),
+ time_make(3),
+ tzfile(5)
+ .SH NOTES
+ This interface is designed so that calling
+ \fBtz_prep\fR(\fBgetenv\fR(\fB"TZ"\fR))
+ will return an object describing the default time zone object that
+ non-thread-aware versions of the time functions will use by default,
+ provided
+ .B TZ
+ (if set) is set to a valid time zone name.
+ .PP
+ .B tzset()
+ interprets a 
+ .B TZ
+ value of the empty string \fB""\fR as meaning "UTC without leap-seconds".
+ .BR tz_prep() ,
+ however, defines it to mean the standard UTC value, with whatever
+ leap-second support the system uses by default.  On POSIX-compliant systems,
+ which may not use leap second support by default, these will be equivalent.
+ .\" @(#)tz_prep.3	1.0
diff -crN -x *~ ../tzcode2001c/tzthread-dummy.c thread-safe-2/tzthread-dummy.c
*** ../tzcode2001c/tzthread-dummy.c	Wed Dec 31 19:00:00 1969
--- thread-safe-2/tzthread-dummy.c	Mon Jun 18 16:15:27 2001
***************
*** 0 ****
--- 1,42 ----
+ /*
+ ** This file is in the public domain.  Contributed by Jonathan Lennox
+ ** <lennox@cs.columbia.edu>
+ */
+ 
+ #include "private.h"
+ #include "tzthread-dummy.h"
+ #include "struct_tzinfo.h"
+ 
+ /*
+ ** "Dummy" definitions of the TZ thread-safety operations.  For use in
+ ** a non-threaded environment.
+ */
+ 
+ #ifdef ALL_STATE
+ struct tzinfo *	_tz_lclptr;
+ struct tzinfo *	_tz_gmtptr;
+ 
+ struct tzinfo * _tz_alloc_lclptr P((void))
+ {
+ 	_tz_lclptr = (struct tzinfo *) malloc(sizeof *_tz_lclptr);
+ 	return _tz_lclptr;
+ }
+ 
+ 
+ struct tzinfo * _tz_alloc_gmtptr P((void))
+ {
+ 	_tz_gmtptr = (struct tzinfo *) malloc(sizeof *_tz_gmtptr);
+ 	return _tz_gmtptr;
+ }
+ 
+ #else /* !defined ALL_STATE */
+ 
+ struct tzinfo	_tz_lclmem;
+ struct tzinfo	_tz_gmtmem;
+ 
+ #endif /* !defined ALL_STATE */
+ 
+ struct tm _tz_tm;
+ 
+ char _tz_asctime_buf[ASCTIME_BUF_SIZE];
+ 
diff -crN -x *~ ../tzcode2001c/tzthread-dummy.h thread-safe-2/tzthread-dummy.h
*** ../tzcode2001c/tzthread-dummy.h	Wed Dec 31 19:00:00 1969
--- thread-safe-2/tzthread-dummy.h	Mon Jun 18 16:15:17 2001
***************
*** 0 ****
--- 1,127 ----
+ #ifndef TZTHREAD_DUMMY_H
+ #define TZTHREAD_DUMMY_H
+ 
+ /*
+ ** This file is in the public domain.  Contributed by Jonathan Lennox
+ ** <lennox@cs.columbia.edu>
+ */
+ 
+ #include "private.h"
+ 
+ /*
+ ** Thread primitives for tzcode.  TZcode requires the following primitive
+ ** functions to be defined (probably as macros):
+ ** void LOCK_LCLPTR(void)	- serialize references to the process-wide or
+ **				  thread-specific local timezone object.
+ ** void LOCK_GMTPTR(void)	- serialize references to write to the global
+ **				  UTC timezone object.
+ ** void UNLOCK_LCLPTR(void)	- Unlock the local timezone object.
+ ** void UNLOCK_GMTPTR(void)	- Unlock the UTC timezone object.
+ ** 
+ ** struct tzinfo * GET_LCLPTR(int *process)
+ **				- Return the current local timezone object.
+ **				  The argument 'process' is filled in with
+ **				  a boolean value indicating whether the
+ **				  returned object is process-wide (true)
+ **				  or thread-specific (false).
+ **				  This will only be called when the local
+ **				  pointer is locked.
+ **				  The thread primitive is not responsible for
+ **				  filling in the value, only for assuring that
+ **				  sizeof(struct tzinfo) bytes are available.
+ **				  This function returns NULL on failure.
+ **
+ ** struct tzinfo * GET_GMTPTR(void)
+ **				- Return the UTC timezone object.
+ **				  This *can* be called when the GMT pointer
+ **				  is not locked.  This function is responsible
+ **				  for any resulting serialization necessary.
+ **				  Any attempts to write to the object *will*
+ **				  lock the object first, however.
+ **				  The thread primitive is not responsible for
+ **				  filling in the value, only for assuring that
+ **				  sizeof(struct tzinfo) bytes are available.
+ **				  This function returns NULL on failure.
+ **
+ ** struct tm *GET_TM_BUF(void)	- Get a pointer to a struct tm, suitable for 
+ **				  use as a return value for the localtime()
+ **				  family of functions.  In a threaded
+ **				  environment, this buffer should be thread-
+ **				  local.
+ **
+ ** int SET_THREAD_LCLPTR(const struct tzinfo *)
+ **				- Set the thread-specific local timezone
+ **				  object to (a copy of) the argument.  Calls to
+ **				  GET_LCLPTR() from the current thread should
+ **				  from now on return this value.
+ **				  If a previous local pointer was set, free
+ **				  it first.
+ **				  Return an errno error code on failure, or 0.
+ **
+ ** int CLEAR_THREAD_LCLPTR(void)
+ **				- Clear the thread-specific local timezone
+ **				  object to the argument.  Calls to
+ **				  GET_LCLPTR() from the current thread should
+ **				  from now on return the global pointer.
+ **				  If a previous local pointer was set, free
+ **				  it first.
+ **				  Return an errno error code on failure, or 0.
+ */
+ 
+ /*
+ ** "Dummy" definitions of the TZ thread-safety operations.  For use in
+ ** a non-threaded environment.
+ */
+ 
+ /*
+ ** There are actually two dummy TZ implementations -- one for ALL_STATE
+ ** (structures allocated as-needed) and one for static.
+ */
+ 
+ #ifdef ALL_STATE
+ extern struct tzinfo *	_tz_lclptr;
+ extern struct tzinfo *	_tz_gmtptr;
+ 
+ #define GET_LCLPTR(_process_wide) \
+ 	(*(_process_wide) = 1, \
+ 		(_tz_lclptr != NULL ? _tz_lclptr : _tz_alloc_lclptr()))
+ #define GET_GMTPTR() \
+ 	(_tz_gmtptr != NULL ? _tz_gmtptr : _tz_alloc_gmtptr())
+ 
+ extern struct tzinfo * _tz_alloc_lclptr P((void));
+ extern struct tzinfo * _tz_alloc_gmtptr P((void));
+ 
+ #else /* !defined ALL_STATE */
+ 
+ extern struct tzinfo	_tz_lclmem;
+ extern struct tzinfo	_tz_gmtmem;
+ #define GET_LCLPTR(_process_wide)	(*(_process_wide) = 1, &_tz_lclmem)
+ #define GET_GMTPTR()			(&_tz_gmtmem)
+ 
+ #endif /* !defined ALL_STATE */
+ 
+ /*
+ ** These dummy implementations apply regardless of ALL_STATE, since we're
+ ** non-threaded.
+ */
+ 
+ #define LOCK_LCLPTR()    do { } while(0)
+ #define LOCK_GMTPTR()    do { } while(0)
+ 
+ #define UNLOCK_LCLPTR()    do { } while(0)
+ #define UNLOCK_GMTPTR()    do { } while(0)
+ 
+ extern struct tm _tz_tm;
+ 
+ #define GET_TM_BUF() (&_tz_tm)
+ 
+ extern char _tz_asctime_buf[ASCTIME_BUF_SIZE];
+ 
+ #define GET_ASCTIME_BUF() (_tz_asctime_buf)
+ 
+ #define SET_THREAD_LCLPTR(x) (ENOSYS)
+ #define CLEAR_THREAD_LCLPTR() (ENOSYS)
+ 
+ #endif /* !defined TZTHREAD_DUMMY_H */
+ 
+ 
diff -crN -x *~ ../tzcode2001c/tzthread-posix.c thread-safe-2/tzthread-posix.c
*** ../tzcode2001c/tzthread-posix.c	Wed Dec 31 19:00:00 1969
--- thread-safe-2/tzthread-posix.c	Mon Jun 18 16:15:47 2001
***************
*** 0 ****
--- 1,282 ----
+ /*
+ ** This file is in the public domain.  Contributed by Jonathan Lennox
+ ** <lennox@cs.columbia.edu>
+ */
+ 
+ #include "private.h"
+ #include "tzthread-posix.h"
+ #include "struct_tzinfo.h"
+ 
+ #include <pthread.h>
+ 
+ /*
+ ** POSIX Thread versions of the TZ thread-safety operations.
+ */
+ 
+ static pthread_mutex_t _tz_lcl_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static pthread_mutex_t _tz_gmt_mutex = PTHREAD_MUTEX_INITIALIZER;
+ 
+ static int get_lclptr_key P((pthread_key_t *));
+ 
+ #ifdef ALL_STATE
+ static struct tzinfo *	_tz_lclptr;
+ static struct tzinfo *	_tz_gmtptr;
+ 
+ struct tzinfo*
+ _tz_get_lclptr(process_wide)
+ int *	process_wide;
+ {
+ 	pthread_key_t lclptr_key;
+ 	struct tzinfo *ret;
+ 
+ 	if (get_lclptr_key(&lclptr_key) != 0) {
+ 		return NULL;
+ 	}
+ 	if ((ret = pthread_getspecific(lclptr_key)) != NULL) {
+ 		*process_wide = 0;
+ 		return ret;
+ 	}
+ 
+ 	*process_wide = 1;
+ 
+ 	if (_tz_lclptr == NULL) {
+ 		_tz_lclptr = (struct tzinfo *) malloc(sizeof *_tz_lclptr);
+ 	}
+ 	return _tz_lclptr;
+ }
+ 
+ 
+ struct tzinfo*
+ _tz_get_gmtptr P((void))
+ {
+ 	/*
+ 	** Fast-path the common case. Nothing sets or alters gmtptr
+ 	** once it's set.
+ 	*/
+ 	if (_tz_gmtptr != NULL) {
+ 		return _tz_gmtptr;
+ 	}
+ 
+ 	_tz_lock_gmtptr();
+ 	/* 
+ 	** Check the pointer again, in case another thread just 
+ 	** allocated it.
+ 	*/
+ 	if (_tz_gmtptr != NULL) {
+ 		_tz_unlock_gmtptr();
+ 		return _tz_gmtptr;
+ 	}
+ 
+ 	_tz_gmtptr = (struct tzinfo *) malloc(sizeof *_tz_gmtptr);
+ 	_tz_unlock_gmtptr();
+ 
+ 	return _tz_gmtptr;
+ }
+ 
+ #else /* !defined ALL_STATE */
+ 
+ static struct tzinfo _tz_lclmem;
+ static struct tzinfo _tz_gmtmem;
+ 
+ struct tzinfo *
+ _tz_get_lclptr(process_wide)
+ int *	process_wide;
+ {
+ 	pthread_key_t lclptr_key;
+ 	struct tzinfo *ret;
+ 
+ 	if (get_lclptr_key(&lclptr_key) != 0) {
+ 		return NULL;
+ 	}
+ 	if ((ret = pthread_getspecific(lclptr_key)) != NULL) {
+ 		*process_wide = 0;
+ 		return ret;
+ 	}
+ 
+ 	*process_wide = 1;
+ 
+ 	return &_tz_lclmem;
+ }
+ 
+ 
+ struct tzinfo *
+ _tz_get_gmtptr P((void))
+ {
+ 	return &_tz_gmtmem;
+ }
+ 
+ #endif /* defined ALL_STATE */
+ 
+ void _tz_lock_lclptr P((void))
+ {
+ 	pthread_mutex_lock(&_tz_lcl_mutex);
+ }
+ 
+ 
+ void _tz_lock_gmtptr P((void))
+ {
+ 	pthread_mutex_lock(&_tz_gmt_mutex);
+ }
+ 
+ 
+ extern void _tz_unlock_lclptr P((void))
+ {
+ 	pthread_mutex_unlock(&_tz_lcl_mutex);
+ }
+ 
+ 
+ extern void _tz_unlock_gmtptr P((void))
+ {
+ 	pthread_mutex_unlock(&_tz_gmt_mutex);
+ }
+ 
+ 
+ 
+ struct tm*
+ _tz_get_tm_buf P((void))
+ {
+ 	static pthread_mutex_t tmbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
+ 	static pthread_key_t tmbuf_key;
+ 	static int tmbuf_initialized = 0;
+ 	struct tm *p_tm;
+ 	int err;
+ 
+ 	if (!tmbuf_initialized) {
+ 		pthread_mutex_lock(&tmbuf_mutex);
+ 		if (!tmbuf_initialized) {
+ 			if ((err = pthread_key_create(&tmbuf_key, free))
+ 					!= 0) {
+ 				errno = err;
+ 				pthread_mutex_unlock(&tmbuf_mutex);
+ 				return NULL;
+ 			}
+ 			tmbuf_initialized = 1;
+ 		}
+ 		pthread_mutex_unlock(&tmbuf_mutex);
+ 	}
+ 
+ 	p_tm = pthread_getspecific(tmbuf_key);
+ 	if (p_tm == NULL) {
+ 		if ((p_tm = (struct tm *) malloc(sizeof(struct tm))) == NULL) {
+ 			return NULL;
+ 		}
+ 		if ((err = pthread_setspecific(tmbuf_key, p_tm)) != 0) {
+ 			errno = err;
+ 			free(p_tm);
+ 			return NULL;
+ 		}
+ 	}
+ 	return p_tm;
+ }
+ 
+ 
+ char* _tz_get_asctime_buf P((void))
+ {
+ 	static pthread_mutex_t asctime_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
+ 	static pthread_key_t asctime_buf_key;
+ 	static int asctime_buf_initialized = 0;
+ 	char *asctime_buf;
+ 	int err;
+ 
+ 	if (!asctime_buf_initialized) {
+ 		pthread_mutex_lock(&asctime_buf_mutex);
+ 		if (!asctime_buf_initialized) {
+ 			if ((err = pthread_key_create(&asctime_buf_key, free))
+ 					!= 0) {
+ 				errno = err;
+ 				pthread_mutex_unlock(&asctime_buf_mutex);
+ 				return NULL;
+ 			}
+ 			asctime_buf_initialized = 1;
+ 		}
+ 		pthread_mutex_unlock(&asctime_buf_mutex);
+ 	}
+ 
+ 	asctime_buf = pthread_getspecific(asctime_buf_key);
+ 	if (asctime_buf == NULL) {
+ 		if ((asctime_buf = (char *) malloc(ASCTIME_BUF_SIZE))
+ 				== NULL) {
+ 			return NULL;
+ 		}
+ 		if ((err = pthread_setspecific(asctime_buf_key, asctime_buf))
+ 				!= 0) {
+ 			errno = err;
+ 			free(asctime_buf);
+ 			return NULL;
+ 		}
+ 	}
+ 	return asctime_buf;
+ }
+ 
+ 
+ int _tz_set_thread_lclptr(tz)
+ const struct tzinfo * tz;
+ {
+ 	pthread_key_t lclptr_key;
+ 	int err;
+ 	struct tzinfo *old, *new;
+ 
+ 	if ((err = get_lclptr_key(&lclptr_key)) != 0) {
+ 		return err;
+ 	}
+ 	if ((old = pthread_getspecific(lclptr_key)) != NULL) {
+ 	  free(old);
+ 	}
+ 
+ 	new = (struct tzinfo *) malloc(sizeof(struct tzinfo));
+ 	if (new == NULL) {
+ 	  return ENOMEM;
+ 	}
+ 
+ 	*new = *tz;
+ 
+ 	if ((err = pthread_setspecific(lclptr_key, new)) != 0) {
+ 	  free(new);
+ 	  return err;
+ 	}
+ 	return 0;
+ }
+ 
+ 
+ int _tz_clear_thread_lclptr P((void))
+ {
+ 	pthread_key_t lclptr_key;
+ 	int err;
+ 	struct tzinfo *old;
+ 
+ 	if ((err = get_lclptr_key(&lclptr_key)) != 0) {
+ 		return err;
+ 	}
+ 	if ((old = pthread_getspecific(lclptr_key)) != NULL) {
+ 	  free(old);
+ 	}
+ 
+ 	return pthread_setspecific(lclptr_key, NULL);
+ }
+ 
+ 
+ static int get_lclptr_key(lclptr_key_p)
+ pthread_key_t *		lclptr_key_p;
+ {
+ 	static pthread_mutex_t lclptr_key_mutex = PTHREAD_MUTEX_INITIALIZER;
+ 	static pthread_key_t lclptr_key;
+ 	static int lclptr_key_initialized = 0;
+ 
+ 	int err;
+ 
+ 	if (!lclptr_key_initialized) {
+ 		pthread_mutex_lock(&lclptr_key_mutex);
+ 		if (!lclptr_key_initialized) {
+ 			if ((err = pthread_key_create(&lclptr_key, free))
+ 					!= 0) {
+ 				pthread_mutex_unlock(&lclptr_key_mutex);
+ 				return err;
+ 			}
+ 			lclptr_key_initialized = 1;
+ 		}
+ 		pthread_mutex_unlock(&lclptr_key_mutex);
+ 	}
+ 
+ 	*lclptr_key_p = lclptr_key;
+ 	return 0;
+ }
diff -crN -x *~ ../tzcode2001c/tzthread-posix.h thread-safe-2/tzthread-posix.h
*** ../tzcode2001c/tzthread-posix.h	Wed Dec 31 19:00:00 1969
--- thread-safe-2/tzthread-posix.h	Mon Jun 18 16:20:38 2001
***************
*** 0 ****
--- 1,109 ----
+ #ifndef TZTHREAD_POSIX_H
+ #define TZTHREAD_POSIX_H
+ 
+ /*
+ ** This file is in the public domain.  Contributed by Jonathan Lennox
+ ** <lennox@cs.columbia.edu>
+ */
+ 
+ /*
+ ** Thread primitives for tzcode.  TZcode requires the following primitive
+ ** functions to be defined (probably as macros):
+ ** void LOCK_LCLPTR(void)	- serialize references to the process-wide or
+ **				  thread-specific local timezone object.
+ ** void LOCK_GMTPTR(void)	- serialize references to write to the global
+ **				  UTC timezone object.
+ ** void UNLOCK_LCLPTR(void)	- Unlock the local timezone object.
+ ** void UNLOCK_GMTPTR(void)	- Unlock the UTC timezone object.
+ ** 
+ ** struct tzinfo * GET_LCLPTR(int *process)
+ **				- Return the current local timezone object.
+ **				  The argument 'process' is filled in with
+ **				  a boolean value indicating whether the
+ **				  returned object is process-wide (true)
+ **				  or thread-specific (false).
+ **				  This will only be called when the local
+ **				  pointer is locked.
+ **				  The thread primitive is not responsible for
+ **				  filling in the value, only for assuring that
+ **				  sizeof(struct tzinfo) bytes are available.
+ **				  This function returns NULL on failure.
+ **
+ ** struct tzinfo * GET_GMTPTR(void)
+ **				- Return the UTC timezone object.
+ **				  This *can* be called when the GMT pointer
+ **				  is not locked.  This function is responsible
+ **				  for any resulting serialization necessary.
+ **				  Any attempts to write to the object *will*
+ **				  lock the object first, however.
+ **				  The thread primitive is not responsible for
+ **				  filling in the value, only for assuring that
+ **				  sizeof(struct tzinfo) bytes are available.
+ **				  This function returns NULL on failure.
+ **
+ ** struct tm *GET_TM_BUF(void)	- Get a pointer to a struct tm, suitable for 
+ **				  use as a return value for the localtime()
+ **				  family of functions.  In a threaded
+ **				  environment, this buffer should be thread-
+ **				  local.
+ **
+ ** int SET_THREAD_LCLPTR(struct tzinfo *)
+ **				- Set the thread-specific local timezone
+ **				  object to the argument.  Calls to
+ **				  GET_LCLPTR() from the current thread should
+ **				  from now on return this value.
+ **				  If a previous local pointer was set, free
+ **				  it first.
+ **				  Return an errno error code on failure, or 0.
+ **
+ ** int CLEAR_THREAD_LCLPTR(void)
+ **				- Clear the thread-specific local timezone
+ **				  object to the argument.  Calls to
+ **				  GET_LCLPTR() from the current thread should
+ **				  from now on return the global pointer.
+ **				  If a previous local pointer was set, free
+ **				  it first.
+ **				  Return an errno error code on failure, or 0.
+ */
+ 
+ #include "private.h"
+ 
+ /*
+ ** POSIX Thread versions of the TZ thread-safety operations.
+ */
+ 
+ extern struct tzinfo *	_tz_get_lclptr P((int *));
+ extern struct tzinfo *	_tz_get_gmtptr P((void));
+ 
+ #define GET_LCLPTR _tz_get_lclptr
+ #define GET_GMTPTR _tz_get_gmtptr
+ 
+ extern void _tz_lock_lclptr P((void));
+ extern void _tz_lock_gmtptr P((void));
+ 
+ extern void _tz_unlock_lclptr P((void));
+ extern void _tz_unlock_gmtptr P((void));
+ 
+ #define LOCK_LCLPTR    _tz_lock_lclptr
+ #define LOCK_GMTPTR    _tz_lock_gmtptr
+ 
+ #define UNLOCK_LCLPTR    _tz_unlock_lclptr
+ #define UNLOCK_GMTPTR    _tz_unlock_gmtptr
+ 
+ extern struct tm* _tz_get_tm_buf P((void));
+ 
+ #define GET_TM_BUF _tz_get_tm_buf
+ 
+ extern char* _tz_get_asctime_buf P((void));
+ 
+ #define GET_ASCTIME_BUF _tz_get_asctime_buf
+ 
+ extern int _tz_set_thread_lclptr P((const struct tzinfo *));
+ extern int _tz_clear_thread_lclptr P((void));
+ 
+ #define SET_THREAD_LCLPTR _tz_set_thread_lclptr
+ #define CLEAR_THREAD_LCLPTR _tz_clear_thread_lclptr
+ 
+ #endif /* !defined TZTHREAD_POSIX_H */
+ 
+ 
diff -crN -x *~ ../tzcode2001c/tztimeext.h thread-safe-2/tztimeext.h
*** ../tzcode2001c/tztimeext.h	Wed Dec 31 19:00:00 1969
--- thread-safe-2/tztimeext.h	Mon Jun 18 15:46:07 2001
***************
*** 0 ****
--- 1,75 ----
+ #ifndef TZTIMEEXT_H
+ #define TZTIMEEXT_H
+ 
+ /*
+ ** This file is in the public domain.  Contributed by Jonathan Lennox
+ ** <lennox@cs.columbia.edu>
+ */
+ 
+ /*
+ ** This is an extended version of <time.h> which provides function definitions
+ ** for the public functions defined by tzcode.
+ */
+ 
+ #include <time.h>
+ 
+ #if __STDC_VERSION__ < 199901
+ #undef  restrict
+ #define restrict
+ #endif
+ 
+ struct tzinfo;
+ 
+ #if defined(__STDC__)
+ 
+ extern void		tzsetwall(void);
+ extern struct tm *	offtime(const time_t *, const long);
+ extern time_t		timelocal(struct tm * const);
+ extern time_t		timegm(struct tm * const);
+ extern time_t		timeoff(struct tm * const, const long);
+ 
+ extern time_t		time2posix(time_t);
+ extern time_t		posix2time(time_t);
+ 
+ extern int		tz_prep(struct tzinfo**, const char *);
+ extern void		tz_free(struct tzinfo*);
+ 
+ extern int		time_make(time_t *, struct tm *,
+ 					const struct tzinfo *);
+ extern int		time_breakup(struct tm *, const time_t *,
+ 					const struct tzinfo *);
+ extern size_t		strftime_z(char * restrict, size_t,
+ 					const char * restrict,
+ 					const struct tm * restrict,
+ 					const struct tzinfo * restrict);
+ 
+ extern int		pthread_settz(const struct tzinfo *);
+ 
+ #else /* !defined(__STDC__) */
+ 
+ extern void		tzsetwall();
+ extern struct tm *	offtime();
+ extern time_t		timelocal();
+ extern time_t		timegm();
+ extern time_t		timeoff();
+ 
+ extern time_t		time2posix();
+ extern time_t		posix2time();
+ 
+ extern int		tz_prep();
+ extern void		tz_free();
+ 
+ extern int		time_make();
+ extern int		time_breakup();
+ extern size_t		strftime_z();
+ 
+ extern int		pthread_settz();
+ 
+ #endif /* defined(__STDC__) */
+ 
+ 
+ #if __STDC_VERSION__ < 199901
+ #undef restrict
+ #endif
+ 
+ #endif /* TZTIMEEXT_H */
