[PATCH libinput 6/7] util: add a helper function to split a string into substrings

Peter Hutterer peter.hutterer at who-t.net
Mon Nov 28 05:20:49 UTC 2016


Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/libinput-util.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/libinput-util.h | 18 ++++++++++++
 test/misc.c         | 45 +++++++++++++++++++++++++++++
 3 files changed, 145 insertions(+)

diff --git a/src/libinput-util.c b/src/libinput-util.c
index 4cf310b..a0b423b 100644
--- a/src/libinput-util.c
+++ b/src/libinput-util.c
@@ -285,3 +285,85 @@ parse_dimension_property(const char *prop, size_t *w, size_t *h)
 	*h = (size_t)y;
 	return true;
 }
+
+/**
+ * Return the next word in a string pointed to by state before the first
+ * separator character. Call repeatedly to tokenize a whole string.
+ *
+ * @param state Current state
+ * @param len String length of the word returned
+ * @param separators List of separator characters
+ *
+ * @return The first word in *state, NOT null-terminated
+ */
+static const char *
+next_word(const char **state, size_t *len, const char *separators)
+{
+	const char *next = *state;
+	size_t l;
+
+	if (!*next)
+		return NULL;
+
+	next += strspn(next, separators);
+	if (!*next) {
+		*state = next;
+		return NULL;
+	}
+
+	l = strcspn(next, separators);
+	*state = next + l;
+	*len = l;
+
+	return next;
+}
+
+/**
+ * Return a null-terminated string array with the tokens in the input
+ * string, e.g. "one two\tthree" with a separator list of " \t" will return
+ * an array [ "one", "two", "three", NULL ].
+ *
+ * Use strv_free() to free the array.
+ *
+ * @param in Input string
+ * @param separators List of separator characters
+ *
+ * @return A null-terminated string array or NULL on errors
+ */
+char **
+strv_from_string(const char *in, const char *separators)
+{
+	const char *s, *word;
+	char **strv = NULL;
+	int nelems = 0, idx;
+	size_t l;
+
+	assert(in != NULL);
+
+	s = in;
+	while ((word = next_word(&s, &l, separators)) != NULL)
+	       nelems++;
+
+	if (nelems == 0)
+		return NULL;
+
+	nelems++; /* NULL-terminated */
+	strv = zalloc(nelems * sizeof *strv);
+	if (!strv)
+		return NULL;
+
+	idx = 0;
+
+	s = in;
+	while ((word = next_word(&s, &l, separators)) != NULL) {
+		char *copy = strndup(word, l);
+		if (!copy) {
+			strv_free(strv);
+			return NULL;
+		}
+
+		strv[idx++] = copy;
+	}
+
+	return strv;
+}
diff --git a/src/libinput-util.h b/src/libinput-util.h
index 9b10e7d..3fd3747 100644
--- a/src/libinput-util.h
+++ b/src/libinput-util.h
@@ -456,4 +456,22 @@ safe_atod(const char *str, double *val)
 	return true;
 }
 
+char **strv_from_string(const char *string, const char *separator);
+
+static inline void
+strv_free(char **strv) {
+	char **s = strv;
+
+	if (!strv)
+		return;
+
+	while (*s != NULL) {
+		free(*s);
+		*s = (char*)0x1; /* detect use-after-free */
+		s++;
+	}
+
+	free (strv);
+}
+
 #endif /* LIBINPUT_UTIL_H */
diff --git a/test/misc.c b/test/misc.c
index b514f90..eadeae3 100644
--- a/test/misc.c
+++ b/test/misc.c
@@ -951,6 +951,50 @@ START_TEST(safe_atod_test)
 }
 END_TEST
 
+struct strsplit_test {
+	const char *string;
+	const char *delim;
+	const char *results[10];
+};
+
+START_TEST(strsplit_test)
+{
+	struct strsplit_test tests[] = {
+		{ "one two three", " ", { "one", "two", "three", NULL } },
+		{ "one", " ", { "one", NULL } },
+		{ "one two ", " ", { "one", "two", NULL } },
+		{ "one  two", " ", { "one", "two", NULL } },
+		{ " one two", " ", { "one", "two", NULL } },
+		{ "one", "\t \r", { "one", NULL } },
+		{ "one two three", " t", { "one", "wo", "hree", NULL } },
+		{ " one two three", "te", { " on", " ", "wo ", "hr", NULL } },
+		{ "one", "ne", { "o", NULL } },
+		{ "onene", "ne", { "o", NULL } },
+		{ NULL, NULL, { NULL }}
+	};
+	struct strsplit_test *t = tests;
+
+	while (t->string) {
+		char **strv;
+		int idx = 0;
+		strv = strv_from_string(t->string, t->delim);
+		while (t->results[idx]) {
+			ck_assert_str_eq(t->results[idx], strv[idx]);
+			idx++;
+		}
+		ck_assert_ptr_eq(strv[idx], NULL);
+		strv_free(strv);
+		t++;
+	}
+
+	/* Special cases */
+	ck_assert_ptr_eq(strv_from_string("", " "), NULL);
+	ck_assert_ptr_eq(strv_from_string(" ", " "), NULL);
+	ck_assert_ptr_eq(strv_from_string("     ", " "), NULL);
+	ck_assert_ptr_eq(strv_from_string("oneoneone", "one"), NULL);
+}
+END_TEST
+
 static int open_restricted_leak(const char *path, int flags, void *data)
 {
 	return *(int*)data;
@@ -1080,6 +1124,7 @@ litest_setup_tests_misc(void)
 	litest_add_no_device("misc:parser", dimension_prop_parser);
 	litest_add_no_device("misc:parser", safe_atoi_test);
 	litest_add_no_device("misc:parser", safe_atod_test);
+	litest_add_no_device("misc:parser", strsplit_test);
 	litest_add_no_device("misc:time", time_conversion);
 
 	litest_add_no_device("misc:fd", fd_no_event_leak);
-- 
2.9.3



More information about the wayland-devel mailing list