X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fecoli_node.c;h=0c5890f89c252f67f6e28861bf8efa4931becfa6;hb=15718f19cbf3868bf6fba79f4e26b5254c57873e;hp=c9315fa5f2430f5c57681a9f849178591a22def7;hpb=575fd5b430d5f1557d9a6bdb19c037efaf023759;p=protos%2Flibecoli.git diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c index c9315fa..0c5890f 100644 --- a/lib/ecoli_node.c +++ b/lib/ecoli_node.c @@ -1,46 +1,35 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the University of California, Berkeley nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2016, Olivier MATZ */ #include #include +#include #include #include #include #include +#include #include #include #include +#include +#include #include +#include +#include +#include +#include + +EC_LOG_TYPE_REGISTER(node); + static struct ec_node_type_list node_type_list = TAILQ_HEAD_INITIALIZER(node_type_list); -struct ec_node_type *ec_node_type_lookup(const char *name) +const struct ec_node_type * +ec_node_type_lookup(const char *name) { struct ec_node_type *type; @@ -49,15 +38,18 @@ struct ec_node_type *ec_node_type_lookup(const char *name) return type; } + errno = ENOENT; return NULL; } int ec_node_type_register(struct ec_node_type *type) { - if (ec_node_type_lookup(type->name) != NULL) - return -EEXIST; - if (type->size < sizeof(struct ec_node)) - return -EINVAL; + EC_CHECK_ARG(type->size >= sizeof(struct ec_node), -1, EINVAL); + + if (ec_node_type_lookup(type->name) != NULL) { + errno = EEXIST; + return -1; + } TAILQ_INSERT_TAIL(&node_type_list, type, next); @@ -72,648 +64,541 @@ void ec_node_type_dump(FILE *out) fprintf(out, "%s\n", type->name); } -struct ec_node *__ec_node_new(const struct ec_node_type *type, const char *id) +struct ec_node *ec_node_from_type(const struct ec_node_type *type, const char *id) { struct ec_node *node = NULL; - char buf[256]; // XXX - ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id); + EC_LOG(EC_LOG_DEBUG, "create node type=%s id=%s\n", + type->name, id); + if (id == NULL) { + errno = EINVAL; + goto fail; + } node = ec_calloc(1, type->size); if (node == NULL) goto fail; - TAILQ_INIT(&node->children); node->type = type; node->refcnt = 1; - if (id != NULL) { - node->id = ec_strdup(id); - if (node->id == NULL) - goto fail; - } + node->id = ec_strdup(id); + if (node->id == NULL) + goto fail; - snprintf(buf, sizeof(buf), "<%s>", type->name); - node->desc = ec_strdup(buf); // XXX ec_asprintf ? - if (node->desc == NULL) + if (ec_asprintf(&node->desc, "<%s>", type->name) < 0) goto fail; - node->attrs = ec_keyval_new(); + node->attrs = ec_keyval(); if (node->attrs == NULL) goto fail; - return node; - - fail: - ec_node_free(node); - return NULL; -} - -struct ec_node *ec_node_new(const char *typename, const char *id) -{ - struct ec_node_type *type; - - type = ec_node_type_lookup(typename); - if (type == NULL) { - ec_log(EC_LOG_ERR, "type=%s does not exist\n", typename); - return NULL; + if (type->init_priv != NULL) { + if (type->init_priv(node) < 0) + goto fail; } - return __ec_node_new(type, id); -} - -void ec_node_free(struct ec_node *node) -{ - if (node == NULL) - return; - - assert(node->refcnt > 0); - - if (--node->refcnt > 0) - return; - - if (node->type != NULL && node->type->free_priv != NULL) - node->type->free_priv(node); - ec_free(node->id); - ec_free(node->desc); - ec_free(node->attrs); - ec_free(node); -} - -struct ec_node *ec_node_clone(struct ec_node *node) -{ - if (node != NULL) - node->refcnt++; return node; -} - -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); - if (id != NULL && node_id != NULL && !strcmp(node_id, id)) - return node; - - TAILQ_FOREACH(child, &node->children, next) { - ret = ec_node_find(child, id); - if (ret != NULL) - return ret; + fail: + if (node != NULL) { + ec_keyval_free(node->attrs); + ec_free(node->desc); + ec_free(node->id); } + ec_free(node); return NULL; } -struct ec_keyval *ec_node_attrs(const struct ec_node *node) -{ - return node->attrs; -} - -const char *ec_node_id(const struct ec_node *node) -{ - return node->id; -} - -struct ec_node *ec_node_parent(const struct ec_node *node) +const struct ec_config_schema * +ec_node_type_schema(const struct ec_node_type *type) { - return node->parent; + return type->schema; } -static void __ec_node_dump(FILE *out, - const struct ec_node *node, size_t indent) +const char * +ec_node_type_name(const struct ec_node_type *type) { - struct ec_node *child; - size_t i; - const char *id = "None", *typename = "None"; - - if (node->id != NULL) - id = node->id; - typename = node->type->name; - - /* XXX enhance */ - for (i = 0; i < indent; i++) { - if (i % 2) - fprintf(out, " "); - else - fprintf(out, "|"); - } - - fprintf(out, "node_type=%s id=%s\n", typename, id); - TAILQ_FOREACH(child, &node->children, next) - __ec_node_dump(out, child, indent + 2); + return type->name; } -void ec_node_dump(FILE *out, const struct ec_node *node) +struct ec_node *ec_node(const char *typename, const char *id) { - fprintf(out, "------------------- node dump:\n"); //XXX + const struct ec_node_type *type; - if (node == NULL) { - fprintf(out, "node is NULL\n"); - return; + type = ec_node_type_lookup(typename); + if (type == NULL) { + EC_LOG(EC_LOG_ERR, "type=%s does not exist\n", + typename); + return NULL; } - __ec_node_dump(out, node, 0); + return ec_node_from_type(type, id); } -struct ec_parsed *ec_node_parse(struct ec_node *node, const char *str) +static void count_references(struct ec_node *node, unsigned int refs) { - struct ec_strvec *strvec = NULL; - struct ec_parsed *parsed; - - errno = ENOMEM; - strvec = ec_strvec_new(); - if (strvec == NULL) - goto fail; - - if (ec_strvec_add(strvec, str) < 0) - goto fail; - - parsed = ec_node_parse_strvec(node, strvec); - if (parsed == NULL) - goto fail; - - ec_strvec_free(strvec); - return parsed; - - fail: - ec_strvec_free(strvec); - return NULL; -} - -struct ec_parsed *ec_node_parse_strvec(struct ec_node *node, - const struct ec_strvec *strvec) -{ - struct ec_parsed *parsed; + struct ec_node *child; + size_t i, n; int ret; - /* build the node if required */ - if (node->type->build != NULL) { - if ((node->flags & EC_NODE_F_BUILT) == 0) { - ret = node->type->build(node); - if (ret < 0) { - errno = -ret; - return NULL; - } - } + if (node->free.state == EC_NODE_FREE_STATE_TRAVERSED) { + node->free.refcnt += refs; + return; } - node->flags |= EC_NODE_F_BUILT; - - if (node->type->parse == NULL) { - errno = ENOTSUP; - return NULL; + node->free.refcnt = refs; + node->free.state = EC_NODE_FREE_STATE_TRAVERSED; + n = ec_node_get_children_count(node); + for (i = 0; i < n; i++) { + ret = ec_node_get_child(node, i, &child, &refs); + assert(ret == 0); + count_references(child, refs); } - - parsed = node->type->parse(node, strvec); - - return parsed; } - -struct ec_parsed *ec_parsed_new(void) +static void mark_freeable(struct ec_node *node, enum ec_node_free_state mark) { - struct ec_parsed *parsed = NULL; - - parsed = ec_calloc(1, sizeof(*parsed)); - if (parsed == NULL) - goto fail; + struct ec_node *child; + unsigned int refs; + size_t i, n; + int ret; - TAILQ_INIT(&parsed->children); + if (mark == node->free.state) + return; - return parsed; + if (node->refcnt > node->free.refcnt) + mark = EC_NODE_FREE_STATE_NOT_FREEABLE; + assert(node->refcnt >= node->free.refcnt); + node->free.state = mark; - fail: - return NULL; -} - -void ec_parsed_set_match(struct ec_parsed *parsed, - const struct ec_node *node, struct ec_strvec *strvec) -{ - parsed->node = node; - parsed->strvec = strvec; + n = ec_node_get_children_count(node); + for (i = 0; i < n; i++) { + ret = ec_node_get_child(node, i, &child, &refs); + assert(ret == 0); + mark_freeable(child, mark); + } } -void ec_parsed_free_children(struct ec_parsed *parsed) +static void reset_mark(struct ec_node *node) { - struct ec_parsed *child; + struct ec_node *child; + unsigned int refs; + size_t i, n; + int ret; - if (parsed == NULL) + if (node->free.state == EC_NODE_FREE_STATE_NONE) return; - while (!TAILQ_EMPTY(&parsed->children)) { - child = TAILQ_FIRST(&parsed->children); - TAILQ_REMOVE(&parsed->children, child, next); - ec_parsed_free(child); + node->free.state = EC_NODE_FREE_STATE_NONE; + node->free.refcnt = 0; + + n = ec_node_get_children_count(node); + for (i = 0; i < n; i++) { + ret = ec_node_get_child(node, i, &child, &refs); + assert(ret == 0); + reset_mark(child); } } -void ec_parsed_free(struct ec_parsed *parsed) +/* free a node, taking care of loops in the node graph */ +void ec_node_free(struct ec_node *node) { - if (parsed == NULL) - return; + size_t n; - ec_parsed_free_children(parsed); - ec_strvec_free(parsed->strvec); - ec_free(parsed); -} + if (node == NULL) + return; -static void __ec_parsed_dump(FILE *out, - const struct ec_parsed *parsed, size_t indent) -{ - struct ec_parsed *child; - const struct ec_strvec *vec; - size_t i; - const char *id = "None", *typename = "None"; - - if (parsed->node != NULL) { - if (parsed->node->id != NULL) - id = parsed->node->id; - typename = parsed->node->type->name; - } + assert(node->refcnt > 0); - /* XXX enhance */ - for (i = 0; i < indent; i++) { - if (i % 2) - fprintf(out, " "); - else - fprintf(out, "|"); + if (node->free.state == EC_NODE_FREE_STATE_NONE && + node->refcnt != 1) { + + /* Traverse the node tree starting from this node, and for each + * node, count the number of reachable references. Then, all + * nodes whose reachable references == total reference are + * marked as freeable, and other are marked as unfreeable. Any + * node reachable from an unfreeable node is also marked as + * unfreeable. */ + if (node->free.state == EC_NODE_FREE_STATE_NONE) { + count_references(node, 1); + mark_freeable(node, EC_NODE_FREE_STATE_FREEABLE); + } } - fprintf(out, "node_type=%s id=%s vec=[", typename, id); - vec = ec_parsed_strvec(parsed); - for (i = 0; i < ec_strvec_len(vec); i++) - fprintf(out, "%s<%s>", - i == 0 ? "" : ",", - ec_strvec_val(vec, i)); - fprintf(out, "]\n"); - - TAILQ_FOREACH(child, &parsed->children, next) - __ec_parsed_dump(out, child, indent + 2); -} - -void ec_parsed_dump(FILE *out, const struct ec_parsed *parsed) -{ - fprintf(out, "------------------- parsed dump:\n"); //XXX - - if (parsed == NULL) { - fprintf(out, "parsed is NULL, error in parse\n"); - return; - } - if (!ec_parsed_matches(parsed)) { - fprintf(out, "no match\n"); + if (node->free.state == EC_NODE_FREE_STATE_NOT_FREEABLE) { + node->refcnt--; + reset_mark(node); return; } - __ec_parsed_dump(out, parsed, 0); -} - -void ec_parsed_add_child(struct ec_parsed *parsed, - struct ec_parsed *child) -{ - TAILQ_INSERT_TAIL(&parsed->children, child, next); -} - -void ec_parsed_del_child(struct ec_parsed *parsed, - struct ec_parsed *child) -{ - TAILQ_REMOVE(&parsed->children, child, next); -} - -struct ec_parsed *ec_parsed_find_first(struct ec_parsed *parsed, - const char *id) -{ - struct ec_parsed *child, *ret; - - if (parsed == NULL) - return NULL; - - if (parsed->node != NULL && - parsed->node->id != NULL && - !strcmp(parsed->node->id, id)) - return parsed; - - TAILQ_FOREACH(child, &parsed->children, next) { - ret = ec_parsed_find_first(child, id); - if (ret != NULL) - return ret; + if (node->free.state != EC_NODE_FREE_STATE_FREEING) { + node->free.state = EC_NODE_FREE_STATE_FREEING; + n = ec_node_get_children_count(node); + /* children should be freed by free_priv() */ + assert(n == 0 || node->type->free_priv != NULL); + if (node->type->free_priv != NULL) + node->type->free_priv(node); } - return NULL; -} + node->refcnt--; + if (node->refcnt != 0) + return; -const struct ec_strvec *ec_parsed_strvec( - const struct ec_parsed *parsed) -{ - if (parsed == NULL || parsed->strvec == NULL) - return NULL; + node->free.state = EC_NODE_FREE_STATE_NONE; + node->free.refcnt = 0; - return parsed->strvec; + ec_free(node->id); + ec_free(node->desc); + ec_keyval_free(node->attrs); + ec_config_free(node->config); + ec_free(node); } -/* number of parsed strings in the vector */ -size_t ec_parsed_len(const struct ec_parsed *parsed) +struct ec_node *ec_node_clone(struct ec_node *node) { - if (parsed == NULL || parsed->strvec == NULL) - return 0; - - return ec_strvec_len(parsed->strvec); + if (node != NULL) + node->refcnt++; + return node; } -size_t ec_parsed_matches(const struct ec_parsed *parsed) +size_t ec_node_get_children_count(const struct ec_node *node) { - if (parsed == NULL) - return 0; - - if (parsed->node == NULL && TAILQ_EMPTY(&parsed->children)) + if (node->type->get_children_count == NULL) return 0; - - return 1; -} - -struct ec_completed *ec_completed_new(void) -{ - struct ec_completed *completed = NULL; - - completed = ec_calloc(1, sizeof(*completed)); - if (completed == NULL) - return NULL; - - TAILQ_INIT(&completed->elts); - completed->count_match = 0; - - return completed; + return node->type->get_children_count(node); } -struct ec_completed_elt *ec_completed_elt_new(const struct ec_node *node, - const char *add) +int +ec_node_get_child(const struct ec_node *node, size_t i, + struct ec_node **child, unsigned int *refs) { - struct ec_completed_elt *elt = NULL; - - elt = ec_calloc(1, sizeof(*elt)); - if (elt == NULL) - return NULL; - - elt->node = node; - if (add != NULL) { - elt->add = ec_strdup(add); - if (elt->add == NULL) { - ec_completed_elt_free(elt); - return NULL; - } - } - - return elt; + *child = NULL; + *refs = 0; + if (node->type->get_child == NULL) + return -1; + return node->type->get_child(node, i, child, refs); } -/* XXX define when to use ec_node_complete() or node->complete() - * (same for parse) - * suggestion: node->op() is internal, user calls the function - * other idea: have 2 functions - */ -struct ec_completed *ec_node_complete(struct ec_node *node, - const char *str) +int +ec_node_set_config(struct ec_node *node, struct ec_config *config) { - struct ec_strvec *strvec = NULL; - struct ec_completed *completed; - - errno = ENOMEM; - strvec = ec_strvec_new(); - if (strvec == NULL) + if (node->type->schema == NULL) { + errno = EINVAL; goto fail; - - if (ec_strvec_add(strvec, str) < 0) + } + if (ec_config_validate(config, node->type->schema) < 0) goto fail; + if (node->type->set_config != NULL) { + if (node->type->set_config(node, config) < 0) + goto fail; + } - completed = ec_node_complete_strvec(node, strvec); - if (completed == NULL) - goto fail; + ec_config_free(node->config); + node->config = config; - ec_strvec_free(strvec); - return completed; + return 0; - fail: - ec_strvec_free(strvec); - return NULL; +fail: + ec_config_free(config); + return -1; } -/* default completion function: return a no-match element */ -struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node, - const struct ec_strvec *strvec) +const struct ec_config *ec_node_get_config(struct ec_node *node) { - struct ec_completed *completed; - struct ec_completed_elt *completed_elt; - - (void)strvec; - - completed = ec_completed_new(); - if (completed == NULL) - return NULL; - - completed_elt = ec_completed_elt_new(gen_node, NULL); - if (completed_elt == NULL) { - ec_completed_free(completed); - return NULL; - } - - ec_completed_add_elt(completed, completed_elt); - - return completed; + return node->config; } -struct ec_completed *ec_node_complete_strvec(struct ec_node *node, - const struct ec_strvec *strvec) +struct ec_node *ec_node_find(struct ec_node *node, const char *id) { + struct ec_node *child, *retnode; + const char *node_id = ec_node_id(node); + unsigned int refs; + size_t i, n; int ret; - /* build the node if required */ - if (node->type->build != NULL) { - if ((node->flags & EC_NODE_F_BUILT) == 0) { - ret = node->type->build(node); - if (ret < 0) { - errno = -ret; - return NULL; - } - } - } - node->flags |= EC_NODE_F_BUILT; + if (id != NULL && node_id != NULL && !strcmp(node_id, id)) + return node; - if (node->type->complete == NULL) { - errno = ENOTSUP; - return NULL; + n = ec_node_get_children_count(node); + for (i = 0; i < n; i++) { + ret = ec_node_get_child(node, i, &child, &refs); + assert(ret == 0); + retnode = ec_node_find(child, id); + if (retnode != NULL) + return retnode; } - return node->type->complete(node, strvec); + return NULL; } -/* count the number of identical chars at the beginning of 2 strings */ -static size_t strcmp_count(const char *s1, const char *s2) +const struct ec_node_type *ec_node_type(const struct ec_node *node) { - size_t i = 0; - - while (s1[i] && s2[i] && s1[i] == s2[i]) - i++; - - return i; + return node->type; } -void ec_completed_add_elt( - struct ec_completed *completed, struct ec_completed_elt *elt) +struct ec_keyval *ec_node_attrs(const struct ec_node *node) { - size_t n; - - TAILQ_INSERT_TAIL(&completed->elts, elt, next); - completed->count++; - if (elt->add != NULL) - completed->count_match++; - if (elt->add != NULL) { - if (completed->smallest_start == NULL) { - completed->smallest_start = ec_strdup(elt->add); - } else { - n = strcmp_count(elt->add, - completed->smallest_start); - completed->smallest_start[n] = '\0'; - } - } + return node->attrs; } -void ec_completed_elt_free(struct ec_completed_elt *elt) +const char *ec_node_id(const struct ec_node *node) { - ec_free(elt->add); - ec_free(elt); + return node->id; } -void ec_completed_merge(struct ec_completed *completed1, - struct ec_completed *completed2) +static void __ec_node_dump(FILE *out, + const struct ec_node *node, size_t indent, struct ec_keyval *dict) { - struct ec_completed_elt *elt; + const char *id, *typename; + struct ec_node *child; + unsigned int refs; + char buf[32]; + size_t i, n; + int ret; - assert(completed1 != NULL); - assert(completed2 != NULL); + id = ec_node_id(node); + typename = node->type->name; - while (!TAILQ_EMPTY(&completed2->elts)) { - elt = TAILQ_FIRST(&completed2->elts); - TAILQ_REMOVE(&completed2->elts, elt, next); - ec_completed_add_elt(completed1, elt); + snprintf(buf, sizeof(buf), "%p", node); + if (ec_keyval_has_key(dict, buf)) { + fprintf(out, "%*s" "type=%s id=%s %p... (loop)\n", + (int)indent * 4, "", typename, id, node); + return; } - ec_completed_free(completed2); -} + ec_keyval_set(dict, buf, NULL, NULL); + fprintf(out, "%*s" "type=%s id=%s %p refs=%u free_state=%d free_refs=%d\n", + (int)indent * 4, "", typename, id, node, node->refcnt, + node->free.state, node->free.refcnt); -void ec_completed_free(struct ec_completed *completed) -{ - struct ec_completed_elt *elt; - - if (completed == NULL) - return; - - while (!TAILQ_EMPTY(&completed->elts)) { - elt = TAILQ_FIRST(&completed->elts); - TAILQ_REMOVE(&completed->elts, elt, next); - ec_completed_elt_free(elt); + n = ec_node_get_children_count(node); + for (i = 0; i < n; i++) { + ret = ec_node_get_child(node, i, &child, &refs); + assert(ret == 0); + __ec_node_dump(out, child, indent + 1, dict); } - ec_free(completed->smallest_start); - ec_free(completed); } -void ec_completed_dump(FILE *out, const struct ec_completed *completed) +void ec_node_dump(FILE *out, const struct ec_node *node) { - struct ec_completed_elt *elt; + struct ec_keyval *dict = NULL; - if (completed == NULL || completed->count == 0) { - fprintf(out, "no completion\n"); + fprintf(out, "------------------- node dump:\n"); + + if (node == NULL) { + fprintf(out, "node is NULL\n"); return; } - fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n", - completed->count, completed->count_match, - completed->smallest_start); + dict = ec_keyval(); + if (dict == NULL) + goto fail; - TAILQ_FOREACH(elt, &completed->elts, next) { - fprintf(out, "add=<%s>, node=%p, node_type=%s\n", - elt->add, elt->node, elt->node->type->name); - } -} + __ec_node_dump(out, node, 0, dict); -const char *ec_completed_smallest_start( - const struct ec_completed *completed) -{ - if (completed == NULL || completed->smallest_start == NULL) - return ""; + ec_keyval_free(dict); + return; - return completed->smallest_start; +fail: + ec_keyval_free(dict); + EC_LOG(EC_LOG_ERR, "failed to dump node\n"); } -unsigned int ec_completed_count( - const struct ec_completed *completed, - enum ec_completed_filter_flags flags) +const char *ec_node_desc(const struct ec_node *node) { - unsigned int count = 0; - - if (completed == NULL) - return count; - - if (flags & EC_MATCH) - count += completed->count_match; - if (flags & EC_NO_MATCH) - count += (completed->count - completed->count_match); //XXX + if (node->type->desc != NULL) + return node->type->desc(node); - return count; + return node->desc; } -struct ec_completed_iter * -ec_completed_iter_new(struct ec_completed *completed, - enum ec_completed_filter_flags flags) +int ec_node_check_type(const struct ec_node *node, + const struct ec_node_type *type) { - struct ec_completed_iter *iter; - - iter = ec_calloc(1, sizeof(*iter)); - if (iter == NULL) - return NULL; - - iter->completed = completed; - iter->flags = flags; - iter->cur = NULL; + if (strcmp(node->type->name, type->name)) { + errno = EINVAL; + return -1; + } - return iter; + return 0; } -const struct ec_completed_elt *ec_completed_iter_next( - struct ec_completed_iter *iter) +/* LCOV_EXCL_START */ +static int ec_node_testcase(void) { - if (iter->completed == NULL) - return NULL; + struct ec_node *node = NULL, *expr = NULL; + struct ec_node *expr2 = NULL, *val = NULL, *op = NULL, *seq = NULL; + const struct ec_node_type *type; + struct ec_node *child; + unsigned int refs; + FILE *f = NULL; + char *buf = NULL; + size_t buflen = 0; + int testres = 0; + int ret; - do { - if (iter->cur == NULL) { - iter->cur = TAILQ_FIRST(&iter->completed->elts); - } else { - iter->cur = TAILQ_NEXT(iter->cur, next); - } + node = EC_NODE_SEQ(EC_NO_ID, + ec_node_str("id_x", "x"), + ec_node_str("id_y", "y")); + if (node == NULL) + goto fail; + + ec_node_clone(node); + ec_node_free(node); + + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_node_dump(f, node); + ec_node_type_dump(f); + ec_node_dump(f, NULL); + fclose(f); + f = NULL; + + testres |= EC_TEST_CHECK( + strstr(buf, "type=seq id=no-id"), "bad dump\n"); + testres |= EC_TEST_CHECK( + strstr(buf, "type=str id=id_x") && + strstr(strstr(buf, "type=str id=id_x") + 1, + "type=str id=id_y"), + "bad dump\n"); + free(buf); + buf = NULL; + + testres |= EC_TEST_CHECK( + !strcmp(ec_node_type(node)->name, "seq") && + !strcmp(ec_node_id(node), EC_NO_ID) && + !strcmp(ec_node_desc(node), ""), + "bad child 0"); + + testres |= EC_TEST_CHECK( + ec_node_get_children_count(node) == 2, + "bad children count\n"); + ret = ec_node_get_child(node, 0, &child, &refs); + testres |= EC_TEST_CHECK(ret == 0 && + child != NULL && + !strcmp(ec_node_type(child)->name, "str") && + !strcmp(ec_node_id(child), "id_x"), + "bad child 0"); + ret = ec_node_get_child(node, 1, &child, &refs); + testres |= EC_TEST_CHECK(ret == 0 && + child != NULL && + !strcmp(ec_node_type(child)->name, "str") && + !strcmp(ec_node_id(child), "id_y"), + "bad child 1"); + ret = ec_node_get_child(node, 2, &child, &refs); + testres |= EC_TEST_CHECK(ret != 0, + "ret should be != 0"); + testres |= EC_TEST_CHECK(child == NULL, + "child 2 should be NULL"); + + child = ec_node_find(node, "id_x"); + testres |= EC_TEST_CHECK(child != NULL && + !strcmp(ec_node_type(child)->name, "str") && + !strcmp(ec_node_id(child), "id_x") && + !strcmp(ec_node_desc(child), "x"), + "bad child id_x"); + child = ec_node_find(node, "id_dezdex"); + testres |= EC_TEST_CHECK(child == NULL, + "child with wrong id should be NULL"); + + ret = ec_keyval_set(ec_node_attrs(node), "key", "val", NULL); + testres |= EC_TEST_CHECK(ret == 0, + "cannot set node attribute\n"); + + type = ec_node_type_lookup("seq"); + testres |= EC_TEST_CHECK(type != NULL && + ec_node_check_type(node, type) == 0, + "cannot get seq node type"); + type = ec_node_type_lookup("str"); + testres |= EC_TEST_CHECK(type != NULL && + ec_node_check_type(node, type) < 0, + "node type should not be str"); - if (iter->cur == NULL) - break; + ec_node_free(node); + node = NULL; + + node = ec_node("deznuindez", EC_NO_ID); + testres |= EC_TEST_CHECK(node == NULL, + "should not be able to create node\n"); + + /* test loop */ + expr = ec_node("or", EC_NO_ID); + val = ec_node_int(EC_NO_ID, 0, 10, 0); + op = ec_node_str(EC_NO_ID, "!"); + seq = EC_NODE_SEQ(EC_NO_ID, + op, + ec_node_clone(expr)); + op = NULL; + if (expr == NULL || val == NULL || seq == NULL) + goto fail; + if (ec_node_or_add(expr, ec_node_clone(seq)) < 0) + goto fail; + ec_node_free(seq); + seq = NULL; + if (ec_node_or_add(expr, ec_node_clone(val)) < 0) + goto fail; + ec_node_free(val); + val = NULL; + + testres |= EC_TEST_CHECK_PARSE(expr, 1, "1"); + testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1"); + testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!"); + + ec_node_free(expr); + expr = NULL; + + /* same loop test, but keep some refs (released later) */ + expr = ec_node("or", EC_NO_ID); + ec_node_clone(expr); + expr2 = expr; + val = ec_node_int(EC_NO_ID, 0, 10, 0); + op = ec_node_str(EC_NO_ID, "!"); + seq = EC_NODE_SEQ(EC_NO_ID, + op, + ec_node_clone(expr)); + op = NULL; + if (expr == NULL || val == NULL || seq == NULL) + goto fail; + if (ec_node_or_add(expr, ec_node_clone(seq)) < 0) + goto fail; + ec_node_free(seq); + seq = NULL; + if (ec_node_or_add(expr, ec_node_clone(val)) < 0) + goto fail; - if (iter->cur->add == NULL && - (iter->flags & EC_NO_MATCH)) - break; + testres |= EC_TEST_CHECK_PARSE(expr, 1, "1"); + testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1"); + testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!"); - if (iter->cur->add != NULL && - (iter->flags & EC_MATCH)) - break; + ec_node_free(expr2); + expr2 = NULL; + ec_node_free(val); + val = NULL; + ec_node_free(expr); + expr = NULL; - } while (iter->cur != NULL); + return testres; - return iter->cur; -} +fail: + ec_node_free(expr); + ec_node_free(expr2); + ec_node_free(val); + ec_node_free(seq); + ec_node_free(node); + if (f != NULL) + fclose(f); + free(buf); -void ec_completed_iter_free(struct ec_completed_iter *iter) -{ - ec_free(iter); + assert(errno != 0); + return -1; } +/* LCOV_EXCL_STOP */ -const char *ec_node_desc(const struct ec_node *node) -{ - if (node->type->desc != NULL) - return node->type->desc(node); +static struct ec_test ec_node_test = { + .name = "node", + .test = ec_node_testcase, +}; - return node->desc; -} +EC_TEST_REGISTER(ec_node_test);