From 63a8ad2ead586c09c0f551c24ef0ecb8f2172d26 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 12 Mar 2025 12:03:06 +1000 Subject: [PATCH] util: add some extra strv helpers Part-of: --- src/util-strings.c | 57 ++++++++++++++++++++++++++++++++++++ src/util-strings.h | 9 ++++++ test/test-utils.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) diff --git a/src/util-strings.c b/src/util-strings.c index d68b2746..e7e68163 100644 --- a/src/util-strings.c +++ b/src/util-strings.c @@ -62,6 +62,63 @@ next_word(const char **state, size_t *len, const char *separators) return next; } +size_t +strv_len(char **strv) +{ + if (!strv) + return 0; + + size_t size = 1; + while (*strv) { + size++; + strv++; + } + return size; +} + +char ** +strv_append_vprintf(char **strv, const char *fmt, va_list args) +{ + char *dup = strdup_vprintf(fmt, args); + char **s = strv_append_take(strv, &dup); + return s; +} + +char ** +strv_append_printf(char **strv, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + char **s = strv_append_vprintf(strv, fmt, args); + va_end(args); + return s; +} + +char ** +strv_append_strdup(char **strv, const char *str) +{ + char *dup = safe_strdup(str); + return strv_append_take(strv, &dup); +} + +char ** +strv_append_take(char **strv, char **str) +{ + if (str && *str) { + size_t len = max(strv_len(strv) + 1, 2); + + char **s = realloc(strv, len * sizeof(*strv)); + if (!s) + abort(); + s[len - 1] = NULL; + s[len - 2] = *str; + *str = NULL; + return s; + } else { + return strv; + } +} + /** * Return a null-terminated string array with the contents of argv * duplicated. diff --git a/src/util-strings.h b/src/util-strings.h index 4c9a3ae1..190cae11 100644 --- a/src/util-strings.h +++ b/src/util-strings.h @@ -301,9 +301,18 @@ safe_atod(const char *str, double *val) return true; } +/* Returns the length of the strv, including the terminating NULL */ +size_t strv_len(char **strv); char **strv_from_argv(int argc, char **argv); char **strv_from_string(const char *in, const char *separator, size_t *num_elements); char *strv_join(char **strv, const char *joiner); +char **strv_append_strdup(char **strv, const char *s); +/* Takes ownership of the string and appends it to strv, s is set to NULL */ +char **strv_append_take(char **strv, char **s); +__attribute__ ((format (printf, 2, 3))) +char **strv_append_printf(char **strv, const char *fmt, ...); +__attribute__ ((format (printf, 2, 0))) +char **strv_append_vprintf(char **strv, const char *fmt, va_list args); typedef int (*strv_foreach_callback_t)(const char *str, size_t index, void *data); int strv_for_each(const char **strv, strv_foreach_callback_t func, void *data); diff --git a/test/test-utils.c b/test/test-utils.c index 6bafebe4..50662060 100644 --- a/test/test-utils.c +++ b/test/test-utils.c @@ -1207,6 +1207,77 @@ START_TEST(strv_for_each_test) } END_TEST +__attribute__ ((format (printf, 1, 0))) +static char ** +test_strv_appendv(char *format, ...) +{ + va_list args; + va_start(args, format); + char **strv = NULL; + strv = strv_append_vprintf(strv, "%s %d", args); + va_end(args); + return strv; +} + +START_TEST(strv_append_test) +{ + { + char *test_strv1[] = {"a", "b", "c", NULL}; + char **test_strv2 = NULL; + + litest_assert_int_eq(strv_len(test_strv1), 4U); + litest_assert_int_eq(strv_len(test_strv2), 0U); + } + { + char **strv = NULL; + char *dup = safe_strdup("test"); + strv = strv_append_take(strv, &dup); + litest_assert_ptr_null(dup); + litest_assert_ptr_notnull(strv); + litest_assert_str_eq(strv[0], "test"); + litest_assert_ptr_eq(strv[1], NULL); + litest_assert_int_eq(strv_len(strv), 2U); + + char *dup2 = safe_strdup("test2"); + strv = strv_append_take(strv, &dup2); + litest_assert_ptr_null(dup2); + litest_assert_str_eq(strv[1], "test2"); + litest_assert_ptr_eq(strv[2], NULL); + litest_assert_int_eq(strv_len(strv), 3U); + + strv = strv_append_take(strv, NULL); + litest_assert_int_eq(strv_len(strv), 3U); + strv_free(strv); + } + { + char **strv = NULL; + strv = strv_append_strdup(strv, "banana"); + litest_assert(strv != NULL); + litest_assert_str_eq(strv[0], "banana"); + litest_assert_ptr_null(strv[1]); + litest_assert_int_eq(strv_len(strv), 2U); + strv_free(strv); + } + { + char **strv = test_strv_appendv("%s %d", "apple", 2); + litest_assert_ptr_notnull(strv); + litest_assert_str_eq(strv[0], "apple 2"); + litest_assert_ptr_null(strv[1]); + litest_assert_int_eq(strv_len(strv), 2U); + strv_free(strv); + } + { + char **strv = NULL; + strv = strv_append_printf(strv, "coco%s", "nut"); + litest_assert_ptr_notnull(strv); + litest_assert_str_eq(strv[0], "coconut"); + litest_assert_ptr_null(strv[1]); + litest_assert_int_eq(strv_len(strv), 2U); + strv_free(strv); + } +} +END_TEST + START_TEST(double_array_from_string_test) { struct double_array_from_string_test { @@ -2092,6 +2163,7 @@ int main(void) ADD_TEST(safe_atod_test); ADD_TEST(strsplit_test); ADD_TEST(strv_for_each_test); + ADD_TEST(strv_append_test); ADD_TEST(double_array_from_string_test); ADD_TEST(strargv_test); ADD_TEST(kvsplit_double_test);