From: Olivier Matz <zer0@droids-corp.org>
Date: Thu, 1 Feb 2018 21:32:43 +0000 (+0100)
Subject: store full token and completion in completed_item
X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=ef05ddd2034f31839d5f0be21e372f8313e5d9eb;p=protos%2Flibecoli.git

store full token and completion in completed_item
---

diff --git a/lib/ecoli_completed.c b/lib/ecoli_completed.c
index 6820971..25ae6cb 100644
--- a/lib/ecoli_completed.c
+++ b/lib/ecoli_completed.c
@@ -32,6 +32,7 @@
 #include <errno.h>
 
 #include <ecoli_malloc.h>
+#include <ecoli_string.h>
 #include <ecoli_strvec.h>
 #include <ecoli_keyval.h>
 #include <ecoli_log.h>
@@ -44,12 +45,14 @@ struct ec_completed_item {
 	enum ec_completed_type type;
 	const struct ec_node *node;
 	struct ec_completed_group *grp;
-	char *str;
-	char *display;
+	char *start;      /* the initial token */
+	char *full;       /* the full token after completion */
+	char *completion; /* chars that are added, NULL if not applicable */
+	char *display;    /* what should be displayed by help/completers */
 	struct ec_keyval *attrs;
 };
 
-struct ec_completed *ec_completed(void)
+struct ec_completed *ec_completed(struct ec_parsed *state)
 {
 	struct ec_completed *completed = NULL;
 
@@ -59,23 +62,17 @@ struct ec_completed *ec_completed(void)
 
 	TAILQ_INIT(&completed->groups);
 
-	completed->attrs = ec_keyval();
-	if (completed->attrs == NULL)
-		goto fail;
-
-	completed->cur_state = NULL;
+	completed->cur_state = state;
 
 	return completed;
 
  fail:
-	if (completed != NULL)
-		ec_keyval_free(completed->attrs);
 	ec_free(completed);
 
 	return NULL;
 }
 
-struct ec_parsed *ec_completed_cur_parse_state(struct ec_completed *completed)
+struct ec_parsed *ec_completed_get_state(struct ec_completed *completed)
 {
 	return completed->cur_state;
 }
@@ -114,7 +111,7 @@ ec_node_complete_child(struct ec_node *node, struct ec_completed *completed,
 	cur_group = completed->cur_group;
 	completed->cur_group = NULL;
 
-	/* complete */
+	/* fill the completed struct with items */
 	ret = node->type->complete(node, completed, strvec);
 
 	/* restore parent parse state */
@@ -145,7 +142,7 @@ struct ec_completed *ec_node_complete_strvec(struct ec_node *node,
 	struct ec_completed *completed = NULL;
 	int ret;
 
-	completed = ec_completed();
+	completed = ec_completed(NULL);
 	if (completed == NULL)
 		goto fail;
 
@@ -195,6 +192,10 @@ ec_completed_group(const struct ec_node *node, struct ec_parsed *parsed)
 	if (grp == NULL)
 		return NULL;
 
+	grp->attrs = ec_keyval();
+	if (grp->attrs == NULL)
+		goto fail;
+
 	grp->state = ec_parsed_dup(parsed);
 	if (grp->state == NULL)
 		goto fail;
@@ -205,116 +206,158 @@ ec_completed_group(const struct ec_node *node, struct ec_parsed *parsed)
 	return grp;
 
 fail:
-	if (grp != NULL)
+	if (grp != NULL) {
 		ec_parsed_free(grp->state);
+		ec_keyval_free(grp->attrs);
+	}
 	ec_free(grp);
 	return NULL;
 }
 
-struct ec_completed_item *
-ec_completed_item(const struct ec_node *node)
+static struct ec_completed_item *
+ec_completed_item(const struct ec_node *node, enum ec_completed_type type,
+		const char *start, const char *full)
 {
 	struct ec_completed_item *item = NULL;
+	struct ec_keyval *attrs = NULL;
+	char *comp_cp = NULL, *start_cp = NULL;
+	char *full_cp = NULL, *display_cp = NULL;
+
+	if (type == EC_COMP_UNKNOWN && full != NULL) {
+		errno = EINVAL;
+		return NULL;
+	}
+	if (type != EC_COMP_UNKNOWN && full == NULL) {
+		errno = EINVAL;
+		return NULL;
+	}
 
 	item = ec_calloc(1, sizeof(*item));
 	if (item == NULL)
 		goto fail;
 
-	item->attrs = ec_keyval();
-	if (item->attrs == NULL)
+	attrs = ec_keyval();
+	if (attrs == NULL)
 		goto fail;
 
-	item->type = EC_COMP_UNKNOWN;
+	if (start != NULL) {
+		start_cp = ec_strdup(start);
+		if (start_cp == NULL)
+			goto fail;
+
+		if (ec_str_startswith(full, start)) {
+			comp_cp = ec_strdup(&full[strlen(start)]);
+			if (comp_cp == NULL)
+				goto fail;
+		}
+	}
+	if (full != NULL) {
+		full_cp = ec_strdup(full);
+		if (full_cp == NULL)
+			goto fail;
+		display_cp = ec_strdup(full);
+		if (display_cp == NULL)
+			goto fail;
+	}
+
 	item->node = node;
+	item->type = type;
+	item->start = start_cp;
+	item->full = full_cp;
+	item->completion = comp_cp;
+	item->display = display_cp;
+	item->attrs = attrs;
 
 	return item;
 
 fail:
-	if (item != NULL) {
-		ec_free(item->str);
-		ec_free(item->display);
-		ec_keyval_free(item->attrs);
-	}
+	ec_keyval_free(item->attrs);
+	ec_free(comp_cp);
+	ec_free(start_cp);
+	ec_free(full_cp);
+	ec_free(display_cp);
 	ec_free(item);
 
 	return NULL;
 }
 
-int
-ec_completed_item_set(struct ec_completed_item *item,
-		enum ec_completed_type type, const char *str)
+int ec_completed_item_set_display(struct ec_completed_item *item,
+				const char *display)
 {
-	char *str_copy = NULL;
 	char *display_copy = NULL;
 	int ret = 0;
 
-	if (item == NULL)
-		return -EINVAL;
-	if (item->str != NULL)
-		return -EEXIST;
-
-	switch (type) {
-	case EC_COMP_UNKNOWN:
-		if (str != NULL)
-			return -EINVAL;
-		break;
-	case EC_COMP_FULL:
-	case EC_PARTIAL_MATCH:
-		if (str == NULL)
-			return -EINVAL;
-		break;
-	default:
+	if (item == NULL || display == NULL ||
+			item->type == EC_COMP_UNKNOWN)
 		return -EINVAL;
-	}
 
-	if (str != NULL) {
-		ret = -ENOMEM;
-		str_copy = ec_strdup(str);
-		if (str_copy == NULL)
-			goto fail;
-		display_copy = ec_strdup(str);
-		if (display_copy == NULL)
-			goto fail;
-	}
+	display_copy = ec_strdup(display);
+	if (display_copy == NULL)
+		goto fail;
 
-	item->type = type;
-	item->str = str_copy;
+	ec_free(item->display);
 	item->display = display_copy;
+
 	return 0;
 
 fail:
-	ec_free(str_copy);
 	ec_free(display_copy);
 	return ret;
 }
 
-int ec_completed_item_set_display(struct ec_completed_item *item,
-				const char *display)
+int
+ec_completed_item_set_completion(struct ec_completed_item *item,
+				const char *completion)
 {
-	char *display_copy = NULL;
+	char *completion_copy = NULL;
 	int ret = 0;
 
-	if (item == NULL || display == NULL ||
-			item->type == EC_COMP_UNKNOWN || item->str == NULL)
+	if (item == NULL || completion == NULL ||
+			item->type == EC_COMP_UNKNOWN)
 		return -EINVAL;
 
 	ret = -ENOMEM;
-	display_copy = ec_strdup(display);
-	if (display_copy == NULL)
+	completion_copy = ec_strdup(completion);
+	if (completion_copy == NULL)
 		goto fail;
 
-	ec_free(item->display);
-	item->display = display_copy;
+	ec_free(item->completion);
+	item->completion = completion_copy;
 
 	return 0;
 
 fail:
-	ec_free(display_copy);
+	ec_free(completion_copy);
 	return ret;
 }
 
-// XXX refactor ec_completed_item(), ec_completed_item_add(), ec_completed_item_set* 
 int
+ec_completed_item_set_str(struct ec_completed_item *item,
+			const char *str)
+{
+	char *str_copy = NULL;
+	int ret = 0;
+
+	if (item == NULL || str == NULL ||
+			item->type == EC_COMP_UNKNOWN)
+		return -EINVAL;
+
+	ret = -ENOMEM;
+	str_copy = ec_strdup(str);
+	if (str_copy == NULL)
+		goto fail;
+
+	ec_free(item->full);
+	item->full = str_copy;
+
+	return 0;
+
+fail:
+	ec_free(str_copy);
+	return ret;
+}
+
+static int
 ec_completed_item_add(struct ec_completed *completed,
 		struct ec_completed_item *item)
 {
@@ -325,7 +368,7 @@ ec_completed_item_add(struct ec_completed *completed,
 	case EC_COMP_UNKNOWN:
 		break;
 	case EC_COMP_FULL:
-	case EC_PARTIAL_MATCH:
+	case EC_COMP_PARTIAL:
 		completed->count_match++; //XXX
 		break;
 	default:
@@ -352,7 +395,7 @@ ec_completed_item_add(struct ec_completed *completed,
 const char *
 ec_completed_item_get_str(const struct ec_completed_item *item)
 {
-	return item->str;
+	return item->full;
 }
 
 const char *
@@ -361,6 +404,12 @@ ec_completed_item_get_display(const struct ec_completed_item *item)
 	return item->display;
 }
 
+const char *
+ec_completed_item_get_completion(const struct ec_completed_item *item)
+{
+	return item->completion;
+}
+
 enum ec_completed_type
 ec_completed_item_get_type(const struct ec_completed_item *item)
 {
@@ -379,42 +428,63 @@ ec_completed_item_get_grp(const struct ec_completed_item *item)
 	return item->grp;
 }
 
-void ec_completed_item_free(struct ec_completed_item *item)
+static void
+ec_completed_item_free(struct ec_completed_item *item)
 {
 	if (item == NULL)
 		return;
 
-	ec_free(item->str);
+	ec_free(item->full);
+	ec_free(item->start);
+	ec_free(item->completion);
 	ec_free(item->display);
 	ec_keyval_free(item->attrs);
 	ec_free(item);
 }
 
+int ec_completed_add_item(struct ec_completed *completed,
+			const struct ec_node *node,
+			struct ec_completed_item **p_item,
+			enum ec_completed_type type,
+			const char *start, const char *full)
+{
+	struct ec_completed_item *item = NULL;
+	int ret;
+
+	item = ec_completed_item(node, type, start, full);
+	if (item == NULL)
+		return -1;
+
+	ret = ec_completed_item_add(completed, item);
+	if (ret < 0)
+		goto fail;
+
+	if (p_item != NULL)
+		*p_item = item;
+
+	return 0;
+
+fail:
+	ec_completed_item_free(item);
+
+	return -1;
+}
+
 /* default completion function: return a no-match element */
 int
 ec_node_default_complete(const struct ec_node *gen_node, // XXX rename in nomatch
 			struct ec_completed *completed,
 			const struct ec_strvec *strvec)
 {
-	struct ec_completed_item *item = NULL;
 	int ret;
 
 	if (ec_strvec_len(strvec) != 1)
 		return 0;
 
-	item = ec_completed_item(gen_node);
-	if (item == NULL)
-		return -ENOMEM;
-	ret = ec_completed_item_set(item, EC_COMP_UNKNOWN, NULL);
-	if (ret < 0) {
-		ec_completed_item_free(item);
-		return ret;
-	}
-	ret = ec_completed_item_add(completed, item);
-	if (ret < 0) {
-		ec_completed_item_free(item);
+	ret = ec_completed_add_item(completed, gen_node, NULL,
+				EC_COMP_UNKNOWN, NULL, NULL);
+	if (ret < 0)
 		return ret;
-	}
 
 	return 0;
 }
@@ -432,6 +502,7 @@ static void ec_completed_group_free(struct ec_completed_group *grp)
 		ec_completed_item_free(item);
 	}
 	ec_parsed_free(ec_parsed_get_root(grp->state));
+	ec_keyval_free(grp->attrs);
 	ec_free(grp);
 }
 
@@ -447,7 +518,6 @@ void ec_completed_free(struct ec_completed *completed)
 		TAILQ_REMOVE(&completed->groups, grp, next);
 		ec_completed_group_free(grp);
 	}
-	ec_keyval_free(completed->attrs);
 	ec_free(completed);
 }
 
@@ -471,18 +541,36 @@ void ec_completed_dump(FILE *out, const struct ec_completed *completed)
 			const char *typestr;
 
 			switch (item->type) {
-			case EC_COMP_UNKNOWN: typestr = "no-match"; break;
-			case EC_COMP_FULL: typestr = "match"; break;
-			case EC_PARTIAL_MATCH: typestr = "partial-match"; break;
+			case EC_COMP_UNKNOWN: typestr = "unknown"; break;
+			case EC_COMP_FULL: typestr = "full"; break;
+			case EC_COMP_PARTIAL: typestr = "partial"; break;
 			default: typestr = "unknown"; break;
 			}
 
-			fprintf(out, "  type=%s str=<%s> disp=<%s>\n",
-				typestr, item->str, item->display);
+			fprintf(out, "  type=%s str=<%s> comp=<%s> disp=<%s>\n",
+				typestr, item->full, item->completion,
+				item->display);
 		}
 	}
 }
 
+int ec_completed_merge(struct ec_completed *to,
+		struct ec_completed *from)
+{
+	struct ec_completed_group *grp;
+
+	while (!TAILQ_EMPTY(&from->groups)) {
+		grp = TAILQ_FIRST(&from->groups);
+		TAILQ_REMOVE(&from->groups, grp, next);
+		TAILQ_INSERT_TAIL(&to->groups, grp, next);
+	}
+	to->count += from->count;
+	to->count_match += from->count_match;
+
+	ec_completed_free(from);
+	return 0;
+}
+
 unsigned int ec_completed_count(
 	const struct ec_completed *completed,
 	enum ec_completed_type type)
@@ -518,12 +606,12 @@ ec_completed_iter(struct ec_completed *completed,
 	return iter;
 }
 
-const struct ec_completed_item *ec_completed_iter_next(
+struct ec_completed_item *ec_completed_iter_next(
 	struct ec_completed_iter *iter)
 {
-	const struct ec_completed *completed = iter->completed;
-	const struct ec_completed_group *cur_node;
-	const struct ec_completed_item *cur_match;
+	struct ec_completed *completed = iter->completed;
+	struct ec_completed_group *cur_node;
+	struct ec_completed_item *cur_match;
 
 	if (completed == NULL)
 		return NULL;
diff --git a/lib/ecoli_completed.h b/lib/ecoli_completed.h
index c054994..f616e4b 100644
--- a/lib/ecoli_completed.h
+++ b/lib/ecoli_completed.h
@@ -44,9 +44,10 @@
 struct ec_node;
 
 enum ec_completed_type {
-	EC_COMP_UNKNOWN,
-	EC_COMP_FULL,
-	EC_PARTIAL_MATCH,
+	EC_COMP_UNKNOWN = 0x1,
+	EC_COMP_FULL = 0x2,
+	EC_COMP_PARTIAL = 0x4,
+	EC_COMP_ALL = 0x7,
 };
 
 struct ec_completed_item;
@@ -58,6 +59,7 @@ struct ec_completed_group {
 	const struct ec_node *node;
 	struct ec_completed_item_list items;
 	struct ec_parsed *state;
+	struct ec_keyval *attrs;
 };
 
 TAILQ_HEAD(ec_completed_group_list, ec_completed_group);
@@ -68,7 +70,6 @@ struct ec_completed {
 	struct ec_parsed *cur_state;
 	struct ec_completed_group *cur_group;
 	struct ec_completed_group_list groups;
-	struct ec_keyval *attrs; // XXX per node instead?
 };
 
 /*
@@ -90,7 +91,7 @@ int ec_node_complete_child(struct ec_node *node,
  *
  *
  */
-struct ec_completed *ec_completed(void);
+struct ec_completed *ec_completed(struct ec_parsed *state);
 
 /**
  * Free a completion object and all its items.
@@ -107,32 +108,28 @@ void ec_completed_free(struct ec_completed *completed);
 void ec_completed_dump(FILE *out,
 	const struct ec_completed *completed);
 
-
-struct ec_parsed *ec_completed_cur_parse_state(struct ec_completed *completed);
-
 /**
- * Create a completion item.
- *
+ * Merge items contained in 'from' into 'to'
  *
+ * The 'from' completed struct is freed.
  */
-struct ec_completed_item *
-ec_completed_item(const struct ec_node *node);
+int ec_completed_merge(struct ec_completed *to,
+		struct ec_completed *from);
 
-/**
- * Set type and value of a completion item.
- *
- *
- */
-int ec_completed_item_set(struct ec_completed_item *item,
-			enum ec_completed_type type, const char *str);
+struct ec_parsed *ec_completed_get_state(struct ec_completed *completed);
+
+/* shortcut for ec_completed_item() + ec_completed_item_add() */
+int ec_completed_add_item(struct ec_completed *completed,
+			const struct ec_node *node,
+			struct ec_completed_item **p_item,
+			enum ec_completed_type type,
+			const char *start, const char *full);
 
 /**
- * Add a completion item to a completion list.
- *
  *
  */
-int ec_completed_item_add(struct ec_completed *completed,
-			struct ec_completed_item *item);
+int ec_completed_item_set_str(struct ec_completed_item *item,
+			const char *str);
 
 /**
  * Get the string value of a completion item.
@@ -150,6 +147,14 @@ ec_completed_item_get_str(const struct ec_completed_item *item);
 const char *
 ec_completed_item_get_display(const struct ec_completed_item *item);
 
+/**
+ * Get the completion string value of a completion item.
+ *
+ *
+ */
+const char *
+ec_completed_item_get_completion(const struct ec_completed_item *item);
+
 /**
  * Get the group of a completion item.
  *
@@ -175,19 +180,20 @@ const struct ec_node *
 ec_completed_item_get_node(const struct ec_completed_item *item);
 
 /**
- *
+ * Set the display value of an item.
  *
  *
  */
-void ec_completed_item_free(struct ec_completed_item *item);
+int ec_completed_item_set_display(struct ec_completed_item *item,
+				const char *display);
 
 /**
- * Set the display value of an item.
+ * Set the completion value of an item.
  *
  *
  */
-int ec_completed_item_set_display(struct ec_completed_item *item,
-				const char *display);
+int ec_completed_item_set_completion(struct ec_completed_item *item,
+				const char *completion);
 
 /**
  *
@@ -215,9 +221,9 @@ unsigned int ec_completed_count(
  */
 struct ec_completed_iter {
 	enum ec_completed_type type;
-	const struct ec_completed *completed;
-	const struct ec_completed_group *cur_node;
-	const struct ec_completed_item *cur_match;
+	struct ec_completed *completed;
+	struct ec_completed_group *cur_node;
+	struct ec_completed_item *cur_match;
 };
 
 /**
@@ -234,7 +240,7 @@ ec_completed_iter(struct ec_completed *completed,
  *
  *
  */
-const struct ec_completed_item *ec_completed_iter_next(
+struct ec_completed_item *ec_completed_iter_next(
 	struct ec_completed_iter *iter);
 
 /**
diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c
index 6cd95d7..089c0bc 100644
--- a/lib/ecoli_node.c
+++ b/lib/ecoli_node.c
@@ -243,3 +243,11 @@ const char *ec_node_desc(const struct ec_node *node)
 
 	return node->desc;
 }
+
+int ec_node_check_type(const struct ec_node *node,
+		const struct ec_node_type *type)
+{
+	if (strcmp(node->type->name, type->name))
+		return -EINVAL;
+	return 0;
+}
diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h
index 8fe2d76..8afcdbc 100644
--- a/lib/ecoli_node.h
+++ b/lib/ecoli_node.h
@@ -187,4 +187,8 @@ const char *ec_node_desc(const struct ec_node *node);
 void ec_node_dump(FILE *out, const struct ec_node *node);
 struct ec_node *ec_node_find(struct ec_node *node, const char *id);
 
+/* check the type of a node */
+int ec_node_check_type(const struct ec_node *node,
+		const struct ec_node_type *type);
+
 #endif
diff --git a/lib/ecoli_node_expr.c b/lib/ecoli_node_expr.c
index 129163e..4c546a7 100644
--- a/lib/ecoli_node_expr.c
+++ b/lib/ecoli_node_expr.c
@@ -243,6 +243,10 @@ int ec_node_expr_set_val_node(struct ec_node *gen_node, struct ec_node *val_node
 	struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
 	int ret;
 
+	ret = ec_node_check_type(gen_node, &ec_node_expr_type);
+	if (ret < 0)
+		return ret;
+
 	ret = -EINVAL;
 	if (val_node == NULL)
 		goto fail;
@@ -270,7 +274,9 @@ int ec_node_expr_add_bin_op(struct ec_node *gen_node, struct ec_node *op)
 	struct ec_node **bin_ops;
 	int ret;
 
-	// XXX check node type
+	ret = ec_node_check_type(gen_node, &ec_node_expr_type);
+	if (ret < 0)
+		return ret;
 
 	ret = -EINVAL;
 	if (node == NULL || op == NULL)
@@ -304,7 +310,9 @@ int ec_node_expr_add_pre_op(struct ec_node *gen_node, struct ec_node *op)
 	struct ec_node **pre_ops;
 	int ret;
 
-	// XXX check node type
+	ret = ec_node_check_type(gen_node, &ec_node_expr_type);
+	if (ret < 0)
+		return ret;
 
 	ret = -EINVAL;
 	if (node == NULL || op == NULL)
@@ -338,7 +346,9 @@ int ec_node_expr_add_post_op(struct ec_node *gen_node, struct ec_node *op)
 	struct ec_node **post_ops;
 	int ret;
 
-	// XXX check node type
+	ret = ec_node_check_type(gen_node, &ec_node_expr_type);
+	if (ret < 0)
+		return ret;
 
 	ret = -EINVAL;
 	if (node == NULL || op == NULL)
@@ -373,7 +383,9 @@ int ec_node_expr_add_parenthesis(struct ec_node *gen_node,
 	struct ec_node **open_ops, **close_ops;
 	int ret;
 
-	// XXX check node type
+	ret = ec_node_check_type(gen_node, &ec_node_expr_type);
+	if (ret < 0)
+		return ret;
 
 	ret = -EINVAL;
 	if (node == NULL || open == NULL || close == NULL)
@@ -416,6 +428,7 @@ enum expr_node_type {
 	PAREN_OPEN,
 	PAREN_CLOSE,
 };
+
 static enum expr_node_type get_node_type(const struct ec_node *expr_gen_node,
 	const struct ec_node *check)
 {
diff --git a/lib/ecoli_node_file.c b/lib/ecoli_node_file.c
index 951c494..8bb7edd 100644
--- a/lib/ecoli_node_file.c
+++ b/lib/ecoli_node_file.c
@@ -118,13 +118,14 @@ ec_node_file_complete(const struct ec_node *gen_node,
 		struct ec_completed *completed,
 		const struct ec_strvec *strvec)
 {
+	char *dname = NULL, *bname = NULL, *effective_dir;
 	struct ec_completed_item *item = NULL;
+	enum ec_completed_type type;
 	struct stat st;
 	const char *input;
 	size_t bname_len;
 	struct dirent *de = NULL;
 	DIR *dir = NULL;
-	char *dname = NULL, *bname = NULL, *effective_dir;
 	char *comp_str = NULL;
 	char *disp_str = NULL;
 	int ret;
@@ -199,13 +200,8 @@ ec_node_file_complete(const struct ec_node *gen_node,
 			is_dir = 0;
 		}
 
-		item = ec_completed_item(gen_node);
-		if (item == NULL) {
-			ret = -ENOMEM;
-			goto out;
-		}
-
 		if (is_dir) {
+			type = EC_COMP_PARTIAL;
 			if (asprintf(&comp_str, "%s%s/", input,
 					&de->d_name[bname_len]) < 0) {
 				ret = -errno;
@@ -215,11 +211,8 @@ ec_node_file_complete(const struct ec_node *gen_node,
 				ret = -errno;
 				goto out;
 			}
-			ret = ec_completed_item_set(item, EC_PARTIAL_MATCH,
-						comp_str);
-			if (ret < 0)
-				goto out;
 		} else {
+			type = EC_COMP_FULL;
 			if (asprintf(&comp_str, "%s%s", input,
 					&de->d_name[bname_len]) < 0) {
 				ret = -errno;
@@ -229,15 +222,15 @@ ec_node_file_complete(const struct ec_node *gen_node,
 				ret = -errno;
 				goto out;
 			}
-			ret = ec_completed_item_set(item, EC_COMP_FULL,
-						comp_str);
-			if (ret < 0)
-				goto out;
 		}
-		ret = ec_completed_item_set_display(item, disp_str);
+		ret = ec_completed_add_item(completed, gen_node, &item,
+					type, input, comp_str);
 		if (ret < 0)
 			goto out;
-		ret = ec_completed_item_add(completed, item);
+
+		/* fix the display string: we don't want to display the full
+		 * path. */
+		ret = ec_completed_item_set_display(item, disp_str);
 		if (ret < 0)
 			goto out;
 
@@ -250,7 +243,6 @@ ec_node_file_complete(const struct ec_node *gen_node,
 	ret = 0;
 
 out:
-	ec_completed_item_free(item);
 	free(comp_str);
 	free(disp_str);
 	ec_free(dname);
diff --git a/lib/ecoli_node_int.c b/lib/ecoli_node_int.c
index cb7f60b..bd262fa 100644
--- a/lib/ecoli_node_int.c
+++ b/lib/ecoli_node_int.c
@@ -209,6 +209,7 @@ int64_t ec_node_int_getval(struct ec_node *gen_node, const char *str)
 
 	// XXX check type here
 	// if gen_node->type != int fail
+	// we may need to change the API (return int + val in a ptr ?)
 
 	parse_llint(node, str, &val);
 
diff --git a/lib/ecoli_node_many.c b/lib/ecoli_node_many.c
index 8395bd1..c718890 100644
--- a/lib/ecoli_node_many.c
+++ b/lib/ecoli_node_many.c
@@ -108,7 +108,7 @@ __ec_node_many_complete(struct ec_node_many *node, unsigned int max,
 			struct ec_completed *completed,
 			const struct ec_strvec *strvec)
 {
-	struct ec_parsed *parsed = ec_completed_cur_parse_state(completed);
+	struct ec_parsed *parsed = ec_completed_get_state(completed);
 	struct ec_strvec *childvec = NULL;
 	unsigned int i;
 	int ret;
diff --git a/lib/ecoli_node_once.c b/lib/ecoli_node_once.c
index 2020088..b01640d 100644
--- a/lib/ecoli_node_once.c
+++ b/lib/ecoli_node_once.c
@@ -93,7 +93,7 @@ ec_node_once_complete(const struct ec_node *gen_node,
 		const struct ec_strvec *strvec)
 {
 	struct ec_node_once *node = (struct ec_node_once *)gen_node;
-	struct ec_parsed *parsed = ec_completed_cur_parse_state(completed);
+	struct ec_parsed *parsed = ec_completed_get_state(completed);
 	unsigned int count;
 	int ret;
 
@@ -131,14 +131,15 @@ EC_NODE_TYPE_REGISTER(ec_node_once_type);
 int ec_node_once_set(struct ec_node *gen_node, struct ec_node *child)
 {
 	struct ec_node_once *node = (struct ec_node_once *)gen_node;
+	int ret;
 
-	// XXX check node type
-
-	assert(node != NULL);
-
-	if (child == NULL)
+	if (gen_node == NULL || child == NULL)
 		return -EINVAL;
 
+	ret = ec_node_check_type(gen_node, &ec_node_once_type);
+	if (ret < 0)
+		return ret;
+
 	gen_node->flags &= ~EC_NODE_F_BUILT;
 
 	node->child = child;
diff --git a/lib/ecoli_node_seq.c b/lib/ecoli_node_seq.c
index 6025fd7..9e0440b 100644
--- a/lib/ecoli_node_seq.c
+++ b/lib/ecoli_node_seq.c
@@ -100,7 +100,7 @@ __ec_node_seq_complete(struct ec_node **table, size_t table_len,
 		struct ec_completed *completed,
 		const struct ec_strvec *strvec)
 {
-	struct ec_parsed *parsed = ec_completed_cur_parse_state(completed);
+	struct ec_parsed *parsed = ec_completed_get_state(completed);
 	struct ec_strvec *childvec = NULL;
 	unsigned int i;
 	int ret;
diff --git a/lib/ecoli_node_sh_lex.c b/lib/ecoli_node_sh_lex.c
index 1a5e4ab..e3580d7 100644
--- a/lib/ecoli_node_sh_lex.c
+++ b/lib/ecoli_node_sh_lex.c
@@ -25,6 +25,7 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#define _GNU_SOURCE /* for asprintf */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -164,6 +165,8 @@ static struct ec_strvec *tokenize(const char *str, int completion,
 		goto fail;
 
 	while (str[off] != '\0') {
+		if (missing_quote != NULL)
+			*missing_quote = '\0';
 		len = eat_spaces(&str[off]);
 		if (len > 0)
 			last_is_space = 1;
@@ -172,6 +175,8 @@ static struct ec_strvec *tokenize(const char *str, int completion,
 		len = 0;
 		suboff = off;
 		while (str[suboff] != '\0') {
+			if (missing_quote != NULL)
+				*missing_quote = '\0';
 			last_is_space = 0;
 			if (str[suboff] == '"' || str[suboff] == '\'') {
 				sublen = eat_quoted_str(&str[suboff]);
@@ -211,8 +216,6 @@ static struct ec_strvec *tokenize(const char *str, int completion,
 			concat = NULL;
 		}
 
-		/* XXX remove all printf comments */
-//		printf("str off=%zd len=%zd\n", off, len);
 		off += len;
 	}
 
@@ -283,7 +286,11 @@ ec_node_sh_lex_complete(const struct ec_node *gen_node,
 			const struct ec_strvec *strvec)
 {
 	struct ec_node_sh_lex *node = (struct ec_node_sh_lex *)gen_node;
+	struct ec_completed *tmp_completed = NULL;
 	struct ec_strvec *new_vec = NULL;
+	struct ec_completed_iter *iter = NULL;
+	struct ec_completed_item *item = NULL;
+	char *new_str = NULL;
 	const char *str;
 	char missing_quote;
 	int ret;
@@ -292,24 +299,63 @@ ec_node_sh_lex_complete(const struct ec_node *gen_node,
 		return 0;
 
 	str = ec_strvec_val(strvec, 0);
-//	printf("\nold:%s\n", str);
 	new_vec = tokenize(str, 1, 1, &missing_quote);
 	if (new_vec == NULL)
 		goto fail;
-//	printf("new:%s\n", ec_strvec_val(new_vec, 0));
 
-	// XXX: complete should add the quotes for !EC_PARTIAL
-	// XXX: if no quotes, replace " " by "\ "
-	ret = ec_node_complete_child(node->child, completed, new_vec);
+	/* we will store the completions in a temporary struct, because
+	 * we want to update them (ex: add missing quotes) */
+	tmp_completed = ec_completed(ec_completed_get_state(completed));
+	if (tmp_completed == NULL)
+		goto fail;
+
+	ret = ec_node_complete_child(node->child, tmp_completed, new_vec);
 	if (ret < 0)
 		goto fail;
 
+	/* add missing quote for full completions  */
+	if (missing_quote != '\0') {
+		iter = ec_completed_iter(tmp_completed, EC_COMP_FULL);
+		if (iter == NULL)
+			goto fail;
+		while ((item = ec_completed_iter_next(iter)) != NULL) {
+			str = ec_completed_item_get_str(item);
+			if (asprintf(&new_str, "%c%s%c", missing_quote, str,
+					missing_quote) < 0) {
+				new_str = NULL;
+				goto fail;
+			}
+			if (ec_completed_item_set_str(item, new_str) < 0)
+				goto fail;
+			free(new_str);
+			new_str = NULL;
+
+			str = ec_completed_item_get_completion(item);
+			if (asprintf(&new_str, "%s%c", str,
+					missing_quote) < 0) {
+				new_str = NULL;
+				goto fail;
+			}
+			if (ec_completed_item_set_completion(item, new_str) < 0)
+				goto fail;
+			free(new_str);
+			new_str = NULL;
+		}
+	}
+
+	ec_completed_iter_free(iter);
 	ec_strvec_free(new_vec);
 
+	ec_completed_merge(completed, tmp_completed);
+
 	return 0;
 
  fail:
+	ec_completed_free(tmp_completed);
+	ec_completed_iter_free(iter);
 	ec_strvec_free(new_vec);
+	free(new_str);
+
 	return -1;
 }
 
@@ -425,6 +471,9 @@ static int ec_node_sh_lex_testcase(void)
 	ret |= EC_TEST_CHECK_COMPLETE(node,
 		"foo barx", EC_NODE_ENDLIST,
 		EC_NODE_ENDLIST);
+	ret |= EC_TEST_CHECK_COMPLETE(node,
+		"foo 'b", EC_NODE_ENDLIST,
+		"'bar'", EC_NODE_ENDLIST);
 
 	ec_node_free(node);
 	return ret;
diff --git a/lib/ecoli_node_str.c b/lib/ecoli_node_str.c
index 6c47980..ab7ff11 100644
--- a/lib/ecoli_node_str.c
+++ b/lib/ecoli_node_str.c
@@ -72,11 +72,9 @@ ec_node_str_complete(const struct ec_node *gen_node,
 		struct ec_completed *completed,
 		const struct ec_strvec *strvec)
 {
-	struct ec_completed_item *item = NULL;
 	struct ec_node_str *node = (struct ec_node_str *)gen_node;
 	const char *str;
 	size_t n = 0;
-	int ret;
 
 	if (ec_strvec_len(strvec) != 1)
 		return 0;
@@ -91,19 +89,9 @@ ec_node_str_complete(const struct ec_node *gen_node,
 	if (str[n] != '\0')
 		return 0; // XXX add a no_match instead?
 
-	item = ec_completed_item(gen_node);
-	if (item == NULL)
-		return -ENOMEM;
-	ret = ec_completed_item_set(item, EC_COMP_FULL, node->string);
-	if (ret < 0) {
-		ec_completed_item_free(item);
-		return ret;
-	}
-	ret = ec_completed_item_add(completed, item);
-	if (ret < 0) {
-		ec_completed_item_free(item);
-		return ret;
-	}
+	if (ec_completed_add_item(completed, gen_node, NULL, EC_COMP_FULL,
+					str, node->string) < 0)
+		return -1;
 
 	return 0;
 }
@@ -143,6 +131,11 @@ EC_NODE_TYPE_REGISTER(ec_node_str_type);
 int ec_node_str_set_str(struct ec_node *gen_node, const char *str)
 {
 	struct ec_node_str *node = (struct ec_node_str *)gen_node;
+	int ret;
+
+	ret = ec_node_check_type(gen_node, &ec_node_str_type);
+	if (ret < 0)
+		return ret;
 
 	if (str == NULL)
 		return -EINVAL;
diff --git a/lib/ecoli_node_subset.c b/lib/ecoli_node_subset.c
index b56bf81..8fe4b65 100644
--- a/lib/ecoli_node_subset.c
+++ b/lib/ecoli_node_subset.c
@@ -181,7 +181,7 @@ __ec_node_subset_complete(struct ec_node **table, size_t table_len,
 			struct ec_completed *completed,
 			const struct ec_strvec *strvec)
 {
-	struct ec_parsed *parsed = ec_completed_cur_parse_state(completed);
+	struct ec_parsed *parsed = ec_completed_get_state(completed);
 	struct ec_strvec *childvec = NULL;
 	struct ec_node *save;
 	size_t i, len;
diff --git a/lib/ecoli_parsed.c b/lib/ecoli_parsed.c
index 3fb9db2..f7ca38f 100644
--- a/lib/ecoli_parsed.c
+++ b/lib/ecoli_parsed.c
@@ -42,6 +42,13 @@
 
 TAILQ_HEAD(ec_parsed_list, ec_parsed);
 
+/* XXX idea for parse: maintain a "cursor" ?
+struct ec_parsed {
+   struct ec_parsed_tree *root;
+   stuct ec_parsed_tree *cursor;
+};
+*/
+
 struct ec_parsed {
 	TAILQ_ENTRY(ec_parsed) next;
 	struct ec_parsed_list children;
diff --git a/lib/main-readline.c b/lib/main-readline.c
index 141c8cd..9dc0380 100644
--- a/lib/main-readline.c
+++ b/lib/main-readline.c
@@ -63,15 +63,15 @@ static char *my_completion_entry(const char *s, int state)
 
 	(void)s;
 
-	/* don't append a quote */
+	/* Don't append a quote. Note: there are still some bugs when
+	 * completing a quoted token. */
 	rl_completion_suppress_quote = 1;
-	rl_basic_quote_characters = "";
+	rl_completer_quote_characters = "\"'";
 
 	if (state == 0) {
 		char *line;
 
 		ec_completed_free(c);
-
 		line = strdup(rl_line_buffer);
 		if (line == NULL)
 			return NULL;
@@ -83,7 +83,7 @@ static char *my_completion_entry(const char *s, int state)
 			return NULL;
 
 		ec_completed_iter_free(iter);
-		iter = ec_completed_iter(c, EC_COMP_FULL | EC_PARTIAL_MATCH);
+		iter = ec_completed_iter(c, EC_COMP_FULL | EC_COMP_PARTIAL);
 		if (iter == NULL)
 			return NULL;
 	}
@@ -134,12 +134,10 @@ static char *get_node_help(const struct ec_completed_item *item)
 	char *help = NULL;
 	const char *node_help = NULL;
 	const char *node_desc = NULL;
-//	size_t i;
 
-	(void)item;
-#if 1
 	grp = ec_completed_item_get_grp(item);
 	state = grp->state;
+	ec_parsed_dump(stdout, ec_parsed_get_root(state));
 	for (state = grp->state; state != NULL;
 	     state = ec_parsed_get_parent(state)) {
 		node = ec_parsed_get_node(state);
@@ -148,11 +146,6 @@ static char *get_node_help(const struct ec_completed_item *item)
 		if (node_desc == NULL)
 			node_desc = ec_node_desc(node);
 	}
-#else
-	node = ec_completed_item_get_node(item);
-	node_help = ec_keyval_get(ec_node_attrs(node), "help");
-	node_desc = ec_node_desc(node);
-#endif
 
 	if (node_help == NULL)
 		node_help = "-";
@@ -199,12 +192,12 @@ static int show_help(int ignore, int invoking_key)
 	if (c == NULL)
 		goto fail;
 
-	//ec_completed_dump(stdout, c);
+	ec_completed_dump(stdout, c);
 
 	/* let's display one contextual help per node */
 	count = 0;
 	iter = ec_completed_iter(c,
-		EC_COMP_UNKNOWN | EC_COMP_FULL | EC_PARTIAL_MATCH);
+		EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL);
 	if (iter == NULL)
 		goto fail;
 
diff --git a/lib/todo.txt b/lib/todo.txt
index 734de6b..7a73930 100644
--- a/lib/todo.txt
+++ b/lib/todo.txt
@@ -322,6 +322,17 @@ changes:
   - ec_completed_item_set_display()
   - ec_completed_item_add()
 
+-----
+
+sh_lex
+  or
+    str(foo)
+    str(foo2)
+    str(bar)
+
+complete(sh_lex, ["'fo"])
+  complete(sh_lex, ["fo"]) -> ["foo", "foo2"]
+  
 
 -----