about summary refs log tree commit diff
path: root/iconv
diff options
context:
space:
mode:
Diffstat (limited to 'iconv')
-rw-r--r--iconv/strtab.c37
1 files changed, 32 insertions, 5 deletions
diff --git a/iconv/strtab.c b/iconv/strtab.c
index 4189f97281..e6feb2cf24 100644
--- a/iconv/strtab.c
+++ b/iconv/strtab.c
@@ -90,13 +90,21 @@ extern size_t strtaboffset (struct Strent *se);
 struct Strtab *
 strtabinit (void)
 {
+  struct Strtab *ret;
+
   if (ps == 0)
     {
       ps = sysconf (_SC_PAGESIZE) - 2 * sizeof (void *);
       assert (sizeof (struct memoryblock) < ps);
     }
 
-  return (struct Strtab *) calloc (1, sizeof (struct Strtab));
+  ret = (struct Strtab *) calloc (1, sizeof (struct Strtab));
+  if (ret != NULL)
+    {
+      ret->null.len = 1;
+      ret->null.string = "";
+    }
+  return ret;
 }
 
 
@@ -141,10 +149,6 @@ newstring (struct Strtab *st, const char *str, size_t len)
   size_t align;
   int i;
 
-  /* Compute the string length if the caller doesn't know it.  */
-  if (len == 0)
-    len = strlen (str) + 1;
-
   /* Compute the amount of padding needed to make the structure aligned.  */
   align = ((__alignof__ (struct Strent)
 	    - (((uintptr_t) st->backp)
@@ -211,6 +215,14 @@ strtabadd (struct Strtab *st, const char *str, size_t len)
   struct Strent *newstr;
   struct Strent **sep;
 
+  /* Compute the string length if the caller doesn't know it.  */
+  if (len == 0)
+    len = strlen (str) + 1;
+
+  /* Make sure all "" strings get offset 0.  */
+  if (len == 1)
+    return &st->null;
+
   /* Allocate memory for the new string and its associated information.  */
   newstr = newstring (st, str, len);
 
@@ -223,6 +235,19 @@ strtabadd (struct Strtab *st, const char *str, size_t len)
       /* This is not the same entry.  This means we have a prefix match.  */
       if ((*sep)->len > newstr->len)
 	{
+	  struct Strent *subs;
+
+	  for (subs = (*sep)->next; subs; subs = subs->next)
+	    if (subs->len == newstr->len)
+	      {
+		/* We have an exact match with a substring.  Free the memory
+		   we allocated.  */
+		st->left += st->backp - (char *) newstr;
+		st->backp = (char *) newstr;
+
+		return subs;
+	      }
+
 	  /* We have a new substring.  This means we don't need the reverse
 	     string of this entry anymore.  */
 	  st->backp -= newstr->len;
@@ -238,6 +263,8 @@ strtabadd (struct Strtab *st, const char *str, size_t len)
 	     it is longer.  In this case we have to put it first.  */
 	  st->total += newstr->len - (*sep)->len;
 	  newstr->next = *sep;
+	  newstr->left = (*sep)->left;
+	  newstr->right = (*sep)->right;
 	  *sep = newstr;
 	}
       else