From 9f6dfe43c8a55b833ae16486bcafe29b543461f9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 1 Aug 2025 15:04:18 -0700 Subject: string-list: align string_list_split() with its _in_place() counterpart The string_list_split_in_place() function was updated by 52acddf3 (string-list: multi-delimiter `string_list_split_in_place()`, 2023-04-24) to take more than one delimiter characters, hoping that we can later use it to replace our uses of strtok(). We however did not make a matching change to the string_list_split() function, which is very similar. Before giving both functions more features in future commits, allow string_list_split() to also take more than one delimiter characters to make them closer to each other. Signed-off-by: Junio C Hamano --- t/unit-tests/u-string-list.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 't/unit-tests/u-string-list.c') diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c index d4ba5f9fa5..150a5f505f 100644 --- a/t/unit-tests/u-string-list.c +++ b/t/unit-tests/u-string-list.c @@ -43,7 +43,7 @@ static void t_string_list_equal(struct string_list *list, expected_strings->items[i].string); } -static void t_string_list_split(const char *data, int delim, int maxsplit, ...) +static void t_string_list_split(const char *data, const char *delim, int maxsplit, ...) { struct string_list expected_strings = STRING_LIST_INIT_DUP; struct string_list list = STRING_LIST_INIT_DUP; @@ -65,13 +65,13 @@ static void t_string_list_split(const char *data, int delim, int maxsplit, ...) void test_string_list__split(void) { - t_string_list_split("foo:bar:baz", ':', -1, "foo", "bar", "baz", NULL); - t_string_list_split("foo:bar:baz", ':', 0, "foo:bar:baz", NULL); - t_string_list_split("foo:bar:baz", ':', 1, "foo", "bar:baz", NULL); - t_string_list_split("foo:bar:baz", ':', 2, "foo", "bar", "baz", NULL); - t_string_list_split("foo:bar:", ':', -1, "foo", "bar", "", NULL); - t_string_list_split("", ':', -1, "", NULL); - t_string_list_split(":", ':', -1, "", "", NULL); + t_string_list_split("foo:bar:baz", ":", -1, "foo", "bar", "baz", NULL); + t_string_list_split("foo:bar:baz", ":", 0, "foo:bar:baz", NULL); + t_string_list_split("foo:bar:baz", ":", 1, "foo", "bar:baz", NULL); + t_string_list_split("foo:bar:baz", ":", 2, "foo", "bar", "baz", NULL); + t_string_list_split("foo:bar:", ":", -1, "foo", "bar", "", NULL); + t_string_list_split("", ":", -1, "", NULL); + t_string_list_split(":", ":", -1, "", "", NULL); } static void t_string_list_split_in_place(const char *data, const char *delim, -- cgit v1.2.3 From 576454974165d51b7e39c0608cde1c84978f1a8a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 1 Aug 2025 15:04:20 -0700 Subject: string-list: optionally trim string pieces split by string_list_split*() Teach the unified split_string() to take an optional "flags" word, and define the first flag STRING_LIST_SPLIT_TRIM to cause the split pieces to be trimmed before they are placed in the string list. Signed-off-by: Junio C Hamano --- string-list.c | 35 ++++++++++++++++++++---- string-list.h | 15 +++++++++++ t/unit-tests/u-string-list.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) (limited to 't/unit-tests/u-string-list.c') diff --git a/string-list.c b/string-list.c index 65b6ceb259..86a309f8fb 100644 --- a/string-list.c +++ b/string-list.c @@ -282,11 +282,18 @@ void unsorted_string_list_delete_item(struct string_list *list, int i, int free_ */ static int append_one(struct string_list *list, const char *p, const char *end, - int in_place) + int in_place, unsigned flags) { if (!end) end = p + strlen(p); + if ((flags & STRING_LIST_SPLIT_TRIM)) { + /* rtrim */ + for (; p < end; end--) + if (!isspace(end[-1])) + break; + } + if (in_place) { *((char *)end) = '\0'; string_list_append(list, p); @@ -307,7 +314,7 @@ static int append_one(struct string_list *list, * returns "char *" pointer into that const string. Yucky but works ;-). */ static int split_string(struct string_list *list, const char *string, const char *delim, - int maxsplit, int in_place) + int maxsplit, int in_place, unsigned flags) { int count = 0; const char *p = string; @@ -320,12 +327,18 @@ static int split_string(struct string_list *list, const char *string, const char for (;;) { char *end; + if (flags & STRING_LIST_SPLIT_TRIM) { + /* ltrim */ + while (*p && isspace(*p)) + p++; + } + if (0 <= maxsplit && maxsplit <= count) end = NULL; else end = strpbrk(p, delim); - count += append_one(list, p, end, in_place); + count += append_one(list, p, end, in_place, flags); if (!end) return count; @@ -336,11 +349,23 @@ static int split_string(struct string_list *list, const char *string, const char int string_list_split(struct string_list *list, const char *string, const char *delim, int maxsplit) { - return split_string(list, string, delim, maxsplit, 0); + return split_string(list, string, delim, maxsplit, 0, 0); } int string_list_split_in_place(struct string_list *list, char *string, const char *delim, int maxsplit) { - return split_string(list, string, delim, maxsplit, 1); + return split_string(list, string, delim, maxsplit, 1, 0); +} + +int string_list_split_f(struct string_list *list, const char *string, + const char *delim, int maxsplit, unsigned flags) +{ + return split_string(list, string, delim, maxsplit, 0, flags); +} + +int string_list_split_in_place_f(struct string_list *list, char *string, + const char *delim, int maxsplit, unsigned flags) +{ + return split_string(list, string, delim, maxsplit, 1, flags); } diff --git a/string-list.h b/string-list.h index 6c8650efde..40e148712d 100644 --- a/string-list.h +++ b/string-list.h @@ -281,4 +281,19 @@ int string_list_split(struct string_list *list, const char *string, */ int string_list_split_in_place(struct string_list *list, char *string, const char *delim, int maxsplit); + +/* Flag bits for split_f and split_in_place_f functions */ +enum { + /* + * trim whitespaces around resulting string piece before adding + * it to the list + */ + STRING_LIST_SPLIT_TRIM = (1 << 0), +}; + +int string_list_split_f(struct string_list *, const char *string, + const char *delim, int maxsplit, unsigned flags); + +int string_list_split_in_place_f(struct string_list *, char *string, + const char *delim, int maxsplit, unsigned flags); #endif /* STRING_LIST_H */ diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c index 150a5f505f..daa9307e45 100644 --- a/t/unit-tests/u-string-list.c +++ b/t/unit-tests/u-string-list.c @@ -63,6 +63,70 @@ static void t_string_list_split(const char *data, const char *delim, int maxspli string_list_clear(&list, 0); } +static void t_string_list_split_f(const char *data, const char *delim, + int maxsplit, unsigned flags, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_DUP; + va_list ap; + int len; + + va_start(ap, flags); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split_f(&list, data, delim, maxsplit, flags); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split_f(void) +{ + t_string_list_split_f("::foo:bar:baz:", ":", -1, 0, + "", "", "foo", "bar", "baz", "", NULL); + t_string_list_split_f(" foo:bar : baz", ":", -1, STRING_LIST_SPLIT_TRIM, + "foo", "bar", "baz", NULL); + t_string_list_split_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM, + "a", "b c", NULL); +} + +static void t_string_list_split_in_place_f(const char *data_, const char *delim, + int maxsplit, unsigned flags, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_NODUP; + char *data = xstrdup(data_); + va_list ap; + int len; + + va_start(ap, flags); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split_in_place_f(&list, data, delim, maxsplit, flags); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + free(data); + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split_in_place_f(void) +{ + t_string_list_split_in_place_f("::foo:bar:baz:", ":", -1, 0, + "", "", "foo", "bar", "baz", "", NULL); + t_string_list_split_in_place_f(" foo:bar : baz", ":", -1, STRING_LIST_SPLIT_TRIM, + "foo", "bar", "baz", NULL); + t_string_list_split_in_place_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM, + "a", "b c", NULL); +} + void test_string_list__split(void) { t_string_list_split("foo:bar:baz", ":", -1, "foo", "bar", "baz", NULL); -- cgit v1.2.3 From 27531efa41cfa882473513dd93e696a16f6eb87b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 1 Aug 2025 15:04:22 -0700 Subject: string-list: optionally omit empty string pieces in string_list_split*() Teach the unified split_string() machinery a new flag bit, STRING_LIST_SPLIT_NONEMPTY, to cause empty split pieces to be omitted from the resulting string list. Signed-off-by: Junio C Hamano --- string-list.c | 3 +++ string-list.h | 2 ++ t/unit-tests/u-string-list.c | 15 +++++++++++++++ 3 files changed, 20 insertions(+) (limited to 't/unit-tests/u-string-list.c') diff --git a/string-list.c b/string-list.c index 86a309f8fb..343cf1ca90 100644 --- a/string-list.c +++ b/string-list.c @@ -294,6 +294,9 @@ static int append_one(struct string_list *list, break; } + if ((flags & STRING_LIST_SPLIT_NONEMPTY) && (end <= p)) + return 0; + if (in_place) { *((char *)end) = '\0'; string_list_append(list, p); diff --git a/string-list.h b/string-list.h index 40e148712d..2b438c7733 100644 --- a/string-list.h +++ b/string-list.h @@ -289,6 +289,8 @@ enum { * it to the list */ STRING_LIST_SPLIT_TRIM = (1 << 0), + /* omit adding empty string piece to the resulting list */ + STRING_LIST_SPLIT_NONEMPTY = (1 << 1), }; int string_list_split_f(struct string_list *, const char *string, diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c index daa9307e45..a2457d7b1e 100644 --- a/t/unit-tests/u-string-list.c +++ b/t/unit-tests/u-string-list.c @@ -92,6 +92,13 @@ void test_string_list__split_f(void) "foo", "bar", "baz", NULL); t_string_list_split_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM, "a", "b c", NULL); + t_string_list_split_f("::foo::bar:baz:", ":", -1, STRING_LIST_SPLIT_NONEMPTY, + "foo", "bar", "baz", NULL); + t_string_list_split_f("foo:baz", ":", -1, STRING_LIST_SPLIT_NONEMPTY, + "foo", "baz", NULL); + t_string_list_split_f("foo :: : baz", ":", -1, + STRING_LIST_SPLIT_NONEMPTY | STRING_LIST_SPLIT_TRIM, + "foo", "baz", NULL); } static void t_string_list_split_in_place_f(const char *data_, const char *delim, @@ -125,6 +132,14 @@ void test_string_list__split_in_place_f(void) "foo", "bar", "baz", NULL); t_string_list_split_in_place_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM, "a", "b c", NULL); + t_string_list_split_in_place_f("::foo::bar:baz:", ":", -1, + STRING_LIST_SPLIT_NONEMPTY, + "foo", "bar", "baz", NULL); + t_string_list_split_in_place_f("foo:baz", ":", -1, STRING_LIST_SPLIT_NONEMPTY, + "foo", "baz", NULL); + t_string_list_split_in_place_f("foo :: : baz", ":", -1, + STRING_LIST_SPLIT_NONEMPTY | STRING_LIST_SPLIT_TRIM, + "foo", "baz", NULL); } void test_string_list__split(void) -- cgit v1.2.3