mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-02-04 05:50:26 +01:00
util: add a helper function to split a string into substrings
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
d6020d7ab2
commit
33100fe88d
3 changed files with 145 additions and 0 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
45
test/misc.c
45
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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue