]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/basic/strv.c
networkd-test: stop varlink socket before setting up runtime directories
[thirdparty/systemd.git] / src / basic / strv.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <fnmatch.h>
4#include <stdio.h>
5
6#include "alloc-util.h"
7#include "env-util.h"
8#include "escape.h"
9#include "extract-word.h"
10#include "fileio.h"
11#include "gunicode.h"
12#include "hashmap.h"
13#include "log.h"
14#include "memory-util.h"
15#include "sort-util.h"
16#include "string-util.h"
17#include "strv.h"
18#include "utf8.h"
19
20char* strv_find(char * const *l, const char *name) {
21 assert(name);
22
23 STRV_FOREACH(i, l)
24 if (streq(*i, name))
25 return *i;
26
27 return NULL;
28}
29
30char* strv_find_case(char * const *l, const char *name) {
31 assert(name);
32
33 STRV_FOREACH(i, l)
34 if (strcaseeq(*i, name))
35 return *i;
36
37 return NULL;
38}
39
40char* strv_find_prefix(char * const *l, const char *name) {
41 assert(name);
42
43 STRV_FOREACH(i, l)
44 if (startswith(*i, name))
45 return *i;
46
47 return NULL;
48}
49
50char* strv_find_startswith(char * const *l, const char *name) {
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) {
57 char *e;
58
59 e = startswith(*i, name);
60 if (e)
61 return e;
62 }
63
64 return NULL;
65}
66
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) {
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
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
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
139char** strv_free(char **l) {
140 STRV_FOREACH(k, l)
141 free(*k);
142
143 return mfree(l);
144}
145
146char** strv_free_erase(char **l) {
147 STRV_FOREACH(i, l)
148 erase_and_freep(i);
149
150 return mfree(l);
151}
152
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
162char** strv_copy_n(char * const *l, size_t m) {
163 _cleanup_strv_free_ char **result = NULL;
164 char **k;
165
166 result = new(char*, MIN(strv_length(l), m) + 1);
167 if (!result)
168 return NULL;
169
170 k = result;
171 STRV_FOREACH(i, l) {
172 if (m == 0)
173 break;
174
175 *k = strdup(*i);
176 if (!*k)
177 return NULL;
178 k++;
179
180 if (m != SIZE_MAX)
181 m--;
182 }
183
184 *k = NULL;
185 return TAKE_PTR(result);
186}
187
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
204size_t strv_length(char * const *l) {
205 size_t n = 0;
206
207 STRV_FOREACH(i, l)
208 n++;
209
210 return n;
211}
212
213char** strv_new_ap(const char *x, va_list ap) {
214 _cleanup_strv_free_ char **a = NULL;
215 size_t n = 0, i = 0;
216 va_list aq;
217
218 /* As a special trick we ignore all listed strings that equal
219 * STRV_IGNORE. This is supposed to be used with the
220 * STRV_IFNOTNULL() macro to include possibly NULL strings in
221 * the string list. */
222
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;
227
228 n++;
229 }
230 va_end(aq);
231
232 a = new(char*, n+1);
233 if (!a)
234 return NULL;
235
236 for (const char *s = x; s; s = va_arg(ap, const char*)) {
237 if (s == STRV_IGNORE)
238 continue;
239
240 a[i] = strdup(s);
241 if (!a[i])
242 return NULL;
243
244 i++;
245 }
246
247 a[i] = NULL;
248
249 return TAKE_PTR(a);
250}
251
252char** strv_new_internal(const char *x, ...) {
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
263int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
264 size_t p, q, i = 0;
265
266 assert(a);
267
268 q = strv_length(b);
269 if (q == 0)
270 return 0;
271
272 p = strv_length(*a);
273 if (p >= SIZE_MAX - q)
274 return -ENOMEM;
275
276 char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
277 if (!t)
278 return -ENOMEM;
279
280 t[p] = NULL;
281 *a = t;
282
283 STRV_FOREACH(s, b) {
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;
293 }
294
295 assert(i <= q);
296
297 return (int) i;
298
299rollback:
300 free_many_charp(t + p, i);
301 t[p] = NULL;
302 return -ENOMEM;
303}
304
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
361int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix) {
362 int r;
363
364 assert(a);
365
366 STRV_FOREACH(s, b) {
367 char *v;
368
369 v = strjoin(strempty(prefix), *s, suffix);
370 if (!v)
371 return -ENOMEM;
372
373 r = strv_consume(a, v);
374 if (r < 0)
375 return r;
376 }
377
378 return 0;
379}
380
381int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) {
382 _cleanup_strv_free_ char **l = NULL;
383 size_t n;
384 int r;
385
386 assert(s);
387
388 /* Special version of strv_split_full() that splits on newlines and
389 * suppresses an empty string at the end. */
390
391 r = strv_split_full(&l, s, NEWLINE, flags);
392 if (r < 0)
393 return r;
394
395 n = strv_length(l);
396 if (n > 0 && isempty(l[n - 1])) {
397 l[n - 1] = mfree(l[n - 1]);
398 n--;
399 }
400
401 *ret = TAKE_PTR(l);
402 return n;
403}
404
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
414int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
415 _cleanup_strv_free_ char **l = NULL;
416 size_t n = 0;
417 int r;
418
419 assert(t);
420 assert(s);
421
422 for (;;) {
423 _cleanup_free_ char *word = NULL;
424
425 r = extract_first_word(&s, &word, separators, flags);
426 if (r < 0)
427 return r;
428 if (r == 0)
429 break;
430
431 if (!GREEDY_REALLOC(l, n + 2))
432 return -ENOMEM;
433
434 l[n++] = TAKE_PTR(word);
435 l[n] = NULL;
436 }
437
438 if (!l) {
439 l = new0(char*, 1);
440 if (!l)
441 return -ENOMEM;
442 }
443
444 *t = TAKE_PTR(l);
445
446 return (int) n;
447}
448
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
458int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
459 char **l;
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
469 r = strv_extend_strv_consume(t, l, filter_duplicates);
470 if (r < 0)
471 return r;
472
473 return (int) strv_length(*t);
474}
475
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
480int strv_split_colon_pairs(char ***t, const char *s) {
481 _cleanup_strv_free_ char **l = NULL;
482 size_t n = 0;
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,
499 &first, &second);
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
512 if (!GREEDY_REALLOC(l, n + 3))
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
532char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) {
533 char *r, *e;
534 size_t n, k, m;
535
536 if (!separator)
537 separator = " ";
538
539 k = strlen(separator);
540 m = strlen_ptr(prefix);
541
542 if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */
543 assert(k == 1);
544
545 n = 0;
546 STRV_FOREACH(s, l) {
547 if (s != l)
548 n += k;
549
550 bool needs_escaping = escape_separator && strchr(*s, *separator);
551
552 n += m + strlen(*s) * (1 + needs_escaping);
553 }
554
555 r = new(char, n+1);
556 if (!r)
557 return NULL;
558
559 e = r;
560 STRV_FOREACH(s, l) {
561 if (s != l)
562 e = stpcpy(e, separator);
563
564 if (prefix)
565 e = stpcpy(e, prefix);
566
567 bool needs_escaping = escape_separator && strchr(*s, *separator);
568
569 if (needs_escaping)
570 for (size_t i = 0; (*s)[i]; i++) {
571 if ((*s)[i] == *separator)
572 *(e++) = '\\';
573 *(e++) = (*s)[i];
574 }
575 else
576 e = stpcpy(e, *s);
577 }
578
579 *e = 0;
580
581 return r;
582}
583
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. */
589
590 if (!value)
591 return 0;
592
593 size_t size = n ? *n : SIZE_MAX;
594 if (size == SIZE_MAX)
595 size = strv_length(*l);
596
597 /* Check for overflow */
598 if (size > SIZE_MAX-2)
599 return -ENOMEM;
600
601 char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
602 if (!c)
603 return -ENOMEM;
604
605 c[size] = value;
606 c[size+1] = NULL;
607
608 *l = c;
609 if (n)
610 *n = size + 1;
611 return 0;
612}
613
614int strv_push_pair(char ***l, char *a, char *b) {
615 char **c;
616 size_t n;
617
618 if (!a && !b)
619 return 0;
620
621 n = strv_length(*l);
622
623 /* Check for overflow */
624 if (n > SIZE_MAX-3)
625 return -ENOMEM;
626
627 /* increase and check for overflow */
628 c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*));
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
642int strv_insert(char ***l, size_t position, char *value) {
643 char **c;
644 size_t n, m;
645
646 assert(l);
647
648 if (!value)
649 return 0;
650
651 n = strv_length(*l);
652 position = MIN(position, n);
653
654 /* check for overflow and increase */
655 if (n > SIZE_MAX - 2)
656 return -ENOMEM;
657 m = n + 2;
658
659 c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
660 if (!c)
661 return -ENOMEM;
662
663 if (n > position)
664 memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
665
666 c[position] = value;
667 c[n + 1] = NULL;
668
669 *l = c;
670 return 0;
671}
672
673int strv_consume_with_size(char ***l, size_t *n, char *value) {
674 int r;
675
676 r = strv_push_with_size(l, n, value);
677 if (r < 0)
678 free(value);
679
680 return r;
681}
682
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
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
702 return r;
703}
704
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
718int strv_extend_with_size(char ***l, size_t *n, const char *value) {
719 char *v;
720
721 if (!value)
722 return 0;
723
724 v = strdup(value);
725 if (!v)
726 return -ENOMEM;
727
728 return strv_consume_with_size(l, n, v);
729}
730
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
791char** strv_uniq(char **l) {
792 /* Drops duplicate entries. The first identical string will be
793 * kept, the others dropped */
794
795 STRV_FOREACH(i, l)
796 strv_remove(i+1, *i);
797
798 return l;
799}
800
801bool strv_is_uniq(char * const *l) {
802 STRV_FOREACH(i, l)
803 if (strv_contains(i+1, *i))
804 return false;
805
806 return true;
807}
808
809char** strv_remove(char **l, const char *s) {
810 char **f, **t;
811
812 if (!l)
813 return NULL;
814
815 assert(s);
816
817 /* Drops every occurrence of s in the string list, edits
818 * in-place. */
819
820 for (f = t = l; *f; f++)
821 if (streq(*f, s))
822 free(*f);
823 else
824 *(t++) = *f;
825
826 *t = NULL;
827 return l;
828}
829
830bool strv_overlap(char * const *a, char * const *b) {
831 STRV_FOREACH(i, a)
832 if (strv_contains(b, *i))
833 return true;
834
835 return false;
836}
837
838static int str_compare(char * const *a, char * const *b) {
839 return strcmp(*a, *b);
840}
841
842char** strv_sort(char **l) {
843 typesafe_qsort(l, strv_length(l), str_compare);
844 return l;
845}
846
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
862int strv_compare(char * const *a, char * const *b) {
863 int r;
864
865 if (strv_isempty(a)) {
866 if (strv_isempty(b))
867 return 0;
868 else
869 return -1;
870 }
871
872 if (strv_isempty(b))
873 return 1;
874
875 for ( ; *a || *b; ++a, ++b) {
876 r = strcmp_ptr(*a, *b);
877 if (r != 0)
878 return r;
879 }
880
881 return 0;
882}
883
884bool strv_equal_ignore_order(char * const *a, char * const *b) {
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
888 * consists of repetitions). */
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
904void strv_print_full(char * const *l, const char *prefix) {
905 STRV_FOREACH(s, l)
906 printf("%s%s\n", strempty(prefix), *s);
907}
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}
923
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
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
944char** strv_reverse(char **l) {
945 size_t n;
946
947 n = strv_length(l);
948 if (n <= 1)
949 return l;
950
951 for (size_t i = 0; i < n / 2; i++)
952 SWAP_TWO(l[i], l[n-1-i]);
953
954 return l;
955}
956
957char** strv_shell_escape(char **l, const char *bad) {
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
968 free_and_replace(*s, v);
969 }
970
971 return l;
972}
973
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;
994
995 return false;
996}
997
998char** strv_skip(char **l, size_t n) {
999 while (n > 0) {
1000 if (strv_isempty(l))
1001 return NULL;
1002
1003 l++, n--;
1004 }
1005
1006 /* To simplify callers, always return NULL instead of a zero-item array. */
1007 if (strv_isempty(l))
1008 return NULL;
1009 return l;
1010}
1011
1012int strv_extend_n(char ***l, const char *value, size_t n) {
1013 size_t i, k;
1014 char **nl;
1015
1016 assert(l);
1017
1018 if (!value)
1019 return 0;
1020 if (n == 0)
1021 return 0;
1022
1023 /* Adds the value n times to l */
1024
1025 k = strv_length(*l);
1026 if (n >= SIZE_MAX - k)
1027 return -ENOMEM;
1028
1029 nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
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 }
1040 nl[i] = NULL;
1041
1042 return 0;
1043
1044rollback:
1045 for (size_t j = k; j < i; j++)
1046 free(nl[j]);
1047 nl[k] = NULL;
1048
1049 return -ENOMEM;
1050}
1051
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
1068int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
1069 bool b = false;
1070 int r;
1071
1072 assert(f);
1073
1074 /* Like fputs(), but for strv, and with a less stupid argument order */
1075
1076 if (!space)
1077 space = &b;
1078
1079 STRV_FOREACH(s, l) {
1080 r = fputs_with_separator(f, *s, separator, space);
1081 if (r < 0)
1082 return r;
1083 }
1084
1085 return 0;
1086}
1087
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
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
1109static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
1110 char **l;
1111 int r;
1112
1113 assert(h);
1114 assert(key);
1115 assert(value);
1116
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;
1144
1145 TAKE_PTR(t);
1146 TAKE_PTR(l2);
1147 }
1148
1149 return 1;
1150}
1151
1152int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
1153 int r;
1154
1155 assert(h);
1156 assert(key);
1157 assert(value);
1158
1159 r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
1160 if (r < 0)
1161 return r;
1162
1163 return string_strv_hashmap_put_internal(*h, key, value);
1164}
1165
1166int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
1167 int r;
1168
1169 assert(h);
1170 assert(key);
1171 assert(value);
1172
1173 r = ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
1174 if (r < 0)
1175 return r;
1176
1177 return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
1178}
1179
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
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;
1262 }
1263
1264 *ret = TAKE_PTR(broken);
1265 return 0;
1266}
1267
1268char** strv_filter_prefix(char * const *l, const char *prefix) {
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
1275 _cleanup_strv_free_ char **f = NULL;
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}