X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fecoli_node.c;h=a181835d0e43adec38ccabbf04a71b28c76aecda;hb=6a40004beef80306d5f7dcea9febec948adf6d50;hp=dd4ed68efae6dbbf24967471e6b148dcf6d21e37;hpb=25be3b5fea4f4b2a06fa9e546c888c0511341e0d;p=protos%2Flibecoli.git diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c index dd4ed68..a181835 100644 --- a/lib/ecoli_node.c +++ b/lib/ecoli_node.c @@ -1,46 +1,33 @@ -/* - * 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 + +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 +36,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); @@ -75,48 +65,58 @@ void ec_node_type_dump(FILE *out) struct ec_node *__ec_node(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", + 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(); if (node->attrs == NULL) goto fail; + if (type->init_priv != NULL) { + if (type->init_priv(node) < 0) + goto fail; + } + return node; fail: - ec_node_free(node); + if (node != NULL) { + ec_keyval_free(node->attrs); + ec_free(node->desc); + ec_free(node->id); + } + ec_free(node); + return NULL; } struct ec_node *ec_node(const char *typename, const char *id) { - struct ec_node_type *type; + const 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); + EC_LOG(EC_LOG_ERR, "type=%s does not exist\n", + typename); return NULL; } @@ -135,9 +135,11 @@ 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); + ec_keyval_free(node->attrs); + ec_config_free(node->config); ec_free(node); } @@ -148,15 +150,111 @@ 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); + assert(errno != 0); + return -1; +} + +int +ec_node_set_config(struct ec_node *node, struct ec_config *config) +{ + if (node->type->schema == NULL) { + errno = EINVAL; + goto fail; + } + if (ec_config_validate(config, node->type->schema, + node->type->schema_len) < 0) + goto fail; + if (node->type->set_config != NULL) { + if (node->type->set_config(node, config) < 0) + goto fail; + } + + ec_config_free(node->config); + node->config = config; + + return 0; + +fail: + return -1; +} + +const struct ec_config *ec_node_get_config(struct ec_node *node) +{ + return node->config; +} + +#if 0 /* later */ +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; +} +#endif + 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; @@ -165,6 +263,11 @@ struct ec_node *ec_node_find(struct ec_node *node, const char *id) return NULL; } +const struct ec_node_type *ec_node_type(const struct ec_node *node) +{ + return node->type; +} + struct ec_keyval *ec_node_attrs(const struct ec_node *node) { return node->attrs; @@ -172,44 +275,31 @@ struct ec_keyval *ec_node_attrs(const struct ec_node *node) const char *ec_node_id(const struct ec_node *node) { - if (node->id == NULL) - return "None"; return node->id; } -struct ec_node *ec_node_parent(const struct ec_node *node) -{ - return node->parent; -} - static void __ec_node_dump(FILE *out, const struct ec_node *node, size_t indent) { - const char *id, *typename, *desc; + const char *id, *typename; struct ec_node *child; - size_t i; + size_t i, n; id = ec_node_id(node); typename = node->type->name; - desc = ec_node_desc(node); - - /* XXX enhance */ - for (i = 0; i < indent; i++) { - if (i % 2) - fprintf(out, " "); - else - fprintf(out, "|"); - } - fprintf(out, "node %p type=%s id=%s desc=%s\n", - node, typename, id, desc); - TAILQ_FOREACH(child, &node->children, next) - __ec_node_dump(out, child, indent + 2); + fprintf(out, "%*s" "type=%s id=%s %p\n", + (int)indent * 4, "", typename, id, node); + n = node->n_children; + for (i = 0; i < n; i++) { + child = node->children[i]; + __ec_node_dump(out, child, indent + 1); + } } void ec_node_dump(FILE *out, const struct ec_node *node) { - fprintf(out, "------------------- node dump:\n"); //XXX + fprintf(out, "------------------- node dump:\n"); if (node == NULL) { fprintf(out, "node is NULL\n"); @@ -226,3 +316,127 @@ const char *ec_node_desc(const struct ec_node *node) return node->desc; } + +int ec_node_check_type(const struct ec_node *node, + const struct ec_node_type *type) +{ + if (strcmp(node->type->name, type->name)) { + errno = EINVAL; + return -1; + } + + return 0; +} + +/* LCOV_EXCL_START */ +static int ec_node_testcase(void) +{ + struct ec_node *node = NULL; + const struct ec_node *child; + const struct ec_node_type *type; + FILE *f = NULL; + char *buf = NULL; + size_t buflen = 0; + int testres = 0; + int ret; + + 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"); + child = ec_node_get_child(node, 0); + testres |= EC_TEST_CHECK(child != NULL && + !strcmp(ec_node_type(child)->name, "str") && + !strcmp(ec_node_id(child), "id_x"), + "bad child 0"); + child = ec_node_get_child(node, 1); + testres |= EC_TEST_CHECK(child != NULL && + !strcmp(ec_node_type(child)->name, "str") && + !strcmp(ec_node_id(child), "id_y"), + "bad child 1"); + child = ec_node_get_child(node, 2); + 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"); + + 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"); + + return testres; + +fail: + ec_node_free(node); + if (f != NULL) + fclose(f); + free(buf); + + assert(errno != 0); + return -1; +} +/* LCOV_EXCL_STOP */ + +static struct ec_test ec_node_test = { + .name = "node", + .test = ec_node_testcase, +}; + +EC_TEST_REGISTER(ec_node_test);