From: Olivier Matz Date: Thu, 26 Jan 2017 18:35:28 +0000 (+0100) Subject: save X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=86d3143560d546c7f7df78f67dc633a574d68397;p=protos%2Flibecoli.git save --- diff --git a/lib/Makefile b/lib/Makefile index db3763c..ca072a5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -37,7 +37,7 @@ srcs := ecoli_tk.c ecoli_malloc.c ecoli_log.c ecoli_strvec.c srcs += ecoli_keyval.c ecoli_test.c srcs += ecoli_tk_str.c ecoli_tk_seq.c ecoli_tk_space.c ecoli_tk_or.c srcs += ecoli_tk_empty.c ecoli_tk_int.c ecoli_tk_option.c ecoli_tk_many.c -srcs += ecoli_tk_shlex.c ecoli_tk_expr.c ecoli_tk_bypass.c ecoli_tk_re_lex.c +srcs += ecoli_tk_sh_lex.c ecoli_tk_expr.c ecoli_tk_weakref.c ecoli_tk_re_lex.c shlib-y-$(O)libecoli.so := $(srcs) ldflags-$(O)test = -rdynamic diff --git a/lib/ecoli_tk_bypass.c b/lib/ecoli_tk_bypass.c deleted file mode 100644 index e71a14c..0000000 --- a/lib/ecoli_tk_bypass.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_bypass { - struct ec_tk gen; - struct ec_tk *child; -}; - -static struct ec_parsed_tk *ec_tk_bypass_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk; - - return ec_tk_parse_tokens(tk->child, strvec); -} - -static struct ec_completed_tk *ec_tk_bypass_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk; - - return ec_tk_complete_tokens(tk->child, strvec); -} - -static void ec_tk_bypass_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk; - - ec_tk_free(tk->child); -} - -static struct ec_tk_ops ec_tk_bypass_ops = { - .typename = "bypass", - .parse = ec_tk_bypass_parse, - .complete = ec_tk_bypass_complete, - .free_priv = ec_tk_bypass_free_priv, -}; - -struct ec_tk *ec_tk_bypass_empty(const char *id) -{ - struct ec_tk_bypass *tk = NULL; - - tk = (struct ec_tk_bypass *)ec_tk_new(id, - &ec_tk_bypass_ops, sizeof(*tk)); - if (tk == NULL) - return NULL; - - tk->child = NULL; - - return &tk->gen; -} - -int ec_tk_bypass_set(struct ec_tk *gen_tk, struct ec_tk *child) -{ - struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk; - - // XXX check tk type - - assert(tk != NULL); - - if (child == NULL) - return -EINVAL; - - gen_tk->flags &= ~EC_TK_F_BUILT; - - tk->child = child; - - child->parent = gen_tk; - TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed? - - return 0; -} - -struct ec_tk *ec_tk_bypass_pop(struct ec_tk *gen_tk) -{ - struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk; - struct ec_tk *child; - - child = tk->child; - tk->child = NULL; - - gen_tk->flags &= ~EC_TK_F_BUILT; - TAILQ_REMOVE(&gen_tk->children, child, next); // XXX really needed? - - return child; -} - -struct ec_tk *ec_tk_bypass(const char *id, struct ec_tk *child) -{ - struct ec_tk *gen_tk = NULL; - - if (child == NULL) - return NULL; - - gen_tk = ec_tk_bypass_empty(id); - if (gen_tk == NULL) { - ec_tk_free(child); - return NULL; - } - - ec_tk_bypass_set(gen_tk, child); - - return gen_tk; -} - -static int ec_tk_bypass_testcase(void) -{ - return 0; -} - -static struct ec_test ec_tk_bypass_test = { - .name = "tk_bypass", - .test = ec_tk_bypass_testcase, -}; - -EC_REGISTER_TEST(ec_tk_bypass_test); diff --git a/lib/ecoli_tk_bypass.h b/lib/ecoli_tk_bypass.h deleted file mode 100644 index 92f84d9..0000000 --- a/lib/ecoli_tk_bypass.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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. - */ - -#ifndef ECOLI_TK_BYPASS_ -#define ECOLI_TK_BYPASS_ - -#include - -/// XXX rename in loop ? -// XXX + provide the helper to free ? - -/* a tk that just behaves like its child - * useful to create cyclic graphs of tokens: - * creating a loop (with clones) result in something that is not - * freeable, due to reference counters - * bypass node can solve the issue: before freeing the graph, - * the loop can be cut, falling back to a valid tree that can - * be freed. - * - * Example: - * seq = seq() - * i = int() - * seq_add(seq, i) - * seq_add(seq, clone(seq)) - * FAIL, cannot be freed - * - * seq = seq() - * bypass = bypass(clone(seq)) - * i = int() - * seq_add(seq, i) - * seq_add(seq, bypass) - * - * TO FREE: - * seq2 = bypass_del(bypass) // breaks the loop (seq2 == seq) - * free(bypass) - * free(seq2) - * free(seq) - */ - -struct ec_tk *ec_tk_bypass(const char *id, struct ec_tk *child); - -struct ec_tk *ec_tk_bypass_empty(const char *id); - -/* child is consumed */ -/* all token given in the list will be freed when freeing this one */ -int ec_tk_bypass_set(struct ec_tk *tk, struct ec_tk *child); - -struct ec_tk *ec_tk_bypass_pop(struct ec_tk *gen_tk); - -#endif diff --git a/lib/ecoli_tk_expr.c b/lib/ecoli_tk_expr.c index 4b54b9f..fe28b20 100644 --- a/lib/ecoli_tk_expr.c +++ b/lib/ecoli_tk_expr.c @@ -44,7 +44,7 @@ #include #include #include -#include +#include struct ec_tk_expr { struct ec_tk gen; @@ -84,7 +84,6 @@ static struct ec_completed_tk *ec_tk_expr_complete(const struct ec_tk *gen_tk, static void ec_tk_expr_free_priv(struct ec_tk *gen_tk) { struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - struct ec_tk *final; unsigned int i; ec_log(EC_LOG_DEBUG, "free %p %p %p\n", tk, tk->child, tk->val_tk); @@ -106,12 +105,7 @@ static void ec_tk_expr_free_priv(struct ec_tk *gen_tk) ec_free(tk->open_ops); ec_free(tk->close_ops); - /* break the graph loop, and free the nodes */ - if (tk->child != NULL) { - final = ec_tk_bypass_pop(tk->child); - ec_tk_free(tk->child); - ec_tk_free(final); - } + ec_tk_free(tk->child); } static int ec_tk_expr_build(struct ec_tk *gen_tk) @@ -119,7 +113,7 @@ static int ec_tk_expr_build(struct ec_tk *gen_tk) struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; struct ec_tk *term = NULL, *expr = NULL, *next = NULL, *pre_op = NULL, *post_op = NULL, - *post = NULL, *final = NULL; + *post = NULL, *weak = NULL; unsigned int i; int ret; @@ -132,9 +126,9 @@ static int ec_tk_expr_build(struct ec_tk *gen_tk) /* create the object, we will initialize it later: this is * needed because we have a circular dependency */ ret = -ENOMEM; - expr = ec_tk_bypass_empty("expr"); - if (expr == NULL) - goto fail; + weak = ec_tk_weakref_empty("weak"); + if (weak == NULL) + return -1; /* prefix unary operators */ pre_op = ec_tk_or("pre-op"); @@ -162,12 +156,12 @@ static int ec_tk_expr_build(struct ec_tk *gen_tk) if (ec_tk_or_add(term, EC_TK_SEQ(NULL, ec_tk_clone(pre_op), - ec_tk_clone(expr))) < 0) + ec_tk_clone(weak))) < 0) goto fail; for (i = 0; i < tk->paren_len; i++) { if (ec_tk_or_add(term, EC_TK_SEQ(NULL, ec_tk_clone(tk->open_ops[i]), - ec_tk_clone(expr), + ec_tk_clone(weak), ec_tk_clone(tk->close_ops[i]))) < 0) goto fail; } @@ -189,11 +183,11 @@ static int ec_tk_expr_build(struct ec_tk *gen_tk) goto fail; } - final = EC_TK_SEQ("final", + expr = EC_TK_SEQ("expr", ec_tk_clone(term), ec_tk_many(NULL, ec_tk_clone(post_op), 0, 0) ); - if (final == NULL) + if (expr == NULL) goto fail; /* free the initial references */ @@ -206,11 +200,11 @@ static int ec_tk_expr_build(struct ec_tk *gen_tk) ec_tk_free(post); post = NULL; - if (ec_tk_bypass_set(expr, ec_tk_clone(final)) < 0) + /* no need to clone here, the node is not consumed */ + if (ec_tk_weakref_set(weak, expr) < 0) goto fail; - - ec_tk_free(final); - final = NULL; + ec_tk_free(weak); + weak = NULL; tk->child = expr; @@ -222,7 +216,7 @@ fail: ec_tk_free(pre_op); ec_tk_free(post_op); ec_tk_free(post); - ec_tk_free(final); + ec_tk_free(weak); return ret; } @@ -420,7 +414,7 @@ fail: static int ec_tk_expr_testcase_manual(void) { struct ec_tk *term = NULL, *factor = NULL, *expr = NULL, *val = NULL, - *pre_op = NULL, *post_op = NULL, *post = NULL, *final = NULL; + *pre_op = NULL, *post_op = NULL, *post = NULL, *weak = NULL; int ret = 0; // XXX check all APIs: pointers are "moved", they are freed by @@ -429,10 +423,8 @@ static int ec_tk_expr_testcase_manual(void) /* Example that generates an expression "manually". We keep it * here for reference. */ - /* create the object, we will initialize it later: this is - * needed because we have a circular dependency */ - expr = ec_tk_bypass_empty("expr"); - if (expr == NULL) + weak = ec_tk_weakref_empty("weak"); + if (weak == NULL) return -1; /* reverse bits */ @@ -450,12 +442,12 @@ static int ec_tk_expr_testcase_manual(void) val, EC_TK_SEQ(NULL, ec_tk_str(NULL, "("), - ec_tk_clone(expr), + ec_tk_clone(weak), ec_tk_str(NULL, ")") ), EC_TK_SEQ(NULL, ec_tk_clone(pre_op), - ec_tk_clone(expr) + ec_tk_clone(weak) ) ); val = NULL; @@ -482,7 +474,7 @@ static int ec_tk_expr_testcase_manual(void) ) ); - final = EC_TK_SEQ("final", + expr = EC_TK_SEQ("expr", ec_tk_clone(post), ec_tk_many(NULL, ec_tk_clone(post_op), 0, 0) ); @@ -499,11 +491,11 @@ static int ec_tk_expr_testcase_manual(void) ec_tk_free(post); post = NULL; - if (ec_tk_bypass_set(expr, ec_tk_clone(final)) < 0) + /* no need to clone here, the node is not consumed */ + if (ec_tk_weakref_set(weak, expr) < 0) goto fail; - - ec_tk_free(final); - final = NULL; + ec_tk_free(weak); + weak = NULL; ret |= EC_TEST_CHECK_TK_PARSE(expr, 1, "1"); ret |= EC_TEST_CHECK_TK_PARSE(expr, 1, "1", "*"); @@ -514,9 +506,7 @@ static int ec_tk_expr_testcase_manual(void) expr, 10, "~", "(", "1", "*", "(", "1", "+", "1", ")", ")"); ret |= EC_TEST_CHECK_TK_PARSE(expr, 4, "1", "+", "~", "1"); - final = ec_tk_bypass_pop(expr); ec_tk_free(expr); - ec_tk_free(final); return ret; @@ -528,7 +518,7 @@ fail: ec_tk_free(pre_op); ec_tk_free(post_op); ec_tk_free(post); - ec_tk_free(final); + ec_tk_free(weak); return 0; } diff --git a/lib/ecoli_tk_re_lex.c b/lib/ecoli_tk_re_lex.c index 5dd273d..8d31543 100644 --- a/lib/ecoli_tk_re_lex.c +++ b/lib/ecoli_tk_re_lex.c @@ -247,6 +247,7 @@ static int ec_tk_re_lex_testcase(void) ret |= ec_tk_re_lex_add(tk, "^[ ]+", 0); if (ret != 0) { ec_log(EC_LOG_ERR, "cannot add regexp to token\n"); + ec_tk_free(tk); return -1; } diff --git a/lib/ecoli_tk_sh_lex.c b/lib/ecoli_tk_sh_lex.c new file mode 100644 index 0000000..ab66e60 --- /dev/null +++ b/lib/ecoli_tk_sh_lex.c @@ -0,0 +1,497 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_tk_sh_lex { + struct ec_tk gen; + struct ec_tk *child; +}; + +static size_t eat_spaces(const char *str) +{ + size_t i = 0; + + /* skip spaces */ + while (isblank(str[i])) + i++; + + return i; +} + +/* + * Allocate a new string which is a copy of the input string with quotes + * removed. If quotes are not closed properly, set missing_quote to the + * missing quote char. + */ +static char *unquote_str(const char *str, size_t n, int allow_missing_quote, + char *missing_quote) +{ + unsigned s = 1, d = 0; + char quote = str[0]; + char *dst; + int closed = 0; + + dst = ec_malloc(n); + if (dst == NULL) { + errno = ENOMEM; + return NULL; + } + + /* copy token and remove quotes */ + while (s < n && d < n && str[s] != '\0') { + if (str[s] == '\\' && str[s+1] == quote) { + dst[d++] = quote; + s += 2; + continue; + } + if (str[s] == '\\' && str[s+1] == '\\') { + dst[d++] = '\\'; + s += 2; + continue; + } + if (str[s] == quote) { + s++; + closed = 1; + break; + } + dst[d++] = str[s++]; + } + + /* not enough room in dst buffer (should not happen) */ + if (d >= n) { + ec_free(dst); + errno = EMSGSIZE; + return NULL; + } + + /* quote not closed */ + if (closed == 0) { + if (missing_quote != NULL) + *missing_quote = str[0]; + if (allow_missing_quote == 0) { + ec_free(dst); + errno = EINVAL; + return NULL; + } + } + dst[d++] = '\0'; + + return dst; +} + +static size_t eat_quoted_str(const char *str) +{ + size_t i = 0; + char quote = str[0]; + + while (str[i] != '\0') { + if (str[i] != '\\' && str[i+1] == quote) + return i + 2; + i++; + } + + /* unclosed quote, will be detected later */ + return i; +} + +static size_t eat_str(const char *str) +{ + size_t i = 0; + + /* skip spaces */ + while (!isblank(str[i]) && str[i] != '\0') + i++; + + return i; +} + +static struct ec_strvec *tokenize(const char *str, int completion, + int allow_missing_quote, char *missing_quote) +{ + struct ec_strvec *strvec = NULL; + size_t off = 0, len, suboff, sublen; + char *word = NULL, *concat = NULL, *tmp; + int last_is_space = 1; + +// printf("str=%s\n", str); + + strvec = ec_strvec_new(); + if (strvec == NULL) + goto fail; + + while (str[off] != '\0') { + len = eat_spaces(&str[off]); + if (len > 0) + last_is_space = 1; +// printf("space=%zd\n", len); + off += len; + + len = 0; + suboff = off; + while (str[suboff] != '\0') { + last_is_space = 0; + if (str[suboff] == '"' || str[suboff] == '\'') { + sublen = eat_quoted_str(&str[suboff]); +// printf("sublen=%zd\n", sublen); + word = unquote_str(&str[suboff], sublen, + allow_missing_quote, missing_quote); + } else { + sublen = eat_str(&str[suboff]); +// printf("sublen=%zd\n", sublen); + if (sublen == 0) + break; + word = ec_strndup(&str[suboff], sublen); + } + + if (word == NULL) + goto fail; +// printf("word=%s\n", word); + + len += sublen; + suboff += sublen; + + if (concat == NULL) { + concat = word; + word = NULL; + } else { + tmp = ec_realloc(concat, len + 1); + if (tmp == NULL) + goto fail; + concat = tmp; + strcat(concat, word); + ec_free(word); + word = NULL; + } + } + + if (concat != NULL) { + if (ec_strvec_add(strvec, concat) < 0) + goto fail; + ec_free(concat); + concat = NULL; + } + + /* XXX remove all printf comments */ +// printf("str off=%zd len=%zd\n", off, len); + off += len; + } + + /* in completion mode, append an empty token if the string ends + * with space */ + if (completion && last_is_space) { + if (ec_strvec_add(strvec, "") < 0) + goto fail; + } + + return strvec; + + fail: + ec_free(word); + ec_free(concat); + ec_strvec_free(strvec); + return NULL; +} + +static struct ec_parsed_tk *ec_tk_sh_lex_parse(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk; + struct ec_strvec *new_vec = NULL, *match_strvec; + struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk; + const char *str; + + parsed_tk = ec_parsed_tk_new(); + if (parsed_tk == NULL) + return NULL; + + if (ec_strvec_len(strvec) == 0) + return parsed_tk; + + str = ec_strvec_val(strvec, 0); + new_vec = tokenize(str, 0, 0, NULL); + if (new_vec == NULL) + goto fail; + + child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec); + if (child_parsed_tk == NULL) + goto fail; + + if (!ec_parsed_tk_matches(child_parsed_tk) || + ec_parsed_tk_len(child_parsed_tk) != + ec_strvec_len(new_vec)) { + ec_strvec_free(new_vec); + ec_parsed_tk_free(child_parsed_tk); + return parsed_tk; + } + ec_strvec_free(new_vec); + new_vec = NULL; + + ec_parsed_tk_add_child(parsed_tk, child_parsed_tk); + match_strvec = ec_strvec_ndup(strvec, 0, 1); + if (match_strvec == NULL) + goto fail; + ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); + + return parsed_tk; + + fail: + ec_strvec_free(new_vec); + ec_parsed_tk_free(parsed_tk); + + return NULL; +} + +static struct ec_completed_tk *ec_tk_sh_lex_complete(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk; + struct ec_completed_tk *completed_tk, *child_completed_tk = NULL; + struct ec_strvec *new_vec = NULL; + const char *str; + char missing_quote; + +// printf("==================\n"); + completed_tk = ec_completed_tk_new(); + if (completed_tk == NULL) + return NULL; + + if (ec_strvec_len(strvec) != 1) + return completed_tk; + + str = ec_strvec_val(strvec, 0); + new_vec = tokenize(str, 1, 1, &missing_quote); + if (new_vec == NULL) + goto fail; + +// ec_strvec_dump(new_vec, stdout); + + child_completed_tk = ec_tk_complete_tokens(tk->child, new_vec); + if (child_completed_tk == NULL) + goto fail; + + ec_strvec_free(new_vec); + new_vec = NULL; + ec_completed_tk_merge(completed_tk, child_completed_tk); + + return completed_tk; + + +#if 0 + for (i = 0, t = &tokens[0]; i < tk->len; i++, t++) { + if (*(t + 1) != NULL) { + child_parsed_tk = ec_tk_parse(tk->table[i], *t); + if (child_parsed_tk == NULL) + goto fail; + + if (strlen(child_parsed_tk->str) == 0) + t--; + else if (strlen(child_parsed_tk->str) != strlen(*t)) { + ec_parsed_tk_free(child_parsed_tk); + goto fail; + } + + ec_parsed_tk_free(child_parsed_tk); + } else { + child_completed_tk = ec_tk_complete(tk->table[i], *t); + if (child_completed_tk == NULL) { + ec_completed_tk_free(completed_tk); + return NULL; + } + ec_completed_tk_merge(completed_tk, child_completed_tk); + + child_parsed_tk = ec_tk_parse(tk->table[i], ""); + if (child_parsed_tk == NULL) + break; + ec_parsed_tk_free(child_parsed_tk); + t--; + } + } + + if (tokens != NULL) { + for (t = &tokens[0]; *t != NULL; t++) + ec_free(*t); + ec_free(tokens); + tokens = NULL; + } + + ec_completed_tk_dump(stdout, completed_tk); +#endif + + fail: + ec_strvec_free(new_vec); + ec_completed_tk_free(completed_tk); + return NULL; +} + +static void ec_tk_sh_lex_free_priv(struct ec_tk *gen_tk) +{ + struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk; + + ec_tk_free(tk->child); +} + +static struct ec_tk_ops ec_tk_sh_lex_ops = { + .typename = "sh_lex", + .parse = ec_tk_sh_lex_parse, + .complete = ec_tk_sh_lex_complete, + .free_priv = ec_tk_sh_lex_free_priv, +}; + +struct ec_tk *ec_tk_sh_lex_new(const char *id, struct ec_tk *child) +{ + struct ec_tk_sh_lex *tk = NULL; + + if (child == NULL) + return NULL; + + tk = (struct ec_tk_sh_lex *)ec_tk_new(id, &ec_tk_sh_lex_ops, + sizeof(*tk)); + if (tk == NULL) { + ec_tk_free(child); + return NULL; + } + + tk->child = child; + + return &tk->gen; +} + +static int ec_tk_sh_lex_testcase(void) +{ + struct ec_tk *tk; + int ret = 0; + + tk = ec_tk_sh_lex_new(NULL, + EC_TK_SEQ(NULL, + ec_tk_str(NULL, "foo"), + ec_tk_option_new(NULL, + ec_tk_str(NULL, "toto") + ), + ec_tk_str(NULL, "bar") + ) + ); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + return -1; + } + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo bar"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " foo bar"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 'foo' \"bar\""); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 'f'oo 'toto' bar"); + ec_tk_free(tk); + + /* test completion */ + tk = ec_tk_sh_lex_new(NULL, + EC_TK_SEQ(NULL, + ec_tk_str(NULL, "foo"), + ec_tk_option_new(NULL, + ec_tk_str(NULL, "toto") + ), + ec_tk_str(NULL, "bar"), + ec_tk_str(NULL, "titi") + ) + ); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + return -1; + } + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "", EC_TK_ENDLIST, + "foo", EC_TK_ENDLIST, + "foo"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + " ", EC_TK_ENDLIST, + "foo", EC_TK_ENDLIST, + "foo"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "f", EC_TK_ENDLIST, + "oo", EC_TK_ENDLIST, + "oo"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "foo", EC_TK_ENDLIST, + "", EC_TK_ENDLIST, + ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "foo ", EC_TK_ENDLIST, + "bar", "toto", EC_TK_ENDLIST, + ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "foo t", EC_TK_ENDLIST, + "oto", EC_TK_ENDLIST, + "oto"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "foo b", EC_TK_ENDLIST, + "ar", EC_TK_ENDLIST, + "ar"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "foo bar", EC_TK_ENDLIST, + "", EC_TK_ENDLIST, + ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "foo bar ", EC_TK_ENDLIST, + "titi", EC_TK_ENDLIST, + "titi"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "foo toto bar ", EC_TK_ENDLIST, + "titi", EC_TK_ENDLIST, + "titi"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "x", EC_TK_ENDLIST, + EC_TK_ENDLIST, + ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "foo barx", EC_TK_ENDLIST, + EC_TK_ENDLIST, + ""); + + ec_tk_free(tk); + return ret; +} + +static struct ec_test ec_tk_sh_lex_test = { + .name = "tk_sh_lex", + .test = ec_tk_sh_lex_testcase, +}; + +EC_REGISTER_TEST(ec_tk_sh_lex_test); diff --git a/lib/ecoli_tk_sh_lex.h b/lib/ecoli_tk_sh_lex.h new file mode 100644 index 0000000..7031d1c --- /dev/null +++ b/lib/ecoli_tk_sh_lex.h @@ -0,0 +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. + */ + +#ifndef ECOLI_TK_SHLEX_ +#define ECOLI_TK_SHLEX_ + +#include + +struct ec_tk *ec_tk_sh_lex_new(const char *id, struct ec_tk *child); + +#endif diff --git a/lib/ecoli_tk_shlex.c b/lib/ecoli_tk_shlex.c deleted file mode 100644 index a7bb6ff..0000000 --- a/lib/ecoli_tk_shlex.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_shlex { - struct ec_tk gen; - struct ec_tk *child; -}; - -static size_t eat_spaces(const char *str) -{ - size_t i = 0; - - /* skip spaces */ - while (isblank(str[i])) - i++; - - return i; -} - -/* - * Allocate a new string which is a copy of the input string with quotes - * removed. If quotes are not closed properly, set missing_quote to the - * missing quote char. - */ -static char *unquote_str(const char *str, size_t n, int allow_missing_quote, - char *missing_quote) -{ - unsigned s = 1, d = 0; - char quote = str[0]; - char *dst; - int closed = 0; - - dst = ec_malloc(n); - if (dst == NULL) { - errno = ENOMEM; - return NULL; - } - - /* copy token and remove quotes */ - while (s < n && d < n && str[s] != '\0') { - if (str[s] == '\\' && str[s+1] == quote) { - dst[d++] = quote; - s += 2; - continue; - } - if (str[s] == '\\' && str[s+1] == '\\') { - dst[d++] = '\\'; - s += 2; - continue; - } - if (str[s] == quote) { - s++; - closed = 1; - break; - } - dst[d++] = str[s++]; - } - - /* not enough room in dst buffer (should not happen) */ - if (d >= n) { - ec_free(dst); - errno = EMSGSIZE; - return NULL; - } - - /* quote not closed */ - if (closed == 0) { - if (missing_quote != NULL) - *missing_quote = str[0]; - if (allow_missing_quote == 0) { - ec_free(dst); - errno = EINVAL; - return NULL; - } - } - dst[d++] = '\0'; - - return dst; -} - -static size_t eat_quoted_str(const char *str) -{ - size_t i = 0; - char quote = str[0]; - - while (str[i] != '\0') { - if (str[i] != '\\' && str[i+1] == quote) - return i + 2; - i++; - } - - /* unclosed quote, will be detected later */ - return i; -} - -static size_t eat_str(const char *str) -{ - size_t i = 0; - - /* skip spaces */ - while (!isblank(str[i]) && str[i] != '\0') - i++; - - return i; -} - -static struct ec_strvec *tokenize(const char *str, int completion, - int allow_missing_quote, char *missing_quote) -{ - struct ec_strvec *strvec = NULL; - size_t off = 0, len, suboff, sublen; - char *word = NULL, *concat = NULL, *tmp; - int last_is_space = 1; - -// printf("str=%s\n", str); - - strvec = ec_strvec_new(); - if (strvec == NULL) - goto fail; - - while (str[off] != '\0') { - len = eat_spaces(&str[off]); - if (len > 0) - last_is_space = 1; -// printf("space=%zd\n", len); - off += len; - - len = 0; - suboff = off; - while (str[suboff] != '\0') { - last_is_space = 0; - if (str[suboff] == '"' || str[suboff] == '\'') { - sublen = eat_quoted_str(&str[suboff]); -// printf("sublen=%zd\n", sublen); - word = unquote_str(&str[suboff], sublen, - allow_missing_quote, missing_quote); - } else { - sublen = eat_str(&str[suboff]); -// printf("sublen=%zd\n", sublen); - if (sublen == 0) - break; - word = ec_strndup(&str[suboff], sublen); - } - - if (word == NULL) - goto fail; -// printf("word=%s\n", word); - - len += sublen; - suboff += sublen; - - if (concat == NULL) { - concat = word; - word = NULL; - } else { - tmp = ec_realloc(concat, len + 1); - if (tmp == NULL) - goto fail; - concat = tmp; - strcat(concat, word); - ec_free(word); - word = NULL; - } - } - - if (concat != NULL) { - if (ec_strvec_add(strvec, concat) < 0) - goto fail; - ec_free(concat); - concat = NULL; - } - - /* XXX remove all printf comments */ -// printf("str off=%zd len=%zd\n", off, len); - off += len; - } - - /* in completion mode, append an empty token if the string ends - * with space */ - if (completion && last_is_space) { - if (ec_strvec_add(strvec, "") < 0) - goto fail; - } - - return strvec; - - fail: - ec_free(word); - ec_free(concat); - ec_strvec_free(strvec); - return NULL; -} - -static struct ec_parsed_tk *ec_tk_shlex_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk; - struct ec_strvec *new_vec = NULL, *match_strvec; - struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk; - const char *str; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - return NULL; - - if (ec_strvec_len(strvec) == 0) - return parsed_tk; - - str = ec_strvec_val(strvec, 0); - new_vec = tokenize(str, 0, 0, NULL); - if (new_vec == NULL) - goto fail; - - child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec); - if (child_parsed_tk == NULL) - goto fail; - - if (!ec_parsed_tk_matches(child_parsed_tk) || - ec_parsed_tk_len(child_parsed_tk) != - ec_strvec_len(new_vec)) { - ec_strvec_free(new_vec); - ec_parsed_tk_free(child_parsed_tk); - return parsed_tk; - } - ec_strvec_free(new_vec); - new_vec = NULL; - - ec_parsed_tk_add_child(parsed_tk, child_parsed_tk); - match_strvec = ec_strvec_ndup(strvec, 0, 1); - if (match_strvec == NULL) - goto fail; - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_strvec_free(new_vec); - ec_parsed_tk_free(parsed_tk); - - return NULL; -} - -static struct ec_completed_tk *ec_tk_shlex_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk; - struct ec_completed_tk *completed_tk, *child_completed_tk = NULL; - struct ec_strvec *new_vec = NULL; - const char *str; - char missing_quote; - -// printf("==================\n"); - completed_tk = ec_completed_tk_new(); - if (completed_tk == NULL) - return NULL; - - if (ec_strvec_len(strvec) != 1) - return completed_tk; - - str = ec_strvec_val(strvec, 0); - new_vec = tokenize(str, 1, 1, &missing_quote); - if (new_vec == NULL) - goto fail; - -// ec_strvec_dump(new_vec, stdout); - - child_completed_tk = ec_tk_complete_tokens(tk->child, new_vec); - if (child_completed_tk == NULL) - goto fail; - - ec_strvec_free(new_vec); - new_vec = NULL; - ec_completed_tk_merge(completed_tk, child_completed_tk); - - return completed_tk; - - -#if 0 - for (i = 0, t = &tokens[0]; i < tk->len; i++, t++) { - if (*(t + 1) != NULL) { - child_parsed_tk = ec_tk_parse(tk->table[i], *t); - if (child_parsed_tk == NULL) - goto fail; - - if (strlen(child_parsed_tk->str) == 0) - t--; - else if (strlen(child_parsed_tk->str) != strlen(*t)) { - ec_parsed_tk_free(child_parsed_tk); - goto fail; - } - - ec_parsed_tk_free(child_parsed_tk); - } else { - child_completed_tk = ec_tk_complete(tk->table[i], *t); - if (child_completed_tk == NULL) { - ec_completed_tk_free(completed_tk); - return NULL; - } - ec_completed_tk_merge(completed_tk, child_completed_tk); - - child_parsed_tk = ec_tk_parse(tk->table[i], ""); - if (child_parsed_tk == NULL) - break; - ec_parsed_tk_free(child_parsed_tk); - t--; - } - } - - if (tokens != NULL) { - for (t = &tokens[0]; *t != NULL; t++) - ec_free(*t); - ec_free(tokens); - tokens = NULL; - } - - ec_completed_tk_dump(stdout, completed_tk); -#endif - - fail: - ec_strvec_free(new_vec); - ec_completed_tk_free(completed_tk); - return NULL; -} - -static void ec_tk_shlex_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk; - - ec_tk_free(tk->child); -} - -static struct ec_tk_ops ec_tk_shlex_ops = { - .typename = "shlex", - .parse = ec_tk_shlex_parse, - .complete = ec_tk_shlex_complete, - .free_priv = ec_tk_shlex_free_priv, -}; - -struct ec_tk *ec_tk_shlex_new(const char *id, struct ec_tk *child) -{ - struct ec_tk_shlex *tk = NULL; - - if (child == NULL) - return NULL; - - tk = (struct ec_tk_shlex *)ec_tk_new(id, &ec_tk_shlex_ops, - sizeof(*tk)); - if (tk == NULL) { - ec_tk_free(child); - return NULL; - } - - tk->child = child; - - return &tk->gen; -} - -static int ec_tk_shlex_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = ec_tk_shlex_new(NULL, - EC_TK_SEQ(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_option_new(NULL, - ec_tk_str(NULL, "toto") - ), - ec_tk_str(NULL, "bar") - ) - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " foo bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 'foo' \"bar\""); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 'f'oo 'toto' bar"); - ec_tk_free(tk); - - /* test completion */ - tk = ec_tk_shlex_new(NULL, - EC_TK_SEQ(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_option_new(NULL, - ec_tk_str(NULL, "toto") - ), - ec_tk_str(NULL, "bar"), - ec_tk_str(NULL, "titi") - ) - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - "foo", EC_TK_ENDLIST, - "foo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - " ", EC_TK_ENDLIST, - "foo", EC_TK_ENDLIST, - "foo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "f", EC_TK_ENDLIST, - "oo", EC_TK_ENDLIST, - "oo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", EC_TK_ENDLIST, - "", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo ", EC_TK_ENDLIST, - "bar", "toto", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo t", EC_TK_ENDLIST, - "oto", EC_TK_ENDLIST, - "oto"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo b", EC_TK_ENDLIST, - "ar", EC_TK_ENDLIST, - "ar"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo bar", EC_TK_ENDLIST, - "", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo bar ", EC_TK_ENDLIST, - "titi", EC_TK_ENDLIST, - "titi"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo toto bar ", EC_TK_ENDLIST, - "titi", EC_TK_ENDLIST, - "titi"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "x", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo barx", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - - ec_tk_free(tk); - return ret; -} - -static struct ec_test ec_tk_shlex_test = { - .name = "tk_shlex", - .test = ec_tk_shlex_testcase, -}; - -EC_REGISTER_TEST(ec_tk_shlex_test); diff --git a/lib/ecoli_tk_shlex.h b/lib/ecoli_tk_shlex.h deleted file mode 100644 index 5aa2246..0000000 --- a/lib/ecoli_tk_shlex.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ - -#ifndef ECOLI_TK_SHLEX_ -#define ECOLI_TK_SHLEX_ - -#include - -struct ec_tk *ec_tk_shlex_new(const char *id, struct ec_tk *child); - -#endif diff --git a/lib/ecoli_tk_weakref.c b/lib/ecoli_tk_weakref.c new file mode 100644 index 0000000..5aee97c --- /dev/null +++ b/lib/ecoli_tk_weakref.c @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_tk_weakref { + struct ec_tk gen; + struct ec_tk *child; +}; + +static struct ec_parsed_tk *ec_tk_weakref_parse(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk; + + return ec_tk_parse_tokens(tk->child, strvec); +} + +static struct ec_completed_tk *ec_tk_weakref_complete(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk; + + return ec_tk_complete_tokens(tk->child, strvec); +} + +static struct ec_tk_ops ec_tk_weakref_ops = { + .typename = "weakref", + .parse = ec_tk_weakref_parse, + .complete = ec_tk_weakref_complete, +}; + +struct ec_tk *ec_tk_weakref_empty(const char *id) +{ + struct ec_tk_weakref *tk = NULL; + + tk = (struct ec_tk_weakref *)ec_tk_new(id, + &ec_tk_weakref_ops, sizeof(*tk)); + if (tk == NULL) + return NULL; + + tk->child = NULL; + + return &tk->gen; +} + +int ec_tk_weakref_set(struct ec_tk *gen_tk, struct ec_tk *child) +{ + struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk; + + // XXX check tk type + + assert(tk != NULL); + + if (child == NULL) + return -EINVAL; + + gen_tk->flags &= ~EC_TK_F_BUILT; + + tk->child = child; + + child->parent = gen_tk; + TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed? + + return 0; +} + +struct ec_tk *ec_tk_weakref(const char *id, struct ec_tk *child) +{ + struct ec_tk *gen_tk = NULL; + + if (child == NULL) + return NULL; + + gen_tk = ec_tk_weakref_empty(id); + if (gen_tk == NULL) + return NULL; + + ec_tk_weakref_set(gen_tk, child); + + return gen_tk; +} + +static int ec_tk_weakref_testcase(void) +{ + return 0; +} + +static struct ec_test ec_tk_weakref_test = { + .name = "tk_weakref", + .test = ec_tk_weakref_testcase, +}; + +EC_REGISTER_TEST(ec_tk_weakref_test); diff --git a/lib/ecoli_tk_weakref.h b/lib/ecoli_tk_weakref.h new file mode 100644 index 0000000..c911400 --- /dev/null +++ b/lib/ecoli_tk_weakref.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef ECOLI_TK_BYPASS_ +#define ECOLI_TK_BYPASS_ + +#include + +/* a tk that just behaves like its child and that does not free + * its child when freed. + * + * useful to create cyclic graphs of tokens: + * creating a loop (with clones) result in something that is not + * freeable, due to reference counters + * + * Example: + * val = int(0, 10) + * op = str("!") + * expr = or() + * seq = seq(clone(op), clone(expr)) + * expr.add(clone(seq)) + * expr.add(clone(val)) + * free(val) + * free(op) + * free(seq) + * + * FAIL: expr cannot be freed due to cyclic refs + * The references are like this: + * + * val + * ^ + * | + * $user ---> expr ---> seq ---> op + * <--- + * + * It is solved with: + * val = int(0, 10) + * op = str("!") + * expr = or() + * weak = weak(expr) + * seq = seq(clone(op), clone(weak)) + * expr.add(clone(seq)) + * expr.add(clone(val)) + * free(val) + * free(op) + * free(weak) + * free(seq) + * + * + * val + * ^ + * | + * $user ---> expr ---------------> seq ---> op + * <- - - weak <--- + * + * The node expr can be freed. + */ + +/* on error, child is *not* freed */ +struct ec_tk *ec_tk_weakref(const char *id, struct ec_tk *child); + +struct ec_tk *ec_tk_weakref_empty(const char *id); + +/* on error, child is *not* freed */ +int ec_tk_weakref_set(struct ec_tk *tk, struct ec_tk *child); + +#endif diff --git a/lib/main-readline.c b/lib/main-readline.c index 9489b70..7a0d5b8 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include static struct ec_tk *commands; @@ -201,7 +201,7 @@ static int create_commands(void) if (ec_tk_or_add(cmdlist, cmd) < 0) goto fail; - commands = ec_tk_shlex_new(NULL, cmdlist); + commands = ec_tk_sh_lex_new(NULL, cmdlist); if (commands == NULL) goto fail;