From a936ea741c1135e46e04b4dd75b95adc2ac201cb Mon Sep 17 00:00:00 2001
From: Olivier Matz <zer0@droids-corp.org>
Date: Fri, 10 Nov 2017 22:05:03 +0100
Subject: [PATCH] save

---
 lib/Makefile          |   3 +-
 lib/ecoli_completed.c |  25 ++++++++--
 lib/ecoli_completed.h |   3 +-
 lib/ecoli_keyval.c    |   5 ++
 lib/ecoli_keyval.h    |   2 +
 lib/ecoli_node_cmd.c  |  15 +++---
 lib/ecoli_node_expr.c |   4 +-
 lib/ecoli_node_int.c  | 106 ++++++++++++++++++++++++++++++++++++------
 lib/ecoli_node_int.h  |  14 ++++--
 lib/ecoli_parsed.c    |   9 ++++
 lib/ecoli_parsed.h    |   2 +-
 lib/ecoli_vec.c       |   1 +
 lib/ecoli_vec.h       |   2 +
 lib/main-readline.c   |   2 +-
 lib/todo.txt          |   3 ++
 15 files changed, 162 insertions(+), 34 deletions(-)

diff --git a/lib/Makefile b/lib/Makefile
index d6a4e80..b258752 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -50,6 +50,7 @@ srcs += ecoli_malloc.c
 srcs += ecoli_strvec.c
 srcs += ecoli_test.c
 srcs += ecoli_node.c
+srcs += ecoli_node_any.c
 srcs += ecoli_node_cmd.c
 srcs += ecoli_node_empty.c
 srcs += ecoli_node_expr.c
@@ -78,7 +79,7 @@ ldflags-$(O)test = -rdynamic
 exe-y-$(O)test = $(srcs) main.c
 
 ldflags-$(O)readline = -lreadline -ltermcap
-exe-y-$(O)readline = $(srcs) main-readline.c
+#exe-y-$(O)readline = $(srcs) main-readline.c
 
 include $(ECOLI)/mk/ecoli-post.mk
 
diff --git a/lib/ecoli_completed.c b/lib/ecoli_completed.c
index 64bb70d..553eccc 100644
--- a/lib/ecoli_completed.c
+++ b/lib/ecoli_completed.c
@@ -45,11 +45,22 @@ struct ec_completed *ec_completed(void)
 
 	completed = ec_calloc(1, sizeof(*completed));
 	if (completed == NULL)
-		return NULL;
+		goto fail;
 
 	TAILQ_INIT(&completed->nodes);
 
+	completed->attrs = ec_keyval();
+	if (completed->attrs == NULL)
+		goto fail;
+
 	return completed;
+
+ fail:
+	if (completed != NULL)
+		ec_keyval_free(completed->attrs);
+	ec_free(completed);
+
+	return NULL;
 }
 
 /* XXX on error, states are not freed ?
@@ -181,7 +192,11 @@ ec_completed_item(struct ec_parsed *state, const struct ec_node *node)
 
 	item = ec_calloc(1, sizeof(*item));
 	if (item == NULL)
-		return NULL;
+		goto fail;
+
+	item->attrs = ec_keyval();
+	if (item->attrs == NULL)
+		goto fail;
 
 	/* get path len */
 	for (p = state, len = 0; p != NULL;
@@ -207,8 +222,9 @@ fail:
 		ec_free(item->path);
 		ec_free(item->str);
 		ec_free(item->display);
+		ec_keyval_free(item->attrs);
 	}
-	ec_completed_item_free(item);
+	ec_free(item);
 
 	return NULL;
 }
@@ -284,7 +300,6 @@ int ec_completed_item_set_display(struct ec_completed_item *item,
 fail:
 	ec_free(display_copy);
 	return ret;
-
 }
 
 int
@@ -333,6 +348,7 @@ void ec_completed_item_free(struct ec_completed_item *item)
 	ec_free(item->str);
 	ec_free(item->display);
 	ec_free(item->path);
+	ec_keyval_free(item->attrs);
 	ec_free(item);
 }
 
@@ -385,6 +401,7 @@ void ec_completed_free(struct ec_completed *completed)
 		}
 		ec_free(compnode);
 	}
+	ec_keyval_free(completed->attrs);
 	ec_free(completed);
 }
 
diff --git a/lib/ecoli_completed.h b/lib/ecoli_completed.h
index 1627cfe..22016e5 100644
--- a/lib/ecoli_completed.h
+++ b/lib/ecoli_completed.h
@@ -46,7 +46,7 @@ struct ec_completed_item {
 	const struct ec_node *node;
 	char *str;
 	char *display;
-	/* XXX add a keyval (attrs) */
+	struct ec_keyval *attrs;
 
 	/* reverse order: [0] = last, [len-1] = root */
 	const struct ec_node **path;
@@ -67,6 +67,7 @@ struct ec_completed {
 	unsigned count;
 	unsigned count_match;
 	struct ec_completed_node_list nodes;
+	struct ec_keyval *attrs; // XXX per node instead?
 };
 
 /*
diff --git a/lib/ecoli_keyval.c b/lib/ecoli_keyval.c
index a47ded9..e80dd08 100644
--- a/lib/ecoli_keyval.c
+++ b/lib/ecoli_keyval.c
@@ -87,6 +87,11 @@ static void ec_keyval_elt_free(struct ec_keyval_elt *elt)
 		elt->free(elt->val);
 }
 
+bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key)
+{
+	return !!ec_keyval_lookup(keyval, key);
+}
+
 void *ec_keyval_get(const struct ec_keyval *keyval, const char *key)
 {
 	struct ec_keyval_elt *elt;
diff --git a/lib/ecoli_keyval.h b/lib/ecoli_keyval.h
index 82fc9e4..30232d0 100644
--- a/lib/ecoli_keyval.h
+++ b/lib/ecoli_keyval.h
@@ -29,6 +29,7 @@
 #define ECOLI_KEYVAL_
 
 #include <stdio.h>
+#include <stdbool.h>
 
 typedef void (*ec_keyval_elt_free_t)(void *);
 
@@ -37,6 +38,7 @@ struct ec_keyval;
 struct ec_keyval *ec_keyval(void);
 
 void *ec_keyval_get(const struct ec_keyval *keyval, const char *key);
+bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key);
 int ec_keyval_del(struct ec_keyval *keyval, const char *key);
 int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
 	ec_keyval_elt_free_t free_cb);
diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c
index db08cbf..0aca9b1 100644
--- a/lib/ecoli_node_cmd.c
+++ b/lib/ecoli_node_cmd.c
@@ -85,7 +85,7 @@ ec_node_cmd_eval_var(void **result, void *userctx,
 
 	for (i = 0; i < node->len; i++) {
 		id = ec_node_id(node->table[i]);
-		printf("i=%d id=%s\n", i, id);
+		//printf("i=%d id=%s\n", i, id);
 		if (id == NULL)
 			continue;
 		if (strcmp(str, id))
@@ -104,7 +104,7 @@ ec_node_cmd_eval_var(void **result, void *userctx,
 			return -ENOMEM;
 	}
 
-	printf("eval var %s %p\n", str, eval);
+	//printf("eval var %s %p\n", str, eval);
 	*result = eval;
 
 	return 0;
@@ -141,7 +141,7 @@ ec_node_cmd_eval_post_op(void **result, void *userctx, void *operand,
 	else
 		return -EINVAL;
 
-	printf("eval post_op %p\n", eval);
+	//printf("eval post_op %p\n", eval);
 	*result = eval;
 
 	return 0;
@@ -159,7 +159,7 @@ ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1,
 
 	(void)userctx;
 
-	printf("eval bin_op %p %p\n", in1, in2);
+	//printf("eval bin_op %p %p\n", in1, in2);
 
 	/* get parsed string vector, it should contain only one str */
 	vec = ec_parsed_strvec(operator);
@@ -192,7 +192,7 @@ ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1,
 		return -EINVAL;
 	}
 
-	printf("eval bin_op out %p\n", *result);
+	//printf("eval bin_op out %p\n", *result);
 
 	return 0;
 }
@@ -226,7 +226,7 @@ ec_node_cmd_eval_parenthesis(void **result, void *userctx,
 		return -EINVAL;
 	}
 
-	printf("eval paren\n");
+	//printf("eval paren\n");
 	*result = out;
 
 	return 0;
@@ -374,7 +374,7 @@ static int ec_node_cmd_build(struct ec_node *gen_node)
 	}
 	ec_parsed_free(p);
 	p = NULL;
-	ec_node_dump(stdout, cmd);
+	//ec_node_dump(stdout, cmd);
 
 	ec_node_free(node->expr);
 	node->expr = expr;
@@ -413,7 +413,6 @@ int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child)
 
 	assert(node != NULL);
 
-	printf("add child %s\n", child->id);
 	if (child == NULL)
 		return -EINVAL;
 
diff --git a/lib/ecoli_node_expr.c b/lib/ecoli_node_expr.c
index 24bdfd9..9207e4d 100644
--- a/lib/ecoli_node_expr.c
+++ b/lib/ecoli_node_expr.c
@@ -213,7 +213,7 @@ static int ec_node_expr_build(struct ec_node *gen_node)
 	weak = NULL;
 
 	node->child = expr;
-	ec_node_dump(stdout, node->child); //XXX
+	//ec_node_dump(stdout, node->child); //XXX
 
 	return 0;
 
@@ -595,7 +595,7 @@ int ec_node_expr_eval(void **user_result, const struct ec_node *node,
 	if (!ec_parsed_matches(parsed))
 		return -EINVAL;
 
-	ec_parsed_dump(stdout, parsed); //XXX
+	//ec_parsed_dump(stdout, parsed); //XXX
 	ret = eval_expression(&result, userctx, ops, node, parsed);
 	if (ret < 0)
 		return ret;
diff --git a/lib/ecoli_node_int.c b/lib/ecoli_node_int.c
index b685f16..cb7f60b 100644
--- a/lib/ecoli_node_int.c
+++ b/lib/ecoli_node_int.c
@@ -47,24 +47,32 @@ EC_LOG_TYPE_REGISTER(node_int);
 
 struct ec_node_int {
 	struct ec_node gen;
+	bool is_signed;
 	bool check_min;
-	long long int min;
 	bool check_max;
-	long long int max;
+	union {
+		int64_t min;
+		uint64_t umin;
+	};
+	union {
+		int64_t max;
+		uint64_t umax;
+	};
 	unsigned int base;
 };
 
 static int parse_llint(struct ec_node_int *node, const char *str,
-	long long *val)
+	int64_t *val)
 {
 	char *endptr;
 
 	errno = 0;
 	*val = strtoll(str, &endptr, node->base);
 
-	/* out of range */
-	if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
-			(errno != 0 && *val == 0))
+	if (errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN))
+		return -1;
+
+	if (errno != 0 && *val == 0)
 		return -1;
 
 	if (node->check_min && *val < node->min)
@@ -79,13 +87,44 @@ static int parse_llint(struct ec_node_int *node, const char *str,
 	return 0;
 }
 
+static int parse_ullint(struct ec_node_int *node, const char *str,
+			uint64_t *val)
+{
+	char *endptr;
+
+	/* since a negative input is silently converted to a positive
+	 * one by strtoull(), first check that it is positive */
+	if (strchr(str, '-'))
+		return -1;
+
+	errno = 0;
+	*val = strtoull(str, &endptr, node->base);
+
+	if (errno == ERANGE && *val == ULLONG_MAX)
+		return -1;
+
+	if (errno != 0 && *val == 0)
+		return -1;
+
+	if (node->check_min && *val < node->umin)
+		return -1;
+
+	if (node->check_max && *val > node->umax)
+		return -1;
+
+	if (*endptr != 0)
+		return -1;
+
+	return 0;
+}
+
 static int ec_node_int_parse(const struct ec_node *gen_node,
 			struct ec_parsed *state,
 			const struct ec_strvec *strvec)
 {
 	struct ec_node_int *node = (struct ec_node_int *)gen_node;
 	const char *str;
-	long long val;
+	int64_t val;
 
 	(void)state;
 
@@ -115,12 +154,15 @@ static struct ec_node_type ec_node_int_type = {
 
 EC_NODE_TYPE_REGISTER(ec_node_int_type);
 
-struct ec_node *ec_node_int(const char *id, long long int min,
-	long long int max, unsigned int base)
+struct ec_node *ec_node_int(const char *id, int64_t min,
+	int64_t max, unsigned int base)
 {
 	struct ec_node *gen_node = NULL;
 	struct ec_node_int *node = NULL;
 
+	if (min > max)
+		return NULL;
+
 	gen_node = __ec_node(&ec_node_int_type, id);
 	if (gen_node == NULL)
 		return NULL;
@@ -131,14 +173,39 @@ struct ec_node *ec_node_int(const char *id, long long int min,
 	node->check_max = true;
 	node->max = max;
 	node->base = base;
+	node->is_signed = true;
 
 	return &node->gen;
 }
 
-long long ec_node_int_getval(struct ec_node *gen_node, const char *str)
+struct ec_node *ec_node_uint(const char *id, uint64_t min,
+	uint64_t max, unsigned int base)
+{
+	struct ec_node *gen_node = NULL;
+	struct ec_node_int *node = NULL;
+
+	if (min > max)
+		return NULL;
+
+	gen_node = __ec_node(&ec_node_int_type, id);
+	if (gen_node == NULL)
+		return NULL;
+	node = (struct ec_node_int *)gen_node;
+
+	node->check_min = true;
+	node->min = min;
+	node->check_max = true;
+	node->max = max;
+	node->base = base;
+	node->is_signed = true;
+
+	return &node->gen;
+}
+
+int64_t ec_node_int_getval(struct ec_node *gen_node, const char *str)
 {
 	struct ec_node_int *node = (struct ec_node_int *)gen_node;
-	long long val = 0;
+	int64_t val = 0;
 
 	// XXX check type here
 	// if gen_node->type != int fail
@@ -148,6 +215,19 @@ long long ec_node_int_getval(struct ec_node *gen_node, const char *str)
 	return val;
 }
 
+uint64_t ec_node_uint_getval(struct ec_node *gen_node, const char *str)
+{
+	struct ec_node_int *node = (struct ec_node_int *)gen_node;
+	uint64_t val = 0;
+
+	// XXX check type here
+	// if gen_node->type != int fail
+
+	parse_ullint(node, str, &val);
+
+	return val;
+}
+
 /* LCOV_EXCL_START */
 static int ec_node_int_testcase(void)
 {
@@ -156,7 +236,7 @@ static int ec_node_int_testcase(void)
 	const char *s;
 	int ret = 0;
 
-	node = ec_node_int(NULL, 0, 256, 0);
+	node = ec_node_uint(NULL, 0, 256, 0);
 	if (node == NULL) {
 		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
@@ -175,7 +255,7 @@ static int ec_node_int_testcase(void)
 
 	p = ec_node_parse(node, "10");
 	s = ec_strvec_val(ec_parsed_strvec(p), 0);
-	EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 10);
+	EC_TEST_ASSERT(s != NULL && ec_node_uint_getval(node, s) == 10);
 	ec_parsed_free(p);
 	ec_node_free(node);
 
diff --git a/lib/ecoli_node_int.h b/lib/ecoli_node_int.h
index d03dd3d..b871529 100644
--- a/lib/ecoli_node_int.h
+++ b/lib/ecoli_node_int.h
@@ -28,12 +28,20 @@
 #ifndef ECOLI_NODE_INT_
 #define ECOLI_NODE_INT_
 
+#include <stdint.h>
+
 #include <ecoli_node.h>
 
 // XXX remove min, max, base from new(), and add ec_node_int_set_limits() +
 // XXX ec_node_int_set_base() ?
-struct ec_node *ec_node_int(const char *id, long long int min,
-	long long int max, unsigned int base);
-long long ec_node_int_getval(struct ec_node *node, const char *str);
+
+struct ec_node *ec_node_int(const char *id, int64_t min,
+			int64_t max, unsigned int base);
+int64_t ec_node_int_getval(struct ec_node *node, const char *str);
+
+struct ec_node *ec_node_uint(const char *id, uint64_t min,
+			uint64_t max, unsigned int base);
+uint64_t ec_node_uint_getval(struct ec_node *node, const char *str);
+
 
 #endif
diff --git a/lib/ecoli_parsed.c b/lib/ecoli_parsed.c
index c717723..e951671 100644
--- a/lib/ecoli_parsed.c
+++ b/lib/ecoli_parsed.c
@@ -147,9 +147,17 @@ struct ec_parsed *ec_parsed(void)
 
 	TAILQ_INIT(&parsed->children);
 
+	parsed->attrs = ec_keyval();
+	if (parsed->attrs == NULL)
+		goto fail;
+
 	return parsed;
 
  fail:
+	if (parsed != NULL)
+		ec_keyval_free(parsed->attrs);
+	ec_free(parsed);
+
 	return NULL;
 }
 
@@ -174,6 +182,7 @@ void ec_parsed_free(struct ec_parsed *parsed)
 
 	ec_parsed_free_children(parsed);
 	ec_strvec_free(parsed->strvec);
+	ec_keyval_free(parsed->attrs);
 	ec_free(parsed);
 }
 
diff --git a/lib/ecoli_parsed.h b/lib/ecoli_parsed.h
index 1a246ce..459f93d 100644
--- a/lib/ecoli_parsed.h
+++ b/lib/ecoli_parsed.h
@@ -47,7 +47,7 @@ struct ec_parsed {
 	struct ec_parsed *parent;
 	const struct ec_node *node;
 	struct ec_strvec *strvec;
-	/* XXX add a keyval (attrs) */
+	struct ec_keyval *attrs;
 };
 
 struct ec_parsed *ec_parsed(void);
diff --git a/lib/ecoli_vec.c b/lib/ecoli_vec.c
index 001891e..871911d 100644
--- a/lib/ecoli_vec.c
+++ b/lib/ecoli_vec.c
@@ -93,6 +93,7 @@ int ec_vec_add_by_ref(struct ec_vec *vec, void *ptr)
 		new_vec = ec_realloc(vec->vec, vec->elt_size * (vec->len + 1));
 		if (new_vec == NULL)
 			return -ENOMEM;
+		vec->size = vec->len + 1;
 	}
 
 	vec->vec = new_vec;
diff --git a/lib/ecoli_vec.h b/lib/ecoli_vec.h
index defd652..493e453 100644
--- a/lib/ecoli_vec.h
+++ b/lib/ecoli_vec.h
@@ -62,6 +62,8 @@ struct ec_vec *ec_vec_dup(const struct ec_vec *vec);
 struct ec_vec *ec_vec_ndup(const struct ec_vec *vec,
 	size_t off, size_t len);
 void ec_vec_free(struct ec_vec *vec);
+
+__attribute__((pure))
 size_t ec_vec_len(const struct ec_vec *vec);
 
 #endif
diff --git a/lib/main-readline.c b/lib/main-readline.c
index 8fecad4..4601ae8 100644
--- a/lib/main-readline.c
+++ b/lib/main-readline.c
@@ -55,7 +55,7 @@ static char *my_completion_entry(const char *s, int state)
 {
 	static struct ec_completed *c;
 	static struct ec_completed_iter *iter;
-	static const struct ec_completed_item *item;
+	const struct ec_completed_item *item;
 
 	(void)s;
 
diff --git a/lib/todo.txt b/lib/todo.txt
index 37a61d7..8d56dc5 100644
--- a/lib/todo.txt
+++ b/lib/todo.txt
@@ -9,6 +9,9 @@ X tk_re
 cleanup / rework
 ================
 
+- ec_completed_item_update()
+- ec_completed_item_set_display_value()
+
 - add_no_match
 - add_partial_match
 - check XXX in code
-- 
2.39.5