Document not_eol and remove mention of regex.c.
[gnulib.git] / lib / argz.c
1 /* Functions for dealing with '\0' separated arg vectors.
2    Copyright (C) 1995-1998, 2000-2002, 2006, 2008-2010 Free Software
3    Foundation, Inc.
4    This file is part of the GNU C Library.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 #include <config.h>
21
22 #include <argz.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27
28
29 /* Add BUF, of length BUF_LEN to the argz vector in ARGZ & ARGZ_LEN.  */
30 error_t
31 argz_append (char **argz, size_t *argz_len, const char *buf, size_t buf_len)
32 {
33   size_t new_argz_len = *argz_len + buf_len;
34   char *new_argz = realloc (*argz, new_argz_len);
35   if (new_argz)
36     {
37       memcpy (new_argz + *argz_len, buf, buf_len);
38       *argz = new_argz;
39       *argz_len = new_argz_len;
40       return 0;
41     }
42   else
43     return ENOMEM;
44 }
45
46 /* Add STR to the argz vector in ARGZ & ARGZ_LEN.  This should be moved into
47    argz.c in libshouldbelibc.  */
48 error_t
49 argz_add (char **argz, size_t *argz_len, const char *str)
50 {
51   return argz_append (argz, argz_len, str, strlen (str) + 1);
52 }
53
54
55
56 error_t
57 argz_add_sep (char **argz, size_t *argz_len, const char *string, int delim)
58 {
59   size_t nlen = strlen (string) + 1;
60
61   if (nlen > 1)
62     {
63       const char *rp;
64       char *wp;
65
66       *argz = (char *) realloc (*argz, *argz_len + nlen);
67       if (*argz == NULL)
68         return ENOMEM;
69
70       wp = *argz + *argz_len;
71       rp = string;
72       do
73         if (*rp == delim)
74           {
75             if (wp > *argz && wp[-1] != '\0')
76               *wp++ = '\0';
77             else
78               --nlen;
79           }
80         else
81           *wp++ = *rp;
82       while (*rp++ != '\0');
83
84       *argz_len += nlen;
85     }
86
87   return 0;
88 }
89
90
91
92 error_t
93 argz_create_sep (const char *string, int delim, char **argz, size_t *len)
94 {
95   size_t nlen = strlen (string) + 1;
96
97   if (nlen > 1)
98     {
99       const char *rp;
100       char *wp;
101
102       *argz = (char *) malloc (nlen);
103       if (*argz == NULL)
104         return ENOMEM;
105
106       rp = string;
107       wp = *argz;
108       do
109         if (*rp == delim)
110           {
111             if (wp > *argz && wp[-1] != '\0')
112               *wp++ = '\0';
113             else
114               --nlen;
115           }
116         else
117           *wp++ = *rp;
118       while (*rp++ != '\0');
119
120       if (nlen == 0)
121         {
122           free (*argz);
123           *argz = NULL;
124           *len = 0;
125         }
126
127       *len = nlen;
128     }
129   else
130     {
131       *argz = NULL;
132       *len = 0;
133     }
134
135   return 0;
136 }
137
138
139 /* Insert ENTRY into ARGZ & ARGZ_LEN before BEFORE, which should be an
140    existing entry in ARGZ; if BEFORE is NULL, ENTRY is appended to the end.
141    Since ARGZ's first entry is the same as ARGZ, argz_insert (ARGZ, ARGZ_LEN,
142    ARGZ, ENTRY) will insert ENTRY at the beginning of ARGZ.  If BEFORE is not
143    in ARGZ, EINVAL is returned, else if memory can't be allocated for the new
144    ARGZ, ENOMEM is returned, else 0.  */
145 error_t
146 argz_insert (char **argz, size_t *argz_len, char *before, const char *entry)
147 {
148   if (! before)
149     return argz_add (argz, argz_len, entry);
150
151   if (before < *argz || before >= *argz + *argz_len)
152     return EINVAL;
153
154   if (before > *argz)
155     /* Make sure before is actually the beginning of an entry.  */
156     while (before[-1])
157       before--;
158
159   {
160     size_t after_before = *argz_len - (before - *argz);
161     size_t entry_len = strlen  (entry) + 1;
162     size_t new_argz_len = *argz_len + entry_len;
163     char *new_argz = realloc (*argz, new_argz_len);
164
165     if (new_argz)
166       {
167         before = new_argz + (before - *argz);
168         memmove (before + entry_len, before, after_before);
169         memmove (before, entry, entry_len);
170         *argz = new_argz;
171         *argz_len = new_argz_len;
172         return 0;
173       }
174     else
175       return ENOMEM;
176   }
177 }
178
179
180 char *
181 argz_next (const char *argz, size_t argz_len, const char *entry)
182 {
183   if (entry)
184     {
185       if (entry < argz + argz_len)
186         entry = strchr (entry, '\0') + 1;
187
188       return entry >= argz + argz_len ? NULL : (char *) entry;
189     }
190   else
191     if (argz_len > 0)
192       return (char *) argz;
193     else
194       return NULL;
195 }
196
197
198 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
199    except the last into the character SEP.  */
200 void
201 argz_stringify (char *argz, size_t len, int sep)
202 {
203   if (len > 0)
204     while (1)
205       {
206         size_t part_len = strnlen (argz, len);
207         argz += part_len;
208         len -= part_len;
209         if (len-- <= 1)         /* includes final '\0' we want to stop at */
210           break;
211         *argz++ = sep;
212       }
213 }
214
215
216 /* Returns the number of strings in ARGZ.  */
217 size_t
218 argz_count (const char *argz, size_t len)
219 {
220   size_t count = 0;
221   while (len > 0)
222     {
223       size_t part_len = strlen (argz);
224       argz += part_len + 1;
225       len -= part_len + 1;
226       count++;
227     }
228   return count;
229 }
230
231
232 /* Puts pointers to each string in ARGZ, plus a terminating 0 element, into
233    ARGV, which must be large enough to hold them all.  */
234 void
235 argz_extract (const char *argz, size_t len, char **argv)
236 {
237   while (len > 0)
238     {
239       size_t part_len = strlen (argz);
240       *argv++ = (char *) argz;
241       argz += part_len + 1;
242       len -= part_len + 1;
243     }
244   *argv = 0;
245 }
246
247
248 /* Make a '\0' separated arg vector from a unix argv vector, returning it in
249    ARGZ, and the total length in LEN.  If a memory allocation error occurs,
250    ENOMEM is returned, otherwise 0.  */
251 error_t
252 argz_create (char *const argv[], char **argz, size_t *len)
253 {
254   int argc;
255   size_t tlen = 0;
256   char *const *ap;
257   char *p;
258
259   for (argc = 0; argv[argc] != NULL; ++argc)
260     tlen += strlen (argv[argc]) + 1;
261
262   if (tlen == 0)
263     *argz = NULL;
264   else
265     {
266       *argz = malloc (tlen);
267       if (*argz == NULL)
268         return ENOMEM;
269
270       for (p = *argz, ap = argv; *ap; ++ap, ++p)
271         p = stpcpy (p, *ap);
272     }
273   *len = tlen;
274
275   return 0;
276 }
277
278
279 /* Delete ENTRY from ARGZ & ARGZ_LEN, if any.  */
280 void
281 argz_delete (char **argz, size_t *argz_len, char *entry)
282 {
283   if (entry)
284     /* Get rid of the old value for NAME.  */
285     {
286       size_t entry_len = strlen (entry) + 1;
287       *argz_len -= entry_len;
288       memmove (entry, entry + entry_len, *argz_len - (entry - *argz));
289       if (*argz_len == 0)
290         {
291           free (*argz);
292           *argz = 0;
293         }
294     }
295 }
296
297
298 /* Append BUF, of length BUF_LEN to *TO, of length *TO_LEN, reallocating and
299    updating *TO & *TO_LEN appropriately.  If an allocation error occurs,
300    *TO's old value is freed, and *TO is set to 0.  */
301 static void
302 str_append (char **to, size_t *to_len, const char *buf, const size_t buf_len)
303 {
304   size_t new_len = *to_len + buf_len;
305   char *new_to = realloc (*to, new_len + 1);
306
307   if (new_to)
308     {
309       *((char *) mempcpy (new_to + *to_len, buf, buf_len)) = '\0';
310       *to = new_to;
311       *to_len = new_len;
312     }
313   else
314     {
315       free (*to);
316       *to = 0;
317     }
318 }
319
320 /* Replace any occurrences of the string STR in ARGZ with WITH, reallocating
321    ARGZ as necessary.  If REPLACE_COUNT is non-zero, *REPLACE_COUNT will be
322    incremented by number of replacements performed.  */
323 error_t
324 argz_replace (char **argz, size_t *argz_len, const char *str, const char *with,
325                 unsigned *replace_count)
326 {
327   error_t err = 0;
328
329   if (str && *str)
330     {
331       char *arg = 0;
332       char *src = *argz;
333       size_t src_len = *argz_len;
334       char *dst = 0;
335       size_t dst_len = 0;
336       int delayed_copy = 1;     /* True while we've avoided copying anything.  */
337       size_t str_len = strlen (str), with_len = strlen (with);
338
339       while (!err && (arg = argz_next (src, src_len, arg)))
340         {
341           char *match = strstr (arg, str);
342           if (match)
343             {
344               char *from = match + str_len;
345               size_t to_len = match - arg;
346               char *to = strndup (arg, to_len);
347
348               while (to && from)
349                 {
350                   str_append (&to, &to_len, with, with_len);
351                   if (to)
352                     {
353                       match = strstr (from, str);
354                       if (match)
355                         {
356                           str_append (&to, &to_len, from, match - from);
357                           from = match + str_len;
358                         }
359                       else
360                         {
361                           str_append (&to, &to_len, from, strlen (from));
362                           from = 0;
363                         }
364                     }
365                 }
366
367               if (to)
368                 {
369                   if (delayed_copy)
370                     /* We avoided copying SRC to DST until we found a match;
371                        now that we've done so, copy everything from the start
372                        of SRC.  */
373                     {
374                       if (arg > src)
375                         err = argz_append (&dst, &dst_len, src, (arg - src));
376                       delayed_copy = 0;
377                     }
378                   if (! err)
379                     err = argz_add (&dst, &dst_len, to);
380                   free (to);
381                 }
382               else
383                 err = ENOMEM;
384
385               if (replace_count)
386                 (*replace_count)++;
387             }
388           else if (! delayed_copy)
389             err = argz_add (&dst, &dst_len, arg);
390         }
391
392       if (! err)
393         {
394           if (! delayed_copy)
395             /* We never found any instances of str.  */
396             {
397               free (src);
398               *argz = dst;
399               *argz_len = dst_len;
400             }
401         }
402       else if (dst_len > 0)
403         free (dst);
404     }
405
406   return err;
407 }