]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/strv.c
Merge branch 'unblockable-kill' of https://github.com/arachsys-prs/util-linux
[thirdparty/util-linux.git] / lib / strv.c
CommitLineData
548b9714 1/*
faeb1b64 2 * SPDX-License-Identifier: LGPL-2.1-or-later
548b9714 3 *
faeb1b64
KZ
4 * Copyright (C) 2010 Lennart Poettering
5 * Copyright (C) 2015-2022 Karel Zak <kzak@redhat.com>
548b9714
KZ
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 *
13 * Copyright (C) 2015 Karel Zak <kzak@redhat.com>
14 * Modified the original version from systemd project for util-linux.
15 */
16
17#include <stdlib.h>
18#include <stdarg.h>
19#include <string.h>
20#include <errno.h>
21#include <stdbool.h>
22#include <assert.h>
23
24#include "strutils.h"
25#include "strv.h"
26
27void strv_clear(char **l) {
28 char **k;
29
30 if (!l)
31 return;
32
33 for (k = l; *k; k++)
34 free(*k);
35
36 *l = NULL;
37}
38
39char **strv_free(char **l) {
40 strv_clear(l);
41 free(l);
42 return NULL;
43}
44
45char **strv_copy(char * const *l) {
46 char **r, **k;
47
48 k = r = malloc(sizeof(char *) * (strv_length(l) + 1));
49 if (!r)
50 return NULL;
51
52 if (l)
53 for (; *l; k++, l++) {
54 *k = strdup(*l);
55 if (!*k) {
56 strv_free(r);
57 return NULL;
58 }
59 }
60
61 *k = NULL;
62 return r;
63}
64
65unsigned strv_length(char * const *l) {
66 unsigned n = 0;
67
68 if (!l)
69 return 0;
70
71 for (; *l; l++)
72 n++;
73
74 return n;
75}
76
77char **strv_new_ap(const char *x, va_list ap) {
78 const char *s;
79 char **a;
80 unsigned n = 0, i = 0;
81 va_list aq;
82
83 /* As a special trick we ignore all listed strings that equal
84 * (const char*) -1. This is supposed to be used with the
85 * STRV_IFNOTNULL() macro to include possibly NULL strings in
86 * the string list. */
87
88 if (x) {
89 n = x == (const char*) -1 ? 0 : 1;
90
91 va_copy(aq, ap);
92 while ((s = va_arg(aq, const char*))) {
93 if (s == (const char*) -1)
94 continue;
95
96 n++;
97 }
98
99 va_end(aq);
100 }
101
102 a = malloc(sizeof(char *) * (n + 1));
103 if (!a)
104 return NULL;
105
106 if (x) {
107 if (x != (const char*) -1) {
108 a[i] = strdup(x);
109 if (!a[i])
110 goto fail;
111 i++;
112 }
113
114 while ((s = va_arg(ap, const char*))) {
115
116 if (s == (const char*) -1)
117 continue;
118
119 a[i] = strdup(s);
120 if (!a[i])
121 goto fail;
122
123 i++;
124 }
125 }
126
127 a[i] = NULL;
128
129 return a;
130
131fail:
132 strv_free(a);
133 return NULL;
134}
135
136char **strv_new(const char *x, ...) {
137 char **r;
138 va_list ap;
139
140 va_start(ap, x);
141 r = strv_new_ap(x, ap);
142 va_end(ap);
143
144 return r;
145}
146
147int strv_extend_strv(char ***a, char **b) {
148 int r;
149 char **s;
150
151 STRV_FOREACH(s, b) {
152 r = strv_extend(a, *s);
153 if (r < 0)
154 return r;
155 }
156
157 return 0;
158}
159
160int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
161 int r;
162 char **s;
163
164 STRV_FOREACH(s, b) {
165 char *v;
166
8420463b 167 v = strconcat(*s, suffix);
548b9714
KZ
168 if (!v)
169 return -ENOMEM;
170
171 r = strv_push(a, v);
172 if (r < 0) {
173 free(v);
174 return r;
175 }
176 }
177
178 return 0;
179}
180
181
182#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
183 for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
184
185#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
186 _FOREACH_WORD(word, length, s, separator, false, state)
187
188
189char **strv_split(const char *s, const char *separator) {
190 const char *word, *state;
191 size_t l;
192 unsigned n, i;
193 char **r;
194
195 assert(s);
196
197 n = 0;
198 FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
199 n++;
200
201 r = malloc(sizeof(char *) * (n + 1));
202 if (!r)
203 return NULL;
204
205 i = 0;
206 FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
207 r[i] = strndup(word, l);
208 if (!r[i]) {
209 strv_free(r);
210 return NULL;
211 }
212
213 i++;
214 }
215
216 r[i] = NULL;
217 return r;
218}
219
220char *strv_join(char **l, const char *separator) {
221 char *r, *e;
222 char **s;
223 size_t n, k;
224
225 if (!separator)
226 separator = " ";
227
228 k = strlen(separator);
229
230 n = 0;
231 STRV_FOREACH(s, l) {
232 if (n != 0)
233 n += k;
234 n += strlen(*s);
235 }
236
237 r = malloc(n + 1);
238 if (!r)
239 return NULL;
240
241 e = r;
242 STRV_FOREACH(s, l) {
243 if (e != r)
244 e = stpcpy(e, separator);
245
246 e = stpcpy(e, *s);
247 }
248
249 *e = 0;
250
251 return r;
252}
253
254int strv_push(char ***l, char *value) {
255 char **c;
256 unsigned n, m;
257
258 if (!value)
259 return 0;
260
261 n = strv_length(*l);
262
263 /* Increase and check for overflow */
264 m = n + 2;
265 if (m < n)
266 return -ENOMEM;
267
64d6d400 268 c = reallocarray(*l, m, sizeof(char *));
548b9714
KZ
269 if (!c)
270 return -ENOMEM;
271
272 c[n] = value;
273 c[n+1] = NULL;
274
275 *l = c;
276 return 0;
277}
278
279int strv_push_prepend(char ***l, char *value) {
280 char **c;
281 unsigned n, m, i;
282
283 if (!value)
284 return 0;
285
286 n = strv_length(*l);
287
288 /* increase and check for overflow */
289 m = n + 2;
290 if (m < n)
291 return -ENOMEM;
292
293 c = malloc(sizeof(char *) * m);
294 if (!c)
295 return -ENOMEM;
296
297 for (i = 0; i < n; i++)
298 c[i+1] = (*l)[i];
299
300 c[0] = value;
301 c[n+1] = NULL;
302
303 free(*l);
304 *l = c;
305
306 return 0;
307}
308
309int strv_consume(char ***l, char *value) {
310 int r;
311
312 r = strv_push(l, value);
313 if (r < 0)
314 free(value);
315
316 return r;
317}
318
319int strv_consume_prepend(char ***l, char *value) {
320 int r;
321
322 r = strv_push_prepend(l, value);
323 if (r < 0)
324 free(value);
325
326 return r;
327}
328
329int strv_extend(char ***l, const char *value) {
330 char *v;
331
332 if (!value)
333 return 0;
334
335 v = strdup(value);
336 if (!v)
337 return -ENOMEM;
338
339 return strv_consume(l, v);
340}
341
342char **strv_remove(char **l, const char *s) {
343 char **f, **t;
344
345 if (!l)
346 return NULL;
347
348 assert(s);
349
350 /* Drops every occurrence of s in the string list, edits
351 * in-place. */
352
353 for (f = t = l; *f; f++)
354 if (strcmp(*f, s) == 0)
355 free(*f);
356 else
357 *(t++) = *f;
358
359 *t = NULL;
360 return l;
361}
362
363int strv_extendf(char ***l, const char *format, ...) {
364 va_list ap;
365 char *x;
366 int r;
367
368 va_start(ap, format);
369 r = vasprintf(&x, format, ap);
370 va_end(ap);
371
372 if (r < 0)
373 return -ENOMEM;
374
375 return strv_consume(l, x);
376}
377
de8d3863
KZ
378int strv_extendv(char ***l, const char *format, va_list ap) {
379 char *x;
380 int r;
381
382 r = vasprintf(&x, format, ap);
383 if (r < 0)
384 return -ENOMEM;
385
386 return strv_consume(l, x);
387}
388
548b9714
KZ
389char **strv_reverse(char **l) {
390 unsigned n, i;
391
392 n = strv_length(l);
393 if (n <= 1)
394 return l;
395
396 for (i = 0; i < n / 2; i++) {
397 char *t;
398
399 t = l[i];
400 l[i] = l[n-1-i];
401 l[n-1-i] = t;
402 }
403
404 return l;
405}