]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/strv.c
Merge pull request #13022 from keszybz/coverity-cleanups
[thirdparty/systemd.git] / src / basic / strv.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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
LP
8#include <string.h>
9
b5efdb8a 10#include "alloc-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
20char *strv_find(char **l, const char *name) {
5f9a22c3
LP
21 char **i;
22
60918275
LP
23 assert(name);
24
5f9a22c3
LP
25 STRV_FOREACH(i, l)
26 if (streq(*i, name))
27 return *i;
60918275
LP
28
29 return NULL;
30}
31
a4bfb399
LP
32char *strv_find_prefix(char **l, const char *name) {
33 char **i;
34
35 assert(name);
36
37 STRV_FOREACH(i, l)
38 if (startswith(*i, name))
39 return *i;
40
41 return NULL;
42}
43
28849dba
LP
44char *strv_find_startswith(char **l, const char *name) {
45 char **i, *e;
46
47 assert(name);
48
49 /* Like strv_find_prefix, but actually returns only the
50 * suffix, not the whole item */
51
52 STRV_FOREACH(i, l) {
53 e = startswith(*i, name);
54 if (e)
55 return e;
56 }
57
58 return NULL;
59}
60
dd9c7723 61void strv_clear(char **l) {
60918275
LP
62 char **k;
63
64 if (!l)
65 return;
66
67 for (k = l; *k; k++)
68 free(*k);
69
dd9c7723
TG
70 *l = NULL;
71}
72
33c2ce7b 73char **strv_free(char **l) {
dd9c7723 74 strv_clear(l);
6b430fdb 75 return mfree(l);
60918275
LP
76}
77
ab84f5b9
ZJS
78char **strv_free_erase(char **l) {
79 char **i;
80
81 STRV_FOREACH(i, l)
309c6b19 82 erase_and_freep(i);
ab84f5b9 83
309c6b19 84 return mfree(l);
ab84f5b9
ZJS
85}
86
2fd9ae2e 87char **strv_copy(char * const *l) {
60918275
LP
88 char **r, **k;
89
1fd8d04e
LP
90 k = r = new(char*, strv_length(l) + 1);
91 if (!r)
60918275
LP
92 return NULL;
93
ede27aab 94 if (l)
1fd8d04e
LP
95 for (; *l; k++, l++) {
96 *k = strdup(*l);
97 if (!*k) {
98 strv_free(r);
99 return NULL;
100 }
101 }
60918275
LP
102
103 *k = NULL;
104 return r;
60918275
LP
105}
106
da6053d0
LP
107size_t strv_length(char * const *l) {
108 size_t n = 0;
60918275
LP
109
110 if (!l)
111 return 0;
112
113 for (; *l; l++)
114 n++;
115
116 return n;
117}
118
257eca1a 119char **strv_new_ap(const char *x, va_list ap) {
60918275 120 const char *s;
01111587 121 _cleanup_strv_free_ char **a = NULL;
da6053d0 122 size_t n = 0, i = 0;
257eca1a
LP
123 va_list aq;
124
07719a21 125 /* As a special trick we ignore all listed strings that equal
f9d14060 126 * STRV_IGNORE. This is supposed to be used with the
07719a21
LP
127 * STRV_IFNOTNULL() macro to include possibly NULL strings in
128 * the string list. */
129
60918275 130 if (x) {
f9d14060 131 n = x == STRV_IGNORE ? 0 : 1;
60918275 132
257eca1a 133 va_copy(aq, ap);
07719a21 134 while ((s = va_arg(aq, const char*))) {
f9d14060 135 if (s == STRV_IGNORE)
07719a21
LP
136 continue;
137
60918275 138 n++;
07719a21
LP
139 }
140
257eca1a 141 va_end(aq);
60918275
LP
142 }
143
07719a21
LP
144 a = new(char*, n+1);
145 if (!a)
60918275
LP
146 return NULL;
147
148 if (x) {
f9d14060 149 if (x != STRV_IGNORE) {
07719a21
LP
150 a[i] = strdup(x);
151 if (!a[i])
01111587 152 return NULL;
07719a21 153 i++;
60918275
LP
154 }
155
60918275 156 while ((s = va_arg(ap, const char*))) {
07719a21 157
f9d14060 158 if (s == STRV_IGNORE)
07719a21
LP
159 continue;
160
161 a[i] = strdup(s);
162 if (!a[i])
01111587 163 return NULL;
60918275
LP
164
165 i++;
166 }
60918275
LP
167 }
168
169 a[i] = NULL;
257eca1a 170
01111587 171 return TAKE_PTR(a);
60918275 172}
034c6ed7 173
bea1a013 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
e287086b
LP
185int strv_extend_strv(char ***a, char **b, bool filter_duplicates) {
186 char **s, **t;
187 size_t p, q, i = 0, j;
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
62d74c78 197 t = reallocarray(*a, p + q + 1, sizeof(char *));
e287086b
LP
198 if (!t)
199 return -ENOMEM;
200
201 t[p] = NULL;
202 *a = t;
07719a21 203
e3e45d4f 204 STRV_FOREACH(s, b) {
e287086b
LP
205
206 if (filter_duplicates && strv_contains(t, *s))
207 continue;
208
209 t[p+i] = strdup(*s);
210 if (!t[p+i])
211 goto rollback;
212
213 i++;
214 t[p+i] = NULL;
07719a21 215 }
034c6ed7 216
e287086b
LP
217 assert(i <= q);
218
219 return (int) i;
220
221rollback:
222 for (j = 0; j < i; j++)
223 free(t[p + j]);
224
225 t[p] = NULL;
226 return -ENOMEM;
5f9a22c3
LP
227}
228
e3e45d4f
SP
229int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
230 int r;
231 char **s;
5f9a22c3 232
e3e45d4f
SP
233 STRV_FOREACH(s, b) {
234 char *v;
5f9a22c3 235
b910cc72 236 v = strjoin(*s, suffix);
e3e45d4f
SP
237 if (!v)
238 return -ENOMEM;
5f9a22c3 239
e3e45d4f
SP
240 r = strv_push(a, v);
241 if (r < 0) {
242 free(v);
243 return r;
8ea913b2 244 }
8ea913b2 245 }
5f9a22c3 246
e3e45d4f 247 return 0;
5f9a22c3
LP
248}
249
8059aa9c 250char **strv_split_full(const char *s, const char *separator, SplitFlags flags) {
a2a5291b 251 const char *word, *state;
5f9a22c3 252 size_t l;
da6053d0 253 size_t n, i;
5f9a22c3
LP
254 char **r;
255
256 assert(s);
257
2c3a11d8
YW
258 if (!separator)
259 separator = WHITESPACE;
260
0745ce75
YW
261 s += strspn(s, separator);
262 if (isempty(s))
263 return new0(char*, 1);
264
5f9a22c3 265 n = 0;
8059aa9c 266 _FOREACH_WORD(word, l, s, separator, flags, state)
5f9a22c3
LP
267 n++;
268
1fd8d04e
LP
269 r = new(char*, n+1);
270 if (!r)
5f9a22c3
LP
271 return NULL;
272
273 i = 0;
8059aa9c 274 _FOREACH_WORD(word, l, s, separator, flags, state) {
a2a5291b 275 r[i] = strndup(word, l);
1fd8d04e 276 if (!r[i]) {
5f9a22c3
LP
277 strv_free(r);
278 return NULL;
279 }
280
1fd8d04e
LP
281 i++;
282 }
283
5f9a22c3
LP
284 r[i] = NULL;
285 return r;
286}
287
26d04f86
LP
288char **strv_split_newlines(const char *s) {
289 char **l;
da6053d0 290 size_t n;
26d04f86
LP
291
292 assert(s);
293
294 /* Special version of strv_split() that splits on newlines and
295 * suppresses an empty string at the end */
296
297 l = strv_split(s, NEWLINE);
298 if (!l)
299 return NULL;
300
301 n = strv_length(l);
302 if (n <= 0)
303 return l;
304
ece174c5 305 if (isempty(l[n - 1]))
a1e58e8e 306 l[n - 1] = mfree(l[n - 1]);
26d04f86
LP
307
308 return l;
309}
310
8adaf7bd 311int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
f88e6be5 312 _cleanup_strv_free_ char **l = NULL;
8dd4c05b 313 size_t n = 0, allocated = 0;
f88e6be5
LP
314 int r;
315
316 assert(t);
317 assert(s);
318
319 for (;;) {
320 _cleanup_free_ char *word = NULL;
321
8adaf7bd 322 r = extract_first_word(&s, &word, separators, flags);
f88e6be5
LP
323 if (r < 0)
324 return r;
ece174c5 325 if (r == 0)
f88e6be5
LP
326 break;
327
328 if (!GREEDY_REALLOC(l, allocated, n + 2))
329 return -ENOMEM;
330
ae2a15bc 331 l[n++] = TAKE_PTR(word);
f88e6be5
LP
332
333 l[n] = NULL;
334 }
335
8dd4c05b 336 if (!l) {
f88e6be5 337 l = new0(char*, 1);
8dd4c05b
LP
338 if (!l)
339 return -ENOMEM;
340 }
f88e6be5 341
ae2a15bc 342 *t = TAKE_PTR(l);
f88e6be5 343
8dd4c05b 344 return (int) n;
f88e6be5
LP
345}
346
2b9a7d2e 347char *strv_join_prefix(char **l, const char *separator, const char *prefix) {
5f9a22c3
LP
348 char *r, *e;
349 char **s;
2b9a7d2e 350 size_t n, k, m;
5f9a22c3
LP
351
352 if (!separator)
353 separator = " ";
354
355 k = strlen(separator);
2b9a7d2e 356 m = strlen_ptr(prefix);
5f9a22c3
LP
357
358 n = 0;
359 STRV_FOREACH(s, l) {
afe773b0 360 if (s != l)
5f9a22c3 361 n += k;
2b9a7d2e 362 n += m + strlen(*s);
5f9a22c3
LP
363 }
364
1fd8d04e
LP
365 r = new(char, n+1);
366 if (!r)
5f9a22c3
LP
367 return NULL;
368
369 e = r;
370 STRV_FOREACH(s, l) {
afe773b0 371 if (s != l)
5f9a22c3
LP
372 e = stpcpy(e, separator);
373
2b9a7d2e
YW
374 if (prefix)
375 e = stpcpy(e, prefix);
376
5f9a22c3
LP
377 e = stpcpy(e, *s);
378 }
379
8d49745c
LP
380 *e = 0;
381
5f9a22c3
LP
382 return r;
383}
384
4468addc 385int strv_push(char ***l, char *value) {
5926ccca 386 char **c;
da6053d0 387 size_t n, m;
5926ccca
LP
388
389 if (!value)
390 return 0;
391
82dde599 392 n = strv_length(*l);
97569e15 393
98940a3c 394 /* Increase and check for overflow */
97569e15
LP
395 m = n + 2;
396 if (m < n)
397 return -ENOMEM;
398
aa484f35 399 c = reallocarray(*l, m, sizeof(char*));
4468addc 400 if (!c)
82dde599 401 return -ENOMEM;
82dde599 402
4468addc 403 c[n] = value;
82dde599
LP
404 c[n+1] = NULL;
405
5926ccca
LP
406 *l = c;
407 return 0;
408}
409
98940a3c
LP
410int strv_push_pair(char ***l, char *a, char *b) {
411 char **c;
da6053d0 412 size_t n, m;
98940a3c
LP
413
414 if (!a && !b)
415 return 0;
416
417 n = strv_length(*l);
418
419 /* increase and check for overflow */
420 m = n + !!a + !!b + 1;
421 if (m < n)
422 return -ENOMEM;
423
aa484f35 424 c = reallocarray(*l, m, sizeof(char*));
98940a3c
LP
425 if (!c)
426 return -ENOMEM;
427
428 if (a)
429 c[n++] = a;
430 if (b)
431 c[n++] = b;
432 c[n] = NULL;
433
434 *l = c;
435 return 0;
436}
437
da6053d0 438int strv_insert(char ***l, size_t position, char *value) {
9a00f57a 439 char **c;
da6053d0 440 size_t n, m, i;
9a00f57a
LP
441
442 if (!value)
443 return 0;
444
445 n = strv_length(*l);
6e888894 446 position = MIN(position, n);
97569e15
LP
447
448 /* increase and check for overflow */
449 m = n + 2;
450 if (m < n)
451 return -ENOMEM;
452
453 c = new(char*, m);
9a00f57a
LP
454 if (!c)
455 return -ENOMEM;
456
6e888894
ZJS
457 for (i = 0; i < position; i++)
458 c[i] = (*l)[i];
459 c[position] = value;
460 for (i = position; i < n; i++)
9a00f57a
LP
461 c[i+1] = (*l)[i];
462
9a00f57a
LP
463 c[n+1] = NULL;
464
465 free(*l);
466 *l = c;
467
468 return 0;
469}
470
6e18964d
ZJS
471int strv_consume(char ***l, char *value) {
472 int r;
473
474 r = strv_push(l, value);
475 if (r < 0)
476 free(value);
477
9a00f57a
LP
478 return r;
479}
480
98940a3c
LP
481int strv_consume_pair(char ***l, char *a, char *b) {
482 int r;
483
484 r = strv_push_pair(l, a, b);
485 if (r < 0) {
486 free(a);
487 free(b);
488 }
489
490 return r;
491}
492
9a00f57a
LP
493int strv_consume_prepend(char ***l, char *value) {
494 int r;
495
496 r = strv_push_prepend(l, value);
497 if (r < 0)
498 free(value);
499
6e18964d
ZJS
500 return r;
501}
502
4468addc
LP
503int strv_extend(char ***l, const char *value) {
504 char *v;
4468addc
LP
505
506 if (!value)
507 return 0;
508
509 v = strdup(value);
510 if (!v)
511 return -ENOMEM;
512
6e18964d 513 return strv_consume(l, v);
4468addc
LP
514}
515
4f4afc88
LP
516int strv_extend_front(char ***l, const char *value) {
517 size_t n, m;
518 char *v, **c;
519
520 assert(l);
521
522 /* Like strv_extend(), but prepends rather than appends the new entry */
523
bcab914f
LP
524 if (!value)
525 return 0;
526
4f4afc88
LP
527 n = strv_length(*l);
528
529 /* Increase and overflow check. */
530 m = n + 2;
531 if (m < n)
532 return -ENOMEM;
533
bcab914f
LP
534 v = strdup(value);
535 if (!v)
536 return -ENOMEM;
4f4afc88 537
aa484f35 538 c = reallocarray(*l, m, sizeof(char*));
4f4afc88
LP
539 if (!c) {
540 free(v);
541 return -ENOMEM;
542 }
543
544 memmove(c+1, c, n * sizeof(char*));
545 c[0] = v;
546 c[n+1] = NULL;
547
548 *l = c;
549 return 0;
550}
551
5f9a22c3 552char **strv_uniq(char **l) {
cba8922f
LP
553 char **i;
554
5f9a22c3
LP
555 /* Drops duplicate entries. The first identical string will be
556 * kept, the others dropped */
557
cba8922f 558 STRV_FOREACH(i, l)
5f9a22c3
LP
559 strv_remove(i+1, *i);
560
561 return l;
562}
563
e1dd6790
LP
564bool strv_is_uniq(char **l) {
565 char **i;
566
567 STRV_FOREACH(i, l)
568 if (strv_find(i+1, *i))
569 return false;
570
571 return true;
572}
573
5f9a22c3
LP
574char **strv_remove(char **l, const char *s) {
575 char **f, **t;
576
577 if (!l)
578 return NULL;
579
5d6ab905
LP
580 assert(s);
581
582 /* Drops every occurrence of s in the string list, edits
583 * in-place. */
5f9a22c3 584
e3e45d4f
SP
585 for (f = t = l; *f; f++)
586 if (streq(*f, s))
71ecc858 587 free(*f);
e3e45d4f
SP
588 else
589 *(t++) = *f;
71ecc858
LP
590
591 *t = NULL;
592 return l;
593}
594
21bc923a 595char **strv_parse_nulstr(const char *s, size_t l) {
b60df13b
ZJS
596 /* l is the length of the input data, which will be split at NULs into
597 * elements of the resulting strv. Hence, the number of items in the resulting strv
598 * will be equal to one plus the number of NUL bytes in the l bytes starting at s,
599 * unless s[l-1] is NUL, in which case the final empty string is not stored in
600 * the resulting strv, and length is equal to the number of NUL bytes.
601 *
602 * Note that contrary to a normal nulstr which cannot contain empty strings, because
603 * the input data is terminated by any two consequent NUL bytes, this parser accepts
604 * empty strings in s.
605 */
606
21bc923a 607 const char *p;
da6053d0 608 size_t c = 0, i = 0;
21bc923a
LP
609 char **v;
610
611 assert(s || l <= 0);
612
613 if (l <= 0)
49b832c5 614 return new0(char*, 1);
21bc923a
LP
615
616 for (p = s; p < s + l; p++)
617 if (*p == 0)
618 c++;
619
620 if (s[l-1] != 0)
621 c++;
622
1fd8d04e
LP
623 v = new0(char*, c+1);
624 if (!v)
21bc923a
LP
625 return NULL;
626
627 p = s;
628 while (p < s + l) {
629 const char *e;
630
631 e = memchr(p, 0, s + l - p);
632
1fd8d04e
LP
633 v[i] = strndup(p, e ? e - p : s + l - p);
634 if (!v[i]) {
21bc923a
LP
635 strv_free(v);
636 return NULL;
637 }
638
1fd8d04e
LP
639 i++;
640
21bc923a
LP
641 if (!e)
642 break;
643
644 p = e + 1;
645 }
646
647 assert(i == c);
648
649 return v;
650}
0c85a4f3 651
fabe5c0e
LP
652char **strv_split_nulstr(const char *s) {
653 const char *i;
654 char **r = NULL;
655
656 NULSTR_FOREACH(i, s)
657 if (strv_extend(&r, i) < 0) {
658 strv_free(r);
659 return NULL;
660 }
661
662 if (!r)
bea1a013 663 return strv_new(NULL);
fabe5c0e
LP
664
665 return r;
666}
667
e287086b 668int strv_make_nulstr(char **l, char **p, size_t *q) {
b60df13b
ZJS
669 /* A valid nulstr with two NULs at the end will be created, but
670 * q will be the length without the two trailing NULs. Thus the output
671 * string is a valid nulstr and can be iterated over using NULSTR_FOREACH,
672 * and can also be parsed by strv_parse_nulstr as long as the length
673 * is provided separately.
674 */
675
e287086b
LP
676 size_t n_allocated = 0, n = 0;
677 _cleanup_free_ char *m = NULL;
678 char **i;
679
680 assert(p);
681 assert(q);
682
683 STRV_FOREACH(i, l) {
684 size_t z;
685
686 z = strlen(*i);
687
b60df13b 688 if (!GREEDY_REALLOC(m, n_allocated, n + z + 2))
e287086b
LP
689 return -ENOMEM;
690
691 memcpy(m + n, *i, z + 1);
692 n += z + 1;
693 }
694
695 if (!m) {
696 m = new0(char, 1);
697 if (!m)
698 return -ENOMEM;
b60df13b
ZJS
699 n = 1;
700 } else
701 /* make sure there is a second extra NUL at the end of resulting nulstr */
702 m[n] = '\0';
e287086b 703
b60df13b 704 assert(n > 0);
e287086b 705 *p = m;
b60df13b 706 *q = n - 1;
e287086b
LP
707
708 m = NULL;
709
710 return 0;
711}
712
0c85a4f3 713bool strv_overlap(char **a, char **b) {
e3e45d4f 714 char **i;
0c85a4f3 715
e3e45d4f
SP
716 STRV_FOREACH(i, a)
717 if (strv_contains(b, *i))
718 return true;
0c85a4f3
LP
719
720 return false;
721}
857a493d 722
93bab288 723static int str_compare(char * const *a, char * const *b) {
857a493d
LP
724 return strcmp(*a, *b);
725}
726
727char **strv_sort(char **l) {
93bab288 728 typesafe_qsort(l, strv_length(l), str_compare);
857a493d
LP
729 return l;
730}
7c2d8094 731
0f84a72e 732bool strv_equal(char **a, char **b) {
e287086b
LP
733
734 if (strv_isempty(a))
735 return strv_isempty(b);
736
737 if (strv_isempty(b))
738 return false;
0f84a72e
DH
739
740 for ( ; *a || *b; ++a, ++b)
741 if (!streq_ptr(*a, *b))
742 return false;
743
744 return true;
745}
746
7c2d8094
TA
747void strv_print(char **l) {
748 char **s;
749
7c2d8094
TA
750 STRV_FOREACH(s, l)
751 puts(*s);
752}
4de33e7f
LP
753
754int strv_extendf(char ***l, const char *format, ...) {
755 va_list ap;
756 char *x;
757 int r;
758
759 va_start(ap, format);
760 r = vasprintf(&x, format, ap);
761 va_end(ap);
762
763 if (r < 0)
764 return -ENOMEM;
765
766 return strv_consume(l, x);
767}
e1dd6790
LP
768
769char **strv_reverse(char **l) {
da6053d0 770 size_t n, i;
e1dd6790
LP
771
772 n = strv_length(l);
773 if (n <= 1)
774 return l;
775
fc549b96 776 for (i = 0; i < n / 2; i++)
8a3134b2 777 SWAP_TWO(l[i], l[n-1-i]);
e1dd6790
LP
778
779 return l;
780}
bceccd5e 781
04c14b25
RM
782char **strv_shell_escape(char **l, const char *bad) {
783 char **s;
784
785 /* Escapes every character in every string in l that is in bad,
786 * edits in-place, does not roll-back on error. */
787
788 STRV_FOREACH(s, l) {
789 char *v;
790
791 v = shell_escape(*s, bad);
792 if (!v)
793 return NULL;
794
795 free(*s);
796 *s = v;
797 }
798
799 return l;
800}
801
2404701e 802bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
bceccd5e
ZJS
803 char* const* p;
804
805 STRV_FOREACH(p, patterns)
2027927b 806 if (fnmatch(*p, s, flags) == 0)
bceccd5e
ZJS
807 return true;
808
809 return false;
810}
fe382237
LP
811
812char ***strv_free_free(char ***l) {
813 char ***i;
814
815 if (!l)
816 return NULL;
817
818 for (i = l; *i; i++)
819 strv_free(*i);
820
6b430fdb 821 return mfree(l);
fe382237 822}
e3ead6bb
LP
823
824char **strv_skip(char **l, size_t n) {
825
826 while (n > 0) {
827 if (strv_isempty(l))
828 return l;
829
830 l++, n--;
831 }
832
833 return l;
834}
8dd4c05b
LP
835
836int strv_extend_n(char ***l, const char *value, size_t n) {
837 size_t i, j, k;
838 char **nl;
839
840 assert(l);
841
842 if (!value)
843 return 0;
844 if (n == 0)
845 return 0;
846
61233823 847 /* Adds the value n times to l */
8dd4c05b
LP
848
849 k = strv_length(*l);
850
62d74c78 851 nl = reallocarray(*l, k + n + 1, sizeof(char *));
8dd4c05b
LP
852 if (!nl)
853 return -ENOMEM;
854
855 *l = nl;
856
857 for (i = k; i < k + n; i++) {
858 nl[i] = strdup(value);
859 if (!nl[i])
860 goto rollback;
861 }
862
863 nl[i] = NULL;
864 return 0;
865
866rollback:
6fff8ac4 867 for (j = k; j < i; j++)
8dd4c05b
LP
868 free(nl[j]);
869
870 nl[k] = NULL;
5b700370 871 return -ENOMEM;
8dd4c05b 872}
3df9bec5
LP
873
874int fputstrv(FILE *f, char **l, const char *separator, bool *space) {
875 bool b = false;
876 char **s;
877 int r;
878
879 /* Like fputs(), but for strv, and with a less stupid argument order */
880
3df9bec5
LP
881 if (!space)
882 space = &b;
883
884 STRV_FOREACH(s, l) {
d390f8ef 885 r = fputs_with_space(f, *s, separator, space);
3df9bec5
LP
886 if (r < 0)
887 return r;
3df9bec5
LP
888 }
889
890 return 0;
891}