]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/strv.c
Merge pull request #30513 from rpigott/resolved-ede
[thirdparty/systemd.git] / src / basic / strv.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
a7334b09 2
07630cea 3#include <errno.h>
11c3a366 4#include <fnmatch.h>
60918275 5#include <stdarg.h>
11c3a366 6#include <stdio.h>
07630cea 7#include <stdlib.h>
60918275 8
b5efdb8a 9#include "alloc-util.h"
2ed74695 10#include "env-util.h"
4f5dd394 11#include "escape.h"
11c3a366 12#include "extract-word.h"
d390f8ef 13#include "fileio.h"
309c6b19 14#include "memory-util.h"
d8b4d14d 15#include "nulstr-util.h"
760877e9 16#include "sort-util.h"
07630cea 17#include "string-util.h"
60918275
LP
18#include "strv.h"
19
14337c37 20char* strv_find(char * const *l, const char *name) {
60918275
LP
21 assert(name);
22
5f9a22c3
LP
23 STRV_FOREACH(i, l)
24 if (streq(*i, name))
25 return *i;
60918275
LP
26
27 return NULL;
28}
29
14337c37 30char* strv_find_case(char * const *l, const char *name) {
ddd6a22a
LP
31 assert(name);
32
33 STRV_FOREACH(i, l)
34 if (strcaseeq(*i, name))
35 return *i;
36
37 return NULL;
38}
39
14337c37 40char* strv_find_prefix(char * const *l, const char *name) {
a4bfb399
LP
41 assert(name);
42
43 STRV_FOREACH(i, l)
44 if (startswith(*i, name))
45 return *i;
46
47 return NULL;
48}
49
14337c37 50char* strv_find_startswith(char * const *l, const char *name) {
28849dba
LP
51 assert(name);
52
53 /* Like strv_find_prefix, but actually returns only the
54 * suffix, not the whole item */
55
56 STRV_FOREACH(i, l) {
de010b0b
YW
57 char *e;
58
28849dba
LP
59 e = startswith(*i, name);
60 if (e)
61 return e;
62 }
63
64 return NULL;
65}
66
2ed74695
LB
67char* strv_find_first_field(char * const *needles, char * const *haystack) {
68 STRV_FOREACH(k, needles) {
69 char *value = strv_env_pairs_get((char **)haystack, *k);
70 if (value)
71 return value;
72 }
73
74 return NULL;
75}
76
14337c37 77char** strv_free(char **l) {
d0b4f13e 78 STRV_FOREACH(k, l)
60918275
LP
79 free(*k);
80
6b430fdb 81 return mfree(l);
60918275
LP
82}
83
14337c37 84char** strv_free_erase(char **l) {
ab84f5b9 85 STRV_FOREACH(i, l)
309c6b19 86 erase_and_freep(i);
ab84f5b9 87
309c6b19 88 return mfree(l);
ab84f5b9
ZJS
89}
90
a39cba25
LB
91void strv_free_many(char ***strvs, size_t n) {
92 assert(strvs || n == 0);
93
94 FOREACH_ARRAY (i, strvs, n)
95 strv_free(*i);
96
97 free(strvs);
98}
99
4ea517a6 100char** strv_copy_n(char * const *l, size_t m) {
9eb81481 101 _cleanup_strv_free_ char **result = NULL;
de010b0b 102 char **k;
60918275 103
4ea517a6 104 result = new(char*, MIN(strv_length(l), m) + 1);
9eb81481 105 if (!result)
60918275
LP
106 return NULL;
107
9eb81481
YW
108 k = result;
109 STRV_FOREACH(i, l) {
4ea517a6
LP
110 if (m == 0)
111 break;
112
9eb81481
YW
113 *k = strdup(*i);
114 if (!*k)
115 return NULL;
116 k++;
4ea517a6
LP
117
118 if (m != SIZE_MAX)
119 m--;
9eb81481 120 }
60918275
LP
121
122 *k = NULL;
9eb81481 123 return TAKE_PTR(result);
60918275
LP
124}
125
da6053d0
LP
126size_t strv_length(char * const *l) {
127 size_t n = 0;
60918275 128
d0b4f13e 129 STRV_FOREACH(i, l)
60918275
LP
130 n++;
131
132 return n;
133}
134
14337c37 135char** strv_new_ap(const char *x, va_list ap) {
01111587 136 _cleanup_strv_free_ char **a = NULL;
da6053d0 137 size_t n = 0, i = 0;
257eca1a
LP
138 va_list aq;
139
07719a21 140 /* As a special trick we ignore all listed strings that equal
f9d14060 141 * STRV_IGNORE. This is supposed to be used with the
07719a21
LP
142 * STRV_IFNOTNULL() macro to include possibly NULL strings in
143 * the string list. */
144
3d2b1fa4
RV
145 va_copy(aq, ap);
146 for (const char *s = x; s; s = va_arg(aq, const char*)) {
147 if (s == STRV_IGNORE)
148 continue;
07719a21 149
3d2b1fa4 150 n++;
60918275 151 }
3d2b1fa4 152 va_end(aq);
60918275 153
07719a21
LP
154 a = new(char*, n+1);
155 if (!a)
60918275
LP
156 return NULL;
157
3d2b1fa4
RV
158 for (const char *s = x; s; s = va_arg(ap, const char*)) {
159 if (s == STRV_IGNORE)
160 continue;
07719a21 161
3d2b1fa4
RV
162 a[i] = strdup(s);
163 if (!a[i])
164 return NULL;
60918275 165
3d2b1fa4 166 i++;
60918275
LP
167 }
168
169 a[i] = NULL;
257eca1a 170
01111587 171 return TAKE_PTR(a);
60918275 172}
034c6ed7 173
14337c37 174char** strv_new_internal(const char *x, ...) {
257eca1a
LP
175 char **r;
176 va_list ap;
177
178 va_start(ap, x);
179 r = strv_new_ap(x, ap);
180 va_end(ap);
181
182 return r;
183}
184
479ddcdf 185int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
14337c37 186 size_t p, q, i = 0;
de010b0b 187 char **t;
e287086b
LP
188
189 assert(a);
190
191 if (strv_isempty(b))
192 return 0;
193
194 p = strv_length(*a);
195 q = strv_length(b);
196
60476376
LP
197 if (p >= SIZE_MAX - q)
198 return -ENOMEM;
199
200 t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
e287086b
LP
201 if (!t)
202 return -ENOMEM;
203
204 t[p] = NULL;
205 *a = t;
07719a21 206
e3e45d4f 207 STRV_FOREACH(s, b) {
e287086b
LP
208 if (filter_duplicates && strv_contains(t, *s))
209 continue;
210
211 t[p+i] = strdup(*s);
212 if (!t[p+i])
213 goto rollback;
214
215 i++;
216 t[p+i] = NULL;
07719a21 217 }
034c6ed7 218
e287086b
LP
219 assert(i <= q);
220
221 return (int) i;
222
223rollback:
24ae45cb 224 free_many_charp(t + p, i);
e287086b
LP
225 t[p] = NULL;
226 return -ENOMEM;
5f9a22c3
LP
227}
228
479ddcdf 229int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
e3e45d4f 230 int r;
5f9a22c3 231
e3e45d4f
SP
232 STRV_FOREACH(s, b) {
233 char *v;
5f9a22c3 234
b910cc72 235 v = strjoin(*s, suffix);
e3e45d4f
SP
236 if (!v)
237 return -ENOMEM;
5f9a22c3 238
e3e45d4f
SP
239 r = strv_push(a, v);
240 if (r < 0) {
241 free(v);
242 return r;
8ea913b2 243 }
8ea913b2 244 }
5f9a22c3 245
e3e45d4f 246 return 0;
5f9a22c3
LP
247}
248
f385c447
YW
249int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) {
250 _cleanup_strv_free_ char **l = NULL;
da6053d0 251 size_t n;
f385c447 252 int r;
26d04f86
LP
253
254 assert(s);
255
f385c447
YW
256 /* Special version of strv_split_full() that splits on newlines and
257 * suppresses an empty string at the end. */
26d04f86 258
f385c447
YW
259 r = strv_split_full(&l, s, NEWLINE, flags);
260 if (r < 0)
261 return r;
26d04f86
LP
262
263 n = strv_length(l);
f385c447 264 if (n > 0 && isempty(l[n - 1])) {
a1e58e8e 265 l[n - 1] = mfree(l[n - 1]);
f385c447
YW
266 n--;
267 }
26d04f86 268
f385c447
YW
269 *ret = TAKE_PTR(l);
270 return n;
26d04f86
LP
271}
272
90e30d76 273int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
f88e6be5 274 _cleanup_strv_free_ char **l = NULL;
319a4f4b 275 size_t n = 0;
f88e6be5
LP
276 int r;
277
278 assert(t);
279 assert(s);
280
281 for (;;) {
282 _cleanup_free_ char *word = NULL;
283
8adaf7bd 284 r = extract_first_word(&s, &word, separators, flags);
f88e6be5
LP
285 if (r < 0)
286 return r;
ece174c5 287 if (r == 0)
f88e6be5
LP
288 break;
289
319a4f4b 290 if (!GREEDY_REALLOC(l, n + 2))
f88e6be5
LP
291 return -ENOMEM;
292
ae2a15bc 293 l[n++] = TAKE_PTR(word);
f88e6be5
LP
294 l[n] = NULL;
295 }
296
8dd4c05b 297 if (!l) {
f88e6be5 298 l = new0(char*, 1);
8dd4c05b
LP
299 if (!l)
300 return -ENOMEM;
301 }
f88e6be5 302
ae2a15bc 303 *t = TAKE_PTR(l);
f88e6be5 304
8dd4c05b 305 return (int) n;
f88e6be5
LP
306}
307
3c318caa
FS
308int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
309 _cleanup_strv_free_ char **l = NULL;
310 int r;
311
312 assert(t);
313 assert(s);
314
315 r = strv_split_full(&l, s, separators, flags);
316 if (r < 0)
317 return r;
318
319 r = strv_extend_strv(t, l, filter_duplicates);
320 if (r < 0)
321 return r;
322
323 return (int) strv_length(*t);
324}
325
a082edd5
LB
326int strv_split_colon_pairs(char ***t, const char *s) {
327 _cleanup_strv_free_ char **l = NULL;
319a4f4b 328 size_t n = 0;
a082edd5
LB
329 int r;
330
331 assert(t);
332 assert(s);
333
334 for (;;) {
335 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
336
337 r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
338 if (r < 0)
339 return r;
340 if (r == 0)
341 break;
342
343 const char *p = tuple;
344 r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
345 &first, &second, NULL);
346 if (r < 0)
347 return r;
348 if (r == 0)
349 continue;
350 /* Enforce that at most 2 colon-separated words are contained in each group */
351 if (!isempty(p))
352 return -EINVAL;
353
354 second_or_empty = strdup(strempty(second));
355 if (!second_or_empty)
356 return -ENOMEM;
357
319a4f4b 358 if (!GREEDY_REALLOC(l, n + 3))
a082edd5
LB
359 return -ENOMEM;
360
361 l[n++] = TAKE_PTR(first);
362 l[n++] = TAKE_PTR(second_or_empty);
363
364 l[n] = NULL;
365 }
366
367 if (!l) {
368 l = new0(char*, 1);
369 if (!l)
370 return -ENOMEM;
371 }
372
373 *t = TAKE_PTR(l);
374
375 return (int) n;
376}
377
e5f2d77b 378char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) {
5f9a22c3 379 char *r, *e;
2b9a7d2e 380 size_t n, k, m;
5f9a22c3
LP
381
382 if (!separator)
383 separator = " ";
384
385 k = strlen(separator);
2b9a7d2e 386 m = strlen_ptr(prefix);
5f9a22c3 387
e5f2d77b 388 if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */
d4d9f034
ZJS
389 assert(k == 1);
390
5f9a22c3
LP
391 n = 0;
392 STRV_FOREACH(s, l) {
afe773b0 393 if (s != l)
5f9a22c3 394 n += k;
d4d9f034 395
e5f2d77b 396 bool needs_escaping = escape_separator && strchr(*s, *separator);
d4d9f034
ZJS
397
398 n += m + strlen(*s) * (1 + needs_escaping);
5f9a22c3
LP
399 }
400
1fd8d04e
LP
401 r = new(char, n+1);
402 if (!r)
5f9a22c3
LP
403 return NULL;
404
405 e = r;
406 STRV_FOREACH(s, l) {
afe773b0 407 if (s != l)
5f9a22c3
LP
408 e = stpcpy(e, separator);
409
2b9a7d2e
YW
410 if (prefix)
411 e = stpcpy(e, prefix);
412
e5f2d77b 413 bool needs_escaping = escape_separator && strchr(*s, *separator);
d4d9f034
ZJS
414
415 if (needs_escaping)
416 for (size_t i = 0; (*s)[i]; i++) {
e5f2d77b 417 if ((*s)[i] == *separator)
d4d9f034
ZJS
418 *(e++) = '\\';
419 *(e++) = (*s)[i];
420 }
421 else
422 e = stpcpy(e, *s);
5f9a22c3
LP
423 }
424
8d49745c
LP
425 *e = 0;
426
5f9a22c3
LP
427 return r;
428}
429
3ec3ae68
ZJS
430int strv_push_with_size(char ***l, size_t *n, char *value) {
431 /* n is a pointer to a variable to store the size of l.
432 * If not given (i.e. n is NULL or *n is SIZE_MAX), size will be calculated using strv_length().
433 * If n is not NULL, the size after the push will be returned.
434 * If value is empty, no action is taken and *n is not set. */
5926ccca
LP
435
436 if (!value)
437 return 0;
438
3ec3ae68
ZJS
439 size_t size = n ? *n : SIZE_MAX;
440 if (size == SIZE_MAX)
441 size = strv_length(*l);
97569e15 442
60476376 443 /* Check for overflow */
3ec3ae68 444 if (size > SIZE_MAX-2)
97569e15
LP
445 return -ENOMEM;
446
3ec3ae68 447 char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
4468addc 448 if (!c)
82dde599 449 return -ENOMEM;
82dde599 450
3ec3ae68
ZJS
451 c[size] = value;
452 c[size+1] = NULL;
82dde599 453
5926ccca 454 *l = c;
3ec3ae68
ZJS
455 if (n)
456 *n = size + 1;
5926ccca
LP
457 return 0;
458}
459
98940a3c
LP
460int strv_push_pair(char ***l, char *a, char *b) {
461 char **c;
60476376 462 size_t n;
98940a3c
LP
463
464 if (!a && !b)
465 return 0;
466
467 n = strv_length(*l);
468
60476376
LP
469 /* Check for overflow */
470 if (n > SIZE_MAX-3)
98940a3c
LP
471 return -ENOMEM;
472
60476376
LP
473 /* increase and check for overflow */
474 c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*));
98940a3c
LP
475 if (!c)
476 return -ENOMEM;
477
478 if (a)
479 c[n++] = a;
480 if (b)
481 c[n++] = b;
482 c[n] = NULL;
483
484 *l = c;
485 return 0;
486}
487
da6053d0 488int strv_insert(char ***l, size_t position, char *value) {
9a00f57a 489 char **c;
14337c37 490 size_t n, m;
9a00f57a
LP
491
492 if (!value)
493 return 0;
494
495 n = strv_length(*l);
6e888894 496 position = MIN(position, n);
97569e15
LP
497
498 /* increase and check for overflow */
499 m = n + 2;
500 if (m < n)
501 return -ENOMEM;
502
503 c = new(char*, m);
9a00f57a
LP
504 if (!c)
505 return -ENOMEM;
506
14337c37 507 for (size_t i = 0; i < position; i++)
6e888894
ZJS
508 c[i] = (*l)[i];
509 c[position] = value;
14337c37 510 for (size_t i = position; i < n; i++)
9a00f57a 511 c[i+1] = (*l)[i];
9a00f57a
LP
512 c[n+1] = NULL;
513
14337c37 514 return free_and_replace(*l, c);
9a00f57a
LP
515}
516
3ec3ae68 517int strv_consume_with_size(char ***l, size_t *n, char *value) {
6e18964d
ZJS
518 int r;
519
3ec3ae68 520 r = strv_push_with_size(l, n, value);
6e18964d
ZJS
521 if (r < 0)
522 free(value);
523
9a00f57a
LP
524 return r;
525}
526
98940a3c
LP
527int strv_consume_pair(char ***l, char *a, char *b) {
528 int r;
529
530 r = strv_push_pair(l, a, b);
531 if (r < 0) {
532 free(a);
533 free(b);
534 }
535
536 return r;
537}
538
9a00f57a
LP
539int strv_consume_prepend(char ***l, char *value) {
540 int r;
541
542 r = strv_push_prepend(l, value);
543 if (r < 0)
544 free(value);
545
6e18964d
ZJS
546 return r;
547}
548
82443be5 549int strv_prepend(char ***l, const char *value) {
550 char *v;
551
552 if (!value)
553 return 0;
554
555 v = strdup(value);
556 if (!v)
557 return -ENOMEM;
558
559 return strv_consume_prepend(l, v);
560}
561
3ec3ae68 562int strv_extend_with_size(char ***l, size_t *n, const char *value) {
4468addc 563 char *v;
4468addc
LP
564
565 if (!value)
566 return 0;
567
568 v = strdup(value);
569 if (!v)
570 return -ENOMEM;
571
3ec3ae68 572 return strv_consume_with_size(l, n, v);
4468addc
LP
573}
574
4f4afc88
LP
575int strv_extend_front(char ***l, const char *value) {
576 size_t n, m;
577 char *v, **c;
578
579 assert(l);
580
581 /* Like strv_extend(), but prepends rather than appends the new entry */
582
bcab914f
LP
583 if (!value)
584 return 0;
585
4f4afc88
LP
586 n = strv_length(*l);
587
588 /* Increase and overflow check. */
589 m = n + 2;
590 if (m < n)
591 return -ENOMEM;
592
bcab914f
LP
593 v = strdup(value);
594 if (!v)
595 return -ENOMEM;
4f4afc88 596
aa484f35 597 c = reallocarray(*l, m, sizeof(char*));
4f4afc88
LP
598 if (!c) {
599 free(v);
600 return -ENOMEM;
601 }
602
603 memmove(c+1, c, n * sizeof(char*));
604 c[0] = v;
605 c[n+1] = NULL;
606
607 *l = c;
608 return 0;
609}
610
14337c37 611char** strv_uniq(char **l) {
5f9a22c3
LP
612 /* Drops duplicate entries. The first identical string will be
613 * kept, the others dropped */
614
cba8922f 615 STRV_FOREACH(i, l)
5f9a22c3
LP
616 strv_remove(i+1, *i);
617
618 return l;
619}
620
479ddcdf 621bool strv_is_uniq(char * const *l) {
e1dd6790 622 STRV_FOREACH(i, l)
d29cc4d6 623 if (strv_contains(i+1, *i))
e1dd6790
LP
624 return false;
625
626 return true;
627}
628
14337c37 629char** strv_remove(char **l, const char *s) {
5f9a22c3
LP
630 char **f, **t;
631
632 if (!l)
633 return NULL;
634
5d6ab905
LP
635 assert(s);
636
637 /* Drops every occurrence of s in the string list, edits
638 * in-place. */
5f9a22c3 639
e3e45d4f
SP
640 for (f = t = l; *f; f++)
641 if (streq(*f, s))
71ecc858 642 free(*f);
e3e45d4f
SP
643 else
644 *(t++) = *f;
71ecc858
LP
645
646 *t = NULL;
647 return l;
648}
649
479ddcdf 650bool strv_overlap(char * const *a, char * const *b) {
e3e45d4f
SP
651 STRV_FOREACH(i, a)
652 if (strv_contains(b, *i))
653 return true;
0c85a4f3
LP
654
655 return false;
656}
857a493d 657
93bab288 658static int str_compare(char * const *a, char * const *b) {
857a493d
LP
659 return strcmp(*a, *b);
660}
661
14337c37 662char** strv_sort(char **l) {
93bab288 663 typesafe_qsort(l, strv_length(l), str_compare);
857a493d
LP
664 return l;
665}
7c2d8094 666
8b75798d
YW
667int strv_compare(char * const *a, char * const *b) {
668 int r;
e287086b 669
8b75798d
YW
670 if (strv_isempty(a)) {
671 if (strv_isempty(b))
672 return 0;
673 else
674 return -1;
675 }
e287086b
LP
676
677 if (strv_isempty(b))
8b75798d 678 return 1;
0f84a72e 679
8b75798d
YW
680 for ( ; *a || *b; ++a, ++b) {
681 r = strcmp_ptr(*a, *b);
682 if (r != 0)
683 return r;
684 }
0f84a72e 685
8b75798d 686 return 0;
0f84a72e
DH
687}
688
00546c18 689void strv_print_full(char * const *l, const char *prefix) {
7c2d8094 690 STRV_FOREACH(s, l)
00546c18 691 printf("%s%s\n", strempty(prefix), *s);
7c2d8094 692}
4de33e7f
LP
693
694int strv_extendf(char ***l, const char *format, ...) {
695 va_list ap;
696 char *x;
697 int r;
698
699 va_start(ap, format);
700 r = vasprintf(&x, format, ap);
701 va_end(ap);
702
703 if (r < 0)
704 return -ENOMEM;
705
706 return strv_consume(l, x);
707}
e1dd6790 708
14337c37
ZJS
709char** strv_reverse(char **l) {
710 size_t n;
e1dd6790
LP
711
712 n = strv_length(l);
713 if (n <= 1)
714 return l;
715
14337c37 716 for (size_t i = 0; i < n / 2; i++)
8a3134b2 717 SWAP_TWO(l[i], l[n-1-i]);
e1dd6790
LP
718
719 return l;
720}
bceccd5e 721
14337c37 722char** strv_shell_escape(char **l, const char *bad) {
04c14b25
RM
723 /* Escapes every character in every string in l that is in bad,
724 * edits in-place, does not roll-back on error. */
725
726 STRV_FOREACH(s, l) {
727 char *v;
728
729 v = shell_escape(*s, bad);
730 if (!v)
731 return NULL;
732
d6f2cd67 733 free_and_replace(*s, v);
04c14b25
RM
734 }
735
736 return l;
737}
738
bcfc0e88
LP
739bool strv_fnmatch_full(
740 char* const* patterns,
741 const char *s,
742 int flags,
743 size_t *ret_matched_pos) {
744
745 assert(s);
746
747 if (patterns)
748 for (size_t i = 0; patterns[i]; i++)
749 /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
750 * process the pattern for some reason we'll consider this equivalent to non-matching. */
751 if (fnmatch(patterns[i], s, flags) == 0) {
752 if (ret_matched_pos)
753 *ret_matched_pos = i;
754 return true;
755 }
756
757 if (ret_matched_pos)
758 *ret_matched_pos = SIZE_MAX;
bceccd5e
ZJS
759
760 return false;
761}
fe382237 762
14337c37 763char** strv_skip(char **l, size_t n) {
e3ead6bb
LP
764
765 while (n > 0) {
766 if (strv_isempty(l))
767 return l;
768
769 l++, n--;
770 }
771
772 return l;
773}
8dd4c05b
LP
774
775int strv_extend_n(char ***l, const char *value, size_t n) {
14337c37 776 size_t i, k;
8dd4c05b
LP
777 char **nl;
778
779 assert(l);
780
781 if (!value)
782 return 0;
783 if (n == 0)
784 return 0;
785
61233823 786 /* Adds the value n times to l */
8dd4c05b
LP
787
788 k = strv_length(*l);
60476376
LP
789 if (n >= SIZE_MAX - k)
790 return -ENOMEM;
8dd4c05b 791
60476376 792 nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
8dd4c05b
LP
793 if (!nl)
794 return -ENOMEM;
795
796 *l = nl;
797
798 for (i = k; i < k + n; i++) {
799 nl[i] = strdup(value);
800 if (!nl[i])
801 goto rollback;
802 }
8dd4c05b 803 nl[i] = NULL;
14337c37 804
8dd4c05b
LP
805 return 0;
806
807rollback:
14337c37 808 for (size_t j = k; j < i; j++)
8dd4c05b 809 free(nl[j]);
8dd4c05b 810 nl[k] = NULL;
14337c37 811
5b700370 812 return -ENOMEM;
8dd4c05b 813}
3df9bec5 814
6658f7c7
DDM
815int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
816 char *j;
817
818 assert(l);
819 assert(lhs);
820
821 if (!rhs) /* value is optional, in which case we suppress the field */
822 return 0;
823
824 j = strjoin(lhs, "=", rhs);
825 if (!j)
826 return -ENOMEM;
827
828 return strv_consume(l, j);
829}
830
479ddcdf 831int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
3df9bec5 832 bool b = false;
3df9bec5
LP
833 int r;
834
835 /* Like fputs(), but for strv, and with a less stupid argument order */
836
3df9bec5
LP
837 if (!space)
838 space = &b;
839
840 STRV_FOREACH(s, l) {
d390f8ef 841 r = fputs_with_space(f, *s, separator, space);
3df9bec5
LP
842 if (r < 0)
843 return r;
3df9bec5
LP
844 }
845
846 return 0;
847}
cde79109
ZJS
848
849static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
850 char **l;
851 int r;
852
853 l = hashmap_get(h, key);
854 if (l) {
855 /* A list for this key already exists, let's append to it if it is not listed yet */
856 if (strv_contains(l, value))
857 return 0;
858
859 r = strv_extend(&l, value);
860 if (r < 0)
861 return r;
862
863 assert_se(hashmap_update(h, key, l) >= 0);
864 } else {
865 /* No list for this key exists yet, create one */
866 _cleanup_strv_free_ char **l2 = NULL;
867 _cleanup_free_ char *t = NULL;
868
869 t = strdup(key);
870 if (!t)
871 return -ENOMEM;
872
873 r = strv_extend(&l2, value);
874 if (r < 0)
875 return r;
876
877 r = hashmap_put(h, t, l2);
878 if (r < 0)
879 return r;
880 TAKE_PTR(t);
881 TAKE_PTR(l2);
882 }
883
884 return 1;
885}
886
856e5195 887int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) {
cde79109
ZJS
888 int r;
889
856e5195 890 r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS);
cde79109
ZJS
891 if (r < 0)
892 return r;
893
894 return string_strv_hashmap_put_internal(*h, key, value);
895}
896
856e5195 897int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) {
cde79109
ZJS
898 int r;
899
856e5195 900 r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS);
cde79109
ZJS
901 if (r < 0)
902 return r;
903
904 return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
905}
906
907DEFINE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
cb599f88
LP
908
909char* strv_endswith(const char *s, char **l) {
910 STRV_FOREACH(i, l) {
911 char *e = endswith(s, *i);
912 if (e)
913 return (char*) e;
914 }
915
916 return NULL;
917}