openat: don’t close (-1)
[gnulib.git] / lib / argz.c
blob2f27fca2a57db9724fb07781b0c60fa704e8cd1a
1 /* Functions for dealing with '\0' separated arg vectors.
2 Copyright (C) 1995-1998, 2000-2002, 2006, 2008-2024 Free Software
3 Foundation, Inc.
4 This file is part of the GNU C Library.
6 This file is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 This file 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 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
19 #include <config.h>
21 #include <argz.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
28 /* Add BUF, of length BUF_LEN to the argz vector in ARGZ & ARGZ_LEN. */
29 error_t
30 argz_append (char **argz, size_t *argz_len, const char *buf, size_t buf_len)
32 size_t new_argz_len = *argz_len + buf_len;
33 char *new_argz = realloc (*argz, new_argz_len);
34 if (new_argz)
36 memcpy (new_argz + *argz_len, buf, buf_len);
37 *argz = new_argz;
38 *argz_len = new_argz_len;
39 return 0;
41 else
42 return ENOMEM;
45 /* Add STR to the argz vector in ARGZ & ARGZ_LEN. This should be moved into
46 argz.c in libshouldbelibc. */
47 error_t
48 argz_add (char **argz, size_t *argz_len, const char *str)
50 return argz_append (argz, argz_len, str, strlen (str) + 1);
55 error_t
56 argz_add_sep (char **argz, size_t *argz_len, const char *string, int delim)
58 size_t nlen = strlen (string) + 1;
60 if (nlen > 1)
62 const char *rp;
63 char *wp;
65 *argz = (char *) realloc (*argz, *argz_len + nlen);
66 if (*argz == NULL)
67 return ENOMEM;
69 wp = *argz + *argz_len;
70 rp = string;
72 if (*rp == delim)
74 if (wp > *argz && wp[-1] != '\0')
75 *wp++ = '\0';
76 else
77 --nlen;
79 else
80 *wp++ = *rp;
81 while (*rp++ != '\0');
83 *argz_len += nlen;
86 return 0;
91 error_t
92 argz_create_sep (const char *string, int delim, char **argz, size_t *len)
94 size_t nlen = strlen (string) + 1;
96 if (nlen > 1)
98 const char *rp;
99 char *wp;
101 *argz = (char *) malloc (nlen);
102 if (*argz == NULL)
103 return ENOMEM;
105 rp = string;
106 wp = *argz;
108 if (*rp == delim)
110 if (wp > *argz && wp[-1] != '\0')
111 *wp++ = '\0';
112 else
113 --nlen;
115 else
116 *wp++ = *rp;
117 while (*rp++ != '\0');
119 if (nlen == 0)
121 free (*argz);
122 *argz = NULL;
123 *len = 0;
126 *len = nlen;
128 else
130 *argz = NULL;
131 *len = 0;
134 return 0;
138 /* Insert ENTRY into ARGZ & ARGZ_LEN before BEFORE, which should be an
139 existing entry in ARGZ; if BEFORE is NULL, ENTRY is appended to the end.
140 Since ARGZ's first entry is the same as ARGZ, argz_insert (ARGZ, ARGZ_LEN,
141 ARGZ, ENTRY) will insert ENTRY at the beginning of ARGZ. If BEFORE is not
142 in ARGZ, EINVAL is returned, else if memory can't be allocated for the new
143 ARGZ, ENOMEM is returned, else 0. */
144 error_t
145 argz_insert (char **argz, size_t *argz_len, char *before, const char *entry)
147 if (! before)
148 return argz_add (argz, argz_len, entry);
150 if (before < *argz || before >= *argz + *argz_len)
151 return EINVAL;
153 if (before > *argz)
154 /* Make sure before is actually the beginning of an entry. */
155 while (before[-1])
156 before--;
159 size_t after_before = *argz_len - (before - *argz);
160 size_t entry_len = strlen (entry) + 1;
161 size_t new_argz_len = *argz_len + entry_len;
162 char *new_argz = realloc (*argz, new_argz_len);
164 if (new_argz)
166 before = new_argz + (before - *argz);
167 memmove (before + entry_len, before, after_before);
168 memmove (before, entry, entry_len);
169 *argz = new_argz;
170 *argz_len = new_argz_len;
171 return 0;
173 else
174 return ENOMEM;
179 char *
180 argz_next (const char *argz, size_t argz_len, const char *entry)
182 if (entry)
184 if (entry < argz + argz_len)
185 entry = strchr (entry, '\0') + 1;
187 return entry >= argz + argz_len ? NULL : (char *) entry;
189 else
190 if (argz_len > 0)
191 return (char *) argz;
192 else
193 return NULL;
197 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
198 except the last into the character SEP. */
199 void
200 argz_stringify (char *argz, size_t len, int sep)
202 if (len > 0)
203 while (1)
205 size_t part_len = strnlen (argz, len);
206 argz += part_len;
207 len -= part_len;
208 if (len-- <= 1) /* includes final '\0' we want to stop at */
209 break;
210 *argz++ = sep;
215 /* Returns the number of strings in ARGZ. */
216 size_t
217 argz_count (const char *argz, size_t len)
219 size_t count = 0;
220 while (len > 0)
222 size_t part_len = strlen (argz);
223 argz += part_len + 1;
224 len -= part_len + 1;
225 count++;
227 return count;
231 /* Puts pointers to each string in ARGZ, plus a terminating 0 element, into
232 ARGV, which must be large enough to hold them all. */
233 void
234 argz_extract (const char *argz, size_t len, char **argv)
236 while (len > 0)
238 size_t part_len = strlen (argz);
239 *argv++ = (char *) argz;
240 argz += part_len + 1;
241 len -= part_len + 1;
243 *argv = 0;
247 /* Make a '\0' separated arg vector from a unix argv vector, returning it in
248 ARGZ, and the total length in LEN. If a memory allocation error occurs,
249 ENOMEM is returned, otherwise 0. */
250 error_t
251 argz_create (char *const argv[], char **argz, size_t *len)
253 int argc;
254 size_t tlen = 0;
255 char *const *ap;
256 char *p;
258 for (argc = 0; argv[argc] != NULL; ++argc)
259 tlen += strlen (argv[argc]) + 1;
261 if (tlen == 0)
262 *argz = NULL;
263 else
265 *argz = malloc (tlen);
266 if (*argz == NULL)
267 return ENOMEM;
269 for (p = *argz, ap = argv; *ap; ++ap, ++p)
270 p = stpcpy (p, *ap);
272 *len = tlen;
274 return 0;
278 /* Delete ENTRY from ARGZ & ARGZ_LEN, if any. */
279 void
280 argz_delete (char **argz, size_t *argz_len, char *entry)
282 if (entry)
283 /* Get rid of the old value for NAME. */
285 size_t entry_len = strlen (entry) + 1;
286 *argz_len -= entry_len;
287 memmove (entry, entry + entry_len, *argz_len - (entry - *argz));
288 if (*argz_len == 0)
290 free (*argz);
291 *argz = 0;
297 /* Append BUF, of length BUF_LEN to *TO, of length *TO_LEN, reallocating and
298 updating *TO & *TO_LEN appropriately. If an allocation error occurs,
299 *TO's old value is freed, and *TO is set to 0. */
300 static void
301 str_append (char **to, size_t *to_len, const char *buf, const size_t buf_len)
303 size_t new_len = *to_len + buf_len;
304 char *new_to = realloc (*to, new_len + 1);
306 if (new_to)
308 *((char *) mempcpy (new_to + *to_len, buf, buf_len)) = '\0';
309 *to = new_to;
310 *to_len = new_len;
312 else
314 free (*to);
315 *to = 0;
319 /* Replace any occurrences of the string STR in ARGZ with WITH, reallocating
320 ARGZ as necessary. If REPLACE_COUNT is non-zero, *REPLACE_COUNT will be
321 incremented by number of replacements performed. */
322 error_t
323 argz_replace (char **argz, size_t *argz_len, const char *str, const char *with,
324 unsigned *replace_count)
326 error_t err = 0;
328 if (str && *str)
330 char *arg = 0;
331 char *src = *argz;
332 size_t src_len = *argz_len;
333 char *dst = 0;
334 size_t dst_len = 0;
335 int delayed_copy = 1; /* True while we've avoided copying anything. */
336 size_t str_len = strlen (str), with_len = strlen (with);
338 while (!err && (arg = argz_next (src, src_len, arg)))
340 char *match = strstr (arg, str);
341 if (match)
343 char *from = match + str_len;
344 size_t to_len = match - arg;
345 char *to = strndup (arg, to_len);
347 while (to && from)
349 str_append (&to, &to_len, with, with_len);
350 if (to)
352 match = strstr (from, str);
353 if (match)
355 str_append (&to, &to_len, from, match - from);
356 from = match + str_len;
358 else
360 str_append (&to, &to_len, from, strlen (from));
361 from = 0;
366 if (to)
368 if (delayed_copy)
369 /* We avoided copying SRC to DST until we found a match;
370 now that we've done so, copy everything from the start
371 of SRC. */
373 if (arg > src)
374 err = argz_append (&dst, &dst_len, src, (arg - src));
375 delayed_copy = 0;
377 if (! err)
378 err = argz_add (&dst, &dst_len, to);
379 free (to);
381 else
382 err = ENOMEM;
384 if (replace_count)
385 (*replace_count)++;
387 else if (! delayed_copy)
388 err = argz_add (&dst, &dst_len, arg);
391 if (! err)
393 if (! delayed_copy)
394 /* We never found any instances of str. */
396 free (src);
397 *argz = dst;
398 *argz_len = dst_len;
401 else if (dst_len > 0)
402 free (dst);
405 return err;