rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

commit 1af85be3f08fb32d3a609e8a7996b17b02bd0a47
parent 99231c355b7d1848425fa74adeafbedfa0178844
Author: vaplv <vaplv@free.fr>
Date:   Wed, 24 Mar 2021 15:54:53 +0100

Add and test the cstr_parse_list function

Parse a string as a list and let the caller define the function used to
parse each element.

Diffstat:
Msrc/cstr.c | 44++++++++++++++++++++++++++++++++++++++++++++
Msrc/cstr.h | 12++++++++++++
Msrc/test_cstr.c | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 130 insertions(+), 0 deletions(-)

diff --git a/src/cstr.c b/src/cstr.c @@ -13,7 +13,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L /* strtok_r support */ + #include "cstr_to_list.h" +#include "str.h" #define CSTR_LIST_TYPE double #include "cstr_to_list.h" @@ -24,3 +27,44 @@ #define CSTR_LIST_TYPE unsigned #define CSTR_LIST_SUFFIX uint #include "cstr_to_list.h" + +RSYS_API res_T +cstr_parse_list + (const char* str, + const char delimiter, + res_T (*parse_element)(const char* elmt, void* ctx), + void* ctx) /* User defined data sent to 'parse_element' */ +{ + struct str buf; + char delim[2] = {'\0', '\0'}; + char* tk; + char* tk_ctx; + res_T res = RES_OK; + + str_init(NULL, &buf); + + if(!str || !parse_element) { + res = RES_BAD_ARG; + goto error; + } + + /* Copy str in a temporary buffer to parse */ + res = str_set(&buf, str); + if(res != RES_OK) goto error; + + /* Parse the list */ + delim[0] = delimiter; + tk = strtok_r(str_get(&buf), delim, &tk_ctx); + while(tk) { + res = parse_element(tk, ctx); + if(res != RES_OK) goto error; + tk = strtok_r(NULL, delim, &tk_ctx); + } + +exit: + str_release(&buf); + return res; +error: + goto exit; +} + diff --git a/src/cstr.h b/src/cstr.h @@ -150,6 +150,18 @@ res_to_cstr(const res_T res) BEGIN_DECLS +/* Parse a string representing a list whose its elements are separated by the + * 'delimeter' char. The functor 'parse_element' is invoked on each element of + * the list. If it notifies an error, i.e. if the parsing of an element failed, + * the overall parsing is instantly stopped and the error is returned to the + * caller */ +RSYS_API res_T +cstr_parse_list + (const char* str, + const char delimiter, + res_T (*parse_element)(const char* elmt, void* ctx), + void* ctx); /* User defined data sent to 'parse_element' */ + /* Convert a string "A:B:C:D:E:F" in a list of { A, B, C, D, E, F }. ':' can be * any user defined character */ RSYS_API res_T diff --git a/src/test_cstr.c b/src/test_cstr.c @@ -13,9 +13,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L /* strtok_r support */ + #include "cstr.h" #include "math.h" +#include <string.h> + static void test_double(void) { @@ -152,6 +156,75 @@ test_ulong(void) CHK(cstr_to_ulong(buf, &ul) == RES_BAD_ARG); } +static res_T +count_elements(const char* str, void* ptr) +{ + int* counter = ptr; + *counter += 1; + CHK(str && str[0] != '\0'); + return RES_OK; +} + +static res_T +parse_elmt(const char* str, void* ptr) +{ + char buf[32]; + char* key = NULL; + char* val = NULL; + char* tk_ctx = NULL; + int i; + (void)ptr; + + CHK(str && str[0] != '\0'); + CHK(strlen(str)+1/*Null char*/ < sizeof(buf)); + + strncpy(buf, str, sizeof(buf)); + key = strtok_r(buf, "=", &tk_ctx); + val = strtok_r(NULL, "=", &tk_ctx); + CHK(key); + CHK(val); + + if(!strcmp(key, "good")) { + CHK(cstr_to_int(val, &i) == RES_OK); + } else if(!strcmp(key, "bad")) { + CHK(cstr_to_int(val, &i) == RES_OK); + i = !i; + } else { + FATAL("Unreachable code.\n"); + } + return i ? RES_OK : RES_BAD_ARG; +} + +static void +test_list(void) +{ + int n = 0; + CHK(cstr_parse_list(NULL, ':', count_elements, &n) == RES_BAD_ARG); + CHK(cstr_parse_list("", ':', NULL, NULL) == RES_BAD_ARG); + CHK(cstr_parse_list("", ':', count_elements, &n) == RES_OK); + CHK(n == 0); + CHK(cstr_parse_list("Hello", ':', count_elements, &n) == RES_OK); + CHK(n == 1); + n = 0; + CHK(cstr_parse_list("Hello, world!", ':', count_elements, &n) == RES_OK); + CHK(n == 1); + n = 0; + CHK(cstr_parse_list("Hello, world!", ' ', count_elements, &n) == RES_OK); + CHK(n == 2); + n = 0; + CHK(cstr_parse_list("1;2;3;1e-7;abcdef;0x32;key=value", ';', count_elements, &n) == RES_OK); + CHK(n == 7); + + CHK(cstr_parse_list("good=1", ',', parse_elmt, NULL) == RES_OK); + CHK(cstr_parse_list("bad=0", ',', parse_elmt, NULL) == RES_OK); + CHK(cstr_parse_list("good=1,bad=0", ',', parse_elmt, NULL) == RES_OK); + CHK(cstr_parse_list("good=0,bad=0", ',', parse_elmt, NULL) == RES_BAD_ARG); + CHK(cstr_parse_list("good=1,bad=1", ',', parse_elmt, NULL) == RES_BAD_ARG); + CHK(cstr_parse_list("good=0,bad=1", ',', parse_elmt, NULL) == RES_BAD_ARG); + CHK(cstr_parse_list("bad=0,good=0", ',', parse_elmt, NULL) == RES_BAD_ARG); + CHK(cstr_parse_list("bad=0,good=1", ',', parse_elmt, NULL) == RES_OK); +} + static void test_list_double(void) { @@ -280,6 +353,7 @@ main(int argc, char** argv) test_int(); test_uint(); test_ulong(); + test_list(); test_list_double(); test_list_float(); test_list_uint();