[PATCH v2 04/71] lib: Add a function to split a string into substrings

Simon Glass sjg at chromium.org
Sun Jan 8 03:49:40 CET 2023


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 <sjg at chromium.org>
---

(no changes since v1)

 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 e006af200fd..ed8a060ee17 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 6462d4fddf1..154921165cb 100644
--- a/lib/strto.c
+++ b/lib/strto.c
@@ -11,6 +11,7 @@
 
 #include <common.h>
 #include <errno.h>
+#include <malloc.h>
 #include <linux/ctype.h>
 
 /* 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 5a844347c2b..fa9328ede50 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.0.314.g84b9a713c41-goog



More information about the U-Boot mailing list