]> git.droids-corp.org - protos/libecoli.git/commitdiff
save
authorOlivier Matz <zer0@droids-corp.org>
Thu, 26 Jan 2017 18:35:28 +0000 (19:35 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 26 Jan 2017 18:35:28 +0000 (19:35 +0100)
12 files changed:
lib/Makefile
lib/ecoli_tk_bypass.c [deleted file]
lib/ecoli_tk_bypass.h [deleted file]
lib/ecoli_tk_expr.c
lib/ecoli_tk_re_lex.c
lib/ecoli_tk_sh_lex.c [new file with mode: 0644]
lib/ecoli_tk_sh_lex.h [new file with mode: 0644]
lib/ecoli_tk_shlex.c [deleted file]
lib/ecoli_tk_shlex.h [deleted file]
lib/ecoli_tk_weakref.c [new file with mode: 0644]
lib/ecoli_tk_weakref.h [new file with mode: 0644]
lib/main-readline.c

index db3763c2caec54c6ad472305cf34df0ee2e4465e..ca072a536e5b95ad5e74242925d10d911031f64a 100644 (file)
@@ -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 (file)
index e71a14c..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <stdarg.h>
-#include <errno.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_option.h>
-#include <ecoli_tk_bypass.h>
-
-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 (file)
index 92f84d9..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
- *
- * 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 <ecoli_tk.h>
-
-/// 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
index 4b54b9f25b41cedd60900ed632953a6a34e8ba18..fe28b209e85d0e07551b4a21b46c54a11941092e 100644 (file)
@@ -44,7 +44,7 @@
 #include <ecoli_tk_many.h>
 #include <ecoli_tk_or.h>
 #include <ecoli_tk_expr.h>
-#include <ecoli_tk_bypass.h>
+#include <ecoli_tk_weakref.h>
 
 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;
 }
 
index 5dd273dddea1dfb29b5505851e3fe27ff2a3055c..8d31543f3eb8305e08a4349ea2850755695b5983 100644 (file)
@@ -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 (file)
index 0000000..ab66e60
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_seq.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_sh_lex.h>
+
+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 (file)
index 0000000..7031d1c
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <ecoli_tk.h>
+
+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 (file)
index a7bb6ff..0000000
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_seq.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_option.h>
-#include <ecoli_tk_shlex.h>
-
-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 (file)
index 5aa2246..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
- *
- * 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 <ecoli_tk.h>
-
-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 (file)
index 0000000..5aee97c
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_weakref.h>
+
+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 (file)
index 0000000..c911400
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <ecoli_tk.h>
+
+/* 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
index 9489b701b25c28dc5ffaa6b5271e672065d15801..7a0d5b860d794b2f052c3ba76b9d31b218649143 100644 (file)
@@ -40,7 +40,7 @@
 #include <ecoli_tk_seq.h>
 #include <ecoli_tk_space.h>
 #include <ecoli_tk_or.h>
-#include <ecoli_tk_shlex.h>
+#include <ecoli_tk_sh_lex.h>
 #include <ecoli_tk_int.h>
 
 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;