X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fecoli_node_cmd.c;h=c61b75922f1c3c79b479f6d84cfb9f875782a77c;hb=804dfa9a29a52ebe59bbf93d222adde3c1c3938c;hp=9a6c4ebd8a4a267040bc476c87c895edafaf81cc;hpb=17c8ded3af09cbb91c62b86be701fcfe3a1ad5de;p=protos%2Flibecoli.git diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c index 9a6c4eb..c61b759 100644 --- a/lib/ecoli_node_cmd.c +++ b/lib/ecoli_node_cmd.c @@ -1,28 +1,5 @@ -/* - * Copyright (c) 2016-2017, 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 @@ -39,8 +16,10 @@ #include #include #include -#include -#include +#include +#include +#include +#include #include #include #include @@ -53,56 +32,62 @@ #include #include +EC_LOG_TYPE_REGISTER(node_cmd); + struct ec_node_cmd { struct ec_node gen; char *cmd_str; /* the command string. */ - struct ec_node *cmd; /* the command node. */ - struct ec_node *lex; /* the lexer node. */ - struct ec_node *expr; /* the expression parser. */ - struct ec_node **table; /* table of node referenced in command. */ + struct ec_node *cmd; /* the command node. */ + struct ec_node *parser; /* the expression parser. */ + struct ec_node *expr; /* the expression parser without lexer. */ + struct ec_node **table; /* table of node referenced in command. */ unsigned int len; /* len of the table. */ }; +/* passed as user context to expression parser */ +struct ec_node_cmd_ctx { + struct ec_node **table; + unsigned int len; +}; + static int ec_node_cmd_eval_var(void **result, void *userctx, - const struct ec_parsed *var) + const struct ec_parse *var) { const struct ec_strvec *vec; - struct ec_node_cmd *node = userctx; + struct ec_node_cmd_ctx *ctx = userctx; struct ec_node *eval = NULL; const char *str, *id; unsigned int i; - (void)userctx; - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_strvec(var); - if (ec_strvec_len(vec) != 1) - return -EINVAL; + vec = ec_parse_strvec(var); + if (ec_strvec_len(vec) != 1) { + errno = EINVAL; + return -1; + } str = ec_strvec_val(vec, 0); - for (i = 0; i < node->len; i++) { - id = ec_node_id(node->table[i]); - printf("i=%d id=%s\n", i, id); + for (i = 0; i < ctx->len; i++) { + id = ec_node_id(ctx->table[i]); if (id == NULL) continue; if (strcmp(str, id)) continue; /* if id matches, use a node provided by the user... */ - eval = ec_node_clone(node->table[i]); + eval = ec_node_clone(ctx->table[i]); if (eval == NULL) - return -ENOMEM; + return -1; break; } /* ...or create a string node */ if (eval == NULL) { - eval = ec_node_str(NULL, str); + eval = ec_node_str(EC_NO_ID, str); if (eval == NULL) - return -ENOMEM; + return -1; } - printf("eval var %s %p\n", str, eval); *result = eval; return 0; @@ -110,44 +95,52 @@ ec_node_cmd_eval_var(void **result, void *userctx, static int ec_node_cmd_eval_pre_op(void **result, void *userctx, void *operand, - const struct ec_parsed *operator) + const struct ec_parse *operator) { (void)result; (void)userctx; (void)operand; (void)operator; - return -EINVAL; + errno = EINVAL; + return -1; } static int ec_node_cmd_eval_post_op(void **result, void *userctx, void *operand, - const struct ec_parsed *operator) + const struct ec_parse *operator) { const struct ec_strvec *vec; - struct ec_node *eval = operand;; + struct ec_node *in = operand;; + struct ec_node *out = NULL;; (void)userctx; /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_strvec(operator); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - - if (!strcmp(ec_strvec_val(vec, 0), "*")) - eval = NULL; //XXX - else - return -EINVAL; + vec = ec_parse_strvec(operator); + if (ec_strvec_len(vec) != 1) { + errno = EINVAL; + return -1; + } - printf("eval post_op %p\n", eval); - *result = eval; + if (!strcmp(ec_strvec_val(vec, 0), "*")) { + out = ec_node_many(EC_NO_ID, + ec_node_clone(in), 0, 0); + if (out == NULL) + return -1; + ec_node_free(in); + *result = out; + } else { + errno = EINVAL; + return -1; + } return 0; } static int ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1, - const struct ec_parsed *operator, void *operand2) + const struct ec_parse *operator, void *operand2) { const struct ec_strvec *vec; @@ -157,29 +150,71 @@ ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1, (void)userctx; - printf("eval bin_op %p %p\n", in1, in2); - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_strvec(operator); - if (ec_strvec_len(vec) != 1) - return -EINVAL; + vec = ec_parse_strvec(operator); + if (ec_strvec_len(vec) > 1) { + errno = EINVAL; + return -1; + } - if (!strcmp(ec_strvec_val(vec, 0), "|")) { - out = EC_NODE_OR(NULL, ec_node_clone(in1), ec_node_clone(in2)); - if (out == NULL) - return -EINVAL; - ec_node_free(in1); - ec_node_free(in2); - *result = out; + if (ec_strvec_len(vec) == 0) { + if (!strcmp(in1->type->name, "seq")) { + if (ec_node_seq_add(in1, ec_node_clone(in2)) < 0) + return -1; + ec_node_free(in2); + *result = in1; + } else { + out = EC_NODE_SEQ(EC_NO_ID, ec_node_clone(in1), + ec_node_clone(in2)); + if (out == NULL) + return -1; + ec_node_free(in1); + ec_node_free(in2); + *result = out; + } + } else if (!strcmp(ec_strvec_val(vec, 0), "|")) { + if (!strcmp(in2->type->name, "or")) { + if (ec_node_or_add(in2, ec_node_clone(in1)) < 0) + return -1; + ec_node_free(in1); + *result = in2; + } else if (!strcmp(in1->type->name, "or")) { + if (ec_node_or_add(in1, ec_node_clone(in2)) < 0) + return -1; + ec_node_free(in2); + *result = in1; + } else { + out = EC_NODE_OR(EC_NO_ID, ec_node_clone(in1), + ec_node_clone(in2)); + if (out == NULL) + return -1; + ec_node_free(in1); + ec_node_free(in2); + *result = out; + } } else if (!strcmp(ec_strvec_val(vec, 0), ",")) { - out = EC_NODE_SUBSET(NULL, ec_node_clone(in1), ec_node_clone(in2)); - if (out == NULL) - return -EINVAL; - ec_node_free(in1); - ec_node_free(in2); - *result = out; + if (!strcmp(in2->type->name, "subset")) { + if (ec_node_subset_add(in2, ec_node_clone(in1)) < 0) + return -1; + ec_node_free(in1); + *result = in2; + } else if (!strcmp(in1->type->name, "subset")) { + if (ec_node_subset_add(in1, ec_node_clone(in2)) < 0) + return -1; + ec_node_free(in2); + *result = in1; + } else { + out = EC_NODE_SUBSET(EC_NO_ID, ec_node_clone(in1), + ec_node_clone(in2)); + if (out == NULL) + return -1; + ec_node_free(in1); + ec_node_free(in2); + *result = out; + } } else { - return -EINVAL; + errno = EINVAL; + return -1; } return 0; @@ -187,8 +222,8 @@ ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1, static int ec_node_cmd_eval_parenthesis(void **result, void *userctx, - const struct ec_parsed *open_paren, - const struct ec_parsed *close_paren, + const struct ec_parse *open_paren, + const struct ec_parse *close_paren, void *value) { const struct ec_strvec *vec; @@ -199,22 +234,24 @@ ec_node_cmd_eval_parenthesis(void **result, void *userctx, (void)close_paren; /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_strvec(open_paren); - if (ec_strvec_len(vec) != 1) - return -EINVAL; + vec = ec_parse_strvec(open_paren); + if (ec_strvec_len(vec) != 1) { + errno = EINVAL; + return -1; + } if (!strcmp(ec_strvec_val(vec, 0), "[")) { - out = ec_node_option(NULL, ec_node_clone(in)); + out = ec_node_option(EC_NO_ID, ec_node_clone(in)); if (out == NULL) - return -EINVAL; + return -1; ec_node_free(in); } else if (!strcmp(ec_strvec_val(vec, 0), "(")) { out = in; } else { - return -EINVAL; + errno = EINVAL; + return -1; } - printf("eval paren\n"); *result = out; return 0; @@ -227,7 +264,7 @@ ec_node_cmd_eval_free(void *result, void *userctx) ec_free(result); } -static const struct ec_node_expr_eval_ops test_ops = { +static const struct ec_node_expr_eval_ops expr_ops = { .eval_var = ec_node_cmd_eval_var, .eval_pre_op = ec_node_cmd_eval_pre_op, .eval_post_op = ec_node_cmd_eval_post_op, @@ -236,79 +273,59 @@ static const struct ec_node_expr_eval_ops test_ops = { .eval_free = ec_node_cmd_eval_free, }; -static struct ec_parsed *ec_node_cmd_parse(const struct ec_node *gen_node, - const struct ec_strvec *strvec) -{ - struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - - return ec_node_parse_strvec(node->cmd, strvec); -} - -static struct ec_completed *ec_node_cmd_complete(const struct ec_node *gen_node, - const struct ec_strvec *strvec) -{ - struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - - return ec_node_complete_strvec(node->cmd, strvec); -} - -static void ec_node_cmd_free_priv(struct ec_node *gen_node) -{ - struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - unsigned int i; - - ec_free(node->cmd_str); - ec_node_free(node->cmd); - ec_node_free(node->expr); - ec_node_free(node->lex); - for (i = 0; i < node->len; i++) - ec_node_free(node->table[i]); - ec_free(node->table); -} - -static int ec_node_cmd_build(struct ec_node *gen_node) +static struct ec_node * +ec_node_cmd_build_expr(void) { - struct ec_node *expr = NULL, *lex = NULL, *cmd = NULL; - struct ec_parsed *p, *child; - struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - void *result; + struct ec_node *expr = NULL; int ret; - /* XXX the expr parser can be moved in the node init */ - /* build the expression parser */ - ret = -ENOMEM; expr = ec_node("expr", "expr"); if (expr == NULL) goto fail; - ret = ec_node_expr_set_val_node(expr, ec_node_re(NULL, "[a-zA-Z0-9]+")); + ret = ec_node_expr_set_val_node(expr, ec_node_re(EC_NO_ID, + "[a-zA-Z0-9]+")); if (ret < 0) goto fail; - ret = ec_node_expr_add_bin_op(expr, ec_node_str(NULL, ",")); + ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, ",")); if (ret < 0) goto fail; - ret = ec_node_expr_add_bin_op(expr, ec_node_str(NULL, "|")); + ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, "|")); if (ret < 0) goto fail; - ret = ec_node_expr_add_post_op(expr, ec_node_str(NULL, "+")); + ret = ec_node_expr_add_bin_op(expr, ec_node("empty", EC_NO_ID)); if (ret < 0) goto fail; - ret = ec_node_expr_add_post_op(expr, ec_node_str(NULL, "*")); + ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "+")); if (ret < 0) goto fail; - ret = ec_node_expr_add_parenthesis(expr, ec_node_str(NULL, "["), - ec_node_str(NULL, "]")); + ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "*")); if (ret < 0) goto fail; - ec_node_expr_add_parenthesis(expr, ec_node_str(NULL, "("), - ec_node_str(NULL, ")")); + ret = ec_node_expr_add_parenthesis(expr, ec_node_str(EC_NO_ID, "["), + ec_node_str(EC_NO_ID, "]")); if (ret < 0) goto fail; + ec_node_expr_add_parenthesis(expr, ec_node_str(EC_NO_ID, "("), + ec_node_str(EC_NO_ID, ")")); + if (ret < 0) + goto fail; + + return expr; - /* prepend a lexer and a "many" to the expression node */ - ret = -ENOMEM; - lex = ec_node_re_lex(NULL, - ec_node_many(NULL, ec_node_clone(expr), 1, 0)); +fail: + ec_node_free(expr); + return NULL; +} + +static struct ec_node * +ec_node_cmd_build_parser(struct ec_node *expr) +{ + struct ec_node *lex = NULL; + int ret; + + /* prepend a lexer to the expression node */ + lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr)); if (lex == NULL) goto fail; @@ -328,191 +345,334 @@ static int ec_node_cmd_build(struct ec_node *gen_node) if (ret < 0) goto fail; + return lex; + +fail: + ec_node_free(lex); + + return NULL; +} + +static struct ec_node * +ec_node_cmd_build(struct ec_node_cmd *node, const char *cmd_str, + struct ec_node **table, size_t len) +{ + struct ec_node_cmd_ctx ctx = { table, len }; + struct ec_parse *p = NULL; + void *result; + int ret; + /* parse the command expression */ - ret = -ENOMEM; - p = ec_node_parse(lex, node->cmd_str); + p = ec_node_parse(node->parser, cmd_str); if (p == NULL) goto fail; - ret = -EINVAL; - if (!ec_parsed_matches(p)) - goto fail; - if (TAILQ_EMPTY(&p->children)) - goto fail; - if (TAILQ_EMPTY(&TAILQ_FIRST(&p->children)->children)) + if (!ec_parse_matches(p)) { + errno = EINVAL; goto fail; - - ret = -ENOMEM; - cmd = ec_node("seq", NULL); - if (cmd == NULL) + } + if (!ec_parse_has_child(p)) { + errno = EINVAL; goto fail; - - TAILQ_FOREACH(child, &TAILQ_FIRST(&p->children)->children, next) { - ret = ec_node_expr_eval(&result, expr, child, - &test_ops, node); - if (ret < 0) - goto fail; - ret = ec_node_seq_add(cmd, result); - if (ret < 0) - goto fail; } - ec_parsed_free(p); - p = NULL; - ec_node_dump(stdout, cmd); - ec_node_free(node->expr); - node->expr = expr; - ec_node_free(node->lex); - node->lex = lex; - ec_node_free(node->cmd); - node->cmd = cmd; + ret = ec_node_expr_eval(&result, node->expr, + ec_parse_get_first_child(p), + &expr_ops, &ctx); + if (ret < 0) + goto fail; - return 0; + ec_parse_free(p); + return result; fail: - ec_parsed_free(p); - ec_node_free(expr); - ec_node_free(lex); - ec_node_free(cmd); - return ret; + ec_parse_free(p); + return NULL; } -static struct ec_node_type ec_node_cmd_type = { - .name = "cmd", - .build = ec_node_cmd_build, - .parse = ec_node_cmd_parse, - .complete = ec_node_cmd_complete, - .size = sizeof(struct ec_node_cmd), - .free_priv = ec_node_cmd_free_priv, -}; +static int +ec_node_cmd_parse(const struct ec_node *gen_node, struct ec_parse *state, + const struct ec_strvec *strvec) +{ + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; -EC_NODE_TYPE_REGISTER(ec_node_cmd_type); + return ec_node_parse_child(node->cmd, state, strvec); +} -int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child) +static int +ec_node_cmd_complete(const struct ec_node *gen_node, + struct ec_comp *comp, + const struct ec_strvec *strvec) { struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - struct ec_node **table; - // XXX check node type + return ec_node_complete_child(node->cmd, comp, strvec); +} - assert(node != NULL); +static void ec_node_cmd_free_priv(struct ec_node *gen_node) +{ + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; + size_t i; - printf("add child %s\n", child->id); - if (child == NULL) - return -EINVAL; + ec_free(node->cmd_str); + node->cmd_str = NULL; + ec_node_free(node->expr); + node->expr = NULL; + ec_node_free(node->parser); + node->parser = NULL; + ec_node_free(node->cmd); + node->cmd = NULL; + for (i = 0; i < node->len; i++) + ec_node_free(node->table[i]); + ec_free(node->table); + node->table = NULL; + node->len = 0; +} - gen_node->flags &= ~EC_NODE_F_BUILT; +static const struct ec_config_schema ec_node_cmd_subschema[] = { + { + .desc = "A child node whose id is referenced in the expression.", + .type = EC_CONFIG_TYPE_NODE, + }, + { + .type = EC_CONFIG_TYPE_NONE, + }, +}; - table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); - if (table == NULL) { - ec_node_free(child); - return -ENOMEM; +static const struct ec_config_schema ec_node_cmd_schema[] = { + { + .key = "expr", + .desc = "The expression to match. Supported operators " + "are or '|', list ',', many '+', many-or-zero '*', " + "option '[]', group '()'. An identifier (alphanumeric) can " + "reference a node whose node_id matches. Else it is " + "interpreted as ec_node_str() matching this string. " + "Example: command [option] (subset1, subset2) x|y", + .type = EC_CONFIG_TYPE_STRING, + }, + { + .key = "children", + .desc = "The list of children nodes.", + .type = EC_CONFIG_TYPE_LIST, + .subschema = ec_node_cmd_subschema, + }, + { + .type = EC_CONFIG_TYPE_NONE, + }, +}; + +static int ec_node_cmd_set_config(struct ec_node *gen_node, + const struct ec_config *config) +{ + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; + const struct ec_config *expr = NULL; + struct ec_node *cmd = NULL; + struct ec_node **table = NULL; + char *cmd_str = NULL; + size_t len = 0, i; + + /* retrieve config locally */ + expr = ec_config_dict_get(config, "expr"); + if (expr == NULL) { + errno = EINVAL; + goto fail; } - node->table = table; - table[node->len] = child; - node->len++; + table = ec_node_config_node_list_to_table( + ec_config_dict_get(config, "children"), &len); + if (table == NULL) + goto fail; + + cmd_str = ec_strdup(expr->string); + if (cmd_str == NULL) + goto fail; + + /* parse expression to build the cmd child node */ + cmd = ec_node_cmd_build(node, cmd_str, table, len); + if (cmd == NULL) + goto fail; - child->parent = gen_node; - TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed? + /* ok, store the config */ + ec_node_free(node->cmd); + node->cmd = cmd; + ec_free(node->cmd_str); + node->cmd_str = cmd_str; + for (i = 0; i < node->len; i++) + ec_node_free(node->table[i]); + ec_free(node->table); + node->table = table; + node->len = len; return 0; + +fail: + for (i = 0; i < len; i++) + ec_node_free(table[i]); + ec_free(table); + ec_free(cmd_str); + ec_node_free(cmd); + return -1; } -struct ec_node *ec_node_cmd(const char *id, const char *cmd_str) +static size_t +ec_node_cmd_get_children_count(const struct ec_node *gen_node) { - struct ec_node *gen_node = NULL; - struct ec_node_cmd *node = NULL; + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - gen_node = __ec_node(&ec_node_cmd_type, id); - if (gen_node == NULL) - goto fail; + if (node->cmd == NULL) + return 0; + return 1; +} - node = (struct ec_node_cmd *)gen_node; - node->cmd_str = ec_strdup(cmd_str); - if (node->cmd_str == NULL) - goto fail; +static int +ec_node_cmd_get_child(const struct ec_node *gen_node, size_t i, + struct ec_node **child, unsigned int *refs) +{ + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - return gen_node; + if (i > 0) + return -1; -fail: - ec_node_free(gen_node); - return NULL; + *child = node->cmd; + *refs = 1; + return 0; } +static struct ec_node_type ec_node_cmd_type = { + .name = "cmd", + .schema = ec_node_cmd_schema, + .set_config = ec_node_cmd_set_config, + .parse = ec_node_cmd_parse, + .complete = ec_node_cmd_complete, + .size = sizeof(struct ec_node_cmd), + .free_priv = ec_node_cmd_free_priv, + .get_children_count = ec_node_cmd_get_children_count, + .get_child = ec_node_cmd_get_child, +}; + +EC_NODE_TYPE_REGISTER(ec_node_cmd_type); + struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...) { + struct ec_config *config = NULL, *children = NULL; struct ec_node *gen_node = NULL; struct ec_node_cmd *node = NULL; - struct ec_node *child; va_list ap; - int fail = 0; + int ret; + /* this block must stay first, it frees the nodes on error */ va_start(ap, cmd); + children = ec_node_config_node_list_from_vargs(ap); + va_end(ap); + if (children == NULL) + goto fail; - gen_node = ec_node_cmd(id, cmd); + gen_node = ec_node_from_type(&ec_node_cmd_type, id); + if (gen_node == NULL) + goto fail; node = (struct ec_node_cmd *)gen_node; - if (node == NULL) - fail = 1;; - - for (child = va_arg(ap, struct ec_node *); - child != EC_NODE_ENDLIST; - child = va_arg(ap, struct ec_node *)) { - - /* on error, don't quit the loop to avoid leaks */ - if (fail == 1 || child == NULL || - ec_node_cmd_add_child(&node->gen, child) < 0) { - fail = 1; - ec_node_free(child); - } + + node->expr = ec_node_cmd_build_expr(); + if (node->expr == NULL) + goto fail; + + node->parser = ec_node_cmd_build_parser(node->expr); + if (node->parser == NULL) + goto fail; + + config = ec_config_dict(); + if (config == NULL) + goto fail; + + if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0) + goto fail; + + if (ec_config_dict_set(config, "children", children) < 0) { + children = NULL; /* freed */ + goto fail; } + children = NULL; - if (fail == 1) + ret = ec_node_set_config(gen_node, config); + config = NULL; /* freed */ + if (ret < 0) goto fail; - va_end(ap); return gen_node; fail: - ec_node_free(gen_node); /* will also free children */ - va_end(ap); + ec_node_free(gen_node); /* will also free added children */ + ec_config_free(children); + ec_config_free(config); + return NULL; } +/* LCOV_EXCL_START */ static int ec_node_cmd_testcase(void) { struct ec_node *node; - int ret = 0; + int testres = 0; - node = EC_NODE_CMD(NULL, - "command [option] (subset1, subset2) x | y", + node = EC_NODE_CMD(EC_NO_ID, + "command [option] (subset1, subset2, subset3, subset4) x|y z*", ec_node_int("x", 0, 10, 10), ec_node_int("y", 20, 30, 10) ); if (node == NULL) { - ec_log(EC_LOG_ERR, "cannot create node\n"); + EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } - ret |= EC_TEST_CHECK_PARSE(node, 2, "command", "1"); - ret |= EC_TEST_CHECK_PARSE(node, 2, "command", "23"); - ret |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23"); - ret |= EC_TEST_CHECK_PARSE(node, -1, "command", "15"); - ret |= EC_TEST_CHECK_PARSE(node, -1, "foo"); + testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "1"); + testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "subset1", "1"); + testres |= EC_TEST_CHECK_PARSE(node, 4, "command", "subset3", "subset2", + "1"); + testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "subset2", "subset3", + "subset1", "1"); + testres |= EC_TEST_CHECK_PARSE(node, 6, "command", "subset3", "subset1", + "subset4", "subset2", "4"); + testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "23"); + testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23"); + testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "option", "23", + "z", "z"); + testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "15"); + testres |= EC_TEST_CHECK_PARSE(node, -1, "foo"); ec_node_free(node); - node = EC_NODE_CMD(NULL, "good morning bob|bobby|michael [count]", + node = EC_NODE_CMD(EC_NO_ID, "good morning [count] bob|bobby|michael", ec_node_int("count", 0, 10, 10)); if (node == NULL) { - ec_log(EC_LOG_ERR, "cannot create node\n"); + EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } - ret |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "bob", "1"); + testres |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob"); + + testres |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + "good", EC_NODE_ENDLIST); + testres |= EC_TEST_CHECK_COMPLETE(node, + "g", EC_NODE_ENDLIST, + "good", EC_NODE_ENDLIST); + testres |= EC_TEST_CHECK_COMPLETE(node, + "good", "morning", "", EC_NODE_ENDLIST, + "bob", "bobby", "michael", EC_NODE_ENDLIST); + ec_node_free(node); - // XXX completion + node = EC_NODE_CMD(EC_NO_ID, "[foo [bar]]"); + if (node == NULL) { + EC_LOG(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + testres |= EC_TEST_CHECK_PARSE(node, 0); + testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); + testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar"); + testres |= EC_TEST_CHECK_PARSE(node, 0, "x"); + ec_node_free(node); - return ret; + return testres; } +/* LCOV_EXCL_STOP */ static struct ec_test ec_node_cmd_test = { .name = "node_cmd",