New module 'memxfrm'.
[gnulib.git] / lib / memxfrm.c
1 /* Locale dependent memory area transformation for comparison.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2009.
4
5    This program is free software: you can redistribute it and/or modify it
6    under the terms of the GNU Lesser General Public License as published
7    by the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include "memxfrm.h"
22
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 char *
28 memxfrm (char *s, size_t n, size_t *lengthp)
29 {
30   /* Result accumulator.  */
31   char *result;
32   size_t length;
33   size_t allocated;
34
35   char orig_sentinel;
36
37   /* Initial memory allocation.  */
38   allocated = (n > 0 ? n : 1);
39   result = (char *) malloc (allocated);
40   if (result == NULL)
41     goto out_of_memory_2;
42   length = 0;
43
44   /* Add sentinel.byte.  */
45   orig_sentinel = s[n];
46   s[n] = '\0';
47
48   /* Iterate through S, transforming each NUL terminated segment.
49      Accumulate the resulting transformed segments in result, separated by
50      NULs.  */
51   {
52     const char *p_end = s + n + 1;
53     const char *p;
54
55     p = s;
56     for (;;)
57       {
58         /* Search next NUL byte.  */
59         const char *q = p + strlen (p);
60
61         for (;;)
62           {
63             size_t k;
64
65             errno = 0;
66             k = strxfrm (result + length, p, allocated - length);
67             if (errno != 0)
68               goto fail;
69             if (k >= allocated - length)
70               {
71                 /* Grow the result buffer.  */
72                 char *new_result;
73
74                 allocated = 2 * allocated;
75                 new_result = (char *) realloc (result, allocated);
76                 if (new_result == NULL)
77                   goto out_of_memory_1;
78                 result = new_result;
79               }
80             else
81               {
82                 length += k;
83                 break;
84               }
85           }
86
87         p = q + 1;
88         if (p == p_end)
89           break;
90         result[length] = '\0';
91         length++;
92       }
93   }
94
95   /* Shrink the allocated memory if possible.  */
96   if ((length > 0 ? length : 1) < allocated)
97     {
98       char *memory = (char *) realloc (result, length > 0 ? length : 1);
99       if (memory != NULL)
100         result = memory;
101     }
102
103   s[n] = orig_sentinel;
104   *lengthp = length;
105   return result;
106
107  fail:
108   {
109     int saved_errno = errno;
110     free (result);
111     s[n] = orig_sentinel;
112     errno = saved_errno;
113     return NULL;
114   }
115
116  out_of_memory_1:
117   free (result);
118   s[n] = orig_sentinel;
119  out_of_memory_2:
120   errno = ENOMEM;
121   return NULL;
122 }