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);
#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;
/* 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;
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)
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);
+
/**
*
*
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)
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) {
}
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;
}
}
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)
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;
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))) {
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);
}
}
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)
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)
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);
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) {
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;
/* 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;
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);
ec_keyval_free(keyval);
-
return 0;
}
/* LCOV_EXCL_STOP */
* 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);
*/
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.
*
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;
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)
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)
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;
}
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) {
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);
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;
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;
}
}
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);
}
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;
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);
}
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);
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++) {
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,
#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)
{
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);
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);
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;
}
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;
* 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
/* 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
* 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 ?
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
}
/* 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)
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;
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;
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)
- 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:
- -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)
(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
[] ->
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)
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()