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 <gitster@pobox.com>

Junio C Hamano committed Aug 1, 2025 at 15:04 UTC 576454974165d51b7e39c0608cde1c84978f1a8a
3 files changed +109 -5
string-list.c
+30 -5
@@ -282,11 +282,18 @@ void unsorted_string_list_delete_item(struct string_list *list, int i, int free_
282 */
283 static int append_one(struct string_list *list,
284 const char *p, const char *end,
285 - int in_place)
285 + int in_place, unsigned flags)
286 {
287 if (!end)
288 end = p + strlen(p);
289
290 + if ((flags & STRING_LIST_SPLIT_TRIM)) {
291 + /* rtrim */
292 + for (; p < end; end--)
293 + if (!isspace(end[-1]))
294 + break;
295 + }
296 +
297 if (in_place) {
298 *((char *)end) = '\0';
299 string_list_append(list, p);
@@ -307,7 +314,7 @@ static int append_one(struct string_list *list,
314 * returns "char *" pointer into that const string. Yucky but works ;-).
315 */
316 static int split_string(struct string_list *list, const char *string, const char *delim,
310 - int maxsplit, int in_place)
317 + int maxsplit, int in_place, unsigned flags)
318 {
319 int count = 0;
320 const char *p = string;
@@ -320,12 +327,18 @@ static int split_string(struct string_list *list, const char *string, const char
327 for (;;) {
328 char *end;
329
330 + if (flags & STRING_LIST_SPLIT_TRIM) {
331 + /* ltrim */
332 + while (*p && isspace(*p))
333 + p++;
334 + }
335 +
336 if (0 <= maxsplit && maxsplit <= count)
337 end = NULL;
338 else
339 end = strpbrk(p, delim);
340
328 - count += append_one(list, p, end, in_place);
341 + count += append_one(list, p, end, in_place, flags);
342
343 if (!end)
344 return count;
@@ -336,11 +349,23 @@ static int split_string(struct string_list *list, const char *string, const char
349 int string_list_split(struct string_list *list, const char *string,
350 const char *delim, int maxsplit)
351 {
339 - return split_string(list, string, delim, maxsplit, 0);
352 + return split_string(list, string, delim, maxsplit, 0, 0);
353 }
354
355 int string_list_split_in_place(struct string_list *list, char *string,
356 const char *delim, int maxsplit)
357 {
345 - return split_string(list, string, delim, maxsplit, 1);
358 + return split_string(list, string, delim, maxsplit, 1, 0);
359 +}
360 +
361 +int string_list_split_f(struct string_list *list, const char *string,
362 + const char *delim, int maxsplit, unsigned flags)
363 +{
364 + return split_string(list, string, delim, maxsplit, 0, flags);
365 +}
366 +
367 +int string_list_split_in_place_f(struct string_list *list, char *string,
368 + const char *delim, int maxsplit, unsigned flags)
369 +{
370 + return split_string(list, string, delim, maxsplit, 1, flags);
371 }
string-list.h
+15
@@ -281,4 +281,19 @@ int string_list_split(struct string_list *list, const char *string,
281 */
282 int string_list_split_in_place(struct string_list *list, char *string,
283 const char *delim, int maxsplit);
284 +
285 +/* Flag bits for split_f and split_in_place_f functions */
286 +enum {
287 + /*
288 + * trim whitespaces around resulting string piece before adding
289 + * it to the list
290 + */
291 + STRING_LIST_SPLIT_TRIM = (1 << 0),
292 +};
293 +
294 +int string_list_split_f(struct string_list *, const char *string,
295 + const char *delim, int maxsplit, unsigned flags);
296 +
297 +int string_list_split_in_place_f(struct string_list *, char *string,
298 + const char *delim, int maxsplit, unsigned flags);
299 #endif /* STRING_LIST_H */
t/unit-tests/u-string-list.c
+64
@@ -63,6 +63,70 @@ static void t_string_list_split(const char *data, const char *delim, int maxspli
63 string_list_clear(&list, 0);
64 }
65
66 +static void t_string_list_split_f(const char *data, const char *delim,
67 + int maxsplit, unsigned flags, ...)
68 +{
69 + struct string_list expected_strings = STRING_LIST_INIT_DUP;
70 + struct string_list list = STRING_LIST_INIT_DUP;
71 + va_list ap;
72 + int len;
73 +
74 + va_start(ap, flags);
75 + t_vcreate_string_list_dup(&expected_strings, 0, ap);
76 + va_end(ap);
77 +
78 + string_list_clear(&list, 0);
79 + len = string_list_split_f(&list, data, delim, maxsplit, flags);
80 + cl_assert_equal_i(len, expected_strings.nr);
81 + t_string_list_equal(&list, &expected_strings);
82 +
83 + string_list_clear(&expected_strings, 0);
84 + string_list_clear(&list, 0);
85 +}
86 +
87 +void test_string_list__split_f(void)
88 +{
89 + t_string_list_split_f("::foo:bar:baz:", ":", -1, 0,
90 + "", "", "foo", "bar", "baz", "", NULL);
91 + t_string_list_split_f(" foo:bar : baz", ":", -1, STRING_LIST_SPLIT_TRIM,
92 + "foo", "bar", "baz", NULL);
93 + t_string_list_split_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM,
94 + "a", "b c", NULL);
95 +}
96 +
97 +static void t_string_list_split_in_place_f(const char *data_, const char *delim,
98 + int maxsplit, unsigned flags, ...)
99 +{
100 + struct string_list expected_strings = STRING_LIST_INIT_DUP;
101 + struct string_list list = STRING_LIST_INIT_NODUP;
102 + char *data = xstrdup(data_);
103 + va_list ap;
104 + int len;
105 +
106 + va_start(ap, flags);
107 + t_vcreate_string_list_dup(&expected_strings, 0, ap);
108 + va_end(ap);
109 +
110 + string_list_clear(&list, 0);
111 + len = string_list_split_in_place_f(&list, data, delim, maxsplit, flags);
112 + cl_assert_equal_i(len, expected_strings.nr);
113 + t_string_list_equal(&list, &expected_strings);
114 +
115 + free(data);
116 + string_list_clear(&expected_strings, 0);
117 + string_list_clear(&list, 0);
118 +}
119 +
120 +void test_string_list__split_in_place_f(void)
121 +{
122 + t_string_list_split_in_place_f("::foo:bar:baz:", ":", -1, 0,
123 + "", "", "foo", "bar", "baz", "", NULL);
124 + t_string_list_split_in_place_f(" foo:bar : baz", ":", -1, STRING_LIST_SPLIT_TRIM,
125 + "foo", "bar", "baz", NULL);
126 + t_string_list_split_in_place_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM,
127 + "a", "b c", NULL);
128 +}
129 +
130 void test_string_list__split(void)
131 {
132 t_string_list_split("foo:bar:baz", ":", -1, "foo", "bar", "baz", NULL);