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