summary refs log tree commit diff
path: root/time
diff options
context:
space:
mode:
Diffstat (limited to 'time')
-rw-r--r--time/strftime.c12
-rw-r--r--time/tzfile.c15
-rw-r--r--time/tzset.c134
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'))
     {