diff options
Diffstat (limited to 'time')
-rw-r--r-- | time/strftime.c | 12 | ||||
-rw-r--r-- | time/tzfile.c | 15 | ||||
-rw-r--r-- | time/tzset.c | 134 |
3 files changed, 101 insertions, 60 deletions
diff --git a/time/strftime.c b/time/strftime.c index c53f20872c..891d301f5c 100644 --- a/time/strftime.c +++ b/time/strftime.c @@ -424,15 +424,13 @@ strftime (s, maxsize, format, tp) const char *f; zone = NULL; -#if !defined _LIBC && HAVE_TM_ZONE - /* XXX We have some problems here. First, the string pointed to by - tm_zone is dynamically allocated while loading the zone data. But - when another zone is loaded since the information in TP were - computed this would be a stale pointer. - The second problem is the POSIX test suite which assumes setting +#if HAVE_TM_ZONE + /* The POSIX test suite assumes that setting the environment variable TZ to a new value before calling strftime() will influence the result (the %Z format) even if the information in - TP is computed with a totally different time zone. --drepper@gnu */ + TP is computed with a totally different time zone. + This is bogus: though POSIX allows bad behavior like this, + POSIX does not require it. Do the right thing instead. */ zone = (const char *) tp->tm_zone; #endif #if HAVE_TZNAME diff --git a/time/tzfile.c b/time/tzfile.c index 2d0752c147..88e86e33b1 100644 --- a/time/tzfile.c +++ b/time/tzfile.c @@ -43,6 +43,8 @@ struct leap long int change; /* Seconds of correction to apply. */ }; +extern const char * __tzstring (const char *); /* Defined in tzset.c. */ + static struct ttinfo *find_transition (time_t timer); static void compute_tzname_max (size_t); @@ -267,9 +269,9 @@ __tzfile_read (const char *file) info = find_transition (0); for (i = 0; i < num_types && i < sizeof (__tzname) / sizeof (__tzname[0]); ++i) - __tzname[types[i].isdst] = &zone_names[types[i].idx]; + __tzname[types[i].isdst] = __tzstring (&zone_names[types[i].idx]); if (info->isdst < sizeof (__tzname) / sizeof (__tzname[0])) - __tzname[info->isdst] = &zone_names[info->idx]; + __tzname[info->isdst] = __tzstring (&zone_names[info->idx]); compute_tzname_max (chars); @@ -285,7 +287,8 @@ __tzfile_read (const char *file) from the TZDEFRULES file. */ void -__tzfile_default (char *std, char *dst, long int stdoff, long int dstoff) +__tzfile_default (const char *std, const char *dst, + long int stdoff, long int dstoff) { size_t stdlen, dstlen, i; long int rule_offset, rule_stdoff, rule_dstoff; @@ -372,8 +375,8 @@ __tzfile_default (char *std, char *dst, long int stdoff, long int dstoff) types[1].isdst = 1; /* Reset the zone names to point to the user's names. */ - __tzname[0] = &zone_names[0]; - __tzname[1] = &zone_names[stdlen]; + __tzname[0] = (char *) std; + __tzname[1] = (char *) dst; compute_tzname_max (stdlen + dstlen); } @@ -455,7 +458,7 @@ __tzfile_compute (time_t timer, long int *leap_correct, int *leap_hit) void compute_tzname_max (size_t chars) { - extern size_t __tzname_cur_max; /* Defined in __tzset.c. */ + extern size_t __tzname_cur_max; /* Defined in tzset.c. */ const char *p; diff --git a/time/tzset.c b/time/tzset.c index 9eceb73cf5..d1c2c091a5 100644 --- a/time/tzset.c +++ b/time/tzset.c @@ -31,8 +31,9 @@ extern const unsigned short int __mon_yday[2][13]; extern int __use_tzfile; extern void __tzfile_read __P ((const char *file)); -extern void __tzfile_default __P ((char *std, char *dst, +extern void __tzfile_default __P ((const char *std, const char *dst, long int stdoff, long int dstoff)); +extern const char * __tzstring __P ((const char *string)); extern int __tz_compute __P ((time_t timer, const struct tm *tm)); char *__tzname[2] = { (char *) "GMT", (char *) "GMT" }; @@ -53,7 +54,7 @@ weak_alias (__timezone, timezone) timezone given in the POSIX standard TZ envariable. */ typedef struct { - char *name; + const char *name; /* When to change. */ enum { J0, J1, M } type; /* Interpretation of: */ @@ -74,6 +75,68 @@ static tz_rule tz_rules[2]; static int compute_change __P ((tz_rule *rule, int year)); +/* Header for a list of buffers containing time zone strings. */ +struct tzstring_head +{ + struct tzstring_head *next; + /* The buffer itself immediately follows the header. + The buffer contains zero or more (possibly overlapping) strings. + The last string is followed by 2 '\0's instead of the usual 1. */ +}; + +/* First in a list of buffers containing time zone strings. + All the buffers but the last are read-only. */ +static struct +{ + struct tzstring_head head; + char data[48]; +} tzstring_list; + +/* Size of the last buffer in the list, not counting its header. */ +static size_t tzstring_last_buffer_size = sizeof tzstring_list.data; + +/* Allocate a time zone string with given contents. + The string will never be moved or deallocated. + However, its contents may be shared with other such strings. */ +const char * +__tzstring (string) + const char *string; +{ + struct tzstring_head *h = &tzstring_list.head; + size_t needed; + char *p; + + /* Look through time zone string list for a duplicate of this one. */ + for (h = &tzstring_list.head; ; h = h->next) + { + for (p = (char *) (h + 1); p[0] | p[1]; p++) + if (strcmp (p, string) == 0) + return p; + if (! h->next) + break; + } + + /* No duplicate was found. Copy to the end of this buffer if there's room; + otherwise, append a large-enough new buffer to the list and use it. */ + p++; + needed = strlen (string) + 2; /* Need 2 trailing '\0's after last string. */ + + if ((size_t) ((char *) (h + 1) + tzstring_last_buffer_size - p) < needed) + { + size_t buffer_size = tzstring_last_buffer_size; + while ((buffer_size *= 2) < needed) + continue; + if (! (h = h->next = malloc (sizeof *h + buffer_size))) + return NULL; + h->next = NULL; + tzstring_last_buffer_size = buffer_size; + p = (char *) (h + 1); + } + + strncpy (p, string, needed); + return p; +} + static char *old_tz = NULL; /* Interpret the TZ envariable. */ @@ -85,6 +148,7 @@ __tzset_internal (always) static int is_initialized = 0; register const char *tz; register size_t l; + char *tzbuf; unsigned short int hh, mm, ss; unsigned short int whichrule; @@ -112,12 +176,6 @@ __tzset_internal (always) /* No change, simply return. */ return; - /* Free old storage. */ - if (tz_rules[0].name != NULL && *tz_rules[0].name != '\0') - free ((void *) tz_rules[0].name); - if (tz_rules[1].name != NULL && *tz_rules[1].name != '\0' && - tz_rules[1].name != tz_rules[0].name) - free ((void *) tz_rules[1].name); tz_rules[0].name = NULL; tz_rules[1].name = NULL; @@ -135,16 +193,7 @@ __tzset_internal (always) if (tz == NULL || *tz == '\0') { - static const char UTC[] = "UTC"; - size_t len = sizeof UTC; - tz_rules[0].name = (char *) malloc (len); - if (tz_rules[0].name == NULL) - return; - tz_rules[1].name = (char *) malloc (len); - if (tz_rules[1].name == NULL) - return; - memcpy ((void *) tz_rules[0].name, UTC, len); - memcpy ((void *) tz_rules[1].name, UTC, len); + 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; @@ -157,11 +206,11 @@ __tzset_internal (always) /* Clear out old state and reset to unnamed UTC. */ memset (tz_rules, 0, sizeof tz_rules); - tz_rules[0].name = tz_rules[1].name = (char *) ""; + tz_rules[0].name = tz_rules[1].name = ""; /* Get the standard timezone name. */ - tz_rules[0].name = (char *) malloc (strlen (tz) + 1); - if (tz_rules[0].name == NULL) + tzbuf = malloc (strlen (tz) + 1); + if (! tzbuf) { /* Clear the old tz name so we will try again. */ free (old_tz); @@ -169,25 +218,23 @@ __tzset_internal (always) return; } - if (sscanf (tz, "%[^0-9,+-]", tz_rules[0].name) != 1 || - (l = strlen(tz_rules[0].name)) < 3) + if (sscanf (tz, "%[^0-9,+-]", tzbuf) != 1 || + (l = strlen (tzbuf)) < 3) { - free (tz_rules[0].name); - tz_rules[0].name = (char *) ""; + free (tzbuf); return; } - { - char *n = realloc ((void *) tz_rules[0].name, l + 1); - if (n != NULL) - tz_rules[0].name = n; - } + tz_rules[0].name = __tzstring (tzbuf); tz += l; /* Figure out the standard offset from UTC. */ if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz))) - return; + { + free (tzbuf); + return; + } if (*tz == '-' || *tz == '+') tz_rules[0].offset = *tz++ == '-' ? 1L : -1L; @@ -196,6 +243,7 @@ __tzset_internal (always) switch (sscanf (tz, "%hu:%hu:%hu", &hh, &mm, &ss)) { default: + free (tzbuf); return; case 1: mm = 0; @@ -218,23 +266,14 @@ __tzset_internal (always) /* Get the DST timezone name (if any). */ if (*tz != '\0') { - char *n = malloc (strlen (tz) + 1); - if (n != NULL) - { - tz_rules[1].name = n; - if (sscanf (tz, "%[^0-9,+-]", tz_rules[1].name) != 1 || - (l = strlen (tz_rules[1].name)) < 3) - { - free (n); - tz_rules[1].name = (char *) ""; - goto done_names; /* Punt on name, set up the offsets. */ - } - n = realloc ((void *) tz_rules[1].name, l + 1); - if (n != NULL) - tz_rules[1].name = n; + char *n = tzbuf + strlen (tzbuf) + 1; + if (sscanf (tz, "%[^0-9,+-]", n) != 1 || + (l = strlen (n)) < 3) + goto done_names; /* Punt on name, set up the offsets. */ - tz += l; - } + tz_rules[1].name = __tzstring (n); + + tz += l; /* Figure out the DST offset from GMT. */ if (*tz == '-' || *tz == '+') @@ -271,6 +310,7 @@ __tzset_internal (always) tz_rules[1].name = tz_rules[0].name; done_names: + free (tzbuf); if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0')) { |