From 0637595ec5649556fd97c621868a4604e73ef49c Mon Sep 17 00:00:00 2001 From: Siddharth Chandrasekaran Date: Mon, 17 Aug 2020 18:46:48 +0530 Subject: [PATCH] libc: add strtok_r implementation This is a standard function and useful for applications. Signed-off-by: Siddharth Chandrasekaran --- lib/libc/minimal/include/string.h | 1 + lib/libc/minimal/source/string/string.c | 39 ++++++++++++++++++++++ tests/lib/c_lib/src/main.c | 43 ++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/lib/libc/minimal/include/string.h b/lib/libc/minimal/include/string.h index 73a60e7218e..d2a7d6edb32 100644 --- a/lib/libc/minimal/include/string.h +++ b/lib/libc/minimal/include/string.h @@ -25,6 +25,7 @@ extern size_t strlen(const char *s); extern size_t strnlen(const char *s, size_t maxlen); extern int strcmp(const char *s1, const char *s2); extern int strncmp(const char *s1, const char *s2, size_t n); +extern char *strtok_r(char *str, const char *sep, char **state); extern char *strcat(char *_MLIBC_RESTRICT dest, const char *_MLIBC_RESTRICT src); extern char *strncat(char *_MLIBC_RESTRICT d, const char *_MLIBC_RESTRICT s, diff --git a/lib/libc/minimal/source/string/string.c b/lib/libc/minimal/source/string/string.c index 4dbc32dddbf..9344f824ca8 100644 --- a/lib/libc/minimal/source/string/string.c +++ b/lib/libc/minimal/source/string/string.c @@ -170,6 +170,45 @@ int strncmp(const char *s1, const char *s2, size_t n) return (n == 0) ? 0 : (*s1 - *s2); } +/** + * @brief Separate `str` by any char in `sep` and return NULL terminated + * sections. Consecutive `sep` chars in `str` are treated as a single + * separator. + * + * @return pointer to NULL terminated string or NULL on errors. + */ +char *strtok_r(char *str, const char *sep, char **state) +{ + char *start, *end; + + start = str ? str : *state; + + /* skip leading delimiters */ + while (*start && strchr(sep, *start)) { + start++; + } + + if (*start == '\0') { + *state = start; + return NULL; + } + + /* look for token chars */ + end = start; + while (*end && !strchr(sep, *end)) { + end++; + } + + if (*end != '\0') { + *end = '\0'; + *state = end + 1; + } else { + *state = end; + } + + return start; +} + char *strcat(char *_MLIBC_RESTRICT dest, const char *_MLIBC_RESTRICT src) { strcpy(dest + strlen(dest), src); diff --git a/tests/lib/c_lib/src/main.c b/tests/lib/c_lib/src/main.c index fbe4c7d0047..306c3178116 100644 --- a/tests/lib/c_lib/src/main.c +++ b/tests/lib/c_lib/src/main.c @@ -558,6 +558,46 @@ void test_tolower_toupper(void) zassert_equal(strcmp(lw, tolw), 0, "tolower error"); } +void test_strtok_r_do(char *str, char *sep, int tlen, + const char * const *toks, bool expect) +{ + int len = 0; + char *state, *tok, buf[64+1] = {0}; + + strncpy(buf, str, 64); + + tok = strtok_r(buf, sep, &state); + while (tok && len < tlen) { + if (strcmp(tok, toks[len]) != 0) { + break; + } + tok = strtok_r(NULL, sep, &state); + len++; + } + if (expect) { + zassert_equal(len, tlen, + "strtok_r error '%s' / '%s'", str, sep); + } else { + zassert_not_equal(len, tlen, + "strtok_r error '%s' / '%s'", str, sep); + } +} + +void test_strtok_r(void) +{ + static const char * const tc01[] = { "1", "2", "3", "4", "5" }; + + test_strtok_r_do("1,2,3,4,5", ",", 5, tc01, true); + test_strtok_r_do(",, 1 ,2 ,3 4,5 ", ", ", 5, tc01, true); + test_strtok_r_do("1,,,2 3,,,4 5", ", ", 5, tc01, true); + test_strtok_r_do("1,2 3,,,4 5 ", ", ", 5, tc01, true); + test_strtok_r_do("0,1,,,2 3,,,4 5", ", ", 5, tc01, false); + test_strtok_r_do("1,,,2 3,,,4 5", ",", 5, tc01, false); + test_strtok_r_do("A,,,2,3,,,4 5", ",", 5, tc01, false); + test_strtok_r_do("1,,,2,3,,,", ",", 5, tc01, false); + test_strtok_r_do("1|2|3,4|5", "| ", 5, tc01, false); +} + void test_main(void) { ztest_test_suite(test_c_lib, @@ -580,7 +620,8 @@ void test_main(void) ztest_unit_test(test_checktype), ztest_unit_test(test_memstr), ztest_unit_test(test_str_operate), - ztest_unit_test(test_tolower_toupper) + ztest_unit_test(test_tolower_toupper), + ztest_unit_test(test_strtok_r) ); ztest_run_test_suite(test_c_lib); }