* include/time.h: Declare __tzset_parse_tz and __tzset_compute.

* time/tzset.c (tzset_internal): Break TZ string parsing out into
	__tzset_parse_tz and updating of daylight, timezone, tzname into
	update_vars.
	(__tz_compute): Renamed from tz_compute.  Take additional parameters.
	(__tz_convert): Updating of tm_isdst, tm_zone, and tm_gmtoff now
	happens in __tz_compute.
	* time/tzfile.c (__tzfile_read): Also read TZ string.
	(find_transition): Fold into __tzfile_compute.
	(__tzfile_compute): For times beyond the last transition try to
	use the TZ string.
	* timezone/tst-timezone.c: Information in daylight and tzname does
	change for Asia/Tokyo timezone with more concrete information.
	Remove the test.

	* include/stdio.h: Add libc_hidden_proto for ftello.
	* libio/ftello.c: Add libc_hidden_def.
This commit is contained in:
Ulrich Drepper 2007-10-14 09:15:45 +00:00
parent 89dc9d4c5b
commit fa76dde2cf
8 changed files with 325 additions and 260 deletions

View File

@ -1,5 +1,23 @@
2007-10-14 Ulrich Drepper <drepper@redhat.com>
* include/time.h: Declare __tzset_parse_tz and __tzset_compute.
* time/tzset.c (tzset_internal): Break TZ string parsing out into
__tzset_parse_tz and updating of daylight, timezone, tzname into
update_vars.
(__tz_compute): Renamed from tz_compute. Take additional parameters.
(__tz_convert): Updating of tm_isdst, tm_zone, and tm_gmtoff now
happens in __tz_compute.
* time/tzfile.c (__tzfile_read): Also read TZ string.
(find_transition): Fold into __tzfile_compute.
(__tzfile_compute): For times beyond the last transition try to
use the TZ string.
* timezone/tst-timezone.c: Information in daylight and tzname does
change for Asia/Tokyo timezone with more concrete information.
Remove the test.
* include/stdio.h: Add libc_hidden_proto for ftello.
* libio/ftello.c: Add libc_hidden_def.
[BZ #1140]
* time/tzfile.c (__tzfile_compute): Compute tzname[] values based
on the specified time and not the last entries in the file. Move

View File

@ -138,6 +138,7 @@ libc_hidden_proto (rewind)
libc_hidden_proto (fileno)
libc_hidden_proto (fwrite)
libc_hidden_proto (fseek)
libc_hidden_proto (ftello)
libc_hidden_proto (fflush_unlocked)
libc_hidden_proto (fread_unlocked)
libc_hidden_proto (fwrite_unlocked)

View File

@ -46,6 +46,9 @@ extern void __tzfile_compute (time_t timer, int use_localtime,
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

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1993, 1995-2001, 2002, 2003, 2004
/* Copyright (C) 1993, 1995-2001, 2002, 2003, 2004, 2007
Free Software Foundation, Inc.
This file is part of the GNU C Library.
@ -63,3 +63,4 @@ ftello (fp)
}
return pos;
}
libc_hidden_def (ftello)

View File

@ -264,9 +264,9 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
retval = 0;
/* If there are no group members TOTAL_LEN is zero. */
if (total_len > 0)
if (gr_name == NULL)
{
if (gr_name == NULL)
if (total_len > 0)
{
size_t n = __readall (sock, resultbuf->gr_mem[0], total_len);
if (__builtin_expect (n != total_len, 0))
@ -278,26 +278,26 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
else
*result = resultbuf;
}
else
}
else
{
/* Copy the group member names. */
memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
/* Try to detect corrupt databases. */
if (resultbuf->gr_name[gr_name_len - 1] != '\0'
|| resultbuf->gr_passwd[gr_resp.gr_passwd_len - 1] != '\0'
|| ({for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0')
break;
cnt < gr_resp.gr_mem_cnt; }))
{
/* Copy the group member names. */
memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
/* Try to detect corrupt databases. */
if (resultbuf->gr_name[gr_name_len - 1] != '\0'
|| resultbuf->gr_passwd[gr_resp.gr_passwd_len - 1] != '\0'
|| ({for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0')
break;
cnt < gr_resp.gr_mem_cnt; }))
{
/* We cannot use the database. */
retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
goto out_close;
}
*result = resultbuf;
/* We cannot use the database. */
retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
goto out_close;
}
*result = resultbuf;
}
}
else

View File

@ -62,6 +62,7 @@ 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>
@ -113,6 +114,7 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
size_t leaps_idx;
int was_using_tzfile = __use_tzfile;
int trans_width = 4;
size_t tzspec_len;
if (sizeof (time_t) != 4 && sizeof (time_t) != 8)
abort ();
@ -241,10 +243,18 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
& ~(__alignof__ (struct leap) - 1));
leaps_idx = total_size;
total_size += num_leaps * sizeof (struct leap);
tzspec_len = (trans_width == 8
? st.st_size - (ftello (f)
+ num_transitions * (8 + 1)
+ num_types * 6
+ chars
+ num_leaps * 8
+ num_isstd
+ num_isgmt) - 1 : 0);
/* Allocate enough memory including the extra block requested by the
caller. */
transitions = (time_t *) malloc (total_size + extra);
transitions = (time_t *) malloc (total_size + tzspec_len + extra);
if (transitions == NULL)
goto lose;
@ -253,6 +263,10 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
types = (struct ttinfo *) ((char *) transitions + types_idx);
zone_names = (char *) types + num_types * sizeof (struct ttinfo);
leaps = (struct leap *) ((char *) transitions + leaps_idx);
if (trans_width == 8)
tzspec = (char *) leaps + num_leaps * sizeof (struct leap);
else
tzspec = NULL;
if (extra > 0)
*extrap = (char *) &leaps[num_leaps];
@ -356,11 +370,16 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
while (i < num_types)
types[i++].isgmt = 0;
/* XXX When a version 2 file is available it can contain a POSIX TZ-style
formatted string which specifies how times past the last one specified
are supposed to be handled. We might want to handle this at some
point. But it might be overhead since most/all? files have an
open-ended last entry. */
/* Read the POSIX TZ-style information if possible. */
if (tzspec != NULL)
{
/* Skip over the newline first. */
if (getc_unlocked (f) != '\n'
|| fread_unlocked (tzspec, 1, tzspec_len - 1, f) != tzspec_len - 1)
tzspec = NULL;
else
tzspec[tzspec_len - 1] = '\0';
}
fclose (f);
@ -530,128 +549,6 @@ __tzfile_default (const char *std, const char *dst,
compute_tzname_max (stdlen + dstlen);
}
static struct ttinfo *
internal_function
find_transition (time_t timer)
{
size_t i;
__tzname[0] = NULL;
__tzname[1] = NULL;
if (num_transitions == 0 || timer < 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)
{
if (__tzname[1] == NULL)
__tzname[1] = __tzstring (&zone_names[types[i].idx]);
++i;
}
if (i == num_types)
i = 0;
__tzname[0] = __tzstring (&zone_names[types[i].idx]);
if (__tzname[1] == NULL)
{
size_t j = i;
while (j < num_types)
if (types[j].isdst)
{
__tzname[1] = __tzstring (&zone_names[types[j].idx]);
break;
}
else
++j;
}
}
else if (timer >= transitions[num_transitions - 1])
{
i = num_transitions - 1;
goto found;
}
else
{
/* 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;
/* 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 = num_transitions - 1 - i;
if (timer < transitions[i])
{
if (i < 10 || timer >= transitions[i - 10])
{
/* Linear search. */
while (timer < transitions[i - 1])
--i;
goto found;
}
hi = i - 10;
}
else
{
if (i + 10 >= num_transitions || timer < transitions[i + 10])
{
/* Linear search. */
while (timer >= transitions[i])
++i;
goto found;
}
lo = i + 10;
}
}
/* Binary search. */
/* assert (timer >= transitions[lo] && timer < transitions[hi]); */
while (lo + 1 < hi)
{
i = (lo + hi) / 2;
if (timer < transitions[i])
hi = i;
else
lo = i;
}
i = hi;
found:
/* assert (timer >= transitions[i - 1] && timer < transitions[i]); */
__tzname[types[type_idxs[i - 1]].isdst]
= __tzstring (&zone_names[types[type_idxs[i - 1]].idx]);
size_t j = i;
while (j < num_transitions)
{
int type = type_idxs[j];
int dst = types[type].isdst;
int idx = types[type].idx;
if (__tzname[dst] == NULL)
{
__tzname[dst] = __tzstring (&zone_names[idx]);
if (__tzname[1 - dst] != NULL)
break;
}
++j;
}
i = type_idxs[i - 1];
}
return &types[i];
}
void
__tzfile_compute (time_t timer, int use_localtime,
long int *leap_correct, int *leap_hit,
@ -661,7 +558,139 @@ __tzfile_compute (time_t timer, int use_localtime,
if (use_localtime)
{
struct ttinfo *info = find_transition (timer);
__tzname[0] = NULL;
__tzname[1] = NULL;
if (num_transitions == 0 || timer < 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)
{
if (__tzname[1] == NULL)
__tzname[1] = __tzstring (&zone_names[types[i].idx]);
++i;
}
if (i == num_types)
i = 0;
__tzname[0] = __tzstring (&zone_names[types[i].idx]);
if (__tzname[1] == NULL)
{
size_t j = i;
while (j < num_types)
if (types[j].isdst)
{
__tzname[1] = __tzstring (&zone_names[types[j].idx]);
break;
}
else
++j;
}
}
else if (timer >= transitions[num_transitions - 1])
{
if (tzspec == NULL)
{
use_last:
i = num_transitions - 1;
goto found;
}
/* Parse the POSIX TZ-style string. */
__tzset_parse_tz (tzspec);
/* Convert to broken down structure. If this fails do not
use the string. */
if (! __offtime (&timer, 0, tp))
goto use_last;
/* Use the rules from the TZ string to compute the change. */
__tz_compute (timer, tp, 1);
*leap_correct = 0L;
*leap_hit = 0;
return;
}
else
{
/* 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;
/* 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 = num_transitions - 1 - i;
if (timer < transitions[i])
{
if (i < 10 || timer >= transitions[i - 10])
{
/* Linear search. */
while (timer < transitions[i - 1])
--i;
goto found;
}
hi = i - 10;
}
else
{
if (i + 10 >= num_transitions || timer < transitions[i + 10])
{
/* Linear search. */
while (timer >= transitions[i])
++i;
goto found;
}
lo = i + 10;
}
}
/* Binary search. */
/* assert (timer >= transitions[lo] && timer < transitions[hi]); */
while (lo + 1 < hi)
{
i = (lo + hi) / 2;
if (timer < transitions[i])
hi = i;
else
lo = i;
}
i = hi;
found:
/* assert (timer >= transitions[i - 1] && timer < transitions[i]); */
__tzname[types[type_idxs[i - 1]].isdst]
= __tzstring (&zone_names[types[type_idxs[i - 1]].idx]);
size_t j = i;
while (j < num_transitions)
{
int type = type_idxs[j];
int dst = types[type].isdst;
int idx = types[type].idx;
if (__tzname[dst] == NULL)
{
__tzname[dst] = __tzstring (&zone_names[idx]);
if (__tzname[1 - dst] != NULL)
break;
}
++j;
}
i = type_idxs[i - 1];
}
struct ttinfo *info = &types[i];
__daylight = rule_stdoff != rule_dstoff;
__timezone = -rule_stdoff;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1991-2002,2003,2004 Free Software Foundation, Inc.
/* Copyright (C) 1991-2002,2003,2004,2007 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
@ -70,7 +70,6 @@ static tz_rule tz_rules[2];
static void compute_change (tz_rule *rule, int year) __THROW internal_function;
static void tz_compute (const struct tm *tm) __THROW internal_function;
static void tzset_internal (int always, int explicit)
__THROW internal_function;
@ -92,7 +91,7 @@ __tzstring (const char *s)
{
char *p;
struct tzstring_l *t, *u, *new;
size_t len = strlen(s);
size_t len = strlen (s);
/* Walk the list and look for a match. If this string is the same
as the end of an already-allocated string, it can share space. */
@ -140,80 +139,34 @@ __tzname_max ()
static char *old_tz;
/* Interpret the TZ envariable. */
static void
internal_function
tzset_internal (always, explicit)
int always;
int explicit;
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;
/* Keep __tzname_cur_max up to date. */
size_t len0 = strlen (__tzname[0]);
size_t len1 = strlen (__tzname[1]);
if (len0 > __tzname_cur_max)
__tzname_cur_max = len0;
if (len1 > __tzname_cur_max)
__tzname_cur_max = len1;
}
/* Parse the POSIX TZ-style string. */
void
__tzset_parse_tz (tz)
const char *tz;
{
static int is_initialized;
register const char *tz;
register size_t l;
char *tzbuf;
unsigned short int hh, mm, ss;
unsigned short int whichrule;
if (is_initialized && !always)
return;
is_initialized = 1;
/* Examine the TZ environment variable. */
tz = getenv ("TZ");
if (tz == NULL && !explicit)
/* Use the site-wide default. This is a file name which means we
would not see changes to the file if we compare only the file
name for change. We want to notice file changes if tzset() has
been called explicitly. Leave TZ as NULL in this case. */
tz = TZDEFAULT;
if (tz && *tz == '\0')
/* User specified the empty string; use UTC explicitly. */
tz = "Universal";
/* A leading colon means "implementation defined syntax".
We ignore the colon and always use the same algorithm:
try a data file, and if none exists parse the 1003.1 syntax. */
if (tz && *tz == ':')
++tz;
/* Check whether the value changes since the last run. */
if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
/* No change, simply return. */
return;
if (tz == NULL)
/* No user specification; use the site-wide default. */
tz = TZDEFAULT;
tz_rules[0].name = NULL;
tz_rules[1].name = NULL;
/* Save the value of `tz'. */
if (old_tz != NULL)
free (old_tz);
old_tz = tz ? __strdup (tz) : NULL;
/* Try to read a data file. */
__tzfile_read (tz, 0, NULL);
if (__use_tzfile)
return;
/* No data file found. Default to UTC if nothing specified. */
if (tz == NULL || *tz == '\0'
|| (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0))
{
tz_rules[0].name = tz_rules[1].name = "UTC";
tz_rules[0].type = tz_rules[1].type = J0;
tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
tz_rules[0].secs = tz_rules[1].secs = 0;
tz_rules[0].offset = tz_rules[1].offset = 0L;
tz_rules[0].change = tz_rules[1].change = (time_t) -1;
tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
goto out;
}
/* Clear out old state and reset to unnamed UTC. */
memset (tz_rules, 0, sizeof tz_rules);
tz_rules[0].name = tz_rules[1].name = "";
@ -413,20 +366,81 @@ tzset_internal (always, explicit)
}
out:
__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;
update_vars ();
}
{
/* Keep __tzname_cur_max up to date. */
size_t len0 = strlen (__tzname[0]);
size_t len1 = strlen (__tzname[1]);
if (len0 > __tzname_cur_max)
__tzname_cur_max = len0;
if (len1 > __tzname_cur_max)
__tzname_cur_max = len1;
}
/* Interpret the TZ envariable. */
static void
internal_function
tzset_internal (always, explicit)
int always;
int explicit;
{
static int is_initialized;
register const char *tz;
if (is_initialized && !always)
return;
is_initialized = 1;
/* Examine the TZ environment variable. */
tz = getenv ("TZ");
if (tz == NULL && !explicit)
/* Use the site-wide default. This is a file name which means we
would not see changes to the file if we compare only the file
name for change. We want to notice file changes if tzset() has
been called explicitly. Leave TZ as NULL in this case. */
tz = TZDEFAULT;
if (tz && *tz == '\0')
/* User specified the empty string; use UTC explicitly. */
tz = "Universal";
/* A leading colon means "implementation defined syntax".
We ignore the colon and always use the same algorithm:
try a data file, and if none exists parse the 1003.1 syntax. */
if (tz && *tz == ':')
++tz;
/* Check whether the value changes since the last run. */
if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
/* No change, simply return. */
return;
if (tz == NULL)
/* No user specification; use the site-wide default. */
tz = TZDEFAULT;
tz_rules[0].name = NULL;
tz_rules[1].name = NULL;
/* Save the value of `tz'. */
if (old_tz != NULL)
free (old_tz);
old_tz = tz ? __strdup (tz) : NULL;
/* Try to read a data file. */
__tzfile_read (tz, 0, NULL);
if (__use_tzfile)
return;
/* No data file found. Default to UTC if nothing specified. */
if (tz == NULL || *tz == '\0'
|| (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0))
{
tz_rules[0].name = tz_rules[1].name = "UTC";
tz_rules[0].type = tz_rules[1].type = J0;
tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
tz_rules[0].secs = tz_rules[1].secs = 0;
tz_rules[0].offset = tz_rules[1].offset = 0L;
tz_rules[0].change = tz_rules[1].change = (time_t) -1;
tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
update_vars ();
return;
}
__tzset_parse_tz (tz);
}
/* Figure out the exact time (as a time_t) in YEAR
@ -523,13 +537,34 @@ compute_change (rule, year)
/* Figure out the correct timezone for TM and set `__tzname',
`__timezone', and `__daylight' accordingly. */
static void
void
internal_function
tz_compute (tm)
const struct tm *tm;
__tz_compute (timer, tm, use_localtime)
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);
if (use_localtime)
{
int isdst;
/* 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);
else
isdst = (timer >= tz_rules[0].change
&& timer < tz_rules[1].change);
tm->tm_isdst = isdst;
tm->tm_zone = __tzname[isdst];
tm->tm_gmtoff = tz_rules[isdst].offset;
}
}
/* Reinterpret the TZ environment variable and set `tzname'. */
@ -583,35 +618,14 @@ __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
if (! __offtime (timer, 0, tp))
tp = NULL;
else
tz_compute (tp);
__tz_compute (*timer, tp, use_localtime);
leap_correction = 0L;
leap_extra_secs = 0;
}
if (tp)
{
if (use_localtime)
{
if (!__use_tzfile)
{
int isdst;
/* 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);
else
isdst = (*timer >= tz_rules[0].change
&& *timer < tz_rules[1].change);
tp->tm_isdst = isdst;
tp->tm_zone = __tzname[isdst];
tp->tm_gmtoff = tz_rules[isdst].offset;
}
}
else
if (! use_localtime)
{
tp->tm_isdst = 0;
tp->tm_zone = "GMT";

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1998, 1999, 2000, 2005 Free Software Foundation, Inc.
/* Copyright (C) 1998, 1999, 2000, 2005, 2007 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Andreas Jaeger <aj@suse.de>, 1998.
@ -44,7 +44,6 @@ static const struct test_times tests[] =
{ "America/Chicago", 1, 21600, {"CST", "CDT" }},
{ "America/Indiana/Indianapolis", 1, 18000, {"EST", "EDT" }},
{ "America/Los_Angeles", 1, 28800, {"PST", "PDT" }},
{ "Asia/Tokyo", 1, -32400, {"JST", "JDT" }},
{ "Pacific/Auckland", 1, -43200, { "NZST", "NZDT" }},
{ NULL, 0, 0 }
};