unistr/u8-strchr: Fix several bugs.
[gnulib.git] / lib / memxfrm.c
index 9b29c52..a1c6cf8 100644 (file)
@@ -1,5 +1,5 @@
 /* Locale dependent memory area transformation for comparison.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2009.
 
    This program is free software: you can redistribute it and/or modify it
@@ -25,7 +25,7 @@
 #include <string.h>
 
 char *
-memxfrm (char *s, size_t n, size_t *lengthp)
+memxfrm (char *s, size_t n, char *resultbuf, size_t *lengthp)
 {
   /* Result accumulator.  */
   char *result;
@@ -35,10 +35,18 @@ memxfrm (char *s, size_t n, size_t *lengthp)
   char orig_sentinel;
 
   /* Initial memory allocation.  */
-  allocated = (n > 0 ? n : 1);
-  result = (char *) malloc (allocated);
-  if (result == NULL)
-    goto out_of_memory_2;
+  if (resultbuf != NULL && *lengthp > 0)
+    {
+      result = resultbuf;
+      allocated = *lengthp;
+    }
+  else
+    {
+      allocated = (n > 0 ? n : 1);
+      result = (char *) malloc (allocated);
+      if (result == NULL)
+        goto out_of_memory_2;
+    }
   length = 0;
 
   /* Add sentinel.byte.  */
@@ -55,49 +63,54 @@ memxfrm (char *s, size_t n, size_t *lengthp)
     p = s;
     for (;;)
       {
-       /* Search next NUL byte.  */
-       const char *q = p + strlen (p);
-
-       for (;;)
-         {
-           size_t k;
-
-           errno = 0;
-           k = strxfrm (result + length, p, allocated - length);
-           if (errno != 0)
-             goto fail;
-           if (k >= allocated - length)
-             {
-               /* Grow the result buffer.  */
-               char *new_result;
-
-               allocated = 2 * allocated;
-               new_result = (char *) realloc (result, allocated);
-               if (new_result == NULL)
-                 goto out_of_memory_1;
-               result = new_result;
-             }
-           else
-             {
-               length += k;
-               break;
-             }
-         }
-
-       p = q + 1;
-       if (p == p_end)
-         break;
-       result[length] = '\0';
+        /* Search next NUL byte.  */
+        const char *q = p + strlen (p);
+
+        for (;;)
+          {
+            size_t k;
+
+            errno = 0;
+            k = strxfrm (result + length, p, allocated - length);
+            if (errno != 0)
+              goto fail;
+            if (k >= allocated - length)
+              {
+                /* Grow the result buffer.  */
+                char *new_result;
+
+                allocated = 2 * allocated;
+                if (allocated < 64)
+                  allocated = 64;
+                if (result == resultbuf)
+                  new_result = (char *) malloc (allocated);
+                else
+                  new_result = (char *) realloc (result, allocated);
+                if (new_result == NULL)
+                  goto out_of_memory_1;
+                result = new_result;
+              }
+            else
+              {
+                length += k;
+                break;
+              }
+          }
+
+        p = q + 1;
+        if (p == p_end)
+          break;
+        result[length] = '\0';
         length++;
       }
   }
 
   /* Shrink the allocated memory if possible.  */
-  if ((length > 0 ? length : 1) < allocated)
+  if (result != resultbuf && (length > 0 ? length : 1) < allocated)
     {
       char *memory = (char *) realloc (result, length > 0 ? length : 1);
       if (memory != NULL)
-       result = memory;
+        result = memory;
     }
 
   s[n] = orig_sentinel;
@@ -107,14 +120,16 @@ memxfrm (char *s, size_t n, size_t *lengthp)
  fail:
   {
     int saved_errno = errno;
-    free (result);
+    if (result != resultbuf)
+      free (result);
     s[n] = orig_sentinel;
     errno = saved_errno;
     return NULL;
   }
 
  out_of_memory_1:
-  free (result);
+  if (result != resultbuf)
+    free (result);
   s[n] = orig_sentinel;
  out_of_memory_2:
   errno = ENOMEM;