1 /* Functions for dealing with '\0' separated arg vectors.
2 Copyright (C) 1995-1998, 2000-2002, 2006, 2008-2023 Free Software
3 Foundation, Inc.
4 This file is part of the GNU C Library.
5
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.
10
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.
15
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/>. */
18
19 #include <config.h>
20
21 #include <argz.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26
27
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)
31 {
32 size_t new_argz_len = *argz_len + buf_len;
33 char *new_argz = realloc (*argz, new_argz_len);
34 if (new_argz)
35 {
36 memcpy (new_argz + *argz_len, buf, buf_len);
37 *argz = new_argz;
38 *argz_len = new_argz_len;
39 return 0;
40 }
41 else
42 return ENOMEM;
43 }
44
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)
49 {
50 return argz_append (argz, argz_len, str, strlen (str) + 1);
51 }
52
53
54
55 error_t
56 argz_add_sep (char **argz, size_t *argz_len, const char *string, int delim)
57 {
58 size_t nlen = strlen (string) + 1;
59
60 if (nlen > 1)
61 {
62 const char *rp;
63 char *wp;
64
65 *argz = (char *) realloc (*argz, *argz_len + nlen);
66 if (*argz == NULL)
67 return ENOMEM;
68
69 wp = *argz + *argz_len;
70 rp = string;
71 do
72 if (*rp == delim)
73 {
74 if (wp > *argz && wp[-1] != '\0')
75 *wp++ = '\0';
76 else
77 --nlen;
78 }
79 else
80 *wp++ = *rp;
81 while (*rp++ != '\0');
82
83 *argz_len += nlen;
84 }
85
86 return 0;
87 }
88
89
90
91 error_t
92 argz_create_sep (const char *string, int delim, char **argz, size_t *len)
93 {
94 size_t nlen = strlen (string) + 1;
95
96 if (nlen > 1)
97 {
98 const char *rp;
99 char *wp;
100
101 *argz = (char *) malloc (nlen);
102 if (*argz == NULL)
103 return ENOMEM;
104
105 rp = string;
106 wp = *argz;
107 do
108 if (*rp == delim)
109 {
110 if (wp > *argz && wp[-1] != '\0')
111 *wp++ = '\0';
112 else
113 --nlen;
114 }
115 else
116 *wp++ = *rp;
117 while (*rp++ != '\0');
118
119 if (nlen == 0)
120 {
121 free (*argz);
122 *argz = NULL;
123 *len = 0;
124 }
125
126 *len = nlen;
127 }
128 else
129 {
130 *argz = NULL;
131 *len = 0;
132 }
133
134 return 0;
135 }
136
137
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)
146 {
147 if (! before)
148 return argz_add (argz, argz_len, entry);
149
150 if (before < *argz || before >= *argz + *argz_len)
151 return EINVAL;
152
153 if (before > *argz)
154 /* Make sure before is actually the beginning of an entry. */
155 while (before[-1])
156 before--;
157
158 {
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);
163
164 if (new_argz)
165 {
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;
172 }
173 else
174 return ENOMEM;
175 }
176 }
177
178
179 char *
180 argz_next (const char *argz, size_t argz_len, const char *entry)
181 {
182 if (entry)
183 {
184 if (entry < argz + argz_len)
185 entry = strchr (entry, '\0') + 1;
186
187 return entry >= argz + argz_len ? NULL : (char *) entry;
188 }
189 else
190 if (argz_len > 0)
191 return (char *) argz;
192 else
193 return NULL;
194 }
195
196
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)
201 {
202 if (len > 0)
203 while (1)
204 {
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;
211 }
212 }
213
214
215 /* Returns the number of strings in ARGZ. */
216 size_t
217 argz_count (const char *argz, size_t len)
218 {
219 size_t count = 0;
220 while (len > 0)
221 {
222 size_t part_len = strlen (argz);
223 argz += part_len + 1;
224 len -= part_len + 1;
225 count++;
226 }
227 return count;
228 }
229
230
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)
235 {
236 while (len > 0)
237 {
238 size_t part_len = strlen (argz);
239 *argv++ = (char *) argz;
240 argz += part_len + 1;
241 len -= part_len + 1;
242 }
243 *argv = 0;
244 }
245
246
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)
252 {
253 int argc;
254 size_t tlen = 0;
255 char *const *ap;
256 char *p;
257
258 for (argc = 0; argv[argc] != NULL; ++argc)
259 tlen += strlen (argv[argc]) + 1;
260
261 if (tlen == 0)
262 *argz = NULL;
263 else
264 {
265 *argz = malloc (tlen);
266 if (*argz == NULL)
267 return ENOMEM;
268
269 for (p = *argz, ap = argv; *ap; ++ap, ++p)
270 p = stpcpy (p, *ap);
271 }
272 *len = tlen;
273
274 return 0;
275 }
276
277
278 /* Delete ENTRY from ARGZ & ARGZ_LEN, if any. */
279 void
280 argz_delete (char **argz, size_t *argz_len, char *entry)
281 {
282 if (entry)
283 /* Get rid of the old value for NAME. */
284 {
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)
289 {
290 free (*argz);
291 *argz = 0;
292 }
293 }
294 }
295
296
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)
302 {
303 size_t new_len = *to_len + buf_len;
304 char *new_to = realloc (*to, new_len + 1);
305
306 if (new_to)
307 {
308 *((char *) mempcpy (new_to + *to_len, buf, buf_len)) = '\0';
309 *to = new_to;
310 *to_len = new_len;
311 }
312 else
313 {
314 free (*to);
315 *to = 0;
316 }
317 }
318
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)
325 {
326 error_t err = 0;
327
328 if (str && *str)
329 {
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);
337
338 while (!err && (arg = argz_next (src, src_len, arg)))
339 {
340 char *match = strstr (arg, str);
341 if (match)
342 {
343 char *from = match + str_len;
344 size_t to_len = match - arg;
345 char *to = strndup (arg, to_len);
346
347 while (to && from)
348 {
349 str_append (&to, &to_len, with, with_len);
350 if (to)
351 {
352 match = strstr (from, str);
353 if (match)
354 {
355 str_append (&to, &to_len, from, match - from);
356 from = match + str_len;
357 }
358 else
359 {
360 str_append (&to, &to_len, from, strlen (from));
361 from = 0;
362 }
363 }
364 }
365
366 if (to)
367 {
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. */
372 {
373 if (arg > src)
374 err = argz_append (&dst, &dst_len, src, (arg - src));
375 delayed_copy = 0;
376 }
377 if (! err)
378 err = argz_add (&dst, &dst_len, to);
379 free (to);
380 }
381 else
382 err = ENOMEM;
383
384 if (replace_count)
385 (*replace_count)++;
386 }
387 else if (! delayed_copy)
388 err = argz_add (&dst, &dst_len, arg);
389 }
390
391 if (! err)
392 {
393 if (! delayed_copy)
394 /* We never found any instances of str. */
395 {
396 free (src);
397 *argz = dst;
398 *argz_len = dst_len;
399 }
400 }
401 else if (dst_len > 0)
402 free (dst);
403 }
404
405 return err;
406 }