[From nobody Sat Feb 25 02:54:44 2012 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="JRm1TnnYS+" Message-ID: <15150.27462.761068.788510@grandcentral.cs.columbia.edu> X-Mailer: VM 6.75 under Emacs 20.7.1 From: Jonathan Lennox <lennox@grandcentral.cs.columbia.edu> To: tz@elsie.nci.nih.gov Subject: Thread-safe timezones: running code Date: Mon, 18 Jun 2001 16:57:42 -0400 (EDT) --JRm1TnnYS+ Content-Type: text/plain; charset=us-ascii Content-Description: message body text I've finished a first cut (call it beta 1?) of a tzcode-based implementation of my thread-safe time zone API. The API as defined here was fairly straightforward to implement. Only a few changes were necessary. * The most noticable change is that the type representing a time zone object is now 'struct tzinfo', not 'struct timezone'. I discovered that 'struct timezone' is the type of the deprecated second argument to BSD gettimeofday(). * Other than that, I clarified some things, notably the fact that tzname and the other externs aren't defined when you're using pthread_settz(). (I could have implemented them using the trick used for thread-safe errno, but it seemed like too much trouble for a badly-defined API.) The attached patch also contains code to make the 'normal' time functions thread-safe. This is based on FreeBSD's changes to tzcode to make it thread-safe, but rather than sticking #ifdef's directly into the code, as FreeBSD does, I instead implemented them as macros. You can control your thread model based on a Makefile setting. The only part of the thread-safety API that isn't currently implemented in my patch is that the error return value of strftime_z() is still the same as that of strftime(), rather than using the snprintf() error return style. This requires annoyingly intrusive changes to tzcode's strftime(), so I've put it off for now. Attached, find two files: 1. The revised (v3) API proposal. 2. A patch to tzcode2001c implementing the proposal. Comments are very welcome! --JRm1TnnYS+ Content-Type: text/plain Content-Disposition: inline; filename="proposal.txt" A proposal for thread-safe time zone information. A set of extensions to ISO C (99) and IEEE POSIX (200x). by Jonathan Lennox, Columbia University. Version 3. Summary: A new data type is defined, 'struct tzinfo', that represents the time zone information for a particular region. Versions of the ISO C time conversion functions are defined that take this data type as an argument. Definitions: Time zone: The set of information necessary to correctly convert bidirectionally between a time_t and a struct tm, for a particular geographic region. Wall clock time: The system's best approximation of the local time in its physical location, independent of the physical location of any user. Functions defined in this proposal: Functions manipulating struct tzinfo values: Name tz_prep -- prepare a time zone object, based on a descriptive string. Synopsis #include <time.h> int tz_prep(struct tzinfo** tz, const char *tzstring); Description The tz_prep() function creates a new time zone object, corresponding to the given time zone name. tzstring is a pointer to a string, or NULL. The two defined values of tzstring are NULL, representing the system's best approximation of its wall clock time, and the zero-length string "", representing the system's best approximation of Coordinated Universal Time (UTC). On successful completion, *tz will contain a pointer to a struct tzinfo, contains all necessary information to represent times in the specified time zone. It has no externally-visible elements. POSIX extension: In a POSIX-based environment, the syntax of tzstring is the same as that of the "TZ" environment variable, including the implementation-defined values beginning with ':'. Return Value On successful completion, tz_prep() returns a value of 0 and fills in *tz. On failure, an error number is returned and no resources are allocated. Errors The tz_prep() function shall fail if: ENOMEM Not enough memory is available to create the time zone object. ENOENT No known time zone corresponds to tzname. The tz_prep() function may fail if: * There is a problem with the system's configuration (such as an on-disk time zone database) which caused retrieval of the time zone information to fail unexpectedly. In this case any appropriate error number may be returned. Rationale and commentary Systems which define extensions beyond POSIX (such as the Olson time zone names) for the "TZ" environment variable should support the same extensions for tzstring. Under the POSIX rules, strings such as "UTC0" can also represent UTC, but the empty string "" is the portable representation. This representation is also correctly locale-independent -- "UTC0" in fr_FR generates times with the incorrect time zone abbreviation "UTC", whereas "" in that locale should use the correct time zone abbreviation "TUC" if the time zone code correctly supports locales. This interface is designed so that tz_prep(getenv("TZ")) will return an object describing the default time zone object that non-thread-aware versions of the time functions will use by default, provided TZ (if set) is set to a valid time zone name. Question for discussion: should there be an ISO C way of saying "the timezone which localtime() and mktime() would use by default"? Currently this is only possible for POSIX, using thee tz_prep(getenv("TZ")) idiom mentioned above. Name tz_free -- Free resources allocated for a time zone object Synopsis #include <time.h> void tz_free(struct tzinfo* tzobj); Description The tz_free() function frees the resources allocated for a time zone object returned by a call to tz_prep(). Return Value None. Errors None. Rationale and commentary None needed. Functions using struct tzinfo to manipulate time values: Name time_make - derive a time_t from a struct tm, in a specified time zone. Synopsis #include <time.h> int time_make(time_t *clock, struct tm *tm, const struct tzinfo *tz); Description This function interprets the broken-down time in *tm as a local time in the timezone specified in *tz, and writes the corresponding value into *clock, using the same encoding as that of the values returned by the 'time' function. The original values of the tm_wday and tm_yday components of *tm are ignored, and the original values of the other components are not restricted to their normal ranges. (A positive or zero value for tm_isdst causes 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 tm_isdst causes the time_make() function to attempt to divine whether summer time is in effect for the specified time.) On successful completion, the values of the tm_wday and tm_yday components of *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 tm_mday is not set until tm_mon and tm_year are determined. Returns time_make returns 0 on sucessful completion, sets *clock, and normalizes *tm. On failure, it returns an error number and leaves the values of *clock and *tm undefined. Errors time_make() shall fail if: ERANGE *tm does not represent a time representable by a time_t value. time_make() may fail if: EINVAL *tm does not represent a possible time in the timezone *tz. (For instance, a leap-forward interval.) Rationale and commentary This function is the generalization of the ISO C function mktime() and the BSD/tzcode function timegm(). The name and calling conventions of this function are inspired by Markus Kuhn's proposed xtime_make(). struct xtime is a much more sophisticated and better-defined representation of time than time_t; this proposal is designed to be less ambitious while still leaving room for these future improvements. Following Kuhn, this function corrects a flaw of mktime()/timegm(). Those functions use the value (time_t)-1 to represent an error return status. However, this value can also be a correct translation of a struct tm representing the time December 31, 1969, 23:59:59 GMT (assuming POSIX time_t's). This function, instead, uses an out-of-band method to indicate error conditions, leaving the entire time_t space free to represent valid values. Given an impossible time value for the time zone (e.g. one that occurs during a leap-forward interval), implemenetations have a choice of failing and returning EINVAL, or normalizing *tm to a nearby valid time. Name time_breakup - derive a struct tm from a time_t, in a specified time zone. Synopsis #include <time.h> int time_breakup(struct tm *result, const time_t *clock, const struct tzinfo *tz); Description Fill in *result with the values corresponding to *clock in the time zone *tz. Return value time_breakup() returns 0, and fills in *result, always. Errors None. Rationale and commentary This function is the generalization of the ISO C functions localtime() and gmtime(), and the POSIX functions localtime_r() and gmtime_r(). It is inspired by Markus Kuhn's xtime_breakup(). The function assumes that a 'struct tzinfo' will be able to do something reasonable over the whole range of a time_t. This isn't necessarily true; it might be sensible to define an ERANGE error return value? This function omits the localtime side effect that it sets the value of the global variable char *tzname[2]. It's not clear how best to handle the BSD/tzcode tm_zone field of struct tm. This is defined as a char*, but since there is no tm_free() function, the data it points to cannot be dynamically allocated by time_breakup(). One possibility would be to have tm_zone point into data stored within 'struct tz', but then it would be invalidated when tz_free() was called. A second possibility would be to define tm_zone as char tm_zone[TZNAME_MAX], but this would break binary compatibility. A third possibility would be to deprecate tm_zone, and have time_breakup leave it NULL. Zone names can be obtained by calling strftime with a "%Z" format string (assuming POSIX strftime). Name strftime_z - generate a text representation of a time value, in a given time zone. Synopsis #include <time.h> size_t strftime_z(char * restrict buf, size_t maxsize, const char * restrict format, const struct tm * restrict timeptr, const struct tzinfo * restrict tz); Description strftime_z() formats the information from 'timeptr' into the buffer 'buf' according to the format specified by 'format' and the system locale. The format specified by 'format' is the same as that of strftime(). POSIX note: All POSIX extensions to strftime() apply to strftime_z() as well. Return value On successful completion, strftime_z fills in 'buf' and returns the number of bytes converted. On failure, strftime_z returns the number of bytes of buffer that would be required to fully perform the conversion (including the terminating NUL), and leaves 'buf' in an indeterminate state. Errors None. Rationale and commentary This function is a generalization of strftime(). The return value of this function has changed from strftime(), however. strftime() returns 0 if the buffer is not large enough. This function, instead, follows the example of ISO C 99's snprintf(), which allows an appropriately-sized buffer to be allocated in one step after a failure, rather than requiring a binary search. The error status can be easily checked by checking (ret <= maxbuf). The formatting strings of ISO C strftime() are not affected by the time zone, so this function is strictly speaking not necessary in a pure-C environment. POSIX strftime(), however, has the '%z' and '%Z' conversions, which require knowledge of the relevant time zone. It has been argued that this function is redundant, because time_breakup() could embed time zone information into struct tm (either in extension fields or private fields), and thus the standard POSIX strftime could suffice to convert struct tm's in a thread-safe way. However, this is only true for struct tm's created from time_breakup() or the mktime() family. Creation of struct tm values "by hand" is still fairly common, and a time zone specification is needed to print these. Name strftime_zl - generate a text representation of a time value, in a given time zone and locale. Synopsis #include <time.h> size_t strftime_zl(char * restrict buf, size_t maxsize, const char * restrict format, const struct tm * restrict timeptr, const struct tzinfo * restrict tz, const locale_t * restrict l); Description (This is an extension based on Ulrich Drepper's thread-aware locale proposal; see rationale.) strftime_z() formats the information from 'timeptr' into the buffer 'buf' according to the format specified by 'format' and the locale specified by 'l'. Other than the source of locale information, this is in all ways identical to strftime_z. Rationale and commentary This function follows Ulrich Drepper's thread-safe locale proposal; it is the combination of his strftime_l and strftime_z defined above. See <http://www.cygnus.com/~drepper/tllocale.ps.bz2> for details. This function should only be implemented if the rest of Drepper's proposal is as well. The _zl suffix is ugly. More natural would be to define a single function which takes all the necessary thread-safe data as arguments, but I don't want to force implementation of this proposal to be blocked on Drepper's. Thread-support functions Name pthread_settz -- set time zone object for the current POSIX thread. Synopsis #include <time.h> #include <pthread.h> int pthread_settz(const struct tzinfo *tz); Description (This function is only relevant in a POSIX system with the Threads option.) pthread_settz() sets the time zone object to be used for time functions called from the current POSIX thread. The time zone setting for all other threads remains the same. Once pthread_settz() has been called, all calls to the functions localtime(), localtime_r(), ctime(), ctime_r(), asctime(), asctime_r(), mktime(), and strftime() in the thread from which it was called will use the specified time zone rather than the process-wide time zone. If pthread_settz() is called with a NULL pointer as its argument, the current thread will again use the global time zone object. pthread_settz() leaves the values of the global variable tzname, and the XSI global variables daylight and timezone, undefined. Return value Upon successful completion, pthread_settz() returns a value of zero. Otherwise, an error code is returned to indicate an error. Errors pthread_settz() may fail if: ENOMEM Insufficient memory exists to store the time zone information for the thread. EAGAIN The system lacked the necessary resources to associate the time zone information with the thread. Rationale and commentary This function allows existing code which uses the ISO C APIs to work correctly, unmodified, in a threaded environment. The extended parts of the POSIX APIs may not work correctly unmodified. This function would typically be implemented as a wrapper around pthread_setspecific(). It's not clear if a parallel pthread_gettz() is also needed. General rationale and commentary Thanks to many comments from the people of the tz@elsie.nci.nih.gov mailing list. Much inspiration for this work was drawn from Markus Kuhn's proposed extended time APIs for ISO C 200x, and from Ulrich Drepper's proposed thread-aware locale functions. Markus Kuhn's proposed time zone API defined an additional time zone manipulation function tz_jump(), which gets the next or previous discontinuity in a given time zone. This is a potentially useful function, but it was omitted here so as to keep the API definitions modest. (Additionally, it should probably wait on the proper definition of an extended time type, to avoid unnecessary redundancy.) Time zone specific versions of asctime[_r] and ctime[_r] were omitted, as they can be constructed trivially out of the functions defined here. A time zone specific version of strptime is not necessary. The only zone-aware function of strptime that I know of is the BSD extension to scan "%Z"; POSIX 200x does not define this, and it generally doesn't seem to work very well as zone abbreviations are ambiguous. (BSD strptime only recognizes the current local timezone, in standard and summer forms, and "GMT". Even this is not necessarily unambiguous: consider for example the Australian "EST" meaning both "Eastern Standard Time" and "Eastern Summer Time".) wcsftime_z and wcsftime_zl functions are needed, analogous to strftime_z and strftime_zl for wide strings. Their definitions are obvious analogies to the existing functions. Changes from Version 2: The structure was renamed to 'struct tzinfo' from 'struct timezone', because the latter structure is already defined on BSD systems. (It is the deprecated second argument to gettimeofday().) Clarified that pthread_settz() leaves the state of global variables undefined. Corrected incorrect errno value in the time_make() rationale. Changes from Version 1: The time-zone creation and destruction functions were renamed from newtz() and freetz() to tz_prep() and tz_free(). This puts them in a name space, and aligns them with Markus Kuhn's functions. (These functions are slightly different from Kuhn's, though, in that they are defined to return errno values on errors.) tzdup() was dropped as not useful. mktime_z() and localtime_z() were renamed to time_make() and time_breakup(), by analogy with Kuhn's xtime_* functions. Zone-specific versions of asctime(), ctime(), and strptime() were dropped as unnecessary. The text was greatly clarified to make clear the distinctions between ISO C extensions, POSIX extensions, and rationale and commentary. The externally-visible tz_name elements of struct tz were dropped. The strftime_zl function was defined. --JRm1TnnYS+ Content-Type: text/plain Content-Disposition: inline; filename="thread-safe.patch" 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 */ --JRm1TnnYS+ Content-Type: text/plain; charset=us-ascii Content-Description: .signature -- Jonathan Lennox lennox@cs.columbia.edu --JRm1TnnYS+-- ]