Compare commits

...
2 Commits
Author SHA1 Message Date
Florian Weimer 60a91a23d6 WIP reorganization to improve scalability of localtime 2017-03-09 16:34:11 +01:00
Florian Weimer c9611e6f0e Isolate global timezone variables in <time/time-variables.h>
This change makes it more explicit which subsystems access these
variables (which are thread safety hazards).
2017-03-09 16:34:11 +01:00
20 changed files with 707 additions and 429 deletions
+32
View File
@@ -1,3 +1,35 @@
2017-03-08 Florian Weimer <fweimer@redhat.com>
Isolate global timezone variables in <time/time-variables.h>.
* time/time-variables.h: New file.
* time/time.h (__tzname, __daylight, __timezone): Remove private
declarations.
(tzname, daylight, timezone): Update comments.
(__isleap): Remove definition.
* include/time.h (__time_isleap): Declare.
* time/Makefile (routines): Add isleap.
(tests): Add tst-isleap.
* time/Versions (GLIBC_PRIVATE): Export __time_isleap.
* time/dysize.c (dysize): Call __time_isleap
instead of __isleap.
* time/getdate.c (check_mday): Likewise.
* time/offtime.c (__offtime): Likewise.
* time/gmtime.c: Include <time/time-variables.h>.
* time/localtime.c: Likewise.
* time/tzfile.c: Likewise.
* time/tzset.c: Likewise.
(__tzname, __daylight, __timezone): Turn into compat symbols.
(compute_change): Call __time_isleap instead of __isleap.
* time/strftime_l.c: Include <time/time-variables.h>. Reorganize
includes.
(tzname): Declare for !_LIBC only.
(__isleap): Define as __time_isleap.
* time/isleap.c: New file.
* time/tst-isleap.c: Likewise.
* locale/programs/ld-time.c (time_finish): Call __time_isleap
instead of __isleap.
* sysdeps/posix/gettimeofday.c: Include <time/time-variables.h>.
2017-03-08 Yury Norov <ynorov@caviumnetworks.com>
Zack Weinberg <zackw@panix.com>
+3
View File
@@ -11,6 +11,9 @@ Version 2.26
transliteration tables are all updated to Unicode 9.0.0, using
generator scripts contributed by Mike FABIAN (Red Hat).
* The private identifiers __daylight, __isleap, __timezone, __tzname have
been removed from the <time.h> header file.
Security related changes:
[Add security related changes here]
+3 -18
View File
@@ -3,6 +3,7 @@
#ifndef _ISOMAC
# include <xlocale.h>
# include <stdbool.h>
__BEGIN_DECLS
@@ -31,25 +32,12 @@ struct tm;
/* Defined in mktime.c. */
extern const unsigned short int __mon_yday[2][13] attribute_hidden;
/* Defined in localtime.c. */
extern struct tm _tmbuf attribute_hidden;
bool __time_isleap (int year) internal_function;
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. */
@@ -75,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)
+5 -2
View File
@@ -26,6 +26,7 @@
#include <wchar.h>
#include <stdint.h>
#include <sys/uio.h>
#include <time.h>
#include <assert.h>
@@ -353,7 +354,8 @@ No definition for %s category found"), "LC_TIME"));
> days_per_month[time->era_entries[idx].start_date[1]])
|| (time->era_entries[idx].start_date[1] == 2
&& time->era_entries[idx].start_date[2] == 29
&& !__isleap (time->era_entries[idx].start_date[0])))
&& !__time_isleap (time->era_entries[idx]
.start_date[0])))
&& !be_quiet)
WITH_CUR_LOCALE (error (0, 0, _("\
%s: starting date is invalid in string %Zd in `era' field"),
@@ -430,7 +432,8 @@ No definition for %s category found"), "LC_TIME"));
> days_per_month[time->era_entries[idx].stop_date[1]])
|| (time->era_entries[idx].stop_date[1] == 2
&& time->era_entries[idx].stop_date[2] == 29
&& !__isleap (time->era_entries[idx].stop_date[0])))
&& !__time_isleap (time->era_entries[idx]
.stop_date[0])))
&& !be_quiet)
WITH_CUR_LOCALE (error (0, 0, _("\
%s: invalid stopping date in string %Zd in `era' field"),
+3
View File
@@ -19,6 +19,9 @@
#include <time.h>
#include <sys/time.h>
/* For __tzname, __timezone, __daylight. */
#include <time/time-variables.h>
/* Get the current time of day and timezone information,
putting it into *TV and *TZ. If TZ is NULL, *TZ is not filled.
Returns 0 on success, -1 on errors. */
+2 -2
View File
@@ -36,14 +36,14 @@ routines := offtime asctime clock ctime ctime_r difftime \
stime dysize timegm ftime \
getdate strptime strptime_l \
strftime wcsftime strftime_l wcsftime_l \
timespec_get
timespec_get isleap
aux := era alt_digit lc-time-cleanup
tests := test_time clocktest tst-posixtz tst-strptime tst_wcsftime \
tst-getdate tst-mktime tst-mktime2 tst-ftime_l tst-strftime \
tst-mktime3 tst-strptime2 bug-asctime bug-asctime_r bug-mktime1 \
tst-strptime3 bug-getdate1 tst-strptime-whitespace tst-ftime \
tst-tzname
tst-tzname tst-isleap
include ../Rules
+3
View File
@@ -65,4 +65,7 @@ libc {
GLIBC_2.16 {
timespec_get;
}
GLIBC_PRIVATE {
__time_isleap;
}
}
+1 -1
View File
@@ -20,5 +20,5 @@
int
dysize (int year)
{
return __isleap (year) ? 366 : 365;
return __time_isleap (year) ? 366 : 365;
}
+1 -1
View File
@@ -95,7 +95,7 @@ check_mday (int year, int mon, int mday)
return 1;
break;
case 1:
if (mday >= 1 && mday <= (__isleap (year) ? 29 : 28))
if (mday >= 1 && mday <= (__time_isleap (year) ? 29 : 28))
return 1;
break;
}
+5 -2
View File
@@ -17,13 +17,15 @@
<http://www.gnu.org/licenses/>. */
#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)
@@ -33,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);
}
+27
View File
@@ -0,0 +1,27 @@
/* Leap year detection.
Copyright (C) 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/>. */
#include <time.h>
bool
internal_function
__time_isleap (int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
libc_hidden_def (__time_isleap)
+5 -2
View File
@@ -17,6 +17,8 @@
<http://www.gnu.org/licenses/>. */
#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;
@@ -27,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)
@@ -36,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)
+2 -2
View File
@@ -57,7 +57,7 @@ __offtime (const time_t *t, long int offset, struct tm *tp)
#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
while (days < 0 || days >= (__isleap (y) ? 366 : 365))
while (days < 0 || days >= (__time_isleap (y) ? 366 : 365))
{
/* Guess a corrected year, assuming 365 days per year. */
time_t yg = y + days / 365 - (days % 365 < 0);
@@ -76,7 +76,7 @@ __offtime (const time_t *t, long int offset, struct tm *tp)
return 0;
}
tp->tm_yday = days;
ip = __mon_yday[__isleap(y)];
ip = __mon_yday[__time_isleap (y)];
for (y = 11; days < (long int) ip[y]; --y)
continue;
days -= ip[y];
+14 -12
View File
@@ -33,28 +33,30 @@
# define MULTIBYTE_IS_FORMAT_SAFE 1
# define STDC_HEADERS 1
# include "../locale/localeinfo.h"
#endif
#if defined emacs && !defined HAVE_BCOPY
# define HAVE_MEMCPY 1
# define __isleap(year) __time_isleap (year)
# include <time.h>
# include <time/time-variables.h>
#endif
#include <ctype.h>
#include <sys/types.h> /* Some systems define `time_t' here. */
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
#ifndef _LIBC
# ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# else
# include <time.h>
# else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
# endif
#endif
#if HAVE_TZNAME
# if HAVE_TZNAME
extern char *tzname[];
#endif
# endif
#endif /* !_LIBC */
/* Do multibyte processing if multibytes are supported, unless
multibyte sequences are safe in formats. Multibyte sequences are
+148
View File
@@ -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 */
+43
View File
@@ -0,0 +1,43 @@
/* Global variables used by the time subsystem.
Copyright (C) 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/>. */
/* This header should only be used within the time subsystem, but
there are some historic external references. The global variables
declared here cannot be accessed in a thread-safe manner. */
#ifndef TIME_VARIABLES_H
#define TIME_VARIABLES_H
#include <stdbool.h>
#include <time.h>
/* Global variables updated by tzset, localtime. Their counterparts
without the __ prefix are weak aliases of them. The static linker
will ensure that dynamically-linked programs which do not define
their own variable (without the __ prefix) use the variable from
glibc (which is updated by the glibc code through the __-prefixed
alias). If the application defines its own variable, it will be
separate from the glibc variable. */
extern char *__tzname[2];
extern int __daylight;
extern long int __timezone;
/* Defined in localtime.c. Used by localtime and gmtime. */
extern struct tm _tmbuf attribute_hidden;
#endif /* TIME_VARIABLES_H */
+3 -18
View File
@@ -160,16 +160,8 @@ extern char *ctime_r (const time_t *__restrict __timer,
char *__restrict __buf) __THROW;
#endif /* POSIX */
/* Defined in localtime.c. */
extern char *__tzname[2]; /* Current timezone names. */
extern int __daylight; /* If daylight-saving time is ever in use. */
extern long int __timezone; /* Seconds west of UTC. */
#ifdef __USE_POSIX
/* Same as above. */
extern char *tzname[2];
extern char *tzname[2]; /* Current timezone names. */
/* Set time conversion information from the TZ environment variable.
If TZ is not defined, a locale-dependent default is used. */
@@ -177,8 +169,8 @@ extern void tzset (void) __THROW;
#endif
#if defined __USE_MISC || defined __USE_XOPEN
extern int daylight;
extern long int timezone;
extern int daylight; /* If daylight-saving time is ever in use. */
extern long int timezone; /* Seconds west of UTC. */
#endif
#ifdef __USE_MISC
@@ -187,13 +179,6 @@ extern long int timezone;
extern int stime (const time_t *__when) __THROW;
#endif
/* Nonzero if YEAR is a leap year (every 4 years,
except every 100th isn't, and every 400th is). */
#define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#ifdef __USE_MISC
/* Miscellaneous functions many Unices inherited from the public domain
localtime package. These are included only for compatibility. */
+40
View File
@@ -0,0 +1,40 @@
/* Test leap year processing.
Copyright (C) 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/>. */
#include <time.h>
#include <support/check.h>
static int
do_test (void)
{
TEST_VERIFY (!__time_isleap (-100));
TEST_VERIFY (__time_isleap (0));
TEST_VERIFY (!__time_isleap (100));
TEST_VERIFY (!__time_isleap (200));
TEST_VERIFY (!__time_isleap (300));
TEST_VERIFY (__time_isleap (400));
TEST_VERIFY (!__time_isleap (1900));
TEST_VERIFY (__time_isleap (1996));
TEST_VERIFY (__time_isleap (2000));
TEST_VERIFY (__time_isleap (2004));
TEST_VERIFY (__time_isleap (2008));
TEST_VERIFY (!__time_isleap (2100));
return 0;
}
#include <support/test-driver.c>
+248 -243
View File
@@ -26,40 +26,9 @@
#include <sys/stat.h>
#include <stdint.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>
@@ -95,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
@@ -137,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 != '/')
@@ -147,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. */
@@ -167,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);
@@ -190,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. */
@@ -208,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)
@@ -220,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;
@@ -241,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;
@@ -276,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))
@@ -323,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;
@@ -346,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)
@@ -379,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')
{
@@ -448,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;
@@ -518,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;
@@ -551,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. */ ;
@@ -588,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. */
@@ -604,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)
{
@@ -641,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
@@ -673,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;
@@ -681,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. */
@@ -689,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;
@@ -708,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;
}
@@ -730,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;
}
@@ -755,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;
@@ -779,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;
}
@@ -807,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);
}
+119 -126
View File
@@ -24,44 +24,32 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <libc-symbols.h>
#include <shlib-compat.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;
/* 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. */
};
/* When to change. */
enum { J0, J1, M } type; /* Interpretation of: */
unsigned short int m, n, d; /* Month, week, day. */
int secs; /* Time of day. */
static struct tzrule_cache global_tzrule_cache[2];
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];
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. */
@@ -120,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)
{
@@ -146,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;
@@ -176,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
@@ -206,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. */
@@ -270,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;
@@ -311,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. */
@@ -386,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;
@@ -394,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;
@@ -457,7 +431,7 @@ compute_change (tz_rule *rule, int year)
add SECSPERDAY times the day number-1 to the time of
January 1, midnight, to get the day. */
t += (rule->d - 1) * SECSPERDAY;
if (rule->d >= 60 && __isleap (year))
if (rule->d >= 60 && __time_isleap (year))
t += SECSPERDAY;
break;
@@ -473,7 +447,7 @@ compute_change (tz_rule *rule, int year)
unsigned int i;
int d, m1, yy0, yy1, yy2, dow;
const unsigned short int *myday =
&__mon_yday[__isleap (year)][rule->m];
&__mon_yday[__time_isleap (year)][rule->m];
/* First add SECSPERDAY for each day in months before M. */
t += myday[-1] * SECSPERDAY;
@@ -508,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;
}
@@ -517,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)
{
@@ -529,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;
}
}
@@ -552,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;
@@ -582,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;
}
@@ -627,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;
}