about summary refs log tree commit diff
path: root/time/tzset.c
diff options
context:
space:
mode:
Diffstat (limited to 'time/tzset.c')
-rw-r--r--time/tzset.c134
1 files changed, 87 insertions, 47 deletions
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'))
     {