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:
| M | src/cstr.c | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/cstr.h | | | 12 | ++++++++++++ |
| M | src/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();