From 3e96ed44e8c5b63bd0cef1e263e7991ac16c21e3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jan 2023 10:47:14 -0700 Subject: [PATCH] lib: Add a function to split a string into substrings Some environment variables provide a space-separated list of strings. It is easier to process these when they are broken out into an array of strings. Add a utility function to handle this. Signed-off-by: Simon Glass --- include/vsprintf.h | 24 ++++++++++++++ lib/strto.c | 41 +++++++++++++++++++++++ test/str_ut.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) diff --git a/include/vsprintf.h b/include/vsprintf.h index e006af200f..ed8a060ee1 100644 --- a/include/vsprintf.h +++ b/include/vsprintf.h @@ -328,6 +328,30 @@ char *strmhz(char *buf, unsigned long hz); */ void str_to_upper(const char *in, char *out, size_t len); +/** + * str_to_list() - Convert a string to a list of string pointers + * + * Splits a string containing space-delimited substrings into a number of + * separate strings, e.g. "this is" becomes {"this", "is", NULL}. If @instr is + * empty then this returns just {NULL}. The string should have only a single + * space between items, with no leading or trailing spaces. + * + * @instr: String to process (this is alloced by this function) + * Returns: List of string pointers, terminated by NULL. Each entry points to + * a string. If @instr is empty, the list consists just of a single NULL entry. + * Note that the first entry points to the alloced string. + * Returns NULL if out of memory + */ +const char **str_to_list(const char *instr); + +/** + * str_free_list() - Free a string list + * + * @ptr: String list to free, as created by str_to_list(). This can also be + * NULL, in which case the function does nothing + */ +void str_free_list(const char **ptr); + /** * vsscanf - Unformat a buffer into a list of arguments * @inp: input buffer diff --git a/lib/strto.c b/lib/strto.c index 6462d4fddf..154921165c 100644 --- a/lib/strto.c +++ b/lib/strto.c @@ -11,6 +11,7 @@ #include #include +#include #include /* from lib/kstrtox.c */ @@ -222,3 +223,43 @@ void str_to_upper(const char *in, char *out, size_t len) if (len) *out = '\0'; } + +const char **str_to_list(const char *instr) +{ + const char **ptr; + char *str, *p; + int count, i; + + /* don't allocate if the string is empty */ + str = *instr ? strdup(instr) : (char *)instr; + if (!str) + return NULL; + + /* count the number of space-separated strings */ + for (count = *str != '\0', p = str; *p; p++) { + if (*p == ' ') { + count++; + *p = '\0'; + } + } + + /* allocate the pointer array, allowing for a NULL terminator */ + ptr = calloc(count + 1, sizeof(char *)); + if (!ptr) { + if (*str) + free(str); + return NULL; + } + + for (i = 0, p = str; i < count; p += strlen(p) + 1, i++) + ptr[i] = p; + + return ptr; +} + +void str_free_list(const char **ptr) +{ + if (ptr) + free((char *)ptr[0]); + free(ptr); +} diff --git a/test/str_ut.c b/test/str_ut.c index 5a844347c2..fa9328ede5 100644 --- a/test/str_ut.c +++ b/test/str_ut.c @@ -274,6 +274,88 @@ static int str_trailing(struct unit_test_state *uts) } STR_TEST(str_trailing, 0); +static int test_str_to_list(struct unit_test_state *uts) +{ + const char **ptr; + ulong start; + + /* check out of memory */ + start = ut_check_delta(0); + malloc_enable_testing(0); + ut_assertnull(str_to_list("")); + ut_assertok(ut_check_delta(start)); + + ut_assertnull(str_to_list("this is a test")); + ut_assertok(ut_check_delta(start)); + + malloc_enable_testing(1); + ut_assertnull(str_to_list("this is a test")); + ut_assertok(ut_check_delta(start)); + + /* for an empty string, only one nalloc is needed */ + malloc_enable_testing(1); + ptr = str_to_list(""); + ut_assertnonnull(ptr); + ut_assertnull(ptr[0]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + malloc_disable_testing(); + + /* test the same again, without any nalloc restrictions */ + ptr = str_to_list(""); + ut_assertnonnull(ptr); + ut_assertnull(ptr[0]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + /* test a single string */ + start = ut_check_delta(0); + ptr = str_to_list("hi"); + ut_assertnonnull(ptr); + ut_assertnonnull(ptr[0]); + ut_asserteq_str("hi", ptr[0]); + ut_assertnull(ptr[1]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + /* test two strings */ + ptr = str_to_list("hi there"); + ut_assertnonnull(ptr); + ut_assertnonnull(ptr[0]); + ut_asserteq_str("hi", ptr[0]); + ut_assertnonnull(ptr[1]); + ut_asserteq_str("there", ptr[1]); + ut_assertnull(ptr[2]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + /* test leading, trailing and multiple spaces */ + ptr = str_to_list(" more space "); + ut_assertnonnull(ptr); + ut_assertnonnull(ptr[0]); + ut_asserteq_str("", ptr[0]); + ut_assertnonnull(ptr[1]); + ut_asserteq_str("more", ptr[1]); + ut_assertnonnull(ptr[2]); + ut_asserteq_str("", ptr[2]); + ut_assertnonnull(ptr[3]); + ut_asserteq_str("space", ptr[3]); + ut_assertnonnull(ptr[4]); + ut_asserteq_str("", ptr[4]); + ut_assertnonnull(ptr[5]); + ut_asserteq_str("", ptr[5]); + ut_assertnull(ptr[6]); + str_free_list(ptr); + ut_assertok(ut_check_delta(start)); + + /* test freeing a NULL pointer */ + str_free_list(NULL); + + return 0; +} +STR_TEST(test_str_to_list, 0); + int do_ut_str(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(str_test); -- 2.39.5