From 4b0b6d1022749805060f792a2a83d2b70610ebbc Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 8 Mar 2018 19:50:04 +0100 Subject: [PATCH] list children in a table, not in a list so that we can add a child in a list them without modifying it --- lib/ecoli_node.c | 77 +++++++++++++++++++++++++++++++++++++--- lib/ecoli_node.h | 13 ++++--- lib/ecoli_node_cmd.c | 27 ++++++++------ lib/ecoli_node_once.c | 23 +++++++----- lib/ecoli_node_option.c | 46 ++++++++++++++++++------ lib/ecoli_node_option.h | 1 + lib/ecoli_node_or.c | 45 ++++++++++++++--------- lib/ecoli_node_seq.c | 27 ++++++++------ lib/ecoli_node_str.c | 6 ++-- lib/ecoli_node_subset.c | 41 +++++++++++++-------- lib/ecoli_node_weakref.c | 18 ++++++---- lib/ecoli_parsed.h | 5 --- lib/ecoli_vec.c | 2 +- 13 files changed, 233 insertions(+), 98 deletions(-) diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c index 17a18a8..ff52440 100644 --- a/lib/ecoli_node.c +++ b/lib/ecoli_node.c @@ -83,7 +83,7 @@ struct ec_node *__ec_node(const struct ec_node_type *type, const char *id) EC_LOG(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id); if (id == NULL) { - errno = -EINVAL; + errno = EINVAL; goto fail; } @@ -91,7 +91,6 @@ struct ec_node *__ec_node(const struct ec_node_type *type, const char *id) if (node == NULL) goto fail; - TAILQ_INIT(&node->children); node->type = type; node->refcnt = 1; @@ -150,6 +149,7 @@ void ec_node_free(struct ec_node *node) if (node->type != NULL && node->type->free_priv != NULL) node->type->free_priv(node); + ec_free(node->children); ec_free(node->id); ec_free(node->desc); ec_free(node->attrs); @@ -170,15 +170,79 @@ struct ec_node *ec_node_clone(struct ec_node *node) return node; } +size_t ec_node_get_children_count(const struct ec_node *node) +{ + return node->n_children; +} + +struct ec_node * +ec_node_get_child(const struct ec_node *node, size_t i) +{ + if (i >= ec_node_get_children_count(node)) + return NULL; + return node->children[i]; +} + +int ec_node_add_child(struct ec_node *node, struct ec_node *child) +{ + struct ec_node **children = NULL; + size_t n; + + if (node == NULL || child == NULL) { + errno = EINVAL; + goto fail; + } + + n = node->n_children; + children = ec_realloc(node->children, + (n + 1) * sizeof(child)); + if (children == NULL) + goto fail; + + children[n] = child; + node->children = children; + node->n_children = n + 1; + + return 0; + +fail: + ec_free(children); + return -1; +} + +int ec_node_del_child(struct ec_node *node, struct ec_node *child) +{ + size_t i, n; + + if (node == NULL || child == NULL) + goto fail; + + n = node->n_children; + for (i = 0; i < n; i++) { + if (node->children[i] != child) + continue; + memcpy(&node->children[i], &node->children[i+1], + (n - i - 1) * sizeof(child)); + return 0; + } + +fail: + errno = EINVAL; + return -1; +} + struct ec_node *ec_node_find(struct ec_node *node, const char *id) { struct ec_node *child, *ret; const char *node_id = ec_node_id(node); + size_t i, n; if (id != NULL && node_id != NULL && !strcmp(node_id, id)) return node; - TAILQ_FOREACH(child, &node->children, next) { + n = node->n_children; + for (i = 0; i < n; i++) { + child = node->children[i]; ret = ec_node_find(child, id); if (ret != NULL) return ret; @@ -205,7 +269,7 @@ static void __ec_node_dump(FILE *out, const char *id, *typename, *desc; struct ec_node *child; size_t maxlen; - size_t i; + size_t i, n; maxlen = ec_node_get_max_parse_len(node); id = ec_node_id(node); @@ -226,8 +290,11 @@ static void __ec_node_dump(FILE *out, fprintf(out, "maxlen=no\n"); else fprintf(out, "maxlen=%zu\n", maxlen); - TAILQ_FOREACH(child, &node->children, next) + n = node->n_children; + for (i = 0; i < n; i++) { + child = node->children[i]; __ec_node_dump(out, child, indent + 2); + } } void ec_node_dump(FILE *out, const struct ec_node *node) diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h index ac88033..6520d23 100644 --- a/lib/ecoli_node.h +++ b/lib/ecoli_node.h @@ -146,17 +146,14 @@ struct ec_node_type *ec_node_type_lookup(const char *name); */ void ec_node_type_dump(FILE *out); -TAILQ_HEAD(ec_node_list, ec_node); - struct ec_node { const struct ec_node_type *type; char *id; char *desc; struct ec_keyval *attrs; unsigned int refcnt; - - TAILQ_ENTRY(ec_node) next; - struct ec_node_list children; + struct ec_node **children; /* array of children */ + size_t n_children; /* number of children in the array */ }; /* create a new node when the type is known, typically called from the node @@ -169,6 +166,12 @@ struct ec_node *ec_node(const char *typename, const char *id); struct ec_node *ec_node_clone(struct ec_node *node); void ec_node_free(struct ec_node *node); +size_t ec_node_get_children_count(const struct ec_node *node); +struct ec_node * +ec_node_get_child(const struct ec_node *node, size_t i); +int ec_node_add_child(struct ec_node *node, struct ec_node *child); +int ec_node_del_child(struct ec_node *node, struct ec_node *child); + /** * Get the max len of strvec that can be parsed by this node * diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c index 5d47a7b..f479c3f 100644 --- a/lib/ecoli_node_cmd.c +++ b/lib/ecoli_node_cmd.c @@ -412,12 +412,15 @@ int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child) struct ec_node **table; int ret; - // XXX check node type - assert(node != NULL); - if (child == NULL) - return -EINVAL; + if (child == NULL) { + errno = EINVAL; + goto fail; + } + + if (ec_node_check_type(gen_node, &ec_node_cmd_type) < 0) + goto fail; if (node->cmd == NULL) { ret = ec_node_cmd_build(node); @@ -426,18 +429,22 @@ int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child) } table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); - if (table == NULL) { - ec_node_free(child); - return -ENOMEM; - } + if (table == NULL) + goto fail; node->table = table; + + if (ec_node_add_child(gen_node, child) < 0) + goto fail; + table[node->len] = child; node->len++; - TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed? - return 0; + +fail: + ec_node_free(child); + return -1; } struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...) diff --git a/lib/ecoli_node_once.c b/lib/ecoli_node_once.c index 940ab4b..3e716e0 100644 --- a/lib/ecoli_node_once.c +++ b/lib/ecoli_node_once.c @@ -100,7 +100,7 @@ ec_node_once_complete(const struct ec_node *gen_node, /* count the number of occurences of the node: if already parsed, * do not match */ - count = count_node(ec_parsed_get_root(parsed), node->child); //XXX + count = count_node(ec_parsed_get_root(parsed), node->child); if (count > 0) return 0; @@ -131,20 +131,25 @@ 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; - if (gen_node == NULL || child == NULL) - return -EINVAL; + if (gen_node == NULL || child == NULL) { + errno = EINVAL; + goto fail; + } - ret = ec_node_check_type(gen_node, &ec_node_once_type); - if (ret < 0) - return ret; + if (ec_node_check_type(gen_node, &ec_node_once_type) < 0) + goto fail; - node->child = child; + if (ec_node_add_child(gen_node, child) < 0) + goto fail; - TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed? + node->child = child; return 0; + +fail: + ec_node_free(child); + return -1; } struct ec_node *ec_node_once(const char *id, struct ec_node *child) diff --git a/lib/ecoli_node_option.c b/lib/ecoli_node_option.c index bc912ef..f97ae7e 100644 --- a/lib/ecoli_node_option.c +++ b/lib/ecoli_node_option.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -37,9 +38,9 @@ #include #include #include -#include #include #include +#include EC_LOG_TYPE_REGISTER(node_option); @@ -93,26 +94,49 @@ static struct ec_node_type ec_node_option_type = { EC_NODE_TYPE_REGISTER(ec_node_option_type); +int ec_node_option_set(struct ec_node *gen_node, struct ec_node *child) +{ + struct ec_node_option *node = (struct ec_node_option *)gen_node; + + if (gen_node == NULL || child == NULL) { + errno = EINVAL; + goto fail; + } + + if (ec_node_check_type(gen_node, &ec_node_option_type) < 0) + goto fail; + + if (ec_node_add_child(gen_node, child) < 0) + goto fail; + + node->child = child; + + return 0; + +fail: + ec_node_free(child); + return -1; +} + struct ec_node *ec_node_option(const char *id, struct ec_node *child) { struct ec_node *gen_node = NULL; - struct ec_node_option *node = NULL; if (child == NULL) - return NULL; + goto fail; gen_node = __ec_node(&ec_node_option_type, id); - if (gen_node == NULL) { - ec_node_free(child); - return NULL; - } - node = (struct ec_node_option *)gen_node; + if (gen_node == NULL) + goto fail; - node->child = child; + ec_node_option_set(gen_node, child); + child = NULL; - TAILQ_INSERT_TAIL(&gen_node->children, child, next); + return gen_node; - return &node->gen; +fail: + ec_node_free(child); + return NULL; } /* LCOV_EXCL_START */ diff --git a/lib/ecoli_node_option.h b/lib/ecoli_node_option.h index 3b6e3a2..41d4f38 100644 --- a/lib/ecoli_node_option.h +++ b/lib/ecoli_node_option.h @@ -31,5 +31,6 @@ #include struct ec_node *ec_node_option(const char *id, struct ec_node *node); +int ec_node_option_set(struct ec_node *gen_node, struct ec_node *child); #endif diff --git a/lib/ecoli_node_or.c b/lib/ecoli_node_or.c index c0d43e2..03aa5fe 100644 --- a/lib/ecoli_node_or.c +++ b/lib/ecoli_node_or.c @@ -112,6 +112,17 @@ static void ec_node_or_free_priv(struct ec_node *gen_node) ec_free(node->table); } +static struct ec_node_type ec_node_or_type = { + .name = "or", + .parse = ec_node_or_parse, + .complete = ec_node_or_complete, + .get_max_parse_len = ec_node_or_get_max_parse_len, + .size = sizeof(struct ec_node_or), + .free_priv = ec_node_or_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_or_type); + int ec_node_or_add(struct ec_node *gen_node, struct ec_node *child) { struct ec_node_or *node = (struct ec_node_or *)gen_node; @@ -119,32 +130,34 @@ int ec_node_or_add(struct ec_node *gen_node, struct ec_node *child) assert(node != NULL); - if (child == NULL) - return -EINVAL; + assert(node != NULL); + + if (child == NULL) { + errno = EINVAL; + goto fail; + } + + if (ec_node_check_type(gen_node, &ec_node_or_type) < 0) + goto fail; table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); if (table == NULL) - return -1; + goto fail; node->table = table; + + if (ec_node_add_child(gen_node, child) < 0) + goto fail; + table[node->len] = child; node->len++; - TAILQ_INSERT_TAIL(&gen_node->children, child, next); - return 0; -} - -static struct ec_node_type ec_node_or_type = { - .name = "or", - .parse = ec_node_or_parse, - .complete = ec_node_or_complete, - .get_max_parse_len = ec_node_or_get_max_parse_len, - .size = sizeof(struct ec_node_or), - .free_priv = ec_node_or_free_priv, -}; -EC_NODE_TYPE_REGISTER(ec_node_or_type); +fail: + ec_node_free(child); + return -1; +} struct ec_node *__ec_node_or(const char *id, ...) { diff --git a/lib/ecoli_node_seq.c b/lib/ecoli_node_seq.c index f825d54..68aa287 100644 --- a/lib/ecoli_node_seq.c +++ b/lib/ecoli_node_seq.c @@ -221,26 +221,33 @@ int ec_node_seq_add(struct ec_node *gen_node, struct ec_node *child) struct ec_node_seq *node = (struct ec_node_seq *)gen_node; struct ec_node **table; - // XXX check node type - assert(node != NULL); - if (child == NULL) - return -EINVAL; + if (child == NULL) { + errno = EINVAL; + goto fail; + } + + if (ec_node_check_type(gen_node, &ec_node_seq_type) < 0) + goto fail; table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); - if (table == NULL) { - ec_node_free(child); - return -1; - } + if (table == NULL) + goto fail; node->table = table; + + if (ec_node_add_child(gen_node, child) < 0) + goto fail; + table[node->len] = child; node->len++; - TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed? - return 0; + +fail: + ec_node_free(child); + return -1; } struct ec_node *__ec_node_seq(const char *id, ...) diff --git a/lib/ecoli_node_str.c b/lib/ecoli_node_str.c index 3cbc31b..af4f607 100644 --- a/lib/ecoli_node_str.c +++ b/lib/ecoli_node_str.c @@ -87,7 +87,7 @@ ec_node_str_complete(const struct ec_node *gen_node, /* no completion */ if (str[n] != '\0') - return 0; // XXX add a no_match instead? + return EC_PARSED_NOMATCH; if (ec_completed_add_item(completed, gen_node, NULL, EC_COMP_FULL, str, node->string) < 0) @@ -139,9 +139,7 @@ int ec_node_str_set_str(struct ec_node *gen_node, const char *str) if (str == NULL) return -EINVAL; - if (node->string != NULL) - return -EEXIST; // XXX allow to replace - + ec_free(node->string); node->string = ec_strdup(str); if (node->string == NULL) return -ENOMEM; diff --git a/lib/ecoli_node_subset.c b/lib/ecoli_node_subset.c index 9dc6020..f0d2384 100644 --- a/lib/ecoli_node_subset.c +++ b/lib/ecoli_node_subset.c @@ -268,15 +268,30 @@ static void ec_node_subset_free_priv(struct ec_node *gen_node) ec_free(node->table); } +static struct ec_node_type ec_node_subset_type = { + .name = "subset", + .parse = ec_node_subset_parse, + .complete = ec_node_subset_complete, + .size = sizeof(struct ec_node_subset), + .free_priv = ec_node_subset_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_subset_type); + int ec_node_subset_add(struct ec_node *gen_node, struct ec_node *child) { struct ec_node_subset *node = (struct ec_node_subset *)gen_node; struct ec_node **table; - assert(node != NULL); + assert(node != NULL); // XXX specific assert for it, like in libyang + + if (child == NULL) { + errno = EINVAL; + goto fail; + } - if (child == NULL) - return -EINVAL; + if (ec_node_check_type(gen_node, &ec_node_subset_type) < 0) + goto fail; table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); if (table == NULL) { @@ -285,23 +300,19 @@ int ec_node_subset_add(struct ec_node *gen_node, struct ec_node *child) } node->table = table; + + if (ec_node_add_child(gen_node, child) < 0) + goto fail; + table[node->len] = child; node->len++; - TAILQ_INSERT_TAIL(&gen_node->children, child, next); - return 0; -} -static struct ec_node_type ec_node_subset_type = { - .name = "subset", - .parse = ec_node_subset_parse, - .complete = ec_node_subset_complete, - .size = sizeof(struct ec_node_subset), - .free_priv = ec_node_subset_free_priv, -}; - -EC_NODE_TYPE_REGISTER(ec_node_subset_type); +fail: + ec_node_free(child); + return -1; +} struct ec_node *__ec_node_subset(const char *id, ...) { diff --git a/lib/ecoli_node_weakref.c b/lib/ecoli_node_weakref.c index 2a8f1f9..f87866f 100644 --- a/lib/ecoli_node_weakref.c +++ b/lib/ecoli_node_weakref.c @@ -83,19 +83,23 @@ int ec_node_weakref_set(struct ec_node *gen_node, struct ec_node *child) { struct ec_node_weakref *node = (struct ec_node_weakref *)gen_node; - // XXX check node type - assert(node != NULL); - if (child == NULL) - return -EINVAL; + if (child == NULL) { + errno = EINVAL; + goto fail; + } - node->child = child; + if (ec_node_check_type(gen_node, &ec_node_weakref_type) < 0) + goto fail; - // XXX else it breaks the dump() - //TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed? + node->child = child; return 0; + +fail: + ec_node_free(child); + return -1; } struct ec_node *ec_node_weakref(const char *id, struct ec_node *child) diff --git a/lib/ecoli_parsed.h b/lib/ecoli_parsed.h index 340c23e..404392d 100644 --- a/lib/ecoli_parsed.h +++ b/lib/ecoli_parsed.h @@ -80,10 +80,6 @@ struct ec_parsed *ec_parsed_dup(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 - * cache would be per-node, and would be reset for each call to parse() - * or complete() ? ... not sure, since parse result can depend on state - */ /* a NULL return value is an error, with errno set ENOTSUP: no ->parse() operation */ @@ -122,7 +118,6 @@ struct ec_parsed *ec_node_parse_strvec(const struct ec_node *node, * 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 ? */ int ec_node_parse_child(const struct ec_node *node, struct ec_parsed *state, diff --git a/lib/ecoli_vec.c b/lib/ecoli_vec.c index b3249b5..aea0684 100644 --- a/lib/ecoli_vec.c +++ b/lib/ecoli_vec.c @@ -61,7 +61,7 @@ ec_vec(size_t elt_size, size_t size, ec_vec_elt_copy_t copy, struct ec_vec *vec; if (elt_size == 0) { - errno = -EINVAL; + errno = EINVAL; return NULL; } -- 2.20.1