]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/strv.c
presets: Disable by default for initrd presets
[thirdparty/systemd.git] / src / basic / strv.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
a7334b09 2
11c3a366 3#include <fnmatch.h>
11c3a366 4#include <stdio.h>
60918275 5
b5efdb8a 6#include "alloc-util.h"
2ed74695 7#include "env-util.h"
4f5dd394 8#include "escape.h"
11c3a366 9#include "extract-word.h"
d390f8ef 10#include "fileio.h"
aca09301 11#include "gunicode.h"
0c15577a 12#include "hashmap.h"
93a1f792 13#include "log.h"
309c6b19 14#include "memory-util.h"
760877e9 15#include "sort-util.h"
07630cea 16#include "string-util.h"
60918275 17#include "strv.h"
aca09301 18#include "utf8.h"
60918275 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
ffdf4978
YW
67static char* strv_find_closest_prefix(char * const *l, const char *name) {
68 size_t best_distance = SIZE_MAX;
69 char *best = NULL;
70
71 assert(name);
72
73 STRV_FOREACH(s, l) {
74 char *e = startswith(*s, name);
75 if (!e)
76 continue;
77
78 size_t n = strlen(e);
79 if (n < best_distance) {
80 best_distance = n;
81 best = *s;
82 }
83 }
84
85 return best;
86}
87
88static char* strv_find_closest_by_levenshtein(char * const *l, const char *name) {
1e1ac5d5
MF
89 ssize_t best_distance = SSIZE_MAX;
90 char *best = NULL;
91
92 assert(name);
93
94 STRV_FOREACH(i, l) {
95 ssize_t distance;
96
97 distance = strlevenshtein(*i, name);
98 if (distance < 0) {
99 log_debug_errno(distance, "Failed to determine Levenshtein distance between %s and %s: %m", *i, name);
100 return NULL;
101 }
102
103 if (distance > 5) /* If the distance is just too far off, don't make a bad suggestion */
104 continue;
105
106 if (distance < best_distance) {
107 best_distance = distance;
108 best = *i;
109 }
110 }
111
112 return best;
113}
114
ffdf4978
YW
115char* strv_find_closest(char * const *l, const char *name) {
116 assert(name);
117
118 /* Be more helpful to the user, and give a hint what the user might have wanted to type. We search
119 * with two mechanisms: a simple prefix match and – if that didn't yield results –, a Levenshtein
120 * word distance based match. */
121
122 char *found = strv_find_closest_prefix(l, name);
123 if (found)
124 return found;
125
126 return strv_find_closest_by_levenshtein(l, name);
127}
128
2ed74695
LB
129char* strv_find_first_field(char * const *needles, char * const *haystack) {
130 STRV_FOREACH(k, needles) {
131 char *value = strv_env_pairs_get((char **)haystack, *k);
132 if (value)
133 return value;
134 }
135
136 return NULL;
137}
138
14337c37 139char** strv_free(char **l) {
d0b4f13e 140 STRV_FOREACH(k, l)
60918275
LP
141 free(*k);
142
6b430fdb 143 return mfree(l);
60918275
LP
144}
145
14337c37 146char** strv_free_erase(char **l) {
ab84f5b9 147 STRV_FOREACH(i, l)
309c6b19 148 erase_and_freep(i);
ab84f5b9 149
309c6b19 150 return mfree(l);
ab84f5b9
ZJS
151}
152
a39cba25
LB
153void strv_free_many(char ***strvs, size_t n) {
154 assert(strvs || n == 0);
155
156 FOREACH_ARRAY (i, strvs, n)
157 strv_free(*i);
158
159 free(strvs);
160}
161
4ea517a6 162char** strv_copy_n(char * const *l, size_t m) {
9eb81481 163 _cleanup_strv_free_ char **result = NULL;
de010b0b 164 char **k;
60918275 165
4ea517a6 166 result = new(char*, MIN(strv_length(l), m) + 1);
9eb81481 167 if (!result)
60918275
LP
168 return NULL;
169
9eb81481
YW
170 k = result;
171 STRV_FOREACH(i, l) {
4ea517a6
LP
172 if (m == 0)
173 break;
174
9eb81481
YW
175 *k = strdup(*i);
176 if (!*k)
177 return NULL;
178 k++;
4ea517a6
LP
179
180 if (m != SIZE_MAX)
181 m--;
9eb81481 182 }
60918275
LP
183
184 *k = NULL;
9eb81481 185 return TAKE_PTR(result);
60918275
LP
186}
187
5058bd7e
LN
188int strv_copy_unless_empty(char * const *l, char ***ret) {
189 assert(ret);
190
191 if (strv_isempty(l)) {
192 *ret = NULL;
193 return 0;
194 }
195
196 char **copy = strv_copy(l);
197 if (!copy)
198 return -ENOMEM;
199
200 *ret = TAKE_PTR(copy);
201 return 1;
202}
203
da6053d0
LP
204size_t strv_length(char * const *l) {
205 size_t n = 0;
60918275 206
d0b4f13e 207 STRV_FOREACH(i, l)
60918275
LP
208 n++;
209
210 return n;
211}
212
14337c37 213char** strv_new_ap(const char *x, va_list ap) {
01111587 214 _cleanup_strv_free_ char **a = NULL;
da6053d0 215 size_t n = 0, i = 0;
257eca1a
LP
216 va_list aq;
217
07719a21 218 /* As a special trick we ignore all listed strings that equal
f9d14060 219 * STRV_IGNORE. This is supposed to be used with the
07719a21
LP
220 * STRV_IFNOTNULL() macro to include possibly NULL strings in
221 * the string list. */
222
3d2b1fa4
RV
223 va_copy(aq, ap);
224 for (const char *s = x; s; s = va_arg(aq, const char*)) {
225 if (s == STRV_IGNORE)
226 continue;
07719a21 227
3d2b1fa4 228 n++;
60918275 229 }
3d2b1fa4 230 va_end(aq);
60918275 231
07719a21
LP
232 a = new(char*, n+1);
233 if (!a)
60918275
LP
234 return NULL;
235
3d2b1fa4
RV
236 for (const char *s = x; s; s = va_arg(ap, const char*)) {
237 if (s == STRV_IGNORE)
238 continue;
07719a21 239
3d2b1fa4
RV
240 a[i] = strdup(s);
241 if (!a[i])
242 return NULL;
60918275 243
3d2b1fa4 244 i++;
60918275
LP
245 }
246
247 a[i] = NULL;
257eca1a 248
01111587 249 return TAKE_PTR(a);
60918275 250}
034c6ed7 251
14337c37 252char** strv_new_internal(const char *x, ...) {
257eca1a
LP
253 char **r;
254 va_list ap;
255
256 va_start(ap, x);
257 r = strv_new_ap(x, ap);
258 va_end(ap);
259
260 return r;
261}
262
479ddcdf 263int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
14337c37 264 size_t p, q, i = 0;
e287086b
LP
265
266 assert(a);
267
8a7ade74
MY
268 q = strv_length(b);
269 if (q == 0)
e287086b
LP
270 return 0;
271
272 p = strv_length(*a);
60476376
LP
273 if (p >= SIZE_MAX - q)
274 return -ENOMEM;
275
8a7ade74 276 char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
e287086b
LP
277 if (!t)
278 return -ENOMEM;
279
280 t[p] = NULL;
281 *a = t;
07719a21 282
e3e45d4f 283 STRV_FOREACH(s, b) {
e287086b
LP
284 if (filter_duplicates && strv_contains(t, *s))
285 continue;
286
287 t[p+i] = strdup(*s);
288 if (!t[p+i])
289 goto rollback;
290
291 i++;
292 t[p+i] = NULL;
07719a21 293 }
034c6ed7 294
e287086b
LP
295 assert(i <= q);
296
297 return (int) i;
298
299rollback:
24ae45cb 300 free_many_charp(t + p, i);
e287086b
LP
301 t[p] = NULL;
302 return -ENOMEM;
5f9a22c3
LP
303}
304
8a7ade74
MY
305int strv_extend_strv_consume(char ***a, char **b, bool filter_duplicates) {
306 _cleanup_strv_free_ char **b_consume = b;
307 size_t p, q, i;
308
309 assert(a);
310
311 q = strv_length(b);
312 if (q == 0)
313 return 0;
314
315 p = strv_length(*a);
316 if (p == 0) {
317 strv_free_and_replace(*a, b_consume);
318
319 if (filter_duplicates)
320 strv_uniq(*a);
321
322 return strv_length(*a);
323 }
324
325 if (p >= SIZE_MAX - q)
326 return -ENOMEM;
327
328 char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
329 if (!t)
330 return -ENOMEM;
331
332 t[p] = NULL;
333 *a = t;
334
335 if (!filter_duplicates) {
336 *mempcpy_typesafe(t + p, b, q) = NULL;
337 i = q;
338 } else {
339 i = 0;
340
341 STRV_FOREACH(s, b) {
342 if (strv_contains(t, *s)) {
343 free(*s);
344 continue;
345 }
346
347 t[p+i] = *s;
348
349 i++;
350 t[p+i] = NULL;
351 }
352 }
353
354 assert(i <= q);
355
356 b_consume = mfree(b_consume);
357
358 return (int) i;
359}
360
9bc74930 361int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix) {
e3e45d4f 362 int r;
5f9a22c3 363
8a7ade74
MY
364 assert(a);
365
e3e45d4f
SP
366 STRV_FOREACH(s, b) {
367 char *v;
5f9a22c3 368
9bc74930 369 v = strjoin(strempty(prefix), *s, suffix);
e3e45d4f
SP
370 if (!v)
371 return -ENOMEM;
5f9a22c3 372
8f431828
ZJS
373 r = strv_consume(a, v);
374 if (r < 0)
e3e45d4f 375 return r;
8ea913b2 376 }
5f9a22c3 377
e3e45d4f 378 return 0;
5f9a22c3
LP
379}
380
f385c447
YW
381int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) {
382 _cleanup_strv_free_ char **l = NULL;
da6053d0 383 size_t n;
f385c447 384 int r;
26d04f86
LP
385
386 assert(s);
387
f385c447
YW
388 /* Special version of strv_split_full() that splits on newlines and
389 * suppresses an empty string at the end. */
26d04f86 390
f385c447
YW
391 r = strv_split_full(&l, s, NEWLINE, flags);
392 if (r < 0)
393 return r;
26d04f86
LP
394
395 n = strv_length(l);
f385c447 396 if (n > 0 && isempty(l[n - 1])) {
a1e58e8e 397 l[n - 1] = mfree(l[n - 1]);
f385c447
YW
398 n--;
399 }
26d04f86 400
f385c447
YW
401 *ret = TAKE_PTR(l);
402 return n;
26d04f86
LP
403}
404
6553db60
DDM
405char** strv_split_newlines(const char *s) {
406 char **ret;
407
408 if (strv_split_newlines_full(&ret, s, 0) < 0)
409 return NULL;
410
411 return ret;
412}
413
90e30d76 414int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
f88e6be5 415 _cleanup_strv_free_ char **l = NULL;
319a4f4b 416 size_t n = 0;
f88e6be5
LP
417 int r;
418
419 assert(t);
420 assert(s);
421
422 for (;;) {
423 _cleanup_free_ char *word = NULL;
424
8adaf7bd 425 r = extract_first_word(&s, &word, separators, flags);
f88e6be5
LP
426 if (r < 0)
427 return r;
ece174c5 428 if (r == 0)
f88e6be5
LP
429 break;
430
319a4f4b 431 if (!GREEDY_REALLOC(l, n + 2))
f88e6be5
LP
432 return -ENOMEM;
433
ae2a15bc 434 l[n++] = TAKE_PTR(word);
f88e6be5
LP
435 l[n] = NULL;
436 }
437
8dd4c05b 438 if (!l) {
f88e6be5 439 l = new0(char*, 1);
8dd4c05b
LP
440 if (!l)
441 return -ENOMEM;
442 }
f88e6be5 443
ae2a15bc 444 *t = TAKE_PTR(l);
f88e6be5 445
8dd4c05b 446 return (int) n;
f88e6be5
LP
447}
448
6553db60
DDM
449char** strv_split(const char *s, const char *separators) {
450 char **ret;
451
452 if (strv_split_full(&ret, s, separators, EXTRACT_RETAIN_ESCAPE) < 0)
453 return NULL;
454
455 return ret;
456}
457
3c318caa 458int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
a2c8652a 459 char **l;
3c318caa
FS
460 int r;
461
462 assert(t);
463 assert(s);
464
465 r = strv_split_full(&l, s, separators, flags);
466 if (r < 0)
467 return r;
468
a2c8652a 469 r = strv_extend_strv_consume(t, l, filter_duplicates);
3c318caa
FS
470 if (r < 0)
471 return r;
472
473 return (int) strv_length(*t);
474}
475
6553db60
DDM
476int strv_split_and_extend(char ***t, const char *s, const char *separators, bool filter_duplicates) {
477 return strv_split_and_extend_full(t, s, separators, filter_duplicates, 0);
478}
479
a082edd5
LB
480int strv_split_colon_pairs(char ***t, const char *s) {
481 _cleanup_strv_free_ char **l = NULL;
319a4f4b 482 size_t n = 0;
a082edd5
LB
483 int r;
484
485 assert(t);
486 assert(s);
487
488 for (;;) {
489 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
490
491 r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
492 if (r < 0)
493 return r;
494 if (r == 0)
495 break;
496
497 const char *p = tuple;
498 r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
4f495126 499 &first, &second);
a082edd5
LB
500 if (r < 0)
501 return r;
502 if (r == 0)
503 continue;
504 /* Enforce that at most 2 colon-separated words are contained in each group */
505 if (!isempty(p))
506 return -EINVAL;
507
508 second_or_empty = strdup(strempty(second));
509 if (!second_or_empty)
510 return -ENOMEM;
511
319a4f4b 512 if (!GREEDY_REALLOC(l, n + 3))
a082edd5
LB
513 return -ENOMEM;
514
515 l[n++] = TAKE_PTR(first);
516 l[n++] = TAKE_PTR(second_or_empty);
517
518 l[n] = NULL;
519 }
520
521 if (!l) {
522 l = new0(char*, 1);
523 if (!l)
524 return -ENOMEM;
525 }
526
527 *t = TAKE_PTR(l);
528
529 return (int) n;
530}
531
e5f2d77b 532char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) {
5f9a22c3 533 char *r, *e;
2b9a7d2e 534 size_t n, k, m;
5f9a22c3
LP
535
536 if (!separator)
537 separator = " ";
538
539 k = strlen(separator);
2b9a7d2e 540 m = strlen_ptr(prefix);
5f9a22c3 541
e5f2d77b 542 if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */
d4d9f034
ZJS
543 assert(k == 1);
544
5f9a22c3
LP
545 n = 0;
546 STRV_FOREACH(s, l) {
afe773b0 547 if (s != l)
5f9a22c3 548 n += k;
d4d9f034 549
e5f2d77b 550 bool needs_escaping = escape_separator && strchr(*s, *separator);
d4d9f034
ZJS
551
552 n += m + strlen(*s) * (1 + needs_escaping);
5f9a22c3
LP
553 }
554
1fd8d04e
LP
555 r = new(char, n+1);
556 if (!r)
5f9a22c3
LP
557 return NULL;
558
559 e = r;
560 STRV_FOREACH(s, l) {
afe773b0 561 if (s != l)
5f9a22c3
LP
562 e = stpcpy(e, separator);
563
2b9a7d2e
YW
564 if (prefix)
565 e = stpcpy(e, prefix);
566
e5f2d77b 567 bool needs_escaping = escape_separator && strchr(*s, *separator);
d4d9f034
ZJS
568
569 if (needs_escaping)
570 for (size_t i = 0; (*s)[i]; i++) {
e5f2d77b 571 if ((*s)[i] == *separator)
d4d9f034
ZJS
572 *(e++) = '\\';
573 *(e++) = (*s)[i];
574 }
575 else
576 e = stpcpy(e, *s);
5f9a22c3
LP
577 }
578
8d49745c
LP
579 *e = 0;
580
5f9a22c3
LP
581 return r;
582}
583
3ec3ae68
ZJS
584int strv_push_with_size(char ***l, size_t *n, char *value) {
585 /* n is a pointer to a variable to store the size of l.
586 * If not given (i.e. n is NULL or *n is SIZE_MAX), size will be calculated using strv_length().
587 * If n is not NULL, the size after the push will be returned.
588 * If value is empty, no action is taken and *n is not set. */
5926ccca
LP
589
590 if (!value)
591 return 0;
592
3ec3ae68
ZJS
593 size_t size = n ? *n : SIZE_MAX;
594 if (size == SIZE_MAX)
595 size = strv_length(*l);
97569e15 596
60476376 597 /* Check for overflow */
3ec3ae68 598 if (size > SIZE_MAX-2)
97569e15
LP
599 return -ENOMEM;
600
3ec3ae68 601 char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
4468addc 602 if (!c)
82dde599 603 return -ENOMEM;
82dde599 604
3ec3ae68
ZJS
605 c[size] = value;
606 c[size+1] = NULL;
82dde599 607
5926ccca 608 *l = c;
3ec3ae68
ZJS
609 if (n)
610 *n = size + 1;
5926ccca
LP
611 return 0;
612}
613
98940a3c
LP
614int strv_push_pair(char ***l, char *a, char *b) {
615 char **c;
60476376 616 size_t n;
98940a3c
LP
617
618 if (!a && !b)
619 return 0;
620
621 n = strv_length(*l);
622
60476376
LP
623 /* Check for overflow */
624 if (n > SIZE_MAX-3)
98940a3c
LP
625 return -ENOMEM;
626
60476376
LP
627 /* increase and check for overflow */
628 c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*));
98940a3c
LP
629 if (!c)
630 return -ENOMEM;
631
632 if (a)
633 c[n++] = a;
634 if (b)
635 c[n++] = b;
636 c[n] = NULL;
637
638 *l = c;
639 return 0;
640}
641
da6053d0 642int strv_insert(char ***l, size_t position, char *value) {
9a00f57a 643 char **c;
14337c37 644 size_t n, m;
9a00f57a 645
a4343106
LP
646 assert(l);
647
9a00f57a
LP
648 if (!value)
649 return 0;
650
651 n = strv_length(*l);
6e888894 652 position = MIN(position, n);
97569e15 653
d7306348 654 /* check for overflow and increase */
a4343106 655 if (n > SIZE_MAX - 2)
97569e15 656 return -ENOMEM;
a4343106 657 m = n + 2;
97569e15 658
a4343106 659 c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
9a00f57a
LP
660 if (!c)
661 return -ENOMEM;
662
a4343106
LP
663 if (n > position)
664 memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
665
6e888894 666 c[position] = value;
a4343106 667 c[n + 1] = NULL;
9a00f57a 668
a4343106
LP
669 *l = c;
670 return 0;
9a00f57a
LP
671}
672
3ec3ae68 673int strv_consume_with_size(char ***l, size_t *n, char *value) {
6e18964d
ZJS
674 int r;
675
3ec3ae68 676 r = strv_push_with_size(l, n, value);
6e18964d
ZJS
677 if (r < 0)
678 free(value);
679
9a00f57a
LP
680 return r;
681}
682
98940a3c
LP
683int strv_consume_pair(char ***l, char *a, char *b) {
684 int r;
685
686 r = strv_push_pair(l, a, b);
687 if (r < 0) {
688 free(a);
689 free(b);
690 }
691
692 return r;
693}
694
9a00f57a
LP
695int strv_consume_prepend(char ***l, char *value) {
696 int r;
697
698 r = strv_push_prepend(l, value);
699 if (r < 0)
700 free(value);
701
6e18964d
ZJS
702 return r;
703}
704
82443be5 705int strv_prepend(char ***l, const char *value) {
706 char *v;
707
708 if (!value)
709 return 0;
710
711 v = strdup(value);
712 if (!v)
713 return -ENOMEM;
714
715 return strv_consume_prepend(l, v);
716}
717
3ec3ae68 718int strv_extend_with_size(char ***l, size_t *n, const char *value) {
4468addc 719 char *v;
4468addc
LP
720
721 if (!value)
722 return 0;
723
724 v = strdup(value);
725 if (!v)
726 return -ENOMEM;
727
3ec3ae68 728 return strv_consume_with_size(l, n, v);
4468addc
LP
729}
730
80f1e209
LP
731int strv_extend_many_internal(char ***l, const char *value, ...) {
732 va_list ap;
733 size_t n, m;
734 int r;
735
736 assert(l);
737
738 m = n = strv_length(*l);
739
740 r = 0;
741 va_start(ap, value);
742 for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
743 if (!s)
744 continue;
745
746 if (m > SIZE_MAX-1) { /* overflow */
747 r = -ENOMEM;
748 break;
749 }
750 m++;
751 }
752 va_end(ap);
753
754 if (r < 0)
755 return r;
756 if (m > SIZE_MAX-1)
757 return -ENOMEM;
758
759 char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
760 if (!c)
761 return -ENOMEM;
762 *l = c;
763
764 r = 0;
765 size_t i = n;
766 va_start(ap, value);
767 for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
768 if (!s)
769 continue;
770
771 c[i] = strdup(s);
772 if (!c[i]) {
773 r = -ENOMEM;
774 break;
775 }
776 i++;
777 }
778 va_end(ap);
779
780 if (r < 0) {
781 /* rollback on error */
782 for (size_t j = n; j < i; j++)
783 c[j] = mfree(c[j]);
784 return r;
785 }
786
787 c[i] = NULL;
788 return 0;
789}
790
14337c37 791char** strv_uniq(char **l) {
5f9a22c3
LP
792 /* Drops duplicate entries. The first identical string will be
793 * kept, the others dropped */
794
cba8922f 795 STRV_FOREACH(i, l)
5f9a22c3
LP
796 strv_remove(i+1, *i);
797
798 return l;
799}
800
479ddcdf 801bool strv_is_uniq(char * const *l) {
e1dd6790 802 STRV_FOREACH(i, l)
d29cc4d6 803 if (strv_contains(i+1, *i))
e1dd6790
LP
804 return false;
805
806 return true;
807}
808
14337c37 809char** strv_remove(char **l, const char *s) {
5f9a22c3
LP
810 char **f, **t;
811
812 if (!l)
813 return NULL;
814
5d6ab905
LP
815 assert(s);
816
817 /* Drops every occurrence of s in the string list, edits
818 * in-place. */
5f9a22c3 819
e3e45d4f
SP
820 for (f = t = l; *f; f++)
821 if (streq(*f, s))
71ecc858 822 free(*f);
e3e45d4f
SP
823 else
824 *(t++) = *f;
71ecc858
LP
825
826 *t = NULL;
827 return l;
828}
829
479ddcdf 830bool strv_overlap(char * const *a, char * const *b) {
e3e45d4f
SP
831 STRV_FOREACH(i, a)
832 if (strv_contains(b, *i))
833 return true;
0c85a4f3
LP
834
835 return false;
836}
857a493d 837
93bab288 838static int str_compare(char * const *a, char * const *b) {
857a493d
LP
839 return strcmp(*a, *b);
840}
841
14337c37 842char** strv_sort(char **l) {
93bab288 843 typesafe_qsort(l, strv_length(l), str_compare);
857a493d
LP
844 return l;
845}
7c2d8094 846
3dc546ad
YW
847char** strv_sort_uniq(char **l) {
848 if (strv_isempty(l))
849 return l;
850
851 char **tail = strv_sort(l), *prev = NULL;
852 STRV_FOREACH(i, l)
853 if (streq_ptr(*i, prev))
854 free(*i);
855 else
856 *(tail++) = prev = *i;
857
858 *tail = NULL;
859 return l;
860}
861
8b75798d
YW
862int strv_compare(char * const *a, char * const *b) {
863 int r;
e287086b 864
8b75798d
YW
865 if (strv_isempty(a)) {
866 if (strv_isempty(b))
867 return 0;
868 else
869 return -1;
870 }
e287086b
LP
871
872 if (strv_isempty(b))
8b75798d 873 return 1;
0f84a72e 874
8b75798d
YW
875 for ( ; *a || *b; ++a, ++b) {
876 r = strcmp_ptr(*a, *b);
877 if (r != 0)
878 return r;
879 }
0f84a72e 880
8b75798d 881 return 0;
0f84a72e
DH
882}
883
3f23b37c 884bool strv_equal_ignore_order(char * const *a, char * const *b) {
5072f426
LP
885
886 /* Just like strv_equal(), but doesn't care about the order of elements or about redundant entries
887 * (i.e. it's even ok if the number of entries in the array differ, as long as the difference just
c1e05180 888 * consists of repetitions). */
5072f426
LP
889
890 if (a == b)
891 return true;
892
893 STRV_FOREACH(i, a)
894 if (!strv_contains(b, *i))
895 return false;
896
897 STRV_FOREACH(i, b)
898 if (!strv_contains(a, *i))
899 return false;
900
901 return true;
902}
903
00546c18 904void strv_print_full(char * const *l, const char *prefix) {
7c2d8094 905 STRV_FOREACH(s, l)
00546c18 906 printf("%s%s\n", strempty(prefix), *s);
7c2d8094 907}
4de33e7f
LP
908
909int strv_extendf(char ***l, const char *format, ...) {
910 va_list ap;
911 char *x;
912 int r;
913
914 va_start(ap, format);
915 r = vasprintf(&x, format, ap);
916 va_end(ap);
917
918 if (r < 0)
919 return -ENOMEM;
920
921 return strv_consume(l, x);
922}
e1dd6790 923
eba8b541
MY
924char* startswith_strv(const char *s, char * const *l) {
925 STRV_FOREACH(i, l) {
926 char *found = startswith(s, *i);
927 if (found)
928 return found;
929 }
930
931 return NULL;
932}
933
2e6f012b
MY
934char* endswith_strv(const char *s, char * const *l) {
935 STRV_FOREACH(i, l) {
936 char *found = endswith(s, *i);
937 if (found)
938 return found;
939 }
940
941 return NULL;
942}
943
14337c37
ZJS
944char** strv_reverse(char **l) {
945 size_t n;
e1dd6790
LP
946
947 n = strv_length(l);
948 if (n <= 1)
949 return l;
950
14337c37 951 for (size_t i = 0; i < n / 2; i++)
8a3134b2 952 SWAP_TWO(l[i], l[n-1-i]);
e1dd6790
LP
953
954 return l;
955}
bceccd5e 956
14337c37 957char** strv_shell_escape(char **l, const char *bad) {
04c14b25
RM
958 /* Escapes every character in every string in l that is in bad,
959 * edits in-place, does not roll-back on error. */
960
961 STRV_FOREACH(s, l) {
962 char *v;
963
964 v = shell_escape(*s, bad);
965 if (!v)
966 return NULL;
967
d6f2cd67 968 free_and_replace(*s, v);
04c14b25
RM
969 }
970
971 return l;
972}
973
bcfc0e88
LP
974bool strv_fnmatch_full(
975 char* const* patterns,
976 const char *s,
977 int flags,
978 size_t *ret_matched_pos) {
979
980 assert(s);
981
982 if (patterns)
983 for (size_t i = 0; patterns[i]; i++)
984 /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
985 * process the pattern for some reason we'll consider this equivalent to non-matching. */
986 if (fnmatch(patterns[i], s, flags) == 0) {
987 if (ret_matched_pos)
988 *ret_matched_pos = i;
989 return true;
990 }
991
992 if (ret_matched_pos)
993 *ret_matched_pos = SIZE_MAX;
bceccd5e
ZJS
994
995 return false;
996}
fe382237 997
14337c37 998char** strv_skip(char **l, size_t n) {
e3ead6bb
LP
999 while (n > 0) {
1000 if (strv_isempty(l))
55a8d118 1001 return NULL;
e3ead6bb
LP
1002
1003 l++, n--;
1004 }
1005
55a8d118
ZJS
1006 /* To simplify callers, always return NULL instead of a zero-item array. */
1007 if (strv_isempty(l))
1008 return NULL;
e3ead6bb
LP
1009 return l;
1010}
8dd4c05b
LP
1011
1012int strv_extend_n(char ***l, const char *value, size_t n) {
14337c37 1013 size_t i, k;
8dd4c05b
LP
1014 char **nl;
1015
1016 assert(l);
1017
1018 if (!value)
1019 return 0;
1020 if (n == 0)
1021 return 0;
1022
61233823 1023 /* Adds the value n times to l */
8dd4c05b
LP
1024
1025 k = strv_length(*l);
60476376
LP
1026 if (n >= SIZE_MAX - k)
1027 return -ENOMEM;
8dd4c05b 1028
60476376 1029 nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
8dd4c05b
LP
1030 if (!nl)
1031 return -ENOMEM;
1032
1033 *l = nl;
1034
1035 for (i = k; i < k + n; i++) {
1036 nl[i] = strdup(value);
1037 if (!nl[i])
1038 goto rollback;
1039 }
8dd4c05b 1040 nl[i] = NULL;
14337c37 1041
8dd4c05b
LP
1042 return 0;
1043
1044rollback:
14337c37 1045 for (size_t j = k; j < i; j++)
8dd4c05b 1046 free(nl[j]);
8dd4c05b 1047 nl[k] = NULL;
14337c37 1048
5b700370 1049 return -ENOMEM;
8dd4c05b 1050}
3df9bec5 1051
6658f7c7
DDM
1052int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
1053 char *j;
1054
1055 assert(l);
1056 assert(lhs);
1057
1058 if (!rhs) /* value is optional, in which case we suppress the field */
1059 return 0;
1060
1061 j = strjoin(lhs, "=", rhs);
1062 if (!j)
1063 return -ENOMEM;
1064
1065 return strv_consume(l, j);
1066}
1067
479ddcdf 1068int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
3df9bec5 1069 bool b = false;
3df9bec5
LP
1070 int r;
1071
215286a4
MY
1072 assert(f);
1073
3df9bec5
LP
1074 /* Like fputs(), but for strv, and with a less stupid argument order */
1075
3df9bec5
LP
1076 if (!space)
1077 space = &b;
1078
1079 STRV_FOREACH(s, l) {
215286a4 1080 r = fputs_with_separator(f, *s, separator, space);
3df9bec5
LP
1081 if (r < 0)
1082 return r;
3df9bec5
LP
1083 }
1084
1085 return 0;
1086}
cde79109 1087
c540875c
YW
1088void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
1089 assert(key);
1090
1091 if (value) {
1092 char **l = hashmap_get(h, key);
1093 if (!l)
1094 return;
1095
1096 strv_remove(l, value);
1097 if (!strv_isempty(l))
1098 return;
1099 }
1100
1101 _unused_ _cleanup_free_ char *key_free = NULL;
1102 strv_free(hashmap_remove2(h, key, (void**) &key_free));
1103}
1104
0c15577a
DDM
1105void string_strv_ordered_hashmap_remove(OrderedHashmap *h, const char *key, const char *value) {
1106 string_strv_hashmap_remove(PLAIN_HASHMAP(h), key, value);
1107}
1108
cde79109
ZJS
1109static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
1110 char **l;
1111 int r;
1112
527b9e24
MY
1113 assert(h);
1114 assert(key);
1115 assert(value);
1116
cde79109
ZJS
1117 l = hashmap_get(h, key);
1118 if (l) {
1119 /* A list for this key already exists, let's append to it if it is not listed yet */
1120 if (strv_contains(l, value))
1121 return 0;
1122
1123 r = strv_extend(&l, value);
1124 if (r < 0)
1125 return r;
1126
1127 assert_se(hashmap_update(h, key, l) >= 0);
1128 } else {
1129 /* No list for this key exists yet, create one */
1130 _cleanup_strv_free_ char **l2 = NULL;
1131 _cleanup_free_ char *t = NULL;
1132
1133 t = strdup(key);
1134 if (!t)
1135 return -ENOMEM;
1136
1137 r = strv_extend(&l2, value);
1138 if (r < 0)
1139 return r;
1140
1141 r = hashmap_put(h, t, l2);
1142 if (r < 0)
1143 return r;
527b9e24 1144
cde79109
ZJS
1145 TAKE_PTR(t);
1146 TAKE_PTR(l2);
1147 }
1148
1149 return 1;
1150}
1151
c09ce222 1152int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
cde79109
ZJS
1153 int r;
1154
527b9e24
MY
1155 assert(h);
1156 assert(key);
1157 assert(value);
1158
c09ce222 1159 r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
cde79109
ZJS
1160 if (r < 0)
1161 return r;
1162
1163 return string_strv_hashmap_put_internal(*h, key, value);
1164}
1165
c09ce222 1166int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
cde79109
ZJS
1167 int r;
1168
527b9e24
MY
1169 assert(h);
1170 assert(key);
1171 assert(value);
1172
c09ce222 1173 r = ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
cde79109
ZJS
1174 if (r < 0)
1175 return r;
1176
1177 return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
1178}
1179
aca09301
LP
1180int strv_rebreak_lines(char **l, size_t width, char ***ret) {
1181 _cleanup_strv_free_ char **broken = NULL;
1182 int r;
1183
1184 assert(ret);
1185
1186 /* Implements a simple UTF-8 line breaking algorithm
1187 *
1188 * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1189 * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1190 * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1191
1192 if (width == SIZE_MAX) { /* NOP? */
1193 broken = strv_copy(l);
1194 if (!broken)
1195 return -ENOMEM;
1196
1197 *ret = TAKE_PTR(broken);
1198 return 0;
1199 }
1200
1201 STRV_FOREACH(i, l) {
1202 const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1203 bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1204 size_t w = 0;
1205
1206 for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
1207 if (strchr(NEWLINE, *p)) {
1208 in_prefix = true;
1209 whitespace_begin = whitespace_end = NULL;
1210 w = 0;
1211 } else if (strchr(WHITESPACE, *p)) {
1212 if (!in_prefix && (!whitespace_begin || whitespace_end)) {
1213 whitespace_begin = p;
1214 whitespace_end = NULL;
1215 }
1216 } else {
1217 if (whitespace_begin && !whitespace_end)
1218 whitespace_end = p;
1219
1220 in_prefix = false;
1221 }
1222
1223 int cw = utf8_char_console_width(p);
1224 if (cw < 0) {
1225 log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
1226 cw = 1;
1227 }
1228
1229 w += cw;
1230
1231 if (w > width && whitespace_begin && whitespace_end) {
1232 _cleanup_free_ char *truncated = NULL;
1233
1234 truncated = strndup(start, whitespace_begin - start);
1235 if (!truncated)
1236 return -ENOMEM;
1237
1238 r = strv_consume(&broken, TAKE_PTR(truncated));
1239 if (r < 0)
1240 return r;
1241
1242 p = start = whitespace_end;
1243 whitespace_begin = whitespace_end = NULL;
1244 w = cw;
1245 }
1246 }
1247
f2d2aa09
YW
1248 /* Process rest of the line */
1249 assert(start);
1250 if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
1251 r = strv_extend(&broken, "");
1252 else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
1253 _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
1254 if (!truncated)
1255 return -ENOMEM;
1256
1257 r = strv_consume(&broken, TAKE_PTR(truncated));
1258 } else /* Otherwise use line as is */
1259 r = strv_extend(&broken, start);
1260 if (r < 0)
1261 return r;
aca09301
LP
1262 }
1263
1264 *ret = TAKE_PTR(broken);
1265 return 0;
1266}
428146dc 1267
3ab19c1f 1268char** strv_filter_prefix(char * const *l, const char *prefix) {
428146dc
LP
1269
1270 /* Allocates a copy of 'l', but only copies over entries starting with 'prefix' */
1271
1272 if (isempty(prefix))
1273 return strv_copy(l);
1274
3ab19c1f 1275 _cleanup_strv_free_ char **f = NULL;
428146dc
LP
1276 size_t sz = 0;
1277
1278 STRV_FOREACH(i, l) {
1279 if (!startswith(*i, prefix))
1280 continue;
1281
1282 if (strv_extend_with_size(&f, &sz, *i) < 0)
1283 return NULL;
1284 }
1285
1286 return TAKE_PTR(f);
1287}