1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
12 #include "extract-word.h"
15 #include "memory-util.h"
16 #include "nulstr-util.h"
17 #include "sort-util.h"
18 #include "string-util.h"
22 char* strv_find(char * const *l
, const char *name
) {
32 char* strv_find_case(char * const *l
, const char *name
) {
36 if (strcaseeq(*i
, name
))
42 char* strv_find_prefix(char * const *l
, const char *name
) {
46 if (startswith(*i
, name
))
52 char* strv_find_startswith(char * const *l
, const char *name
) {
55 /* Like strv_find_prefix, but actually returns only the
56 * suffix, not the whole item */
61 e
= startswith(*i
, name
);
69 char* strv_find_first_field(char * const *needles
, char * const *haystack
) {
70 STRV_FOREACH(k
, needles
) {
71 char *value
= strv_env_pairs_get((char **)haystack
, *k
);
79 char** strv_free(char **l
) {
86 char** strv_free_erase(char **l
) {
93 void strv_free_many(char ***strvs
, size_t n
) {
94 assert(strvs
|| n
== 0);
96 FOREACH_ARRAY (i
, strvs
, n
)
102 char** strv_copy_n(char * const *l
, size_t m
) {
103 _cleanup_strv_free_
char **result
= NULL
;
106 result
= new(char*, MIN(strv_length(l
), m
) + 1);
125 return TAKE_PTR(result
);
128 int strv_copy_unless_empty(char * const *l
, char ***ret
) {
131 if (strv_isempty(l
)) {
136 char **copy
= strv_copy(l
);
140 *ret
= TAKE_PTR(copy
);
144 size_t strv_length(char * const *l
) {
153 char** strv_new_ap(const char *x
, va_list ap
) {
154 _cleanup_strv_free_
char **a
= NULL
;
158 /* As a special trick we ignore all listed strings that equal
159 * STRV_IGNORE. This is supposed to be used with the
160 * STRV_IFNOTNULL() macro to include possibly NULL strings in
161 * the string list. */
164 for (const char *s
= x
; s
; s
= va_arg(aq
, const char*)) {
165 if (s
== STRV_IGNORE
)
176 for (const char *s
= x
; s
; s
= va_arg(ap
, const char*)) {
177 if (s
== STRV_IGNORE
)
192 char** strv_new_internal(const char *x
, ...) {
197 r
= strv_new_ap(x
, ap
);
203 int strv_extend_strv(char ***a
, char * const *b
, bool filter_duplicates
) {
215 if (p
>= SIZE_MAX
- q
)
218 t
= reallocarray(*a
, GREEDY_ALLOC_ROUND_UP(p
+ q
+ 1), sizeof(char *));
226 if (filter_duplicates
&& strv_contains(t
, *s
))
242 free_many_charp(t
+ p
, i
);
247 int strv_extend_strv_biconcat(char ***a
, const char *prefix
, const char* const *b
, const char *suffix
) {
253 v
= strjoin(strempty(prefix
), *s
, suffix
);
257 r
= strv_consume(a
, v
);
265 int strv_split_newlines_full(char ***ret
, const char *s
, ExtractFlags flags
) {
266 _cleanup_strv_free_
char **l
= NULL
;
272 /* Special version of strv_split_full() that splits on newlines and
273 * suppresses an empty string at the end. */
275 r
= strv_split_full(&l
, s
, NEWLINE
, flags
);
280 if (n
> 0 && isempty(l
[n
- 1])) {
281 l
[n
- 1] = mfree(l
[n
- 1]);
289 int strv_split_full(char ***t
, const char *s
, const char *separators
, ExtractFlags flags
) {
290 _cleanup_strv_free_
char **l
= NULL
;
298 _cleanup_free_
char *word
= NULL
;
300 r
= extract_first_word(&s
, &word
, separators
, flags
);
306 if (!GREEDY_REALLOC(l
, n
+ 2))
309 l
[n
++] = TAKE_PTR(word
);
324 int strv_split_and_extend_full(char ***t
, const char *s
, const char *separators
, bool filter_duplicates
, ExtractFlags flags
) {
325 _cleanup_strv_free_
char **l
= NULL
;
331 r
= strv_split_full(&l
, s
, separators
, flags
);
335 r
= strv_extend_strv(t
, l
, filter_duplicates
);
339 return (int) strv_length(*t
);
342 int strv_split_colon_pairs(char ***t
, const char *s
) {
343 _cleanup_strv_free_
char **l
= NULL
;
351 _cleanup_free_
char *first
= NULL
, *second
= NULL
, *tuple
= NULL
, *second_or_empty
= NULL
;
353 r
= extract_first_word(&s
, &tuple
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RETAIN_ESCAPE
);
359 const char *p
= tuple
;
360 r
= extract_many_words(&p
, ":", EXTRACT_CUNESCAPE
|EXTRACT_UNESCAPE_SEPARATORS
,
366 /* Enforce that at most 2 colon-separated words are contained in each group */
370 second_or_empty
= strdup(strempty(second
));
371 if (!second_or_empty
)
374 if (!GREEDY_REALLOC(l
, n
+ 3))
377 l
[n
++] = TAKE_PTR(first
);
378 l
[n
++] = TAKE_PTR(second_or_empty
);
394 char* strv_join_full(char * const *l
, const char *separator
, const char *prefix
, bool escape_separator
) {
401 k
= strlen(separator
);
402 m
= strlen_ptr(prefix
);
404 if (escape_separator
) /* If the separator was multi-char, we wouldn't know how to escape it. */
412 bool needs_escaping
= escape_separator
&& strchr(*s
, *separator
);
414 n
+= m
+ strlen(*s
) * (1 + needs_escaping
);
424 e
= stpcpy(e
, separator
);
427 e
= stpcpy(e
, prefix
);
429 bool needs_escaping
= escape_separator
&& strchr(*s
, *separator
);
432 for (size_t i
= 0; (*s
)[i
]; i
++) {
433 if ((*s
)[i
] == *separator
)
446 int strv_push_with_size(char ***l
, size_t *n
, char *value
) {
447 /* n is a pointer to a variable to store the size of l.
448 * If not given (i.e. n is NULL or *n is SIZE_MAX), size will be calculated using strv_length().
449 * If n is not NULL, the size after the push will be returned.
450 * If value is empty, no action is taken and *n is not set. */
455 size_t size
= n
? *n
: SIZE_MAX
;
456 if (size
== SIZE_MAX
)
457 size
= strv_length(*l
);
459 /* Check for overflow */
460 if (size
> SIZE_MAX
-2)
463 char **c
= reallocarray(*l
, GREEDY_ALLOC_ROUND_UP(size
+ 2), sizeof(char*));
476 int strv_push_pair(char ***l
, char *a
, char *b
) {
485 /* Check for overflow */
489 /* increase and check for overflow */
490 c
= reallocarray(*l
, GREEDY_ALLOC_ROUND_UP(n
+ !!a
+ !!b
+ 1), sizeof(char*));
504 int strv_insert(char ***l
, size_t position
, char *value
) {
514 position
= MIN(position
, n
);
516 /* check for overflow and increase*/
517 if (n
> SIZE_MAX
- 2)
521 c
= reallocarray(*l
, GREEDY_ALLOC_ROUND_UP(m
), sizeof(char*));
526 memmove(c
+ position
+ 1, c
+ position
, (n
- position
) * sizeof(char*));
535 int strv_consume_with_size(char ***l
, size_t *n
, char *value
) {
538 r
= strv_push_with_size(l
, n
, value
);
545 int strv_consume_pair(char ***l
, char *a
, char *b
) {
548 r
= strv_push_pair(l
, a
, b
);
557 int strv_consume_prepend(char ***l
, char *value
) {
560 r
= strv_push_prepend(l
, value
);
567 int strv_prepend(char ***l
, const char *value
) {
577 return strv_consume_prepend(l
, v
);
580 int strv_extend_with_size(char ***l
, size_t *n
, const char *value
) {
590 return strv_consume_with_size(l
, n
, v
);
593 int strv_extend_many_internal(char ***l
, const char *value
, ...) {
600 m
= n
= strv_length(*l
);
604 for (const char *s
= value
; s
!= POINTER_MAX
; s
= va_arg(ap
, const char*)) {
608 if (m
> SIZE_MAX
-1) { /* overflow */
621 char **c
= reallocarray(*l
, GREEDY_ALLOC_ROUND_UP(m
+1), sizeof(char*));
629 for (const char *s
= value
; s
!= POINTER_MAX
; s
= va_arg(ap
, const char*)) {
643 /* rollback on error */
644 for (size_t j
= n
; j
< i
; j
++)
653 char** strv_uniq(char **l
) {
654 /* Drops duplicate entries. The first identical string will be
655 * kept, the others dropped */
658 strv_remove(i
+1, *i
);
663 bool strv_is_uniq(char * const *l
) {
665 if (strv_contains(i
+1, *i
))
671 char** strv_remove(char **l
, const char *s
) {
679 /* Drops every occurrence of s in the string list, edits
682 for (f
= t
= l
; *f
; f
++)
692 bool strv_overlap(char * const *a
, char * const *b
) {
694 if (strv_contains(b
, *i
))
700 static int str_compare(char * const *a
, char * const *b
) {
701 return strcmp(*a
, *b
);
704 char** strv_sort(char **l
) {
705 typesafe_qsort(l
, strv_length(l
), str_compare
);
709 char** strv_sort_uniq(char **l
) {
713 char **tail
= strv_sort(l
), *prev
= NULL
;
715 if (streq_ptr(*i
, prev
))
718 *(tail
++) = prev
= *i
;
724 int strv_compare(char * const *a
, char * const *b
) {
727 if (strv_isempty(a
)) {
737 for ( ; *a
|| *b
; ++a
, ++b
) {
738 r
= strcmp_ptr(*a
, *b
);
746 void strv_print_full(char * const *l
, const char *prefix
) {
748 printf("%s%s\n", strempty(prefix
), *s
);
751 int strv_extendf(char ***l
, const char *format
, ...) {
756 va_start(ap
, format
);
757 r
= vasprintf(&x
, format
, ap
);
763 return strv_consume(l
, x
);
766 char* startswith_strv(const char *s
, char * const *l
) {
768 char *found
= startswith(s
, *i
);
776 char* endswith_strv(const char *s
, char * const *l
) {
778 char *found
= endswith(s
, *i
);
786 char** strv_reverse(char **l
) {
793 for (size_t i
= 0; i
< n
/ 2; i
++)
794 SWAP_TWO(l
[i
], l
[n
-1-i
]);
799 char** strv_shell_escape(char **l
, const char *bad
) {
800 /* Escapes every character in every string in l that is in bad,
801 * edits in-place, does not roll-back on error. */
806 v
= shell_escape(*s
, bad
);
810 free_and_replace(*s
, v
);
816 bool strv_fnmatch_full(
817 char* const* patterns
,
820 size_t *ret_matched_pos
) {
825 for (size_t i
= 0; patterns
[i
]; i
++)
826 /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
827 * process the pattern for some reason we'll consider this equivalent to non-matching. */
828 if (fnmatch(patterns
[i
], s
, flags
) == 0) {
830 *ret_matched_pos
= i
;
835 *ret_matched_pos
= SIZE_MAX
;
840 char** strv_skip(char **l
, size_t n
) {
852 int strv_extend_n(char ***l
, const char *value
, size_t n
) {
863 /* Adds the value n times to l */
866 if (n
>= SIZE_MAX
- k
)
869 nl
= reallocarray(*l
, GREEDY_ALLOC_ROUND_UP(k
+ n
+ 1), sizeof(char *));
875 for (i
= k
; i
< k
+ n
; i
++) {
876 nl
[i
] = strdup(value
);
885 for (size_t j
= k
; j
< i
; j
++)
892 int strv_extend_assignment(char ***l
, const char *lhs
, const char *rhs
) {
898 if (!rhs
) /* value is optional, in which case we suppress the field */
901 j
= strjoin(lhs
, "=", rhs
);
905 return strv_consume(l
, j
);
908 int fputstrv(FILE *f
, char * const *l
, const char *separator
, bool *space
) {
914 /* Like fputs(), but for strv, and with a less stupid argument order */
920 r
= fputs_with_separator(f
, *s
, separator
, space
);
928 static int string_strv_hashmap_put_internal(Hashmap
*h
, const char *key
, const char *value
) {
932 l
= hashmap_get(h
, key
);
934 /* A list for this key already exists, let's append to it if it is not listed yet */
935 if (strv_contains(l
, value
))
938 r
= strv_extend(&l
, value
);
942 assert_se(hashmap_update(h
, key
, l
) >= 0);
944 /* No list for this key exists yet, create one */
945 _cleanup_strv_free_
char **l2
= NULL
;
946 _cleanup_free_
char *t
= NULL
;
952 r
= strv_extend(&l2
, value
);
956 r
= hashmap_put(h
, t
, l2
);
966 int _string_strv_hashmap_put(Hashmap
**h
, const char *key
, const char *value HASHMAP_DEBUG_PARAMS
) {
969 r
= _hashmap_ensure_allocated(h
, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS
);
973 return string_strv_hashmap_put_internal(*h
, key
, value
);
976 int _string_strv_ordered_hashmap_put(OrderedHashmap
**h
, const char *key
, const char *value HASHMAP_DEBUG_PARAMS
) {
979 r
= _ordered_hashmap_ensure_allocated(h
, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS
);
983 return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h
), key
, value
);
986 DEFINE_HASH_OPS_FULL(string_strv_hash_ops
, char, string_hash_func
, string_compare_func
, free
, char*, strv_free
);
988 int strv_rebreak_lines(char **l
, size_t width
, char ***ret
) {
989 _cleanup_strv_free_
char **broken
= NULL
;
994 /* Implements a simple UTF-8 line breaking algorithm
996 * Goes through all entries in *l, and line-breaks each line that is longer than the specified
997 * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
998 * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1000 if (width
== SIZE_MAX
) { /* NOP? */
1001 broken
= strv_copy(l
);
1005 *ret
= TAKE_PTR(broken
);
1009 STRV_FOREACH(i
, l
) {
1010 const char *start
= *i
, *whitespace_begin
= NULL
, *whitespace_end
= NULL
;
1011 bool in_prefix
= true; /* still in the whitespace in the beginning of the line? */
1014 for (const char *p
= start
; *p
!= 0; p
= utf8_next_char(p
)) {
1015 if (strchr(NEWLINE
, *p
)) {
1017 whitespace_begin
= whitespace_end
= NULL
;
1019 } else if (strchr(WHITESPACE
, *p
)) {
1020 if (!in_prefix
&& (!whitespace_begin
|| whitespace_end
)) {
1021 whitespace_begin
= p
;
1022 whitespace_end
= NULL
;
1025 if (whitespace_begin
&& !whitespace_end
)
1031 int cw
= utf8_char_console_width(p
);
1033 log_debug_errno(cw
, "Comment to line break contains invalid UTF-8, ignoring.");
1039 if (w
> width
&& whitespace_begin
&& whitespace_end
) {
1040 _cleanup_free_
char *truncated
= NULL
;
1042 truncated
= strndup(start
, whitespace_begin
- start
);
1046 r
= strv_consume(&broken
, TAKE_PTR(truncated
));
1050 p
= start
= whitespace_end
;
1051 whitespace_begin
= whitespace_end
= NULL
;
1056 /* Process rest of the line */
1058 if (in_prefix
) /* Never seen anything non-whitespace? Generate empty line! */
1059 r
= strv_extend(&broken
, "");
1060 else if (whitespace_begin
&& !whitespace_end
) { /* Ends in whitespace? Chop it off! */
1061 _cleanup_free_
char *truncated
= strndup(start
, whitespace_begin
- start
);
1065 r
= strv_consume(&broken
, TAKE_PTR(truncated
));
1066 } else /* Otherwise use line as is */
1067 r
= strv_extend(&broken
, start
);
1072 *ret
= TAKE_PTR(broken
);