From: Olivier Matz <zer0@droids-corp.org>
Date: Thu, 9 Nov 2017 16:46:52 +0000 (+0100)
Subject: dynamic log types
X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=7284aef1f5f33a170cbd04aee7ed0a43608982d6;p=protos%2Flibecoli.git

dynamic log types
---

diff --git a/lib/ecoli_completed.h b/lib/ecoli_completed.h
index 16c79a6..1627cfe 100644
--- a/lib/ecoli_completed.h
+++ b/lib/ecoli_completed.h
@@ -46,6 +46,7 @@ struct ec_completed_item {
 	const struct ec_node *node;
 	char *str;
 	char *display;
+	/* XXX add a keyval (attrs) */
 
 	/* reverse order: [0] = last, [len-1] = root */
 	const struct ec_node **path;
diff --git a/lib/ecoli_keyval.c b/lib/ecoli_keyval.c
index c1d881f..a47ded9 100644
--- a/lib/ecoli_keyval.c
+++ b/lib/ecoli_keyval.c
@@ -35,6 +35,8 @@
 #include <ecoli_test.h>
 #include <ecoli_keyval.h>
 
+EC_LOG_TYPE_REGISTER(keyval);
+
 struct ec_keyval_elt {
 	char *key;
 	void *val;
@@ -195,7 +197,7 @@ static int ec_keyval_testcase(void)
 
 	keyval = ec_keyval();
 	if (keyval == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create keyval\n");
+		EC_LOG(EC_LOG_ERR, "cannot create keyval\n");
 		return -1;
 	}
 
diff --git a/lib/ecoli_log.c b/lib/ecoli_log.c
index 53c3acb..3b9d69e 100644
--- a/lib/ecoli_log.c
+++ b/lib/ecoli_log.c
@@ -31,20 +31,28 @@
 #include <string.h>
 #include <errno.h>
 
+#include <ecoli_malloc.h>
 #include <ecoli_log.h>
 
 static ec_log_t ec_log_fct = ec_log_default;
 static void *ec_log_opaque;
 
-int ec_log_default(unsigned int level, void *opaque, const char *str)
+struct ec_log_type {
+	char *name;
+	unsigned int level;
+};
+
+static struct ec_log_type *log_types;
+static size_t log_types_len;
+
+int ec_log_default(int type, unsigned int level, void *opaque, const char *str)
 {
 	(void)opaque;
-	(void)level;
 
-	return printf("%s", str);
+	return printf("[%d] %-12s %s", level, ec_log_name(type), str);
 }
 
-int ec_log_register(ec_log_t usr_log, void *opaque)
+int ec_log_fct_register(ec_log_t usr_log, void *opaque)
 {
 	if (usr_log == NULL)
 		return -1;
@@ -55,12 +63,63 @@ int ec_log_register(ec_log_t usr_log, void *opaque)
 	return 0;
 }
 
-void ec_log_unregister(void)
+void ec_log_fct_unregister(void)
 {
 	ec_log_fct = NULL;
 }
 
-int ec_vlog(unsigned int level, const char *format, va_list ap)
+static int
+ec_log_lookup(const char *name)
+{
+	size_t i;
+
+	for (i = 0; i < log_types_len; i++) {
+		if (log_types[i].name == NULL)
+			continue;
+		if (strcmp(name, log_types[i].name) == 0)
+			return i;
+	}
+
+	return -1;
+}
+
+const char *
+ec_log_name(int type)
+{
+	if (type < 0 || (unsigned int)type >= log_types_len)
+		return "unknown";
+	return log_types[type].name;
+}
+
+int
+ec_log_type_register(const char *name)
+{
+	struct ec_log_type *new_types;
+	char *copy;
+	int id;
+
+	id = ec_log_lookup(name);
+	if (id >= 0)
+		return id;
+
+	new_types = ec_realloc(log_types,
+		sizeof(*new_types) * (log_types_len + 1));
+	if (new_types == NULL)
+		return -ENOMEM;
+	log_types = new_types;
+
+	copy = ec_strdup(name);
+	if (copy == NULL)
+		return -ENOMEM;
+
+	id = log_types_len++;
+	log_types[id].name = copy;
+	log_types[id].level = EC_LOG_DEBUG;
+
+	return id;
+}
+
+int ec_vlog(int type, unsigned int level, const char *format, va_list ap)
 {
 	char *s;
 	int ret;
@@ -74,19 +133,19 @@ int ec_vlog(unsigned int level, const char *format, va_list ap)
 	if (ret < 0)
 		return ret;
 
-	ret = ec_log_fct(level, ec_log_opaque, s);
+	ret = ec_log_fct(type, level, ec_log_opaque, s);
 	free(s);
 
 	return ret;
 }
 
-int ec_log(unsigned int level, const char *format, ...)
+int ec_log(int type, unsigned int level, const char *format, ...)
 {
 	va_list ap;
 	int ret;
 
 	va_start(ap, format);
-	ret = ec_vlog(level, format, ap);
+	ret = ec_vlog(type, level, format, ap);
 	va_end(ap);
 
 	return ret;
diff --git a/lib/ecoli_log.h b/lib/ecoli_log.h
index 089254f..e117af1 100644
--- a/lib/ecoli_log.h
+++ b/lib/ecoli_log.h
@@ -39,19 +39,39 @@
 
 #include <stdarg.h>
 
+#define EC_LOG_TYPE_REGISTER(name)					\
+	static int name##_log_type;					\
+	static int local_log_type;					\
+	__attribute__((constructor, used))				\
+	static void ec_log_register_##name(void)			\
+	{								\
+		local_log_type = ec_log_type_register(#name);		\
+		name##_log_type = local_log_type;			\
+	}
+
+
 /* return -1 on error, len(s) on success */
-typedef int (*ec_log_t)(unsigned int level, void *opaque, const char *str);
+typedef int (*ec_log_t)(int type, unsigned int level, void *opaque,
+			const char *str);
+
+int ec_log_fct_register(ec_log_t usr_log, void *opaque);
+void ec_log_fct_unregister(void);
 
-int ec_log_register(ec_log_t usr_log, void *opaque);
-void ec_log_unregister(void);
+int ec_log_type_register(const char *name);
+
+const char *ec_log_name(int type);
 
 /* same api than printf */
-int ec_log(unsigned int level, const char *format, ...)
-	__attribute__((format(__printf__, 2, 3)));
+int ec_log(int type, unsigned int level, const char *format, ...)
+	__attribute__((format(__printf__, 3, 4)));
+
+int ec_vlog(int type, unsigned int level, const char *format, va_list ap);
 
-int ec_vlog(unsigned int level, const char *format, va_list ap);
+/* to use the macros, the user must have called EC_LOG_TYPE_REGISTER */
+#define EC_LOG(level, args...) ec_log(local_log_type, level, args)
+#define EC_VLOG(level, fmt, ap) ec_vlog(local_log_type, level, fmt, ap)
 
 /* default log handler for the library, use printf */
-int ec_log_default(unsigned int level, void *opaque, const char *str);
+int ec_log_default(int type, unsigned int level, void *opaque, const char *str);
 
 #endif
diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c
index 8ff05ab..6cd95d7 100644
--- a/lib/ecoli_node.c
+++ b/lib/ecoli_node.c
@@ -38,6 +38,8 @@
 #include <ecoli_log.h>
 #include <ecoli_node.h>
 
+EC_LOG_TYPE_REGISTER(node);
+
 static struct ec_node_type_list node_type_list =
 	TAILQ_HEAD_INITIALIZER(node_type_list);
 
@@ -78,7 +80,7 @@ struct ec_node *__ec_node(const struct ec_node_type *type, const char *id)
 	struct ec_node *node = NULL;
 	char buf[256]; // XXX
 
-	ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n",
+	EC_LOG(EC_LOG_DEBUG, "create node type=%s id=%s\n",
 		type->name, id);
 
 	node = ec_calloc(1, type->size);
@@ -117,7 +119,8 @@ struct ec_node *ec_node(const char *typename, const char *id)
 
 	type = ec_node_type_lookup(typename);
 	if (type == NULL) {
-		ec_log(EC_LOG_ERR, "type=%s does not exist\n", typename);
+		EC_LOG(EC_LOG_ERR, "type=%s does not exist\n",
+			typename);
 		return NULL;
 	}
 
diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c
index 9f14ae2..db08cbf 100644
--- a/lib/ecoli_node_cmd.c
+++ b/lib/ecoli_node_cmd.c
@@ -53,6 +53,8 @@
 #include <ecoli_node_re_lex.h>
 #include <ecoli_node_cmd.h>
 
+EC_LOG_TYPE_REGISTER(node_cmd);
+
 struct ec_node_cmd {
 	struct ec_node gen;
 	char *cmd_str;           /* the command string. */
@@ -505,7 +507,7 @@ static int ec_node_cmd_testcase(void)
 		ec_node_int("y", 20, 30, 10)
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 2, "command", "1");
@@ -518,7 +520,7 @@ static int ec_node_cmd_testcase(void)
 	node = EC_NODE_CMD(NULL, "good morning [count] bob|bobby|michael",
 			ec_node_int("count", 0, 10, 10));
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob");
diff --git a/lib/ecoli_node_empty.c b/lib/ecoli_node_empty.c
index e513d6b..5e220aa 100644
--- a/lib/ecoli_node_empty.c
+++ b/lib/ecoli_node_empty.c
@@ -39,6 +39,8 @@
 #include <ecoli_completed.h>
 #include <ecoli_node_empty.h>
 
+EC_LOG_TYPE_REGISTER(node_empty);
+
 struct ec_node_empty {
 	struct ec_node gen;
 };
@@ -70,7 +72,7 @@ static int ec_node_empty_testcase(void)
 
 	node = ec_node("empty", NULL);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 0, "foo");
@@ -81,7 +83,7 @@ static int ec_node_empty_testcase(void)
 	/* never completes */
 	node = ec_node("empty", NULL);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_expr.c b/lib/ecoli_node_expr.c
index e15c296..24bdfd9 100644
--- a/lib/ecoli_node_expr.c
+++ b/lib/ecoli_node_expr.c
@@ -47,6 +47,8 @@
 #include <ecoli_node_weakref.h>
 #include <ecoli_node_expr.h>
 
+EC_LOG_TYPE_REGISTER(node_expr);
+
 struct ec_node_expr {
 	struct ec_node gen;
 
@@ -91,7 +93,7 @@ static void ec_node_expr_free_priv(struct ec_node *gen_node)
 	struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
 	unsigned int i;
 
-	ec_log(EC_LOG_DEBUG, "free %p %p %p\n", node, node->child, node->val_node);
+	EC_LOG(EC_LOG_DEBUG, "free %p %p %p\n", node, node->child, node->val_node);
 	ec_node_free(node->val_node);
 
 	for (i = 0; i < node->bin_ops_len; i++)
diff --git a/lib/ecoli_node_expr_test.c b/lib/ecoli_node_expr_test.c
index c0e149a..748256b 100644
--- a/lib/ecoli_node_expr_test.c
+++ b/lib/ecoli_node_expr_test.c
@@ -40,6 +40,8 @@
 #include <ecoli_node_re_lex.h>
 #include <ecoli_node_expr.h>
 
+EC_LOG_TYPE_REGISTER(node_expr);
+
 struct my_eval_result {
 	int val;
 };
diff --git a/lib/ecoli_node_file.c b/lib/ecoli_node_file.c
index 51badf7..0a78cba 100644
--- a/lib/ecoli_node_file.c
+++ b/lib/ecoli_node_file.c
@@ -45,6 +45,8 @@
 #include <ecoli_completed.h>
 #include <ecoli_node_file.h>
 
+EC_LOG_TYPE_REGISTER(node_file);
+
 struct ec_node_file {
 	struct ec_node gen;
 };
@@ -277,7 +279,7 @@ static int ec_node_file_testcase(void)
 
 	node = ec_node("file", NULL);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	/* any string matches */
diff --git a/lib/ecoli_node_int.c b/lib/ecoli_node_int.c
index 635b549..b685f16 100644
--- a/lib/ecoli_node_int.c
+++ b/lib/ecoli_node_int.c
@@ -43,6 +43,8 @@
 #include <ecoli_node_int.h>
 #include <ecoli_test.h>
 
+EC_LOG_TYPE_REGISTER(node_int);
+
 struct ec_node_int {
 	struct ec_node gen;
 	bool check_min;
@@ -156,7 +158,7 @@ static int ec_node_int_testcase(void)
 
 	node = ec_node_int(NULL, 0, 256, 0);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
@@ -179,7 +181,7 @@ static int ec_node_int_testcase(void)
 
 	node = ec_node_int(NULL, -1, LLONG_MAX, 16);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
@@ -196,7 +198,7 @@ static int ec_node_int_testcase(void)
 
 	node = ec_node_int(NULL, LLONG_MIN, 0, 10);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
@@ -209,7 +211,7 @@ static int ec_node_int_testcase(void)
 	/* test completion */
 	node = ec_node_int(NULL, 0, 10, 0);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_many.c b/lib/ecoli_node_many.c
index 8746269..b40b3bd 100644
--- a/lib/ecoli_node_many.c
+++ b/lib/ecoli_node_many.c
@@ -43,6 +43,8 @@
 #include <ecoli_node_option.h>
 #include <ecoli_node_many.h>
 
+EC_LOG_TYPE_REGISTER(node_many);
+
 struct ec_node_many {
 	struct ec_node gen;
 	unsigned int min;
@@ -224,7 +226,7 @@ static int ec_node_many_testcase(void)
 
 	node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 0, 0);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 0);
@@ -236,7 +238,7 @@ static int ec_node_many_testcase(void)
 
 	node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 1, 0);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, -1, "bar");
@@ -247,7 +249,7 @@ static int ec_node_many_testcase(void)
 
 	node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 1, 2);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, -1, "bar");
@@ -260,7 +262,7 @@ static int ec_node_many_testcase(void)
 	/* test completion */
 	node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 2, 4);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_once.c b/lib/ecoli_node_once.c
index d52e099..54aa67b 100644
--- a/lib/ecoli_node_once.c
+++ b/lib/ecoli_node_once.c
@@ -44,6 +44,8 @@
 #include <ecoli_node_many.h>
 #include <ecoli_node_once.h>
 
+EC_LOG_TYPE_REGISTER(node_once);
+
 struct ec_node_once {
 	struct ec_node gen;
 	struct ec_node *child;
@@ -176,7 +178,7 @@ static int ec_node_once_testcase(void)
 				), 0, 0
 		);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 0);
@@ -201,7 +203,7 @@ static int ec_node_once_testcase(void)
 		ec_node_str(NULL, "titi")
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_option.c b/lib/ecoli_node_option.c
index ee2c4b1..1dcdd88 100644
--- a/lib/ecoli_node_option.c
+++ b/lib/ecoli_node_option.c
@@ -41,6 +41,8 @@
 #include <ecoli_node_str.h>
 #include <ecoli_test.h>
 
+EC_LOG_TYPE_REGISTER(node_option);
+
 struct ec_node_option {
 	struct ec_node gen;
 	struct ec_node *child;
@@ -122,7 +124,7 @@ static int ec_node_option_testcase(void)
 
 	node = ec_node_option(NULL, ec_node_str(NULL, "foo"));
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
@@ -134,7 +136,7 @@ static int ec_node_option_testcase(void)
 	/* test completion */
 	node = ec_node_option(NULL, ec_node_str(NULL, "foo"));
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_or.c b/lib/ecoli_node_or.c
index 391d84e..963f970 100644
--- a/lib/ecoli_node_or.c
+++ b/lib/ecoli_node_or.c
@@ -42,6 +42,8 @@
 #include <ecoli_node_str.h>
 #include <ecoli_test.h>
 
+EC_LOG_TYPE_REGISTER(node_or);
+
 struct ec_node_or {
 	struct ec_node gen;
 	struct ec_node **table;
@@ -200,7 +202,7 @@ static int ec_node_or_testcase(void)
 		ec_node_str(NULL, "bar")
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
@@ -221,7 +223,7 @@ static int ec_node_or_testcase(void)
 		ec_node_str(NULL, "titi")
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_re.c b/lib/ecoli_node_re.c
index 5bcc407..7444aa7 100644
--- a/lib/ecoli_node_re.c
+++ b/lib/ecoli_node_re.c
@@ -40,6 +40,8 @@
 #include <ecoli_completed.h>
 #include <ecoli_node_re.h>
 
+EC_LOG_TYPE_REGISTER(node_re);
+
 struct ec_node_re {
 	struct ec_node gen;
 	char *re_str;
@@ -146,7 +148,7 @@ static int ec_node_re_testcase(void)
 
 	node = ec_node_re(NULL, "fo+|bar");
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
diff --git a/lib/ecoli_node_re_lex.c b/lib/ecoli_node_re_lex.c
index 2de1c64..669c9fb 100644
--- a/lib/ecoli_node_re_lex.c
+++ b/lib/ecoli_node_re_lex.c
@@ -17,6 +17,8 @@
 #include <ecoli_node_int.h>
 #include <ecoli_node_re_lex.h>
 
+EC_LOG_TYPE_REGISTER(node_re_lex);
+
 struct regexp_pattern {
 	char *pattern;
 	regex_t r;
@@ -65,7 +67,7 @@ tokenize(struct regexp_pattern *table, size_t table_len, const char *str)
 
 			c = dup[pos.rm_eo + off];
 			dup[pos.rm_eo + off] = '\0';
-			ec_log(EC_LOG_DEBUG, "re_lex match <%s>\n", &dup[off]);
+			EC_LOG(EC_LOG_DEBUG, "re_lex match <%s>\n", &dup[off]);
 			if (ec_strvec_add(strvec, &dup[off]) < 0)
 				goto fail;
 
@@ -175,7 +177,7 @@ int ec_node_re_lex_add(struct ec_node *gen_node, const char *pattern, int keep)
 
 	ret = regcomp(&table[node->len].r, pattern, REG_EXTENDED);
 	if (ret != 0) {
-		ec_log(EC_LOG_ERR,
+		EC_LOG(EC_LOG_ERR,
 			"Regular expression <%s> compilation failed: %d\n",
 			pattern, ret);
 		if (ret == REG_ESPACE)
@@ -232,7 +234,7 @@ static int ec_node_re_lex_testcase(void)
 		)
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 
@@ -244,7 +246,7 @@ static int ec_node_re_lex_testcase(void)
 	ret |= ec_node_re_lex_add(node, "\\+", 1);
 	ret |= ec_node_re_lex_add(node, "[ 	]+", 0);
 	if (ret != 0) {
-		ec_log(EC_LOG_ERR, "cannot add regexp to node\n");
+		EC_LOG(EC_LOG_ERR, "cannot add regexp to node\n");
 		ec_node_free(node);
 		return -1;
 	}
diff --git a/lib/ecoli_node_seq.c b/lib/ecoli_node_seq.c
index 7b22709..9240e4f 100644
--- a/lib/ecoli_node_seq.c
+++ b/lib/ecoli_node_seq.c
@@ -46,6 +46,8 @@
 #include <ecoli_node_many.h>
 #include <ecoli_node_seq.h>
 
+EC_LOG_TYPE_REGISTER(node_seq);
+
 struct ec_node_seq {
 	struct ec_node gen;
 	struct ec_node **table;
@@ -293,7 +295,7 @@ static int ec_node_seq_testcase(void)
 		ec_node_str(NULL, "bar")
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
@@ -312,7 +314,7 @@ static int ec_node_seq_testcase(void)
 		ec_node_str(NULL, "bar")
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_sh_lex.c b/lib/ecoli_node_sh_lex.c
index 1da0a90..87b3175 100644
--- a/lib/ecoli_node_sh_lex.c
+++ b/lib/ecoli_node_sh_lex.c
@@ -45,6 +45,8 @@
 #include <ecoli_node_option.h>
 #include <ecoli_node_sh_lex.h>
 
+EC_LOG_TYPE_REGISTER(node_sh_lex);
+
 struct ec_node_sh_lex {
 	struct ec_node gen;
 	struct ec_node *child;
@@ -157,8 +159,6 @@ static struct ec_strvec *tokenize(const char *str, int completion,
 	char *word = NULL, *concat = NULL, *tmp;
 	int last_is_space = 1;
 
-//	printf("str=%s\n", str);
-
 	strvec = ec_strvec();
 	if (strvec == NULL)
 		goto fail;
@@ -167,7 +167,6 @@ static struct ec_strvec *tokenize(const char *str, int completion,
 		len = eat_spaces(&str[off]);
 		if (len > 0)
 			last_is_space = 1;
-//		printf("space=%zd\n", len);
 		off += len;
 
 		len = 0;
@@ -176,12 +175,10 @@ static struct ec_strvec *tokenize(const char *str, int completion,
 			last_is_space = 0;
 			if (str[suboff] == '"' || str[suboff] == '\'') {
 				sublen = eat_quoted_str(&str[suboff]);
-//				printf("sublen=%zd\n", sublen);
 				word = unquote_str(&str[suboff], sublen,
 					allow_missing_quote, missing_quote);
 			} else {
 				sublen = eat_str(&str[suboff]);
-//				printf("sublen=%zd\n", sublen);
 				if (sublen == 0)
 					break;
 				word = ec_strndup(&str[suboff], sublen);
@@ -189,7 +186,6 @@ static struct ec_strvec *tokenize(const char *str, int completion,
 
 			if (word == NULL)
 				goto fail;
-//			printf("word=%s\n", word);
 
 			len += sublen;
 			suboff += sublen;
@@ -302,8 +298,8 @@ ec_node_sh_lex_complete(const struct ec_node *gen_node,
 		goto fail;
 //	printf("new:%s\n", ec_strvec_val(new_vec, 0));
 
-	// XXX: complete should add the quotes for !EC_PARTIAL: use another
-	// completed object
+	// XXX: complete should add the quotes for !EC_PARTIAL
+	// XXX: if no quotes, replace " " by "\ "
 	ret = ec_node_complete_child(node->child, completed, parsed, new_vec);
 	if (ret < 0)
 		goto fail;
@@ -368,7 +364,7 @@ static int ec_node_sh_lex_testcase(void)
 		)
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "foo bar");
@@ -390,7 +386,7 @@ static int ec_node_sh_lex_testcase(void)
 		)
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_space.c b/lib/ecoli_node_space.c
index 48ffb6e..0552d1b 100644
--- a/lib/ecoli_node_space.c
+++ b/lib/ecoli_node_space.c
@@ -39,6 +39,8 @@
 #include <ecoli_completed.h>
 #include <ecoli_node_space.h>
 
+EC_LOG_TYPE_REGISTER(node_space);
+
 struct ec_node_space {
 	struct ec_node gen;
 };
@@ -83,7 +85,7 @@ static int ec_node_space_testcase(void)
 
 	node = ec_node("space", NULL);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, " ");
@@ -96,7 +98,7 @@ static int ec_node_space_testcase(void)
 	/* test completion */
 	node = ec_node("space", NULL);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	/* never completes whatever the input */
diff --git a/lib/ecoli_node_str.c b/lib/ecoli_node_str.c
index 0069c83..483f63c 100644
--- a/lib/ecoli_node_str.c
+++ b/lib/ecoli_node_str.c
@@ -39,6 +39,8 @@
 #include <ecoli_completed.h>
 #include <ecoli_node_str.h>
 
+EC_LOG_TYPE_REGISTER(node_str);
+
 struct ec_node_str {
 	struct ec_node gen;
 	char *string;
@@ -186,7 +188,7 @@ static int ec_node_str_testcase(void)
 	/* XXX use EC_NO_ID instead of NULL */
 	node = ec_node_str(NULL, "foo");
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
@@ -198,7 +200,7 @@ static int ec_node_str_testcase(void)
 
 	node = ec_node_str(NULL, "Здравствуйте");
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте");
@@ -211,7 +213,7 @@ static int ec_node_str_testcase(void)
 	/* an empty string node always matches */
 	node = ec_node_str(NULL, "");
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 1, "");
@@ -222,7 +224,7 @@ static int ec_node_str_testcase(void)
 	/* test completion */
 	node = ec_node_str(NULL, "foo");
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_subset.c b/lib/ecoli_node_subset.c
index 1bd7a12..1803b4d 100644
--- a/lib/ecoli_node_subset.c
+++ b/lib/ecoli_node_subset.c
@@ -44,6 +44,8 @@
 #include <ecoli_node_or.h>
 #include <ecoli_test.h>
 
+EC_LOG_TYPE_REGISTER(node_subset);
+
 struct ec_node_subset {
 	struct ec_node gen;
 	struct ec_node **table;
@@ -356,7 +358,7 @@ static int ec_node_subset_testcase(void)
 		ec_node_str(NULL, "toto")
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_PARSE(node, 0);
@@ -380,7 +382,7 @@ static int ec_node_subset_testcase(void)
 		ec_node_str(NULL, "titi")
 	);
 	if (node == NULL) {
-		ec_log(EC_LOG_ERR, "cannot create node\n");
+		EC_LOG(EC_LOG_ERR, "cannot create node\n");
 		return -1;
 	}
 	ret |= EC_TEST_CHECK_COMPLETE(node,
diff --git a/lib/ecoli_node_weakref.c b/lib/ecoli_node_weakref.c
index 73da26b..eacec4c 100644
--- a/lib/ecoli_node_weakref.c
+++ b/lib/ecoli_node_weakref.c
@@ -43,6 +43,8 @@
 #include <ecoli_node_option.h>
 #include <ecoli_node_weakref.h>
 
+EC_LOG_TYPE_REGISTER(node_weakref);
+
 struct ec_node_weakref {
 	struct ec_node gen;
 	struct ec_node *child;
diff --git a/lib/ecoli_parsed.h b/lib/ecoli_parsed.h
index 637059a..1a246ce 100644
--- a/lib/ecoli_parsed.h
+++ b/lib/ecoli_parsed.h
@@ -39,6 +39,7 @@ TAILQ_HEAD(ec_parsed_list, ec_parsed);
 
 /*
   node == NULL + empty children list means "no match"
+  XXX still valid?
 */
 struct ec_parsed {
 	TAILQ_ENTRY(ec_parsed) next;
@@ -46,6 +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_parsed *ec_parsed(void);
@@ -56,7 +58,8 @@ const struct ec_strvec *ec_parsed_strvec(const struct ec_parsed *parsed);
 
 /* XXX we could use a cache to store possible completions or match: the
  * cache would be per-node, and would be reset for each call to parse()
- * or complete() ? */
+ * or complete() ? ... not sure, since parse result can depend on state
+ */
 /* a NULL return value is an error, with errno set
   ENOTSUP: no ->parse() operation
 */
diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c
index 291417d..53cdf62 100644
--- a/lib/ecoli_test.c
+++ b/lib/ecoli_test.c
@@ -41,6 +41,8 @@
 
 static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list);
 
+EC_LOG_TYPE_REGISTER(test);
+
 /* register a driver */
 void ec_test_register(struct ec_test *test)
 {
@@ -76,7 +78,7 @@ int ec_test_check_parse(struct ec_node *tk, int expected, ...)
 	p = ec_node_parse_strvec(tk, vec);
 	ec_parsed_dump(stdout, p); /* XXX only for debug */
 	if (p == NULL) {
-		ec_log(EC_LOG_ERR, "parsed is NULL\n");
+		EC_LOG(EC_LOG_ERR, "parsed is NULL\n");
 	}
 	if (ec_parsed_matches(p))
 		match = ec_parsed_len(p);
@@ -85,7 +87,7 @@ int ec_test_check_parse(struct ec_node *tk, int expected, ...)
 	if (expected == match) {
 		ret = 0;
 	} else {
-		ec_log(EC_LOG_ERR,
+		EC_LOG(EC_LOG_ERR,
 			"tk parsed len (%d) does not match expected (%d)\n",
 			match, expected);
 	}
@@ -152,7 +154,7 @@ int ec_test_check_complete(struct ec_node *tk, ...)
 		}
 
 		if (item == NULL) {
-			ec_log(EC_LOG_ERR,
+			EC_LOG(EC_LOG_ERR,
 				"completion <%s> not in list\n", s);
 			ret = -1;
 		}
@@ -161,7 +163,7 @@ int ec_test_check_complete(struct ec_node *tk, ...)
 
 	/* check if we have more completions (or less) than expected */
 	if (count != ec_completed_count(c, EC_MATCH)) {
-		ec_log(EC_LOG_ERR,
+		EC_LOG(EC_LOG_ERR,
 			"nb_completion (%d) does not match (%d)\n",
 			count, ec_completed_count(c, EC_MATCH));
 		ec_completed_dump(stdout, c);
@@ -186,21 +188,25 @@ static int launch_test(const char *name)
 		if (name != NULL && strcmp(name, test->name))
 			continue;
 
-		ec_log(EC_LOG_INFO, "== starting test %-20s\n", test->name);
+		EC_LOG(EC_LOG_INFO, "== starting test %-20s\n",
+			test->name);
 
 		count++;
 		if (test->test() == 0) {
-			ec_log(EC_LOG_INFO, "== test %-20s success\n",
+			EC_LOG(EC_LOG_INFO,
+				"== test %-20s success\n",
 				test->name);
 		} else {
-			ec_log(EC_LOG_INFO, "== test %-20s failed\n",
+			EC_LOG(EC_LOG_INFO,
+				"== test %-20s failed\n",
 				test->name);
 			ret = -1;
 		}
 	}
 
 	if (name != NULL && count == 0) {
-		ec_log(EC_LOG_WARNING, "== test %s not found\n", name);
+		EC_LOG(EC_LOG_WARNING,
+			"== test %s not found\n", name);
 		ret = -1;
 	}
 
diff --git a/lib/ecoli_test.h b/lib/ecoli_test.h
index 0115837..1fa8480 100644
--- a/lib/ecoli_test.h
+++ b/lib/ecoli_test.h
@@ -74,10 +74,10 @@ int ec_test_one(const char *name);
 int ec_test_check_parse(struct ec_node *node, int expected, ...);
 
 #define EC_TEST_ERR(fmt, ...)						\
-	ec_log(EC_LOG_ERR, "%s:%d: error: " fmt "\n",			\
+	EC_LOG(EC_LOG_ERR, "%s:%d: error: " fmt "\n",			\
 		__FILE__, __LINE__, ##__VA_ARGS__);			\
 
-#define EC_TEST_ASSERT(cond, args...)					\
+#define EC_TEST_ASSERT(cond)						\
 	do {								\
 		if (!(cond))						\
 			EC_TEST_ERR("assertion failure: " #cond);	\
diff --git a/lib/ecoli_vec.c b/lib/ecoli_vec.c
index 8038b31..001891e 100644
--- a/lib/ecoli_vec.c
+++ b/lib/ecoli_vec.c
@@ -37,6 +37,8 @@
 #include <ecoli_test.h>
 #include <ecoli_vec.h>
 
+EC_LOG_TYPE_REGISTER(vec);
+
 struct ec_vec {
 	size_t len;
 	size_t size;
@@ -223,7 +225,7 @@ static void str_free(void *elt)
 }
 
 #define GOTO_FAIL do {					     \
-		ec_log(EC_LOG_ERR, "%s:%d: test failed\n",   \
+		EC_LOG(EC_LOG_ERR, "%s:%d: test failed\n",   \
 			__FILE__, __LINE__);		     \
 		goto fail;				     \
 	} while(0)
diff --git a/lib/main-readline.c b/lib/main-readline.c
index a63adfc..8fecad4 100644
--- a/lib/main-readline.c
+++ b/lib/main-readline.c
@@ -205,7 +205,6 @@ static int show_help(int ignore, int invoking_key)
 		helps[i++] = get_node_help(compnode);
 	}
 
-	ec_completed_dump(stdout, c);
 	ec_completed_free(c);
 
 	rl_display_match_list(helps, count + match, 1000); /* XXX 1000 */
diff --git a/lib/main.c b/lib/main.c
index 51b0946..56bc9d9 100644
--- a/lib/main.c
+++ b/lib/main.c
@@ -36,6 +36,8 @@
 #include <ecoli_test.h>
 #include <ecoli_malloc.h>
 
+EC_LOG_TYPE_REGISTER(main);
+
 #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
 		((size_t)(!(sizeof(x) % sizeof(0[x])))))
 
@@ -199,7 +201,7 @@ static void *debug_malloc(size_t size, const char *file, unsigned int line)
 		ftr->cookie = 0x87654321;
 	}
 
-	ec_log(EC_LOG_DEBUG, "%s:%d: info: malloc(%zd) -> %p\n",
+	EC_LOG(EC_LOG_DEBUG, "%s:%d: info: malloc(%zd) -> %p\n",
 		file, line, size, ret);
 
 	if (ret)
@@ -215,21 +217,21 @@ static void debug_free(void *ptr, const char *file, unsigned int line)
 	(void)file;
 	(void)line;
 
-	ec_log(EC_LOG_DEBUG, "%s:%d: info: free(%p)\n", file, line, ptr);
+	EC_LOG(EC_LOG_DEBUG, "%s:%d: info: free(%p)\n", file, line, ptr);
 
 	if (ptr == NULL)
 		return;
 
 	hdr = (ptr - sizeof(*hdr));
 	if (hdr->cookie != 0x12345678) {
-		ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad start cookie\n",
+		EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad start cookie\n",
 			file, line, ptr);
 		abort();
 	}
 
 	ftr = (ptr + hdr->size);
 	if (ftr->cookie != 0x87654321) {
-		ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
+		EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
 			file, line, ptr);
 		abort();
 	}
@@ -240,7 +242,7 @@ static void debug_free(void *ptr, const char *file, unsigned int line)
 	}
 
 	if (h == NULL) {
-		ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad ptr\n",
+		EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad ptr\n",
 			file, line, ptr);
 		abort();
 	}
@@ -260,7 +262,7 @@ static void *debug_realloc(void *ptr, size_t size, const char *file,
 	if (ptr != NULL) {
 		hdr =  (ptr - sizeof(*hdr));
 		if (hdr->cookie != 0x12345678) {
-			ec_log(EC_LOG_ERR,
+			EC_LOG(EC_LOG_ERR,
 				"%s:%d: error: realloc(%p): bad start cookie\n",
 				file, line, ptr);
 			abort();
@@ -268,7 +270,7 @@ static void *debug_realloc(void *ptr, size_t size, const char *file,
 
 		ftr = (ptr + hdr->size);
 		if (ftr->cookie != 0x87654321) {
-			ec_log(EC_LOG_ERR,
+			EC_LOG(EC_LOG_ERR,
 				"%s:%d: error: realloc(%p): bad end cookie\n",
 				file, line, ptr);
 			abort();
@@ -280,7 +282,7 @@ static void *debug_realloc(void *ptr, size_t size, const char *file,
 		}
 
 		if (h == NULL) {
-			ec_log(EC_LOG_ERR, "%s:%d: error: realloc(%p): bad ptr\n",
+			EC_LOG(EC_LOG_ERR, "%s:%d: error: realloc(%p): bad ptr\n",
 				file, line, ptr);
 			abort();
 		}
@@ -313,7 +315,7 @@ static void *debug_realloc(void *ptr, size_t size, const char *file,
 		ftr->cookie = 0x87654321;
 	}
 
-	ec_log(EC_LOG_DEBUG, "%s:%d: info: realloc(%p, %zd) -> %p\n",
+	EC_LOG(EC_LOG_DEBUG, "%s:%d: info: realloc(%p, %zd) -> %p\n",
 		file, line, ptr, size, ret);
 
 	if (ret)
@@ -327,35 +329,37 @@ static int debug_alloc_dump_leaks(void)
 	int i;
 	char **buffer;
 
-	ec_log(EC_LOG_INFO, "%zd successful allocations\n", alloc_success);
+	EC_LOG(EC_LOG_INFO, "%zd successful allocations\n", alloc_success);
 
 	if (TAILQ_EMPTY(&debug_alloc_hdr_list))
 		return 0;
 
 	TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
-		ec_log(EC_LOG_ERR,
+		EC_LOG(EC_LOG_ERR,
 			"%s:%d: error: memory leak size=%zd ptr=%p\n",
 			hdr->file, hdr->line, hdr->size, hdr + 1);
 		buffer = backtrace_symbols(hdr->stack, hdr->stacklen);
 		if (buffer == NULL) {
 			for (i = 0; i < hdr->stacklen; i++)
-				ec_log(EC_LOG_ERR, "  %p\n", hdr->stack[i]);
+				EC_LOG(EC_LOG_ERR, "  %p\n", hdr->stack[i]);
 		} else {
 			for (i = 0; i < hdr->stacklen; i++)
-				ec_log(EC_LOG_ERR, "  %s\n",
+				EC_LOG(EC_LOG_ERR, "  %s\n",
 					buffer ? buffer[i] : "unknown");
 		}
 		free(buffer);
 	}
 
-	ec_log(EC_LOG_ERR,
+	EC_LOG(EC_LOG_ERR,
 		"  missing static syms, use: addr2line -f -e <prog> <addr>\n");
 
 	return -1;
 }
 
-static int debug_log(unsigned int level, void *opaque, const char *str)
+static int debug_log(int type, unsigned int level, void *opaque,
+		const char *str)
 {
+	(void)type;
 	(void)opaque;
 
 	if (level > (unsigned int)log_level)
@@ -377,12 +381,12 @@ int main(int argc, char **argv)
 
 	srandom(seed);
 
-	ec_log_register(debug_log, NULL);
+	if (0) ec_log_fct_register(debug_log, NULL);
 
 	/* register a new malloc to track memleaks */
 	TAILQ_INIT(&debug_alloc_hdr_list);
 	if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
-		ec_log(EC_LOG_ERR, "cannot register new malloc\n");
+		EC_LOG(EC_LOG_ERR, "cannot register new malloc\n");
 		return -1;
 	}
 
diff --git a/lib/todo.txt b/lib/todo.txt
index 8d56dc5..37a61d7 100644
--- a/lib/todo.txt
+++ b/lib/todo.txt
@@ -9,9 +9,6 @@ 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