save
authorOlivier Matz <zer0@droids-corp.org>
Thu, 11 Jan 2018 18:39:52 +0000 (19:39 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 11 Jan 2018 18:39:52 +0000 (19:39 +0100)
17 files changed:
lib/ecoli_completed.c
lib/ecoli_completed.h
lib/ecoli_keyval.c
lib/ecoli_keyval.h
lib/ecoli_node_cmd.c
lib/ecoli_node_expr.c
lib/ecoli_node_many.c
lib/ecoli_node_once.c
lib/ecoli_node_option.c
lib/ecoli_node_re_lex.c
lib/ecoli_node_seq.c
lib/ecoli_node_sh_lex.c
lib/ecoli_node_subset.c
lib/ecoli_parsed.c
lib/ecoli_parsed.h
lib/main-readline.c
lib/todo.txt

index 4836803..a39c719 100644 (file)
@@ -103,7 +103,7 @@ ec_node_complete_child(struct ec_node *node,
        child_state = ec_parsed();
        if (child_state == NULL)
                return -ENOMEM;
-       child_state->node = node;
+       ec_parsed_set_node(child_state, node);
        ec_parsed_add_child(parsed_state, child_state);
 
        ret = node->type->complete(node, completed, child_state, strvec);
@@ -119,7 +119,7 @@ ec_node_complete_child(struct ec_node *node,
 #endif
 
        ec_parsed_del_child(parsed_state, child_state);
-       assert(TAILQ_EMPTY(&child_state->children));
+       assert(!ec_parsed_has_child(child_state));
        ec_parsed_free(child_state);
 
        return 0;
@@ -223,7 +223,7 @@ ec_completed_item(struct ec_parsed *state, const struct ec_node *node)
        /* write path in array */
        for (p = state, len = 0; p != NULL;
             p = ec_parsed_get_parent(p), len++)
-               item->path[len] = p->node;
+               item->path[len] = ec_parsed_get_node(p);
 
        item->type = EC_NO_MATCH;
        item->node = node;
@@ -371,6 +371,12 @@ ec_completed_item_get_type(const struct ec_completed_item *item)
        return item->type;
 }
 
+const struct ec_node *
+ec_completed_item_get_node(const struct ec_completed_item *item)
+{
+       return item->node;
+}
+
 void ec_completed_item_free(struct ec_completed_item *item)
 {
        if (item == NULL)
index e66cd71..4054a1a 100644 (file)
@@ -154,6 +154,14 @@ ec_completed_item_get_display(const struct ec_completed_item *item);
 enum ec_completed_type
 ec_completed_item_get_type(const struct ec_completed_item *item);
 
+/**
+ * Get the node associated to a completion item.
+ *
+ *
+ */
+const struct ec_node *
+ec_completed_item_get_node(const struct ec_completed_item *item);
+
 /**
  *
  *
index dbc5522..5345ccf 100644 (file)
@@ -49,19 +49,24 @@ EC_LOG_TYPE_REGISTER(keyval);
 static uint32_t ec_keyval_seed;
 
 struct ec_keyval_elt {
-       LIST_ENTRY(ec_keyval_elt) next;
        char *key;
        void *val;
        uint32_t hash;
        ec_keyval_elt_free_t free;
+       unsigned int refcount;
 };
 
-LIST_HEAD(ec_keyval_elt_list, ec_keyval_elt);
+struct ec_keyval_elt_ref {
+       LIST_ENTRY(ec_keyval_elt_ref) next;
+       struct ec_keyval_elt *elt;
+};
+
+LIST_HEAD(ec_keyval_elt_ref_list, ec_keyval_elt_ref);
 
 struct ec_keyval {
        size_t len;
        size_t table_size;
-       struct ec_keyval_elt_list *table;
+       struct ec_keyval_elt_ref_list *table;
 };
 
 struct ec_keyval *ec_keyval(void)
@@ -75,10 +80,10 @@ struct ec_keyval *ec_keyval(void)
        return keyval;
 }
 
-static struct ec_keyval_elt *ec_keyval_lookup(const struct ec_keyval *keyval,
-       const char *key)
+static struct ec_keyval_elt_ref *
+ec_keyval_lookup(const struct ec_keyval *keyval, const char *key)
 {
-       struct ec_keyval_elt *elt;
+       struct ec_keyval_elt_ref *ref;
        uint32_t h, mask = keyval->table_size - 1;
 
        if (keyval == NULL || key == NULL) {
@@ -91,10 +96,10 @@ static struct ec_keyval_elt *ec_keyval_lookup(const struct ec_keyval *keyval,
        }
 
        h = ec_murmurhash3(key, strlen(key), ec_keyval_seed);
-       LIST_FOREACH(elt, &keyval->table[h & mask], next) {
-               if (strcmp(elt->key, key) == 0) {
+       LIST_FOREACH(ref, &keyval->table[h & mask], next) {
+               if (strcmp(ref->elt->key, key) == 0) {
                        errno = 0;
-                       return elt;
+                       return ref;
                }
        }
 
@@ -102,15 +107,21 @@ static struct ec_keyval_elt *ec_keyval_lookup(const struct ec_keyval *keyval,
        return NULL;
 }
 
-static void ec_keyval_elt_free(struct ec_keyval_elt *elt)
+static void ec_keyval_elt_ref_free(struct ec_keyval_elt_ref *ref)
 {
-       if (elt == NULL)
+       struct ec_keyval_elt *elt;
+
+       if (ref == NULL)
                return;
 
-       ec_free(elt->key);
-       if (elt->free != NULL)
-               elt->free(elt->val);
-       ec_free(elt);
+       elt = ref->elt;
+       if (elt != NULL && --elt->refcount == 0) {
+               ec_free(elt->key);
+               if (elt->free != NULL)
+                       elt->free(elt->val);
+               ec_free(elt);
+       }
+       ec_free(ref);
 }
 
 bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key)
@@ -120,27 +131,27 @@ bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key)
 
 void *ec_keyval_get(const struct ec_keyval *keyval, const char *key)
 {
-       struct ec_keyval_elt *elt;
+       struct ec_keyval_elt_ref *ref;
 
-       elt = ec_keyval_lookup(keyval, key);
-       if (elt == NULL)
+       ref = ec_keyval_lookup(keyval, key);
+       if (ref == NULL)
                return NULL;
 
-       return elt->val;
+       return ref->elt->val;
 }
 
 int ec_keyval_del(struct ec_keyval *keyval, const char *key)
 {
-       struct ec_keyval_elt *elt;
+       struct ec_keyval_elt_ref *ref;
 
-       elt = ec_keyval_lookup(keyval, key);
-       if (elt == NULL)
+       ref = ec_keyval_lookup(keyval, key);
+       if (ref == NULL)
                return -1;
 
        /* we could resize table here */
 
-       LIST_REMOVE(elt, next);
-       ec_keyval_elt_free(elt);
+       LIST_REMOVE(ref, next);
+       ec_keyval_elt_ref_free(ref);
        keyval->len--;
 
        return 0;
@@ -148,8 +159,8 @@ int ec_keyval_del(struct ec_keyval *keyval, const char *key)
 
 static int ec_keyval_table_resize(struct ec_keyval *keyval, size_t new_size)
 {
-       struct ec_keyval_elt_list *new_table;
-       struct ec_keyval_elt *elt;
+       struct ec_keyval_elt_ref_list *new_table;
+       struct ec_keyval_elt_ref *ref;
        size_t i;
 
        if (new_size == 0 || (new_size & (new_size - 1))) {
@@ -163,10 +174,11 @@ static int ec_keyval_table_resize(struct ec_keyval *keyval, size_t new_size)
 
        for (i = 0; i < keyval->table_size; i++) {
                while (!LIST_EMPTY(&keyval->table[i])) {
-                       elt = LIST_FIRST(&keyval->table[i]);
-                       LIST_REMOVE(elt, next);
-                       LIST_INSERT_HEAD(&new_table[elt->hash & (new_size - 1)],
-                                       elt, next);
+                       ref = LIST_FIRST(&keyval->table[i]);
+                       LIST_REMOVE(ref, next);
+                       LIST_INSERT_HEAD(
+                               &new_table[ref->elt->hash & (new_size - 1)],
+                               ref, next);
                }
        }
 
@@ -177,27 +189,15 @@ static int ec_keyval_table_resize(struct ec_keyval *keyval, size_t new_size)
        return 0;
 }
 
-int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
-       ec_keyval_elt_free_t free_cb)
+static int
+__ec_keyval_set(struct ec_keyval *keyval, struct ec_keyval_elt_ref *ref)
 {
-       struct ec_keyval_elt *elt;
-       uint32_t h, mask;
        size_t new_size;
+       uint32_t mask;
        int ret;
 
-       if (keyval == NULL || key == NULL) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       elt = ec_keyval_lookup(keyval, key);
-       if (elt != NULL) {
-               if (elt->free != NULL)
-                       elt->free(elt->val);
-               elt->val = val;
-               elt->free = free_cb;
-               return 0;
-       }
+       /* remove previous entry if any */
+       ec_keyval_del(keyval, ref->elt->key);
 
        if (keyval->len >= keyval->table_size) {
                if (keyval->table_size != 0)
@@ -206,40 +206,62 @@ int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
                        new_size =  1 << FACTOR;
                ret = ec_keyval_table_resize(keyval, new_size);
                if (ret < 0)
-                       goto fail;
+                       return ret;
        }
 
+       mask = keyval->table_size - 1;
+       LIST_INSERT_HEAD(&keyval->table[ref->elt->hash & mask], ref, next);
+       keyval->len++;
+
+       return 0;
+}
+
+int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
+       ec_keyval_elt_free_t free_cb)
+{
+       struct ec_keyval_elt *elt = NULL;
+       struct ec_keyval_elt_ref *ref = NULL;
+       uint32_t h;
+
+       if (keyval == NULL || key == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       ref = ec_calloc(1, sizeof(*ref));
+       if (ref == NULL)
+               goto fail;
+
        elt = ec_calloc(1, sizeof(*elt));
        if (elt == NULL)
                goto fail;
+
+       ref->elt = elt;
+       elt->refcount = 1;
+       elt->val = val;
+       val = NULL;
+       elt->free = free_cb;
        elt->key = ec_strdup(key);
        if (elt->key == NULL)
                goto fail;
-       elt->val = val;
-       elt->free = free_cb;
        h = ec_murmurhash3(key, strlen(key), ec_keyval_seed);
        elt->hash = h;
 
-       mask = keyval->table_size - 1;
-       LIST_INSERT_HEAD(&keyval->table[h & mask], elt, next);
-       keyval->len++;
+       if (__ec_keyval_set(keyval, ref) < 0)
+               goto fail;
 
        return 0;
 
 fail:
-       if (free_cb != NULL)
+       if (free_cb != NULL && val != NULL)
                free_cb(val);
-       if (elt != NULL) {
-               ec_free(elt->key);
-               ec_free(elt);
-       }
-
-       return ret;
+       ec_keyval_elt_ref_free(ref);
+       return -1;
 }
 
 void ec_keyval_free(struct ec_keyval *keyval)
 {
-       struct ec_keyval_elt *elt;
+       struct ec_keyval_elt_ref *ref;
        size_t i;
 
        if (keyval == NULL)
@@ -247,9 +269,9 @@ void ec_keyval_free(struct ec_keyval *keyval)
 
        for (i = 0; i < keyval->table_size; i++) {
                while (!LIST_EMPTY(&keyval->table[i])) {
-                       elt = LIST_FIRST(&keyval->table[i]);
-                       LIST_REMOVE(elt, next);
-                       ec_keyval_elt_free(elt);
+                       ref = LIST_FIRST(&keyval->table[i]);
+                       LIST_REMOVE(ref, next);
+                       ec_keyval_elt_ref_free(ref);
                }
        }
        ec_free(keyval->table);
@@ -263,7 +285,7 @@ size_t ec_keyval_len(const struct ec_keyval *keyval)
 
 void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval)
 {
-       struct ec_keyval_elt *elt;
+       struct ec_keyval_elt_ref *ref;
        size_t i;
 
        if (keyval == NULL) {
@@ -273,13 +295,44 @@ void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval)
 
        fprintf(out, "keyval:\n");
        for (i = 0; i < keyval->table_size; i++) {
-               LIST_FOREACH(elt, &keyval->table[i], next) {
+               LIST_FOREACH(ref, &keyval->table[i], next) {
                        fprintf(out, "  %s: %p\n",
-                               elt->key, elt->val);
+                               ref->elt->key, ref->elt->val);
                }
        }
 }
 
+struct ec_keyval *ec_keyval_dup(const struct ec_keyval *keyval)
+{
+       struct ec_keyval *dup = NULL;
+       struct ec_keyval_elt_ref *ref, *dup_ref = NULL;
+       size_t i;
+
+       dup = ec_keyval();
+       if (dup == NULL)
+               return NULL;
+
+       for (i = 0; i < keyval->table_size; i++) {
+               LIST_FOREACH(ref, &keyval->table[i], next) {
+                       dup_ref = ec_calloc(1, sizeof(*ref));
+                       if (dup_ref == NULL)
+                               goto fail;
+                       dup_ref->elt = ref->elt;
+                       ref->elt->refcount++;
+
+                       if (__ec_keyval_set(dup, dup_ref) < 0)
+                               goto fail;
+               }
+       }
+
+       return dup;
+
+fail:
+       ec_keyval_elt_ref_free(dup_ref);
+       ec_keyval_free(dup);
+       return NULL;
+}
+
 static int ec_keyval_init_func(void)
 {
        int fd;
@@ -309,7 +362,7 @@ EC_INIT_REGISTER(ec_keyval_init);
 /* LCOV_EXCL_START */
 static int ec_keyval_testcase(void)
 {
-       struct ec_keyval *keyval;
+       struct ec_keyval *keyval, *dup;
        char *val;
        size_t i;
        int ret;
@@ -355,11 +408,23 @@ static int ec_keyval_testcase(void)
        ec_keyval_dump(stdout, keyval);
 
        for (i = 0; i < 100; i++) {
-               char buf[8];
-               snprintf(buf, sizeof(buf), "k%zd", i);
-               ret = ec_keyval_set(keyval, buf, "val", NULL);
+               char key[8];
+               snprintf(key, sizeof(key), "k%zd", i);
+               ret = ec_keyval_set(keyval, key, "val", NULL);
                EC_TEST_ASSERT_STR(ret == 0, "cannot set key");
        }
+       dup = ec_keyval_dup(keyval);
+       EC_TEST_ASSERT_STR(dup != NULL, "cannot duplicate keyval");
+       if (dup != NULL) {
+               for (i = 0; i < 100; i++) {
+                       char key[8];
+                       snprintf(key, sizeof(key), "k%zd", i);
+                       val = ec_keyval_get(dup, key);
+                       EC_TEST_ASSERT(val != NULL && !strcmp(val, "val"));
+               }
+               ec_keyval_free(dup);
+               dup = NULL;
+       }
 
        /* einval */
        ret = ec_keyval_set(keyval, NULL, "val1", NULL);
@@ -369,7 +434,6 @@ static int ec_keyval_testcase(void)
 
        ec_keyval_free(keyval);
 
-
        return 0;
 }
 /* LCOV_EXCL_STOP */
index 3d739ff..5622b23 100644 (file)
@@ -101,6 +101,7 @@ int ec_keyval_del(struct ec_keyval *keyval, const char *key);
  *   object is destroyed (ec_keyval_del() or ec_keyval_free()).
  * @return
  *   0 on success, or -1 on error (errno is set).
+ *   On error, the passed value is freed (free_cb(val) is called).
  */
 int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
        ec_keyval_elt_free_t free_cb);
@@ -123,6 +124,9 @@ void ec_keyval_free(struct ec_keyval *keyval);
  */
 size_t ec_keyval_len(const struct ec_keyval *keyval);
 
+/* XXX help */
+struct ec_keyval *ec_keyval_dup(const struct ec_keyval *keyval);
+
 /**
  * Dump a hash table.
  *
index 0aca9b1..3068fc0 100644 (file)
@@ -353,9 +353,9 @@ static int ec_node_cmd_build(struct ec_node *gen_node)
        ret = -EINVAL;
        if (!ec_parsed_matches(p))
                goto fail;
-       if (TAILQ_EMPTY(&p->children))
+       if (!ec_parsed_has_child(p))
                goto fail;
-       if (TAILQ_EMPTY(&TAILQ_FIRST(&p->children)->children))
+       if (!ec_parsed_has_child(ec_parsed_get_first_child(p)))
                goto fail;
 
        ret = -ENOMEM;
@@ -363,7 +363,7 @@ static int ec_node_cmd_build(struct ec_node *gen_node)
        if (cmd == NULL)
                goto fail;
 
-       TAILQ_FOREACH(child, &TAILQ_FIRST(&p->children)->children, next) {
+       EC_PARSED_FOREACH_CHILD(child, ec_parsed_get_first_child(p)) {
                ret = ec_node_expr_eval(&result, expr, child,
                        &test_ops, node);
                if (ret < 0)
index 9207e4d..58d4969 100644 (file)
@@ -527,7 +527,7 @@ static int eval_expression(struct result *result,
        memset(result, 0, sizeof(*result));
        memset(&child_result, 0, sizeof(child_result));
 
-       type = get_node_type(expr_gen_node, parsed->node);
+       type = get_node_type(expr_gen_node, ec_parsed_get_node(parsed));
        if (type == VAL) {
                ret = ops->eval_var(&result->val, userctx, parsed);
                if (ret < 0)
@@ -538,9 +538,9 @@ static int eval_expression(struct result *result,
                result->op_type = type;
        }
 
-       TAILQ_FOREACH(child, &parsed->children, next) {
+       EC_PARSED_FOREACH_CHILD(child, parsed) {
 
-               type = get_node_type(expr_gen_node, child->node);
+               type = get_node_type(expr_gen_node, ec_parsed_get_node(child));
                if (type == PAREN_OPEN) {
                        open = child;
                        continue;
index b40b3bd..cc5f738 100644 (file)
@@ -71,13 +71,14 @@ static int ec_node_many_parse(const struct ec_node *gen_node,
                }
 
                ret = ec_node_parse_child(node->child, state, childvec);
+               if (ret < 0)
+                       goto fail;
+
                ec_strvec_free(childvec);
                childvec = NULL;
 
                if (ret == EC_PARSED_NOMATCH)
                        break;
-               else if (ret < 0)
-                       goto fail;
 
                /* it matches an empty strvec, no need to continue */
                if (ret == 0) {
@@ -133,7 +134,7 @@ __ec_node_many_complete(struct ec_node_many *node, unsigned int max,
                        goto fail;
 
                ret = ec_node_parse_child(node->child, parsed, childvec);
-               if (ret < 0 && ret != EC_PARSED_NOMATCH)
+               if (ret < 0)
                        goto fail;
 
                ec_strvec_free(childvec);
index 54aa67b..0df4bcd 100644 (file)
@@ -60,10 +60,10 @@ count_node(struct ec_parsed *parsed, const struct ec_node *node)
        if (parsed == NULL)
                return 0;
 
-       if (parsed->node == node)
+       if (ec_parsed_get_node(parsed) == node)
                count++;
 
-       TAILQ_FOREACH(child, &parsed->children, next)
+       EC_PARSED_FOREACH_CHILD(child, parsed)
                count += count_node(child, node);
 
        return count;
index 1dcdd88..ab4f9f3 100644 (file)
@@ -57,10 +57,11 @@ ec_node_option_parse(const struct ec_node *gen_node,
        int ret;
 
        ret = ec_node_parse_child(node->child, state, strvec);
+       if (ret < 0)
+               return ret;
+
        if (ret == EC_PARSED_NOMATCH)
                return 0;
-       else if (ret < 0)
-               return ret;
 
        return ret;
 }
index 669c9fb..14b3e46 100644 (file)
@@ -113,15 +113,16 @@ ec_node_re_lex_parse(const struct ec_node *gen_node,
        }
 
        ret = ec_node_parse_child(node->child, state, new_vec);
-       if (ret >= 0) {
-               if ((unsigned)ret == ec_strvec_len(new_vec)) {
-                       ret = 1;
-               } else {
-                       child_parsed = ec_parsed_get_last_child(state);
-                       ec_parsed_del_child(state, child_parsed);
-                       ec_parsed_free(child_parsed);
-                       ret = EC_PARSED_NOMATCH;
-               }
+       if (ret < 0)
+               goto fail;
+
+       if ((unsigned)ret == ec_strvec_len(new_vec)) {
+               ret = 1;
+       } else if (ret != EC_PARSED_NOMATCH) {
+               child_parsed = ec_parsed_get_last_child(state);
+               ec_parsed_del_child(state, child_parsed);
+               ec_parsed_free(child_parsed);
+               ret = EC_PARSED_NOMATCH;
        }
 
        ec_strvec_free(new_vec);
index 9240e4f..2b33503 100644 (file)
@@ -74,13 +74,15 @@ ec_node_seq_parse(const struct ec_node *gen_node,
                }
 
                ret = ec_node_parse_child(node->table[i], state, childvec);
+               if (ret < 0)
+                       goto fail;
+
                ec_strvec_free(childvec);
                childvec = NULL;
+
                if (ret == EC_PARSED_NOMATCH) {
                        ec_parsed_free_children(state);
                        return EC_PARSED_NOMATCH;
-               } else if (ret < 0) {
-                       goto fail;
                }
 
                len += ret;
@@ -130,7 +132,7 @@ __ec_node_seq_complete(struct ec_node **table, size_t table_len,
                        goto fail;
 
                ret = ec_node_parse_child(table[0], parsed, childvec);
-               if (ret < 0 && ret != EC_PARSED_NOMATCH)
+               if (ret < 0)
                        goto fail;
 
                ec_strvec_free(childvec);
index 87b3175..94d0d73 100644 (file)
@@ -255,15 +255,16 @@ ec_node_sh_lex_parse(const struct ec_node *gen_node,
        }
 
        ret = ec_node_parse_child(node->child, state, new_vec);
-       if (ret >= 0) {
-               if ((unsigned)ret == ec_strvec_len(new_vec)) {
-                       ret = 1;
-               } else {
-                       child_parsed = ec_parsed_get_last_child(state);
-                       ec_parsed_del_child(state, child_parsed);
-                       ec_parsed_free(child_parsed);
-                       ret = EC_PARSED_NOMATCH;
-               }
+       if (ret < 0)
+               goto fail;
+
+       if ((unsigned)ret == ec_strvec_len(new_vec)) {
+               ret = 1;
+       } else if (ret != EC_PARSED_NOMATCH) {
+               child_parsed = ec_parsed_get_last_child(state);
+               ec_parsed_del_child(state, child_parsed);
+               ec_parsed_free(child_parsed);
+               ret = EC_PARSED_NOMATCH;
        }
 
        ec_strvec_free(new_vec);
index 1803b4d..18de0cb 100644 (file)
@@ -85,10 +85,11 @@ __ec_node_subset_parse(struct parse_result *out, struct ec_node **table,
        for (i = 0; i < table_len; i++) {
                /* try to parse elt i */
                ret = ec_node_parse_child(table[i], state, strvec);
+               if (ret < 0)
+                       goto fail;
+
                if (ret == EC_PARSED_NOMATCH)
                        continue;
-               else if (ret < 0)
-                       goto fail;
 
                /* build a new table without elt i */
                for (j = 0; j < table_len; j++) {
@@ -213,10 +214,11 @@ __ec_node_subset_complete(struct ec_node **table, size_t table_len,
                        continue;
 
                ret = ec_node_parse_child(table[i], parsed, strvec);
+               if (ret < 0)
+                       goto fail;
+
                if (ret == EC_PARSED_NOMATCH)
                        continue;
-               else if (ret < 0)
-                       goto fail;
 
                len = ret;
                childvec = ec_strvec_ndup(strvec, len,
index e8f3cb9..d338953 100644 (file)
 #include <ecoli_node.h>
 #include <ecoli_parsed.h>
 
+TAILQ_HEAD(ec_parsed_list, ec_parsed);
+
+struct ec_parsed {
+       TAILQ_ENTRY(ec_parsed) next;
+       struct ec_parsed_list children;
+       struct ec_parsed *parent;
+       const struct ec_node *node;
+       struct ec_strvec *strvec;
+       struct ec_keyval *attrs;
+};
+
 int ec_node_parse_child(struct ec_node *node, struct ec_parsed *state,
                        const struct ec_strvec *strvec)
 {
@@ -65,16 +76,17 @@ int ec_node_parse_child(struct ec_node *node, struct ec_parsed *state,
        if (child == NULL)
                return -ENOMEM;
 
-       child->node = node;
+       ec_parsed_set_node(child, node);
        ec_parsed_add_child(state, child);
        ret = node->type->parse(node, child, strvec);
+       if (ret < 0)
+               return ret;
+
        if (ret == EC_PARSED_NOMATCH) {
                ec_parsed_del_child(state, child);
                assert(TAILQ_EMPTY(&child->children));
                ec_parsed_free(child);
                return ret;
-       } else if (ret < 0) {
-               return ret;
        }
 
        match_strvec = ec_strvec_ndup(strvec, 0, ret);
@@ -97,10 +109,12 @@ struct ec_parsed *ec_node_parse_strvec(struct ec_node *node,
                return NULL;
 
        ret = ec_node_parse_child(node, parsed, strvec);
-       if (ret < 0 && ret != EC_PARSED_NOMATCH) {
+       if (ret < 0) {
                ec_parsed_free(parsed);
-               parsed = NULL;
-       } else if (ret != EC_PARSED_NOMATCH) {
+               return NULL;
+       }
+
+       if (ret != EC_PARSED_NOMATCH) {
                /* remove dummy root node */
                child_parsed = ec_parsed_get_last_child(parsed);
                ec_parsed_del_child(parsed, child_parsed);
@@ -161,6 +175,45 @@ struct ec_parsed *ec_parsed(void)
        return NULL;
 }
 
+struct ec_parsed *ec_parsed_dup(const struct ec_parsed *parsed)
+{
+       struct ec_parsed *dup = NULL;
+       struct ec_parsed *child, *dup_child;
+       struct ec_keyval *attrs = NULL;
+
+       if (parsed == NULL)
+               return NULL;
+
+       dup = ec_parsed();
+       if (dup == NULL)
+               return NULL;
+
+       attrs = ec_keyval_dup(parsed->attrs);
+       if (attrs == NULL)
+               goto fail;
+       ec_keyval_free(dup->attrs);
+       dup->attrs = attrs;
+
+       if (parsed->strvec != NULL) {
+               dup->strvec = ec_strvec_dup(parsed->strvec);
+               if (dup->strvec == NULL)
+                       goto fail;
+       }
+
+       TAILQ_FOREACH(child, &parsed->children, next) {
+               dup_child = ec_parsed_dup(child);
+               if (dup_child == NULL)
+                       goto fail;
+               ec_parsed_add_child(dup, dup_child);
+       }
+
+       return dup;
+
+fail:
+       ec_parsed_free(dup);
+       return NULL;
+}
+
 void ec_parsed_free_children(struct ec_parsed *parsed)
 {
        struct ec_parsed *child;
@@ -247,11 +300,37 @@ void ec_parsed_del_child(struct ec_parsed *parsed, // XXX rename del in unlink?
 }
 
 struct ec_parsed *
-ec_parsed_get_last_child(struct ec_parsed *parsed)
+ec_parsed_get_first_child(const struct ec_parsed *parsed)
+{
+       return TAILQ_FIRST(&parsed->children);
+}
+
+struct ec_parsed *
+ec_parsed_get_last_child(const struct ec_parsed *parsed)
 {
        return TAILQ_LAST(&parsed->children, ec_parsed_list);
 }
 
+struct ec_parsed *ec_parsed_get_next(const struct ec_parsed *parsed)
+{
+       return TAILQ_NEXT(parsed, next);
+}
+
+bool ec_parsed_has_child(const struct ec_parsed *parsed)
+{
+       return !TAILQ_EMPTY(&parsed->children);
+}
+
+const struct ec_node *ec_parsed_get_node(const struct ec_parsed *parsed)
+{
+       return parsed->node;
+}
+
+void ec_parsed_set_node(struct ec_parsed *parsed, const struct ec_node *node)
+{
+       parsed->node = node;
+}
+
 void ec_parsed_del_last_child(struct ec_parsed *parsed) // rename in free
 {
        struct ec_parsed *child;
index 0755336..afb765e 100644 (file)
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+/**
+ * Node parse API.
+ *
+ * The parse operation is to check if an input (a string or vector of
+ * strings) matches the node tree. On success, the result is stored in a
+ * tree that describes which part of the input matches which node.
+ */
+
 #ifndef ECOLI_PARSED_
 #define ECOLI_PARSED_
 
 #include <stdio.h>
 
 struct ec_node;
+struct ec_parsed;
 
-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;
-       struct ec_parsed_list children;
-       struct ec_parsed *parent;
-       const struct ec_node *node;
-       struct ec_strvec *strvec;
-       struct ec_keyval *attrs;
-};
-
+/**
+ * Create an empty parse tree.
+ *
+ * @return
+ *   The empty parse tree.
+ */
 struct ec_parsed *ec_parsed(void);
+
+/**
+ *
+ *
+ *
+ */
 void ec_parsed_free(struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
 void ec_parsed_free_children(struct ec_parsed *parsed);
 
+/**
+ *
+ *
+ *
+ */
+struct ec_parsed *ec_parsed_dup(const struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
 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
@@ -63,12 +87,28 @@ const struct ec_strvec *ec_parsed_strvec(const struct ec_parsed *parsed);
 /* a NULL return value is an error, with errno set
   ENOTSUP: no ->parse() operation
 */
+/**
+ *
+ *
+ *
+ */
 struct ec_parsed *ec_node_parse(struct ec_node *node, const char *str);
 
+/**
+ *
+ *
+ *
+ */
 struct ec_parsed *ec_node_parse_strvec(struct ec_node *node,
                                const struct ec_strvec *strvec);
 
-#define EC_PARSED_NOMATCH INT_MIN
+/**
+ *
+ *
+ *
+ */
+#define EC_PARSED_NOMATCH INT_MAX
+
 /* internal: used by nodes
  *
  * state is the current parse tree, which is built piece by piece while
@@ -79,8 +119,7 @@ struct ec_parsed *ec_node_parse_strvec(struct ec_node *node,
  *   possible descendants.
  *
  * return:
- * XXX change EC_PARSED_NOMATCH to INT_MAX?
- * EC_PARSED_NOMATCH (negative) if it does not match
+ * EC_PARSED_NOMATCH (positive) if it does not match
  * any other negative value (-errno) for other errors
  * the number of matched strings in strvec
  * XXX state is not freed on error ?
@@ -89,23 +128,127 @@ int ec_node_parse_child(struct ec_node *node,
                        struct ec_parsed *state,
                        const struct ec_strvec *strvec);
 
+/**
+ *
+ *
+ *
+ */
 void ec_parsed_add_child(struct ec_parsed *parsed,
                        struct ec_parsed *child);
+/**
+ *
+ *
+ *
+ */
 void ec_parsed_del_child(struct ec_parsed *parsed,
                        struct ec_parsed *child);
 
+/**
+ *
+ *
+ *
+ */
 struct ec_parsed *ec_parsed_get_root(struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
 struct ec_parsed *ec_parsed_get_parent(struct ec_parsed *parsed);
-struct ec_parsed *ec_parsed_get_last_child(struct ec_parsed *parsed);
+
+/**
+ * Get the first child of a tree.
+ *
+ */
+struct ec_parsed *ec_parsed_get_first_child(const struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
+struct ec_parsed *ec_parsed_get_last_child(const struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
+struct ec_parsed *ec_parsed_get_next(const struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
+#define EC_PARSED_FOREACH_CHILD(child, parsed)                 \
+       for (child = ec_parsed_get_first_child(parsed);         \
+               child != NULL;                                  \
+               child = ec_parsed_get_next(child))              \
+
+/**
+ *
+ *
+ *
+ */
+bool ec_parsed_has_child(const struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
+const struct ec_node *ec_parsed_get_node(const struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
+void ec_parsed_set_node(struct ec_parsed *parsed, const struct ec_node *node);
+
+/**
+ *
+ *
+ *
+ */
 void ec_parsed_del_last_child(struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
 int ec_parsed_get_path(struct ec_parsed *parsed, struct ec_node **path);
 
+/**
+ *
+ *
+ *
+ */
 void ec_parsed_dump(FILE *out, const struct ec_parsed *parsed);
 
+/**
+ *
+ *
+ *
+ */
 struct ec_parsed *ec_parsed_find_first(struct ec_parsed *parsed,
        const char *id);
 
+/**
+ *
+ *
+ *
+ */
 size_t ec_parsed_len(const struct ec_parsed *parsed);
+
+/**
+ *
+ *
+ *
+ */
 size_t ec_parsed_matches(const struct ec_parsed *parsed);
 
 #endif
index 5bbe18e..da447d8 100644 (file)
@@ -126,21 +126,17 @@ static char **my_attempted_completion(const char *text, int start, int end)
 }
 
 /* this function builds the help string */
-static char *get_node_help(const struct ec_completed_node *compnode)
+static char *get_node_help(const struct ec_completed_item *item)
 {
-//     const struct ec_completed_item *item;
-//     const struct ec_node *node;
+       const struct ec_node *node;
        char *help = NULL;
        const char *node_help = NULL;
        const char *node_desc = NULL;
 //     size_t i;
 
-       (void)compnode;
+       (void)item;
 #if 0 //XXX
-       /* Since we display only one help per node, only look at the first item
-        * to get the path. The objective is to retrieve the most precise
-        * help for this node. */
-       item = TAILQ_FIRST(&compnode->items);
+       /* Retrieve the most precise help for this node. */
        for (i = 0; i < item->pathlen; i++) {
                node = item->path[i];
                if (node_help == NULL)
@@ -148,10 +144,14 @@ static char *get_node_help(const struct ec_completed_node *compnode)
                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 = "";
+               node_help = "-";
        if (node_desc == NULL)
                return NULL;
 
@@ -163,11 +163,13 @@ static char *get_node_help(const struct ec_completed_node *compnode)
 
 static int show_help(int ignore, int invoking_key)
 {
-       const struct ec_completed_node *compnode;
+       struct ec_completed_iter *iter;
+       const struct ec_completed_item *item;
+       const struct ec_node *prev_node = NULL;
        struct ec_completed *c;
        struct ec_parsed *p;
-       char *line;
-       unsigned int count, i;
+       char *line = NULL;
+       unsigned int count;
        char **helps = NULL;
        int match = 0;
 
@@ -176,54 +178,74 @@ static int show_help(int ignore, int invoking_key)
 
        line = strdup(rl_line_buffer);
        if (line == NULL)
-               return 1;
+               goto fail;
 
        /* check if the current line matches */
        p = ec_node_parse(commands, line);
        if (ec_parsed_matches(p))
                match = 1;
        ec_parsed_free(p);
+       p = NULL;
 
        /* complete at current cursor position */
        line[rl_point] = '\0';
        c = ec_node_complete(commands, line);
-       //ec_completed_dump(stdout, c);
        free(line);
+       line = NULL;
        if (c == NULL)
-               return 1;
+               goto fail;
+
+       //ec_completed_dump(stdout, c);
 
        /* let's display one contextual help per node */
        count = 0;
-       TAILQ_FOREACH(compnode, &c->nodes, next) {
-               if (TAILQ_EMPTY(&compnode->items))
-                       continue;
-               count++;
-       }
+       iter = ec_completed_iter(c, EC_NO_MATCH | EC_MATCH | EC_PARTIAL_MATCH);
+       if (iter == NULL)
+               goto fail;
 
-       helps = calloc(count + match + 1, sizeof(char *));
+       /* strangely, rl_display_match_list() expects first index at 1 */
+       helps = calloc(match + 1, sizeof(char *));
        if (helps == NULL)
-               return 1;
-
+               goto fail;
        if (match)
                helps[1] = "<return>";
 
-       /* strangely, rl_display_match_list() expects first index at 1 */
-       i = match + 1;
-       TAILQ_FOREACH(compnode, &c->nodes, next) {
-               if (TAILQ_EMPTY(&compnode->items))
+       while ((item = ec_completed_iter_next(iter)) != NULL) {
+               const struct ec_node *node;
+               char **tmp;
+
+               /* keep one help per node, skip other items  */
+               node = ec_completed_item_get_node(item);
+               if (node == prev_node)
                        continue;
-               helps[i++] = get_node_help(compnode);
+
+               tmp = realloc(helps, (count + match + 2) * sizeof(char *));
+               if (tmp == NULL)
+                       goto fail;
+               helps = tmp;
+               helps[count + match + 1] = get_node_help(item);
+               count++;
        }
 
+       ec_completed_iter_free(iter);
        ec_completed_free(c);
-
        rl_display_match_list(helps, count + match, 1000); /* XXX 1000 */
-
        rl_forced_update_display();
 
        return 0;
 
-       // free helps[n] XXX on error ?
+fail:
+       ec_completed_iter_free(iter);
+       ec_parsed_free(p);
+       free(line);
+       ec_completed_free(c);
+       if (helps != NULL) {
+               while (count--)
+                       free(helps[count + match + 1]);
+       }
+       free(helps);
+
+       return 1;
 }
 
 static int create_commands(void)
index 0001de1..58b7996 100644 (file)
@@ -202,7 +202,7 @@ return values:
 - 0: success, child->strvec is set by node (NULL = no_match)
 - -1: error (errno is set)
 maybe complex to use:
-- the node must set it (ex: "return ec_parsed_node_match()")
+- the node must set the match (ex: "return ec_parsed_node_match()")
 - the caller must use accessor to check if it matches or not
 
 alternative idea for return values:
@@ -210,6 +210,7 @@ alternative idea for return values:
 - -1: error (errno is set)
 - -2 or MAX_INT: success, but no match
 This is strange to have a specific value for no match
+With MAX_INT, this is the best (less bad) alternative
 
 alternative idea for return values:
 - ec_parse_result_match(n_tokens >= 0)
@@ -251,6 +252,11 @@ partial: beginning of a completion, does not match the token
          (good example is a directory in a path)
 unknown: could complete, but the node does not know how
 
+struct completion_item {
+  const char *value;
+  const char *disp;
+}
+
 struct completed_elt {
   ec_parsed *parse_tree; // current tree state
   ec_node *last;         // last node of the tree
@@ -274,15 +280,15 @@ a node can filter the completions
 
 [] ->
   foo   3 str(foo)
-  seq
-    option
-      str(foo) <-
+    seq
+      option
+        str(foo) <-
 
-  ?       5 int(1,10)
-  seq
-    option
-    or
-      int <-
+  ""    5 int(1,10)
+    seq
+      option
+      or
+        int <-
 
   bar   6 str(bar)
   foo   7 str(bar)
@@ -308,7 +314,6 @@ a node can filter the completions
 changes:
 - a completion item should contain a strvec for the value
   (the display string remains a string)
-- use a INT_MIN or a specific
 - there is maybe no good reason to split in:
   - ec_completed_item()
   - ec_completed_item_set()