diff options
author | Florian Weimer <fweimer@redhat.com> | 2017-03-09 16:33:57 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2017-03-09 16:34:11 +0100 |
commit | 60a91a23d61a48162f428e6031d8e2c36ca3ea9f (patch) | |
tree | 89d4cb34531757daec7d82c9e8750fe180c4aaf6 | |
parent | c9611e6f0e631edbc043986e3030d57e70fafb90 (diff) | |
download | glibc-fw/bug16145.zip glibc-fw/bug16145.tar.gz glibc-fw/bug16145.tar.bz2 |
WIP reorganization to improve scalability of localtimefw/bug16145
-rw-r--r-- | include/time.h | 16 | ||||
-rw-r--r-- | time/gmtime.c | 6 | ||||
-rw-r--r-- | time/localtime.c | 6 | ||||
-rw-r--r-- | time/time-private.h | 148 | ||||
-rw-r--r-- | time/tzfile.c | 492 | ||||
-rw-r--r-- | time/tzset.c | 240 |
6 files changed, 519 insertions, 389 deletions
diff --git a/include/time.h b/include/time.h index 08454ef..19a750b 100644 --- a/include/time.h +++ b/include/time.h @@ -38,19 +38,6 @@ libc_hidden_proto (__time_isleap) /* Defined in tzset.c. */ extern char *__tzstring (const char *string); -extern int __use_tzfile attribute_hidden; - -extern void __tzfile_read (const char *file, size_t extra, - char **extrap); -extern void __tzfile_compute (time_t timer, int use_localtime, - long int *leap_correct, int *leap_hit, - struct tm *tp); -extern void __tzfile_default (const char *std, const char *dst, - long int stdoff, long int dstoff); -extern void __tzset_parse_tz (const char *tz); -extern void __tz_compute (time_t timer, struct tm *tm, int use_localtime) - __THROW internal_function; - /* Subroutine of `mktime'. Return the `time_t' representation of TP and normalize TP, given that a `struct tm *' maps to a `time_t' as performed by FUNC. Keep track of next guess for time_t offset in *OFFSET. */ @@ -76,9 +63,6 @@ extern int __offtime (const time_t *__timer, extern char *__asctime_r (const struct tm *__tp, char *__buf); extern void __tzset (void); -/* Prototype for the internal function to get information based on TZ. */ -extern struct tm *__tz_convert (const time_t *timer, int use_localtime, struct tm *tp); - extern int __nanosleep (const struct timespec *__requested_time, struct timespec *__remaining); libc_hidden_proto (__nanosleep) diff --git a/time/gmtime.c b/time/gmtime.c index 1fc129b..8a4a0a4 100644 --- a/time/gmtime.c +++ b/time/gmtime.c @@ -18,13 +18,14 @@ #include <time.h> #include <time/time-variables.h> +#include <time/time-private.h> /* Return the `struct tm' representation of *T in UTC, using *TP to store the result. */ struct tm * __gmtime_r (const time_t *t, struct tm *tp) { - return __tz_convert (t, 0, tp); + return __tz_convert (t, /* reentrant */ true, /* localtime */ false, tp); } libc_hidden_def (__gmtime_r) weak_alias (__gmtime_r, gmtime_r) @@ -34,5 +35,6 @@ weak_alias (__gmtime_r, gmtime_r) struct tm * gmtime (const time_t *t) { - return __tz_convert (t, 0, &_tmbuf); + return __tz_convert (t, /* reentrant */ false, /* localtime */ false, + &_tmbuf); } diff --git a/time/localtime.c b/time/localtime.c index 3a38cde..2a95994 100644 --- a/time/localtime.c +++ b/time/localtime.c @@ -18,6 +18,7 @@ #include <time.h> #include <time/time-variables.h> +#include <time/time-private.h> /* The C Standard says that localtime and gmtime return the same pointer. */ struct tm _tmbuf; @@ -28,7 +29,7 @@ struct tm _tmbuf; struct tm * __localtime_r (const time_t *t, struct tm *tp) { - return __tz_convert (t, 1, tp); + return __tz_convert (t, /* reentrant */ true, /* localtime */ true, tp); } weak_alias (__localtime_r, localtime_r) @@ -37,6 +38,7 @@ weak_alias (__localtime_r, localtime_r) struct tm * localtime (const time_t *t) { - return __tz_convert (t, 1, &_tmbuf); + return __tz_convert (t, /* reentrant */ false, /* localtime */ true, + &_tmbuf); } libc_hidden_def (localtime) diff --git a/time/time-private.h b/time/time-private.h new file mode 100644 index 0000000..872fa54 --- /dev/null +++ b/time/time-private.h @@ -0,0 +1,148 @@ +/* Private declarations for the time subsystem. + Copyright (C) 1991-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef TIME_PRIVATE_H +#define TIME_PRIVATE_H + +#include <stdbool.h> +#include <sys/types.h> + +struct ttinfo +{ + long int offset; /* Seconds east of GMT. */ + unsigned char isdst; /* Used to set tm_isdst. */ + unsigned char idx; /* Index into `zone_names'. */ + unsigned char isstd; /* Transition times are in standard time. */ + unsigned char isgmt; /* Transition times are in GMT. */ +}; + +struct leap +{ + time_t transition; /* Time the transition takes effect. */ + long int change; /* Seconds of correction to apply. */ +}; + +/* This structure contains all the information about a + timezone given in the POSIX standard TZ envariable. */ +struct tz_rule +{ + const char *name; + + /* When to change. */ + enum { J0, J1, M } type; /* Interpretation of: */ + unsigned short int m, n, d; /* Month, week, day. */ + int secs; /* Time of day. */ + + long int offset; /* Seconds east of GMT (west if < 0). */ +}; + +struct tzdata +{ + /* Updated by tzfile.c. */ + + /* Used to recognize unchanged tzdata files. */ + dev_t tzfile_dev; + ino64_t tzfile_ino; + time_t tzfile_mtime; + + size_t num_transitions; + time_t *transitions; + unsigned char *type_idxs; + size_t num_types; + struct ttinfo *types; + char *zone_names; + long int rule_stdoff; + long int rule_dstoff; + size_t num_leaps; + struct leap *leaps; + char *tzspec; + bool use_tzfile; + + /* Updated by tzset.c. */ + + /* Used to recognize an unchanged TZ environment variable. */ + char *old_tz; + + /* tz_rules[0] is standard, tz_rules[1] is daylight. */ + struct tz_rule tz_rules[2]; + + /* The remaining members correspond to the global variables of the + same name (with a __ prefix within glibc). */ + + bool daylight; + int timezone; + char *tzname[2]; +}; + +/* Determine the actual location of the timezone file based on FILE. + Return a newly allocated string (which the caller should free), and + NULL on memory allocation failure. */ +char *__tzfile_determine_path (const char *file) + internal_function attribute_hidden; + +/* Return true if the timezone data in *TZDATA is up-to-date with + regards to the modification time of the file at PATH. */ +bool __tzfile_is_current (const char *path, const struct tzdata *tzdata) + internal_function attribute_hidden __nonnull ((1, 2)); + +/* Load the timezone data specified by FILE in *TZDATA. All errors + result in a tzdata object for the UTC time zone without any leap + seconds. */ +void __tzfile_read (struct tzdata *tzdata, const char *file, + size_t extra, char **extrap) + internal_function attribute_hidden __nonnull ((1)); + +/* Deallocate the data in the struct tzdata object, but not the object + itself. */ +void __tzdata_free (struct tzdata *tzdata) + internal_function attribute_hidden __nonnull ((1)); + +/* The user specified a hand-made timezone, but not its DST rules. We + will use the names and offsets from the user, and the rules from + the TZDEFRULES file. */ +void __tzfile_default (struct tzdata *tzdata, + const char *std, const char *dst, + long int stdoff, long int dstoff) + internal_function attribute_hidden __nonnull ((1, 2, 3)); + +/* Parse the POSIX TZ-style string. */ +void __tzset_parse_tz (struct tzdata *tzdata, const char *tz) + internal_function attribute_hidden __nonnull ((1, 2)); + +/* Figure out the correct timezone for TM and set TM->tm_zone, + TM->tm_isdst, and TM->tm_gmtoff accordingly. */ +void __tz_compute (const struct tzdata *tzdata, + time_t timer, struct tm *tm, int use_localtime) + internal_function attribute_hidden __nonnull ((1, 3)); + +/* Write the struct tm representation of *TIMER in the local timezone + to *TP. Use local time if USE_LOCALTIME, UTC otherwise. If + USE_REENTRANT, the global timezone variables are not updated. + Return TP. */ +struct tm *__tz_convert (const time_t *timer, bool use_reentrant, + bool use_localtime, struct tm *tp) + internal_function attribute_hidden __nonnull ((1, 4)); + +extern void __tzfile_compute (const struct tzdata *tzdata, + time_t timer, int use_localtime, + long int *leap_correct, int *leap_hit, + struct tm *tp); + internal_function attribute_hidden __nonnull ((1, 4, 5, 6)); + + +#endif /* TIME_PRIVATE_H */ diff --git a/time/tzfile.c b/time/tzfile.c index 9290dc6..aa7f503 100644 --- a/time/tzfile.c +++ b/time/tzfile.c @@ -26,41 +26,9 @@ #include <sys/stat.h> #include <stdint.h> -#include <time/time-variables.h> +#include <time/time-private.h> #include <timezone/tzfile.h> -int __use_tzfile; -static dev_t tzfile_dev; -static ino64_t tzfile_ino; -static time_t tzfile_mtime; - -struct ttinfo - { - long int offset; /* Seconds east of GMT. */ - unsigned char isdst; /* Used to set tm_isdst. */ - unsigned char idx; /* Index into `zone_names'. */ - unsigned char isstd; /* Transition times are in standard time. */ - unsigned char isgmt; /* Transition times are in GMT. */ - }; - -struct leap - { - time_t transition; /* Time the transition takes effect. */ - long int change; /* Seconds of correction to apply. */ - }; - -static size_t num_transitions; -libc_freeres_ptr (static time_t *transitions); -static unsigned char *type_idxs; -static size_t num_types; -static struct ttinfo *types; -static char *zone_names; -static long int rule_stdoff; -static long int rule_dstoff; -static size_t num_leaps; -static struct leap *leaps; -static char *tzspec; - #include <endian.h> #include <byteswap.h> @@ -96,35 +64,17 @@ decode64 (const void *ptr) return bswap_64 (*(const int64_t *) ptr); } - -void -__tzfile_read (const char *file, size_t extra, char **extrap) +char * +internal_function +__tzfile_determine_path (const char *file) { static const char default_tzdir[] = TZDIR; - size_t num_isstd, num_isgmt; - FILE *f; - struct tzhead tzhead; - size_t chars; - size_t i; - size_t total_size; - size_t types_idx; - size_t leaps_idx; - int was_using_tzfile = __use_tzfile; - int trans_width = 4; - size_t tzspec_len; - char *new = NULL; - - if (sizeof (time_t) != 4 && sizeof (time_t) != 8) - abort (); - - __use_tzfile = 0; - if (file == NULL) /* No user specification; use the site-wide default. */ file = TZDEFAULT; else if (*file == '\0') /* User specified the empty string; use UTC with no leap seconds. */ - goto ret_free_transitions; + ; else { /* We must not allow to read an arbitrary file in a setuid @@ -138,7 +88,7 @@ __tzfile_read (const char *file, size_t extra, char **extrap) || strstr (file, "../") != NULL)) /* This test is certainly a bit too restrictive but it should catch all critical cases. */ - goto ret_free_transitions; + file = ""; /* Use UTC. */ } if (*file != '/') @@ -148,18 +98,45 @@ __tzfile_read (const char *file, size_t extra, char **extrap) tzdir = getenv ("TZDIR"); if (tzdir == NULL || *tzdir == '\0') tzdir = default_tzdir; + char *new; if (__asprintf (&new, "%s/%s", tzdir, file) == -1) - goto ret_free_transitions; - file = new; + return NULL; + return new; } + return strdup (file); +} + +bool +internal_function +__tzfile_is_current (const char *path, const struct tzdata *tzdata) +{ /* If we were already using tzfile, check whether the file changed. */ struct stat64 st; - if (was_using_tzfile - && stat64 (file, &st) == 0 - && tzfile_ino == st.st_ino && tzfile_dev == st.st_dev - && tzfile_mtime == st.st_mtime) - goto done; /* Nothing to do. */ + return tzdata->use_tzfile + && stat64 (path, &st) == 0 + && tzdata->tzfile_ino == st.st_ino + && tzdata->tzfile_dev == st.st_dev + && tzdata->tzfile_mtime == st.st_mtime; +} + +void +internal_function +__tzfile_read (struct tzdata *tzdata, const char *file, + size_t extra, char **extrap) +{ + size_t num_isstd, num_isgmt; + FILE *f; + struct tzhead tzhead; + size_t chars; + size_t i; + size_t total_size; + size_t types_idx; + size_t leaps_idx; + int trans_width = 4; + size_t tzspec_len; + + tzdata->use_tzfile = false; /* Note the file is opened with cancellation in the I/O functions disabled and if available FD_CLOEXEC set. */ @@ -168,19 +145,20 @@ __tzfile_read (const char *file, size_t extra, char **extrap) goto ret_free_transitions; /* Get information about the file we are actually using. */ + struct stat64 st; if (fstat64 (__fileno (f), &st) != 0) { fclose (f); goto ret_free_transitions; } - free ((void *) transitions); - transitions = NULL; + free (tzdata->transitions); + tzdata->transitions = NULL; /* Remember the inode and device number and modification time. */ - tzfile_dev = st.st_dev; - tzfile_ino = st.st_ino; - tzfile_mtime = st.st_mtime; + tzdata->tzfile_dev = st.st_dev; + tzdata->tzfile_ino = st.st_ino; + tzdata->tzfile_mtime = st.st_mtime; /* No threads reading this stream. */ __fsetlocking (f, FSETLOCKING_BYCALLER); @@ -191,14 +169,15 @@ __tzfile_read (const char *file, size_t extra, char **extrap) || memcmp (tzhead.tzh_magic, TZ_MAGIC, sizeof (tzhead.tzh_magic)) != 0) goto lose; - num_transitions = (size_t) decode (tzhead.tzh_timecnt); - num_types = (size_t) decode (tzhead.tzh_typecnt); + tzdata->num_transitions = (size_t) decode (tzhead.tzh_timecnt); + tzdata->num_types = (size_t) decode (tzhead.tzh_typecnt); chars = (size_t) decode (tzhead.tzh_charcnt); - num_leaps = (size_t) decode (tzhead.tzh_leapcnt); + tzdata->num_leaps = (size_t) decode (tzhead.tzh_leapcnt); num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt); num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt); - if (__glibc_unlikely (num_isstd > num_types || num_isgmt > num_types)) + if (__glibc_unlikely (num_isstd > tzdata->num_types + || num_isgmt > tzdata->num_types)) goto lose; /* For platforms with 64-bit time_t we use the new format if available. */ @@ -209,10 +188,10 @@ __tzfile_read (const char *file, size_t extra, char **extrap) trans_width = 8; /* Position the stream before the second header. */ - size_t to_skip = (num_transitions * (4 + 1) - + num_types * 6 + size_t to_skip = (tzdata->num_transitions * (4 + 1) + + tzdata->num_types * 6 + chars - + num_leaps * 8 + + tzdata->num_leaps * 8 + num_isstd + num_isgmt); if (fseek (f, to_skip, SEEK_CUR) != 0) @@ -221,18 +200,18 @@ __tzfile_read (const char *file, size_t extra, char **extrap) goto read_again; } - if (__builtin_expect (num_transitions + if (__builtin_expect (tzdata->num_transitions > ((SIZE_MAX - (__alignof__ (struct ttinfo) - 1)) / (sizeof (time_t) + 1)), 0)) goto lose; - total_size = num_transitions * (sizeof (time_t) + 1); + total_size = tzdata->num_transitions * (sizeof (time_t) + 1); total_size = ((total_size + __alignof__ (struct ttinfo) - 1) & ~(__alignof__ (struct ttinfo) - 1)); types_idx = total_size; - if (__builtin_expect (num_types + if (__builtin_expect (tzdata->num_types > (SIZE_MAX - total_size) / sizeof (struct ttinfo), 0)) goto lose; - total_size += num_types * sizeof (struct ttinfo); + total_size += tzdata->num_types * sizeof (struct ttinfo); if (__glibc_unlikely (chars > SIZE_MAX - total_size)) goto lose; total_size += chars; @@ -242,26 +221,26 @@ __tzfile_read (const char *file, size_t extra, char **extrap) total_size = ((total_size + __alignof__ (struct leap) - 1) & ~(__alignof__ (struct leap) - 1)); leaps_idx = total_size; - if (__builtin_expect (num_leaps + if (__builtin_expect (tzdata->num_leaps > (SIZE_MAX - total_size) / sizeof (struct leap), 0)) goto lose; - total_size += num_leaps * sizeof (struct leap); + total_size += tzdata->num_leaps * sizeof (struct leap); tzspec_len = 0; if (sizeof (time_t) == 8 && trans_width == 8) { off_t rem = st.st_size - __ftello (f); if (__builtin_expect (rem < 0 - || (size_t) rem < (num_transitions * (8 + 1) - + num_types * 6 + || (size_t) rem < (tzdata->num_transitions * (8 + 1) + + tzdata->num_types * 6 + chars), 0)) goto lose; - tzspec_len = (size_t) rem - (num_transitions * (8 + 1) - + num_types * 6 + tzspec_len = (size_t) rem - (tzdata->num_transitions * (8 + 1) + + tzdata->num_types * 6 + chars); - if (__builtin_expect (num_leaps > SIZE_MAX / 12 - || tzspec_len < num_leaps * 12, 0)) + if (__builtin_expect (tzdata->num_leaps > SIZE_MAX / 12 + || tzspec_len < tzdata->num_leaps * 12, 0)) goto lose; - tzspec_len -= num_leaps * 12; + tzspec_len -= tzdata->num_leaps * 12; if (__glibc_unlikely (tzspec_len < num_isstd)) goto lose; tzspec_len -= num_isstd; @@ -277,43 +256,47 @@ __tzfile_read (const char *file, size_t extra, char **extrap) /* Allocate enough memory including the extra block requested by the caller. */ - transitions = (time_t *) malloc (total_size + tzspec_len + extra); - if (transitions == NULL) + tzdata->transitions = (time_t *) malloc (total_size + tzspec_len + extra); + if (tzdata->transitions == NULL) goto lose; - type_idxs = (unsigned char *) transitions + (num_transitions - * sizeof (time_t)); - types = (struct ttinfo *) ((char *) transitions + types_idx); - zone_names = (char *) types + num_types * sizeof (struct ttinfo); - leaps = (struct leap *) ((char *) transitions + leaps_idx); + tzdata->type_idxs = (unsigned char *) tzdata->transitions + + (tzdata->num_transitions * sizeof (time_t)); + tzdata->types = (struct ttinfo *) ((char *) tzdata->transitions + types_idx); + tzdata->zone_names = (char *) tzdata->types + + tzdata->num_types * sizeof (struct ttinfo); + tzdata->leaps = (struct leap *) ((char *) tzdata->transitions + leaps_idx); if (sizeof (time_t) == 8 && trans_width == 8) - tzspec = (char *) leaps + num_leaps * sizeof (struct leap) + extra; + tzdata->tzspec = (char *) tzdata->leaps + + tzdata->num_leaps * sizeof (struct leap) + extra; else - tzspec = NULL; + tzdata->tzspec = NULL; if (extra > 0) - *extrap = (char *) &leaps[num_leaps]; + *extrap = (char *) &tzdata->leaps[tzdata->num_leaps]; if (sizeof (time_t) == 4 || __builtin_expect (trans_width == 8, 1)) { - if (__builtin_expect (__fread_unlocked (transitions, trans_width + 1, - num_transitions, f) - != num_transitions, 0)) + if (__builtin_expect (__fread_unlocked + (tzdata->transitions, trans_width + 1, + tzdata->num_transitions, f) + != tzdata->num_transitions, 0)) goto lose; } else { - if (__builtin_expect (__fread_unlocked (transitions, 4, - num_transitions, f) - != num_transitions, 0) - || __builtin_expect (__fread_unlocked (type_idxs, 1, num_transitions, - f) != num_transitions, 0)) + if (__builtin_expect (__fread_unlocked (tzdata->transitions, 4, + tzdata->num_transitions, f) + != tzdata->num_transitions, 0) + || __builtin_expect (__fread_unlocked + (tzdata->type_idxs, 1, tzdata->num_transitions, + f) != tzdata->num_transitions, 0)) goto lose; } /* Check for bogus indices in the data file, so we can hereafter safely use type_idxs[T] as indices into `types' and never crash. */ - for (i = 0; i < num_transitions; ++i) - if (__glibc_unlikely (type_idxs[i] >= num_types)) + for (i = 0; i < tzdata->num_transitions; ++i) + if (__glibc_unlikely (tzdata->type_idxs[i] >= tzdata->num_types)) goto lose; if ((BYTE_ORDER != BIG_ENDIAN && (sizeof (time_t) == 4 || trans_width == 4)) @@ -324,19 +307,20 @@ __tzfile_read (const char *file, size_t extra, char **extrap) network (big-endian) byte order. We work from the end of the array so as not to clobber the next element to be processed when sizeof (time_t) > 4. */ - i = num_transitions; + i = tzdata->num_transitions; while (i-- > 0) - transitions[i] = decode ((char *) transitions + i * 4); + tzdata->transitions[i] = decode ((char *) tzdata->transitions + i * 4); } else if (BYTE_ORDER != BIG_ENDIAN && sizeof (time_t) == 8) { /* Decode the transition times, stored as 8-byte integers in network (big-endian) byte order. */ - for (i = 0; i < num_transitions; ++i) - transitions[i] = decode64 ((char *) transitions + i * 8); + for (i = 0; i < tzdata->num_transitions; ++i) + tzdata->transitions[i] + = decode64 ((char *) tzdata->transitions + i * 8); } - for (i = 0; i < num_types; ++i) + for (i = 0; i < tzdata->num_types; ++i) { unsigned char x[4]; int c; @@ -347,32 +331,33 @@ __tzfile_read (const char *file, size_t extra, char **extrap) c = getc_unlocked (f); if (__glibc_unlikely ((unsigned int) c > 1u)) goto lose; - types[i].isdst = c; + tzdata->types[i].isdst = c; c = getc_unlocked (f); if (__glibc_unlikely ((size_t) c > chars)) /* Bogus index in data file. */ goto lose; - types[i].idx = c; - types[i].offset = (long int) decode (x); + tzdata->types[i].idx = c; + tzdata->types[i].offset = (long int) decode (x); } - if (__glibc_unlikely (__fread_unlocked (zone_names, 1, chars, f) != chars)) + if (__glibc_unlikely (__fread_unlocked (tzdata->zone_names, 1, chars, f) + != chars)) goto lose; - for (i = 0; i < num_leaps; ++i) + for (i = 0; i < tzdata->num_leaps; ++i) { unsigned char x[8]; if (__builtin_expect (__fread_unlocked (x, 1, trans_width, f) != trans_width, 0)) goto lose; if (sizeof (time_t) == 4 || trans_width == 4) - leaps[i].transition = (time_t) decode (x); + tzdata->leaps[i].transition = (time_t) decode (x); else - leaps[i].transition = (time_t) decode64 (x); + tzdata->leaps[i].transition = (time_t) decode64 (x); if (__glibc_unlikely (__fread_unlocked (x, 1, 4, f) != 4)) goto lose; - leaps[i].change = (long int) decode (x); + tzdata->leaps[i].change = (long int) decode (x); } for (i = 0; i < num_isstd; ++i) @@ -380,31 +365,31 @@ __tzfile_read (const char *file, size_t extra, char **extrap) int c = getc_unlocked (f); if (__glibc_unlikely (c == EOF)) goto lose; - types[i].isstd = c != 0; + tzdata->types[i].isstd = c != 0; } - while (i < num_types) - types[i++].isstd = 0; + while (i < tzdata->num_types) + tzdata->types[i++].isstd = 0; for (i = 0; i < num_isgmt; ++i) { int c = getc_unlocked (f); if (__glibc_unlikely (c == EOF)) goto lose; - types[i].isgmt = c != 0; + tzdata->types[i].isgmt = c != 0; } - while (i < num_types) - types[i++].isgmt = 0; + while (i < tzdata->num_types) + tzdata->types[i++].isgmt = 0; /* Read the POSIX TZ-style information if possible. */ - if (sizeof (time_t) == 8 && tzspec != NULL) + if (sizeof (time_t) == 8 && tzdata->tzspec != NULL) { /* Skip over the newline first. */ if (getc_unlocked (f) != '\n' - || (__fread_unlocked (tzspec, 1, tzspec_len - 1, f) + || (__fread_unlocked (tzdata->tzspec, 1, tzspec_len - 1, f) != tzspec_len - 1)) - tzspec = NULL; + tzdata->tzspec = NULL; else - tzspec[tzspec_len - 1] = '\0'; + tzdata->tzspec[tzspec_len - 1] = '\0'; } else if (sizeof (time_t) == 4 && tzhead.tzh_version[0] != '\0') { @@ -449,69 +434,69 @@ __tzfile_read (const char *file, size_t extra, char **extrap) goto lose; } tzstr[tzspec_len - 1] = '\0'; - tzspec = __tzstring (tzstr); + tzdata->tzspec = __tzstring (tzstr); free (tzstr); } /* Don't use an empty TZ string. */ - if (tzspec != NULL && tzspec[0] == '\0') - tzspec = NULL; + if (tzdata->tzspec != NULL && tzdata->tzspec[0] == '\0') + tzdata->tzspec = NULL; fclose (f); /* First "register" all timezone names. */ - for (i = 0; i < num_types; ++i) - (void) __tzstring (&zone_names[types[i].idx]); + for (i = 0; i < tzdata->num_types; ++i) + (void) __tzstring (&tzdata->zone_names[tzdata->types[i].idx]); /* Find the standard and daylight time offsets used by the rule file. We choose the offsets in the types of each flavor that are transitioned to earliest in time. */ - __tzname[0] = NULL; - __tzname[1] = NULL; - for (i = num_transitions; i > 0; ) + tzdata->tzname[0] = NULL; + tzdata->tzname[1] = NULL; + for (i = tzdata->num_transitions; i > 0; ) { - int type = type_idxs[--i]; - int dst = types[type].isdst; + int type = tzdata->type_idxs[--i]; + int dst = tzdata->types[type].isdst; - if (__tzname[dst] == NULL) + if (tzdata->tzname[dst] == NULL) { - int idx = types[type].idx; + int idx = tzdata->types[type].idx; - __tzname[dst] = __tzstring (&zone_names[idx]); + tzdata->tzname[dst] = __tzstring (&tzdata->zone_names[idx]); - if (__tzname[1 - dst] != NULL) + if (tzdata->tzname[1 - dst] != NULL) break; } } - if (__tzname[0] == NULL) + if (tzdata->tzname[0] == NULL) { /* This should only happen if there are no transition rules. In this case there should be only one single type. */ - assert (num_types == 1); - __tzname[0] = __tzstring (zone_names); + assert (tzdata->num_types == 1); + tzdata->tzname[0] = __tzstring (tzdata->zone_names); } - if (__tzname[1] == NULL) - __tzname[1] = __tzname[0]; + if (tzdata->tzname[1] == NULL) + tzdata->tzname[1] = tzdata->tzname[0]; - if (num_transitions == 0) + if (tzdata->num_transitions == 0) /* Use the first rule (which should also be the only one). */ - rule_stdoff = rule_dstoff = types[0].offset; + tzdata->rule_stdoff = tzdata->rule_dstoff = tzdata->types[0].offset; else { int stdoff_set = 0, dstoff_set = 0; - rule_stdoff = rule_dstoff = 0; - i = num_transitions - 1; + tzdata->rule_stdoff = tzdata->rule_dstoff = 0; + i = tzdata->num_transitions - 1; do { - if (!stdoff_set && !types[type_idxs[i]].isdst) + if (!stdoff_set && !tzdata->types[tzdata->type_idxs[i]].isdst) { stdoff_set = 1; - rule_stdoff = types[type_idxs[i]].offset; + tzdata->rule_stdoff = tzdata->types[tzdata->type_idxs[i]].offset; } - else if (!dstoff_set && types[type_idxs[i]].isdst) + else if (!dstoff_set && tzdata->types[tzdata->type_idxs[i]].isdst) { dstoff_set = 1; - rule_dstoff = types[type_idxs[i]].offset; + tzdata->rule_dstoff = tzdata->types[tzdata->type_idxs[i]].offset; } if (stdoff_set && dstoff_set) break; @@ -519,31 +504,28 @@ __tzfile_read (const char *file, size_t extra, char **extrap) while (i-- > 0); if (!dstoff_set) - rule_dstoff = rule_stdoff; + tzdata->rule_dstoff = tzdata->rule_stdoff; } - __daylight = rule_stdoff != rule_dstoff; - __timezone = -rule_stdoff; - - done: - __use_tzfile = 1; - free (new); + tzdata->daylight = tzdata->rule_stdoff != tzdata->rule_dstoff; + tzdata->timezone = -tzdata->rule_stdoff; + tzdata->use_tzfile = true; return; lose: fclose (f); ret_free_transitions: - free (new); - free ((void *) transitions); - transitions = NULL; + free (tzdata->transitions); + tzdata->transitions = NULL; } /* The user specified a hand-made timezone, but not its DST rules. We will use the names and offsets from the user, and the rules from the TZDEFRULES file. */ - void -__tzfile_default (const char *std, const char *dst, +internal_function +__tzfile_default (struct tzdata *tzdata, + const char *std, const char *dst, long int stdoff, long int dstoff) { size_t stdlen = strlen (std) + 1; @@ -552,35 +534,39 @@ __tzfile_default (const char *std, const char *dst, int isdst; char *cp; - __tzfile_read (TZDEFRULES, stdlen + dstlen, &cp); - if (!__use_tzfile) + __tzfile_read (tzdata, TZDEFRULES, stdlen + dstlen, &cp); + if (!tzdata->use_tzfile) return; - if (num_types < 2) + if (tzdata->num_types < 2) { - __use_tzfile = 0; + tzdata->use_tzfile = false; return; } /* Ignore the zone names read from the file and use the given ones instead. */ __mempcpy (__mempcpy (cp, std, stdlen), dst, dstlen); - zone_names = cp; + tzdata->zone_names = cp; /* Now there are only two zones, regardless of what the file contained. */ - num_types = 2; + tzdata->num_types = 2; + + /* Reset the zone names to point to the user's names. */ + tzdata->tzname[0] = tzdata->zone_names; + tzdata->tzname[1] = tzdata->zone_names + stdlen; /* Now correct the transition times for the user-specified standard and daylight offsets from GMT. */ isdst = 0; - for (i = 0; i < num_transitions; ++i) + for (i = 0; i < tzdata->num_transitions; ++i) { - struct ttinfo *trans_type = &types[type_idxs[i]]; + struct ttinfo *trans_type = &tzdata->types[tzdata->type_idxs[i]]; /* We will use only types 0 (standard) and 1 (daylight). Fix up this transition to point to whichever matches the flavor of its original type. */ - type_idxs[i] = trans_type->isdst; + tzdata->type_idxs[i] = trans_type->isdst; if (trans_type->isgmt) /* The transition time is in GMT. No correction to apply. */ ; @@ -589,13 +575,13 @@ __tzfile_default (const char *std, const char *dst, wall clock time as of the previous transition was DST. Correct for the difference between the rule's DST offset and the user's DST offset. */ - transitions[i] += dstoff - rule_dstoff; + tzdata->transitions[i] += dstoff - tzdata->rule_dstoff; else /* This transition is in "local wall clock time", and wall clock time as of this iteration is non-DST. Correct for the difference between the rule's standard offset and the user's standard offset. */ - transitions[i] += stdoff - rule_stdoff; + tzdata->transitions[i] += stdoff - tzdata->rule_stdoff; /* The DST state of "local wall clock time" for the next iteration is as specified by this transition. */ @@ -605,33 +591,30 @@ __tzfile_default (const char *std, const char *dst, /* Now that we adjusted the transitions to the requested offsets, reset the rule_stdoff and rule_dstoff values appropriately. They are used elsewhere. */ - rule_stdoff = stdoff; - rule_dstoff = dstoff; + tzdata->rule_stdoff = stdoff; + tzdata->rule_dstoff = dstoff; /* Reset types 0 and 1 to describe the user's settings. */ - types[0].idx = 0; - types[0].offset = stdoff; - types[0].isdst = 0; - types[1].idx = stdlen; - types[1].offset = dstoff; - types[1].isdst = 1; - - /* Reset the zone names to point to the user's names. */ - __tzname[0] = (char *) std; - __tzname[1] = (char *) dst; + tzdata->types[0].idx = 0; + tzdata->types[0].offset = stdoff; + tzdata->types[0].isdst = 0; + tzdata->types[1].idx = stdlen; + tzdata->types[1].offset = dstoff; + tzdata->types[1].isdst = 1; /* Set the timezone. */ - __timezone = -types[0].offset; + __timezone = -tzdata->types[0].offset; /* Invalidate the tzfile attribute cache to force rereading TZDEFRULES the next time it is used. */ - tzfile_dev = 0; - tzfile_ino = 0; - tzfile_mtime = 0; + tzdata->tzfile_dev = 0; + tzdata->tzfile_ino = 0; + tzdata->tzfile_mtime = 0; } void -__tzfile_compute (time_t timer, int use_localtime, +internal_function +__tzfile_compute (const struct tzdata *tzdata, time_t timer, int use_localtime, long int *leap_correct, int *leap_hit, struct tm *tp) { @@ -642,30 +625,34 @@ __tzfile_compute (time_t timer, int use_localtime, __tzname[0] = NULL; __tzname[1] = NULL; - if (__glibc_unlikely (num_transitions == 0 || timer < transitions[0])) + if (__glibc_unlikely (tzdata->num_transitions == 0 + || timer < tzdata->transitions[0])) { /* TIMER is before any transition (or there are no transitions). Choose the first non-DST type (or the first if they're all DST types). */ i = 0; - while (i < num_types && types[i].isdst) + while (i < tzdata->num_types && tzdata->types[i].isdst) { if (__tzname[1] == NULL) - __tzname[1] = __tzstring (&zone_names[types[i].idx]); + __tzname[1] = __tzstring + (&tzdata->zone_names[tzdata->types[i].idx]); ++i; } - if (i == num_types) + if (i == tzdata->num_types) i = 0; - __tzname[0] = __tzstring (&zone_names[types[i].idx]); + __tzname[0] = __tzstring + (&tzdata->zone_names[tzdata->types[i].idx]); if (__tzname[1] == NULL) { size_t j = i; - while (j < num_types) - if (types[j].isdst) + while (j < tzdata->num_types) + if (tzdata->types[j].isdst) { - __tzname[1] = __tzstring (&zone_names[types[j].idx]); + __tzname[1] = __tzstring + (&tzdata->zone_names[tzdata->types[j].idx]); break; } else @@ -674,7 +661,7 @@ __tzfile_compute (time_t timer, int use_localtime, } else if (__glibc_unlikely (timer >= transitions[num_transitions - 1])) { - if (__glibc_unlikely (tzspec == NULL)) + if (__glibc_unlikely (tzdata->tzspec == NULL)) { use_last: i = num_transitions; @@ -682,7 +669,7 @@ __tzfile_compute (time_t timer, int use_localtime, } /* Parse the POSIX TZ-style string. */ - __tzset_parse_tz (tzspec); + __tzset_parse_tz (tzdata, tzdata->tzspec); /* Convert to broken down structure. If this fails do not use the string. */ @@ -690,16 +677,18 @@ __tzfile_compute (time_t timer, int use_localtime, goto use_last; /* Use the rules from the TZ string to compute the change. */ - __tz_compute (timer, tp, 1); + __tz_compute (tzdata, timer, tp, 1); /* If tzspec comes from posixrules loaded by __tzfile_default, override the STD and DST zone names with the ones user requested in TZ envvar. */ - if (__glibc_unlikely (zone_names == (char *) &leaps[num_leaps])) + if (__glibc_unlikely (tzdata->zone_names + == (char *) &tzdata->leaps[tzdata->num_leaps])) { - assert (num_types == 2); - __tzname[0] = __tzstring (zone_names); - __tzname[1] = __tzstring (&zone_names[strlen (zone_names) + 1]); + assert (tzdata->num_types == 2); + __tzname[0] = __tzstring (tzdata->zone_names); + __tzname[1] = __tzstring + (&tzdata->zone_names[strlen (tzdata->zone_names) + 1]); } goto leap; @@ -709,21 +698,22 @@ __tzfile_compute (time_t timer, int use_localtime, /* Find the first transition after TIMER, and then pick the type of the transition before it. */ size_t lo = 0; - size_t hi = num_transitions - 1; + size_t hi = tzdata->num_transitions - 1; /* Assume that DST is changing twice a year and guess initial search spot from it. Half of a gregorian year has on average 365.2425 * 86400 / 2 = 15778476 seconds. */ - i = (transitions[num_transitions - 1] - timer) / 15778476; - if (i < num_transitions) + i = (tzdata->transitions[tzdata->num_transitions - 1] - timer) + / 15778476; + if (i < tzdata->num_transitions) { i = num_transitions - 1 - i; - if (timer < transitions[i]) + if (timer < tzdata->transitions[i]) { - if (i < 10 || timer >= transitions[i - 10]) + if (i < 10 || timer >= tzdata->transitions[i - 10]) { /* Linear search. */ - while (timer < transitions[i - 1]) + while (timer < tzdata->transitions[i - 1]) --i; goto found; } @@ -731,10 +721,11 @@ __tzfile_compute (time_t timer, int use_localtime, } else { - if (i + 10 >= num_transitions || timer < transitions[i + 10]) + if (i + 10 >= tzdata->num_transitions + || timer < tzdata->transitions[i + 10]) { /* Linear search. */ - while (timer >= transitions[i]) + while (timer >= tzdata->transitions[i]) ++i; goto found; } @@ -756,19 +747,20 @@ __tzfile_compute (time_t timer, int use_localtime, found: /* assert (timer >= transitions[i - 1] - && (i == num_transitions || timer < transitions[i])); */ - __tzname[types[type_idxs[i - 1]].isdst] - = __tzstring (&zone_names[types[type_idxs[i - 1]].idx]); + && (i == tzdata->num_transitions || timer < transitions[i])); */ + __tzname[tzdata->types[tzdata->type_idxs[i - 1]].isdst] + = __tzstring (&tzdata->zone_names + [tzdata->types[tzdata->type_idxs[i - 1]].idx]); size_t j = i; - while (j < num_transitions) + while (j < tzdata->num_transitions) { - int type = type_idxs[j]; - int dst = types[type].isdst; - int idx = types[type].idx; + int type = tzdata->type_idxs[j]; + int dst = tzdata->types[type].isdst; + int idx = tzdata->types[type].idx; if (__tzname[dst] == NULL) { - __tzname[dst] = __tzstring (&zone_names[idx]); + __tzname[dst] = __tzstring (&tzdata->zone_names[idx]); if (__tzname[1 - dst] != NULL) break; @@ -780,25 +772,26 @@ __tzfile_compute (time_t timer, int use_localtime, if (__glibc_unlikely (__tzname[0] == NULL)) __tzname[0] = __tzname[1]; - i = type_idxs[i - 1]; + i = tzdata->type_idxs[i - 1]; } - struct ttinfo *info = &types[i]; - __daylight = rule_stdoff != rule_dstoff; - __timezone = -rule_stdoff; + struct ttinfo *info = &tzdata->types[i]; + __daylight = tzdata->rule_stdoff != tzdata->rule_dstoff; + __timezone = -tzdata->rule_stdoff; if (__tzname[0] == NULL) { /* This should only happen if there are no transition rules. In this case there should be only one single type. */ - assert (num_types == 1); - __tzname[0] = __tzstring (zone_names); + assert (tzdata->num_types == 1); + __tzname[0] = __tzstring (tzdata->zone_names); } if (__tzname[1] == NULL) /* There is no daylight saving time. */ __tzname[1] = __tzname[0]; tp->tm_isdst = info->isdst; - assert (strcmp (&zone_names[info->idx], __tzname[tp->tm_isdst]) == 0); + assert (strcmp (&tzdata->zone_names[info->idx], + __tzname[tp->tm_isdst]) == 0); tp->tm_zone = __tzname[tp->tm_isdst]; tp->tm_gmtoff = info->offset; } @@ -808,26 +801,37 @@ __tzfile_compute (time_t timer, int use_localtime, *leap_hit = 0; /* Find the last leap second correction transition time before TIMER. */ - i = num_leaps; + i = tzdata->num_leaps; do if (i-- == 0) return; - while (timer < leaps[i].transition); + while (timer < tzdata->leaps[i].transition); /* Apply its correction. */ - *leap_correct = leaps[i].change; + *leap_correct = tzdata->leaps[i].change; - if (timer == leaps[i].transition && /* Exactly at the transition time. */ - ((i == 0 && leaps[i].change > 0) || - leaps[i].change > leaps[i - 1].change)) + if (/* Exactly at the transition time. */ + timer == tzdata->leaps[i].transition + && ((i == 0 && tzdata->leaps[i].change > 0) || + tzdata->leaps[i].change > tzdata->leaps[i - 1].change)) { *leap_hit = 1; while (i > 0 - && leaps[i].transition == leaps[i - 1].transition + 1 - && leaps[i].change == leaps[i - 1].change + 1) + && (tzdata->leaps[i].transition + == tzdata->leaps[i - 1].transition + 1) + && tzdata->leaps[i].change == tzdata->leaps[i - 1].change + 1) { ++*leap_hit; --i; } } } + +void +internal_function +__tzdata_free (struct tzdata *tzdata) +{ + free (tzdata->transitions); + free (tzdata->old_tz); + free (tzdata); +} diff --git a/time/tzset.c b/time/tzset.c index 1fc3ac0..b164527 100644 --- a/time/tzset.c +++ b/time/tzset.c @@ -27,44 +27,29 @@ #include <libc-symbols.h> #include <shlib-compat.h> -#include <time/time-variables.h> +#include <time/time-private.h> #include <timezone/tzfile.h> -char *__tzname[2] = { (char *) "GMT", (char *) "GMT" }; -int __daylight = 0; -long int __timezone = 0L; +static void update_vars (const struct tzdata *tzdata); -weak_alias (__tzname, tzname) -weak_alias (__daylight, daylight) -weak_alias (__timezone, timezone) +/* Global time zone data object. Contains parsed timezone file and + timezone rule data. */ +static struct tzdata global_tzdata; -/* This locks all the state variables in tzfile.c and this file. */ +/* Access to global_tzdata is protected by this lock. Also covers the + other global variables in this file. */ __libc_lock_define_initialized (static, tzset_lock) -/* This structure contains all the information about a - timezone given in the POSIX standard TZ envariable. */ -typedef struct - { - const char *name; - - /* When to change. */ - enum { J0, J1, M } type; /* Interpretation of: */ - unsigned short int m, n, d; /* Month, week, day. */ - int secs; /* Time of day. */ - - long int offset; /* Seconds east of GMT (west if < 0). */ - - /* We cache the computed time of change for a - given year so we don't have to recompute it. */ - time_t change; /* When to change to this zone. */ - int computed_for; /* Year above is computed for. */ - } tz_rule; - -/* tz_rules[0] is standard, tz_rules[1] is daylight. */ -static tz_rule tz_rules[2]; +/* We cache the computed time of change for a + given year so we don't have to recompute it. */ +struct tzrule_cache +{ + time_t change; /* When to change to this zone. */ + int computed_for; /* Year above is computed for. */ +}; +static struct tzrule_cache global_tzrule_cache[2]; -static void compute_change (tz_rule *rule, int year) __THROW internal_function; static void tzset_internal (int always); /* List of buffers containing time zone strings. */ @@ -123,19 +108,6 @@ __tzstring (const char *s) return __tzstring_len (s, strlen (s)); } -static char *old_tz; - -static void -internal_function -update_vars (void) -{ - __daylight = tz_rules[0].offset != tz_rules[1].offset; - __timezone = -tz_rules[0].offset; - __tzname[0] = (char *) tz_rules[0].name; - __tzname[1] = (char *) tz_rules[1].name; -} - - static unsigned int compute_offset (unsigned int ss, unsigned int mm, unsigned int hh) { @@ -149,10 +121,10 @@ compute_offset (unsigned int ss, unsigned int mm, unsigned int hh) } /* Parses the time zone name at *TZP, and writes a pointer to an - interned string to tz_rules[WHICHRULE].name. On success, advances - *TZP, and returns true. Returns false otherwise. */ + interned string to TZDATA->tz_rules[WHICHRULE].name. On success, + advances *TZP, and returns true. Returns false otherwise. */ static bool -parse_tzname (const char **tzp, int whichrule) +parse_tzname (struct tzdata *tzdata, const char **tzp, int whichrule) { const char *start = *tzp; const char *p = start; @@ -179,17 +151,17 @@ parse_tzname (const char **tzp, int whichrule) const char *name = __tzstring_len (start, len); if (name == NULL) return false; - tz_rules[whichrule].name = name; + tzdata->tz_rules[whichrule].name = name; *tzp = p; return true; } /* Parses the time zone offset at *TZP, and writes it to - tz_rules[WHICHRULE].offset. Returns true if the parse was + TZDATA->tz_rules[WHICHRULE].offset. Returns true if the parse was successful. */ static bool -parse_offset (const char **tzp, int whichrule) +parse_offset (struct tzdata *tzdata, const char **tzp, int whichrule) { const char *tz = *tzp; if (whichrule == 0 @@ -209,30 +181,30 @@ parse_offset (const char **tzp, int whichrule) int consumed = 0; if (sscanf (tz, "%hu%n:%hu%n:%hu%n", &hh, &consumed, &mm, &consumed, &ss, &consumed) > 0) - tz_rules[whichrule].offset = sign * compute_offset (ss, mm, hh); + tzdata->tz_rules[whichrule].offset = sign * compute_offset (ss, mm, hh); else /* Nothing could be parsed. */ if (whichrule == 0) { /* Standard time defaults to offset zero. */ - tz_rules[0].offset = 0; + tzdata->tz_rules[0].offset = 0; return false; } else /* DST defaults to one hour later than standard time. */ - tz_rules[1].offset = tz_rules[0].offset + (60 * 60); + tzdata->tz_rules[1].offset = tzdata->tz_rules[0].offset + (60 * 60); *tzp = tz + consumed; return true; } /* Parses the standard <-> DST rules at *TZP. Updates - tz_rule[WHICHRULE]. On success, advances *TZP and returns true. - Otherwise, returns false. */ + TZDATA->tz_rule[WHICHRULE]. On success, advances *TZP and returns + true. Otherwise, returns false. */ static bool -parse_rule (const char **tzp, int whichrule) +parse_rule (struct tzdata *tzdata, const char **tzp, int whichrule) { const char *tz = *tzp; - tz_rule *tzr = &tz_rules[whichrule]; + struct tz_rule *tzr = &tzdata->tz_rules[whichrule]; /* Ignore comma to support string following the incorrect specification in early POSIX.1 printings. */ @@ -273,7 +245,7 @@ parse_rule (const char **tzp, int whichrule) Below is the equivalent of "M3.2.0,M11.1.0" [/2 not needed since 2:00AM is the default]. */ tzr->type = M; - if (tzr == &tz_rules[0]) + if (tzr == &tzdata->tz_rules[0]) { tzr->m = 3; tzr->n = 2; @@ -314,55 +286,51 @@ parse_rule (const char **tzp, int whichrule) /* Default to 2:00 AM. */ tzr->secs = 2 * 60 * 60; - tzr->computed_for = -1; *tzp = tz; return true; } -/* Parse the POSIX TZ-style string. */ void -__tzset_parse_tz (const char *tz) +internal_function +__tzset_parse_tz (struct tzdata *tzdata, const char *tz) { - /* Clear out old state and reset to unnamed UTC. */ - memset (tz_rules, '\0', sizeof tz_rules); - tz_rules[0].name = tz_rules[1].name = ""; + /* Set to unnamed UTC. */ + tzdata->tz_rules[0].name = tzdata->tz_rules[1].name = ""; /* Get the standard timezone name. */ - if (parse_tzname (&tz, 0) && parse_offset (&tz, 0)) + if (parse_tzname (tzdata, &tz, 0) && parse_offset (tzdata, &tz, 0)) { /* Get the DST timezone name (if any). */ if (*tz != '\0') { - if (parse_tzname (&tz, 1)) + if (parse_tzname (tzdata, &tz, 1)) { - parse_offset (&tz, 1); + parse_offset (tzdata, &tz, 1); if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0')) { /* There is no rule. See if there is a default rule file. */ - __tzfile_default (tz_rules[0].name, tz_rules[1].name, - tz_rules[0].offset, tz_rules[1].offset); - if (__use_tzfile) + __tzfile_default (tzdata, tzdata->tz_rules[0].name, tzdata->tz_rules[1].name, + tzdata->tz_rules[0].offset, tzdata->tz_rules[1].offset); + if (tzdata->use_tzfile) { - free (old_tz); - old_tz = NULL; + free (tzdata->old_tz); + tzdata->old_tz = NULL; return; } } } /* Figure out the standard <-> DST rules. */ - if (parse_rule (&tz, 0)) - parse_rule (&tz, 1); + if (parse_rule (tzdata, &tz, 0)) + parse_rule (tzdata, &tz, 1); } else { /* There is no DST. */ - tz_rules[1].name = tz_rules[0].name; - tz_rules[1].offset = tz_rules[0].offset; + tzdata->tz_rules[1].name = tzdata->tz_rules[0].name; + tzdata->tz_rules[1].offset = tzdata->tz_rules[0].offset; } } - - update_vars (); } /* Interpret the TZ envariable. */ @@ -389,7 +357,8 @@ tzset_internal (int always) ++tz; /* Check whether the value changed since the last run. */ - if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0) + if (global_tzdata.old_tz != NULL && tz != NULL + && strcmp (tz, global_tzdata.old_tz) == 0) /* No change, simply return. */ return; @@ -397,45 +366,47 @@ tzset_internal (int always) /* No user specification; use the site-wide default. */ tz = TZDEFAULT; - tz_rules[0].name = NULL; - tz_rules[1].name = NULL; + global_tzdata.tz_rules[0].name = NULL; + global_tzdata.tz_rules[1].name = NULL; + + /* Invalidate the tzrule cache. */ + global_tzrule_cache[0].computed_for = -1; + global_tzrule_cache[1].computed_for = -1; /* Save the value of `tz'. */ - free (old_tz); - old_tz = tz ? __strdup (tz) : NULL; + free (global_tzdata.old_tz); + global_tzdata.old_tz = tz ? __strdup (tz) : NULL; /* Try to read a data file. */ - __tzfile_read (tz, 0, NULL); - if (__use_tzfile) + __tzfile_read (&global_tzdata, tz, 0, NULL); + if (global_tzdata.use_tzfile) return; /* No data file found. Default to UTC if nothing specified. */ - if (tz == NULL || *tz == '\0' || (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0)) { - memset (tz_rules, '\0', sizeof tz_rules); - tz_rules[0].name = tz_rules[1].name = "UTC"; - if (J0 != 0) - tz_rules[0].type = tz_rules[1].type = J0; - tz_rules[0].change = tz_rules[1].change = (time_t) -1; - update_vars (); + memset (global_tzdata.tz_rules, '\0', sizeof (global_tzdata.tz_rules)); + global_tzdata.tz_rules[0].name = global_tzdata.tz_rules[1].name = "UTC"; + global_tzrule_cache[0].change = global_tzrule_cache[1].change = -1; + update_vars (&global_tzdata); return; } - - __tzset_parse_tz (tz); + __tzset_parse_tz (&global_tzdata, tz); + update_vars (&global_tzdata); } /* Figure out the exact time (as a time_t) in YEAR when the change described by RULE will occur and put it in RULE->change, saving YEAR in RULE->computed_for. */ static void -internal_function -compute_change (tz_rule *rule, int year) +compute_change (const struct tz_rule *rule, + struct tzrule_cache *cache, + int year) { time_t t; - if (year != -1 && rule->computed_for == year) + if (year != -1 && cache->computed_for == year) /* Operations on times in 2 BC will be slower. Oh well. */ return; @@ -511,8 +482,8 @@ compute_change (tz_rule *rule, int year) /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want. Just add the time of day and local offset from GMT, and we're done. */ - rule->change = t - rule->offset + rule->secs; - rule->computed_for = year; + cache->change = t - rule->offset + rule->secs; + cache->computed_for = year; } @@ -520,10 +491,13 @@ compute_change (tz_rule *rule, int year) `__timezone', and `__daylight' accordingly. */ void internal_function -__tz_compute (time_t timer, struct tm *tm, int use_localtime) +__tz_compute (const struct tzdata *tzdata, + time_t timer, struct tm *tm, int use_localtime) { - compute_change (&tz_rules[0], 1900 + tm->tm_year); - compute_change (&tz_rules[1], 1900 + tm->tm_year); + compute_change (&tzdata->tz_rules[0], &global_tzrule_cache[0], + 1900 + tm->tm_year); + compute_change (&tzdata->tz_rules[1], &global_tzrule_cache[1], + 1900 + tm->tm_year); if (use_localtime) { @@ -532,16 +506,16 @@ __tz_compute (time_t timer, struct tm *tm, int use_localtime) /* We have to distinguish between northern and southern hemisphere. For the latter the daylight saving time ends in the next year. */ - if (__builtin_expect (tz_rules[0].change - > tz_rules[1].change, 0)) - isdst = (timer < tz_rules[1].change - || timer >= tz_rules[0].change); + if (__builtin_expect (global_tzrule_cache[0].change + > global_tzrule_cache[1].change, 0)) + isdst = (timer < global_tzrule_cache[1].change + || timer >= global_tzrule_cache[0].change); else - isdst = (timer >= tz_rules[0].change - && timer < tz_rules[1].change); + isdst = (timer >= global_tzrule_cache[0].change + && timer < global_tzrule_cache[1].change); tm->tm_isdst = isdst; - tm->tm_zone = __tzname[isdst]; - tm->tm_gmtoff = tz_rules[isdst].offset; + tm->tm_zone = tzdata->tzname[isdst]; + tm->tm_gmtoff = tzdata->tz_rules[isdst].offset; } } @@ -555,21 +529,17 @@ __tzset (void) tzset_internal (1); - if (!__use_tzfile) - { - /* Set `tzname'. */ - __tzname[0] = (char *) tz_rules[0].name; - __tzname[1] = (char *) tz_rules[1].name; - } + if (!global_tzdata.use_tzfile) + update_vars (&global_tzdata); __libc_lock_unlock (tzset_lock); } weak_alias (__tzset, tzset) - -/* Return the `struct tm' representation of *TIMER in the local timezone. - Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */ + struct tm * -__tz_convert (const time_t *timer, int use_localtime, struct tm *tp) +internal_function +__tz_convert (const time_t *timer, bool use_reentrant, + bool use_localtime, struct tm *tp) { long int leap_correction; int leap_extra_secs; @@ -585,17 +555,17 @@ __tz_convert (const time_t *timer, int use_localtime, struct tm *tp) /* Update internal database according to current TZ setting. POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname. This is a good idea since this allows at least a bit more parallelism. */ - tzset_internal (tp == &_tmbuf && use_localtime); + tzset_internal (!use_reentrant && use_localtime); - if (__use_tzfile) - __tzfile_compute (*timer, use_localtime, &leap_correction, + if (global_tzdata.use_tzfile) + __tzfile_compute (&global_tzdata, *timer, use_localtime, &leap_correction, &leap_extra_secs, tp); else { if (! __offtime (timer, 0, tp)) tp = NULL; else - __tz_compute (*timer, tp, use_localtime); + __tz_compute (&global_tzdata, *timer, tp, use_localtime); leap_correction = 0L; leap_extra_secs = 0; } @@ -630,6 +600,26 @@ libc_freeres_fn (free_mem) tzstring_list = tzstring_list->next; free (old); } - free (old_tz); - old_tz = NULL; +} + +/* Do this at the very end, so that the code above does not + accidentally use any of these global variables. */ + +#include <time/time-variables.h> + +char *__tzname[2] = { (char *) "GMT", (char *) "GMT" }; +int __daylight = 0; +long int __timezone = 0L; + +weak_alias (__tzname, tzname) +weak_alias (__daylight, daylight) +weak_alias (__timezone, timezone) + +static void +update_vars (const struct tzdata *tzdata) +{ + __daylight = tzdata->tz_rules[0].offset != tzdata->tz_rules[1].offset; + __timezone = -tzdata->tz_rules[0].offset; + __tzname[0] = (char *) tzdata->tz_rules[0].name; + __tzname[1] = (char *) tzdata->tz_rules[1].name; } |