]> git.droids-corp.org - protos/libecoli.git/commitdiff
option, shlex
authorOlivier Matz <zer0@droids-corp.org>
Thu, 3 Nov 2016 19:29:26 +0000 (20:29 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 3 Nov 2016 19:29:26 +0000 (20:29 +0100)
16 files changed:
lib/Makefile
lib/build/test
lib/ecoli_test.c
lib/ecoli_tk.c
lib/ecoli_tk.h
lib/ecoli_tk_empty.c
lib/ecoli_tk_int.c
lib/ecoli_tk_option.c [new file with mode: 0644]
lib/ecoli_tk_option.h [new file with mode: 0644]
lib/ecoli_tk_or.c
lib/ecoli_tk_seq.c
lib/ecoli_tk_shlex.c [new file with mode: 0644]
lib/ecoli_tk_shlex.h [new file with mode: 0644]
lib/ecoli_tk_space.c
lib/ecoli_tk_str.c
lib/main-readline.c [new file with mode: 0644]

index 08a1b40cebd04bfd6ec8e79b75f75a34df1c9d83..42a969e28b32e57466e1711a53afdc4bc647f791 100644 (file)
@@ -36,11 +36,15 @@ CFLAGS += -I.
 srcs := ecoli_tk.c ecoli_tk_str.c ecoli_tk_seq.c
 srcs += ecoli_tk_space.c ecoli_tk_or.c ecoli_test.c
 srcs += ecoli_tk_empty.c ecoli_tk_int.c
-srcs += ecoli_malloc.c ecoli_log.c
+srcs += ecoli_malloc.c ecoli_log.c ecoli_tk_option.c
+srcs += ecoli_tk_shlex.c
 shlib-y-$(O)libecoli.so := $(srcs)
 
 exe-y-$(O)test = $(srcs) main.c
 
+ldflags-$(O)readline = -lreadline
+exe-y-$(O)readline = $(srcs) main-readline.c
+
 include $(ECOLI)/mk/ecoli-post.mk
 
 all: _ecoli_all
index 8ef7e3e67779de88bc45f316ca2fbe7014effaa5..6484b701ea0da19ca4b39b87c1ef215f5455280e 100755 (executable)
Binary files a/lib/build/test and b/lib/build/test differ
index f6521da14f58b66ab766051c163ed58ace7ac8a3..c859d073e35ce7f222386fc919acd8add4571b0e 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <assert.h>
 
 #include <ecoli_log.h>
 #include <ecoli_malloc.h>
@@ -75,20 +76,16 @@ int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
        const char *s;
        int ret = -1;
 
+       assert(expected != NULL);
+
        c = ec_tk_complete(tk, input);
        s = ec_completed_tk_smallest_start(c);
-       if (s == NULL && expected == NULL)
-               ret = 0;
-       else if (s != NULL && expected != NULL &&
-               !strcmp(s, expected))
+       if (!strcmp(s, expected))
                ret = 0;
 
-       if (expected == NULL && ret != 0)
-               ec_log(EC_LOG_ERR,
-                       "tk should not complete but completes with <%s>\n", s);
-       if (expected != NULL && ret != 0)
+       if (ret != 0)
                ec_log(EC_LOG_ERR,
-                       "tk should complete with <%s> but completes with <%s>\n",
+                       "should complete with <%s> but completes with <%s>\n",
                        expected, s);
 
        ec_completed_tk_free(c);
@@ -135,6 +132,7 @@ int ec_test_check_tk_complete_list(const struct ec_tk *tk,
                ec_log(EC_LOG_ERR,
                        "nb_completion (%d) does not match (%d)\n",
                        count, ec_completed_tk_count(c));
+               ec_completed_tk_dump(stdout, c);
                goto out;
        }
 
@@ -310,10 +308,6 @@ void debug_alloc_dump(void)
        }
 }
 
-/* XXX todo */
-/* int ec_test_check_tk_complete_list(const struct ec_tk *tk, */
-/*     const char *input, ...) */
-
 int ec_test_all(void)
 {
        struct ec_test *test;
@@ -322,7 +316,7 @@ int ec_test_all(void)
        TAILQ_INIT(&debug_alloc_hdr_list);
 
        /* register a new malloc to trac memleaks */
-       if (0 && ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
+       if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
                ec_log(EC_LOG_ERR, "cannot register new malloc\n");
                return -1;
        }
index cdb064ee5cb8d2f142cb01552b88c2663c4e2729..8f22fbda9bdaff8be91247e947959243dbde8d11 100644 (file)
@@ -223,6 +223,7 @@ struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
 /* XXX define when to use ec_tk_complete() or tk->complete()
  * (same for parse)
  * suggestion: tk->op() is internal, user calls the function
+ * other idea: have 2 functions
  */
 struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
        const char *str)
@@ -273,17 +274,13 @@ void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt)
        ec_free(elt);
 }
 
-struct ec_completed_tk *ec_completed_tk_merge(
-       struct ec_completed_tk *completed_tk1,
+void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
        struct ec_completed_tk *completed_tk2)
 {
        struct ec_completed_tk_elt *elt;
 
-       if (completed_tk2 == NULL)
-               return completed_tk1;
-
-       if (completed_tk1 == NULL)
-               return completed_tk2;
+       assert(completed_tk1 != NULL);
+       assert(completed_tk2 != NULL);
 
        while (!TAILQ_EMPTY(&completed_tk2->elts)) {
                elt = TAILQ_FIRST(&completed_tk2->elts);
@@ -292,8 +289,6 @@ struct ec_completed_tk *ec_completed_tk_merge(
        }
 
        ec_completed_tk_free(completed_tk2);
-
-       return completed_tk1;
 }
 
 void ec_completed_tk_free(struct ec_completed_tk *completed_tk)
@@ -333,8 +328,8 @@ void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk)
 const char *ec_completed_tk_smallest_start(
        const struct ec_completed_tk *completed_tk)
 {
-       if (completed_tk == NULL)
-               return NULL;
+       if (completed_tk == NULL || completed_tk->smallest_start == NULL)
+               return "";
 
        return completed_tk->smallest_start;
 }
@@ -346,3 +341,26 @@ unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk)
 
        return completed_tk->count;
 }
+
+void ec_completed_tk_iter_start(struct ec_completed_tk *completed_tk)
+{
+       if (completed_tk == NULL)
+               return;
+
+       completed_tk->cur = NULL;
+}
+
+const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
+       struct ec_completed_tk *completed_tk)
+{
+       if (completed_tk == NULL)
+               return NULL;
+
+       if (completed_tk->cur == NULL) {
+               completed_tk->cur = TAILQ_FIRST(&completed_tk->elts);
+       } else {
+               completed_tk->cur = TAILQ_NEXT(completed_tk->cur, next);
+       }
+
+       return completed_tk->cur;
+}
index cc0a20819d39e6d2f7ef30b553cee08a19028344..72aee307a97d759004482e373281e6d51a542dbd 100644 (file)
@@ -97,6 +97,7 @@ TAILQ_HEAD(ec_completed_tk_elt_list, ec_completed_tk_elt);
 
 struct ec_completed_tk {
        struct ec_completed_tk_elt_list elts;
+       const struct ec_completed_tk_elt *cur;
        unsigned count;
        char *smallest_start;
 };
@@ -113,17 +114,20 @@ struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
 void ec_completed_tk_add_elt(struct ec_completed_tk *completed_tk,
        struct ec_completed_tk_elt *elt);
 void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt);
-struct ec_completed_tk *ec_completed_tk_merge(
-       struct ec_completed_tk *completed_tk1,
+void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
        struct ec_completed_tk *completed_tk2);
 void ec_completed_tk_free(struct ec_completed_tk *completed_tk);
 void ec_completed_tk_dump(FILE *out,
        const struct ec_completed_tk *completed_tk);
 
-/* can return NULL */
+/* cannot return NULL */
 const char *ec_completed_tk_smallest_start(
        const struct ec_completed_tk *completed_tk);
 
 unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk);
 
+void ec_completed_tk_iter_start(struct ec_completed_tk *completed_tk);
+const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
+       struct ec_completed_tk *completed_tk);
+
 #endif
index e3e24f9ba87a02a184e7a19603f7afb8809a9793..bc180d742cef73292531bc8da0158d14bf6da77f 100644 (file)
@@ -64,7 +64,6 @@ static int ec_tk_empty_testcase(void)
        struct ec_tk *tk;
        int ret = 0;
 
-       /* all inputs match */
        tk = ec_tk_empty_new(NULL);
        if (tk == NULL) {
                ec_log(EC_LOG_ERR, "cannot create tk\n");
@@ -81,8 +80,8 @@ static int ec_tk_empty_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", NULL);
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
        ec_tk_free(tk);
 
        return ret;
index 8aeb6bc8a65a16424f059121c822d7057ecfeeac..a6cc12d7def7c0823826c6e083ec8dba54993952 100644 (file)
@@ -198,9 +198,9 @@ static int ec_tk_int_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", NULL);
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "1", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "1", "");
        ec_tk_free(tk);
 
        return ret;
diff --git a/lib/ecoli_tk_option.c b/lib/ecoli_tk_option.c
new file mode 100644 (file)
index 0000000..2b4e4cb
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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 <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_test.h>
+
+static struct ec_parsed_tk *ec_tk_option_parse(const struct ec_tk *gen_tk,
+       const char *str)
+{
+       struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
+       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+
+       parsed_tk = ec_parsed_tk_new(gen_tk);
+       if (parsed_tk == NULL)
+               return NULL;
+
+       child_parsed_tk = ec_tk_parse(tk->child, str);
+       if (child_parsed_tk != NULL) {
+               ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+               parsed_tk->str = ec_strndup(child_parsed_tk->str,
+                       strlen(child_parsed_tk->str));
+       } else {
+               parsed_tk->str = ec_strdup("");
+       }
+
+       return parsed_tk;
+}
+
+static struct ec_completed_tk *ec_tk_option_complete(const struct ec_tk *gen_tk,
+       const char *str)
+{
+       struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
+
+       return ec_tk_complete(tk->child, str);
+}
+
+static void ec_tk_option_free_priv(struct ec_tk *gen_tk)
+{
+       struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
+
+       ec_tk_free(tk->child);
+}
+
+static struct ec_tk_ops ec_tk_option_ops = {
+       .parse = ec_tk_option_parse,
+       .complete = ec_tk_option_complete,
+       .free_priv = ec_tk_option_free_priv,
+};
+
+struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *child)
+{
+       struct ec_tk_option *tk = NULL;
+
+       if (child == NULL)
+               return NULL;
+
+       tk = (struct ec_tk_option *)ec_tk_new(id, &ec_tk_option_ops,
+               sizeof(*tk));
+       if (tk == NULL)
+               return NULL;
+
+       tk->child = child;
+
+       return &tk->gen;
+}
+
+static int ec_tk_option_testcase(void)
+{
+       struct ec_tk *tk;
+       int ret = 0;
+
+       tk = ec_tk_option_new(NULL, ec_tk_str_new(NULL, "foo"));
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "", "");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", "");
+       ec_tk_free(tk);
+
+       /* test completion */
+       tk = ec_tk_option_new(NULL, ec_tk_str_new(NULL, "foo"));
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "b", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "",
+               "foo", EC_TK_ENDLIST);
+       ec_tk_free(tk);
+
+       return ret;
+}
+
+static struct ec_test ec_tk_option_test = {
+       .name = "tk_option",
+       .test = ec_tk_option_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_option_test);
diff --git a/lib/ecoli_tk_option.h b/lib/ecoli_tk_option.h
new file mode 100644 (file)
index 0000000..cadcc05
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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_OPTION_
+#define ECOLI_TK_OPTION_
+
+#include <ecoli_tk.h>
+
+struct ec_tk_option {
+       struct ec_tk gen;
+       struct ec_tk *child;
+};
+
+struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *tk);
+
+#endif
index 4c782dd94305e01511f187a5adea93cfc047e92c..900ee2398b69d0ad643a5127c617341fb4f967cf 100644 (file)
@@ -74,17 +74,20 @@ static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
-       struct ec_completed_tk *completed_tk = NULL, *child_completed_tk;
+       struct ec_completed_tk *completed_tk, *child_completed_tk;
        size_t n;
 
+       completed_tk = ec_completed_tk_new();
+       if (completed_tk == NULL)
+               return NULL;
+
        for (n = 0; n < tk->len; n++) {
                child_completed_tk = ec_tk_complete(tk->table[n], str);
 
                if (child_completed_tk == NULL)
                        continue;
 
-               completed_tk = ec_completed_tk_merge(completed_tk,
-                       child_completed_tk);
+               ec_completed_tk_merge(completed_tk, child_completed_tk);
        }
 
        return completed_tk;
@@ -174,7 +177,6 @@ static int ec_tk_or_testcase(void)
        struct ec_tk *tk;
        int ret = 0;
 
-       /* all inputs starting with foo should match */
        tk = ec_tk_or_new_list(NULL,
                ec_tk_str_new(NULL, "foo"),
                ec_tk_str_new(NULL, "bar"),
@@ -206,7 +208,7 @@ static int ec_tk_or_testcase(void)
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "b", "ar");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "t", "");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "to", "to");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
        ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "",
                "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST);
        ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "f",
index fb1b434fc4e6b4159e476191499b94689a522ff0..7f840c0e4dd57bd432218815a24851b0ac338860 100644 (file)
 #include <ecoli_test.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
 #include <ecoli_tk_seq.h>
 
-// XXX to handle the quote, it will be done in tk_shseq
-// it will unquote the string and parse each token separately
 static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
        const char *str)
 {
@@ -74,16 +73,26 @@ static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
-       struct ec_completed_tk *completed_tk;
+       struct ec_completed_tk *completed_tk, *child_completed_tk;
        struct ec_parsed_tk *parsed_tk;
        size_t len = 0;
        unsigned int i;
 
+       completed_tk = ec_completed_tk_new();
+       if (completed_tk == NULL)
+               return NULL;
+
        if (tk->len == 0)
-               return ec_completed_tk_new();
+               return completed_tk;
+
+       for (i = 0; i < tk->len; i++) {
+               child_completed_tk = ec_tk_complete(tk->table[i], str + len);
+               if (child_completed_tk == NULL) {
+                       ec_completed_tk_free(completed_tk);
+                       return NULL;
+               }
+               ec_completed_tk_merge(completed_tk, child_completed_tk);
 
-       /* parse the first tokens */
-       for (i = 0; i < tk->len - 1; i++) {
                parsed_tk = ec_tk_parse(tk->table[i], str + len);
                if (parsed_tk == NULL)
                        break;
@@ -92,8 +101,6 @@ static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
                ec_parsed_tk_free(parsed_tk);
        }
 
-       completed_tk = ec_tk_complete(tk->table[i], str + len);
-
        return completed_tk;
 }
 
@@ -183,7 +190,6 @@ static int ec_tk_seq_testcase(void)
        struct ec_tk *tk;
        int ret = 0;
 
-       /* all inputs starting with foo should match */
        tk = ec_tk_seq_new_list(NULL,
                ec_tk_str_new(NULL, "foo"),
                ec_tk_str_new(NULL, "bar"),
@@ -202,6 +208,7 @@ static int ec_tk_seq_testcase(void)
        /* test completion */
        tk = ec_tk_seq_new_list(NULL,
                ec_tk_str_new(NULL, "foo"),
+               ec_tk_option_new(NULL, ec_tk_str_new(NULL, "toto")),
                ec_tk_str_new(NULL, "bar"),
                EC_TK_ENDLIST);
        if (tk == NULL) {
@@ -210,11 +217,14 @@ static int ec_tk_seq_testcase(void)
        }
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "bar");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "foo",
+               "bar", "toto", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foot", "oto");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foob", "ar");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobar", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobarx", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobarx", "");
        ec_tk_free(tk);
 
        return ret;
diff --git a/lib/ecoli_tk_shlex.c b/lib/ecoli_tk_shlex.c
new file mode 100644 (file)
index 0000000..7b62c0c
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * 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_tk.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_shlex.h>
+
+static int isend(char c)
+{
+       if (c == '\0' || c == '#' || c == '\n' || c == '\r')
+               return 1;
+       return 0;
+}
+
+/* Remove quotes and stop when we reach the end of token. Return the
+ * number of "eaten" bytes from the source buffer, or a negative value
+ * on error */
+/* XXX support simple quotes, try to be posix-compatible */
+int get_token(const char *src, char **p_dst)
+{
+       unsigned s = 0, d = 0, dstlen;
+       int quoted = 0;
+       char *dst;
+
+       dstlen = strlen(src) + 1;
+       dst = ec_malloc(dstlen);
+       if (dst == NULL)
+               return -ENOMEM;
+
+       /* skip spaces */
+       while (isblank(src[s]))
+               s++;
+
+       /* empty token */
+       if (isend(src[s])) {
+               ec_free(dst);
+               return -ENOENT;
+       }
+
+       /* copy token and remove quotes */
+       while (src[s] != '\0') {
+               if (d >= dstlen) {
+                       ec_free(dst);
+                       return -EMSGSIZE;
+               }
+
+               if ((isblank(src[s]) || isend(src[s])) && quoted == 0)
+                       break;
+
+               if (src[s] == '\\' && src[s+1] == '"') {
+                       dst[d++] = '"';
+                       s += 2;
+                       continue;
+               }
+               if (src[s] == '\\' && src[s+1] == '\\') {
+                       dst[d++] = '\\';
+                       s += 2;
+                       continue;
+               }
+               if (src[s] == '"') {
+                       s++;
+                       quoted = !quoted;
+                       continue;
+               }
+               dst[d++] = src[s++];
+       }
+
+       /* not enough room in dst buffer */
+       if (d >= dstlen) {
+               ec_free(dst);
+               return -EMSGSIZE;
+       }
+
+       /* end of string during quote */
+       if (quoted) {
+               ec_free(dst);
+               return -EINVAL;
+       }
+
+       dst[d++] = '\0';
+       *p_dst = dst;
+       return s;
+}
+
+static int safe_realloc(void *arg, size_t size)
+{
+       void **pptr = arg;
+       void *new_ptr = ec_realloc(*pptr, size);
+
+       if (new_ptr == NULL)
+               return -1;
+       *pptr = new_ptr;
+       return 0;
+}
+
+static char **tokenize(const char *str, int add_empty)
+{
+       char **table = NULL, *token;
+       unsigned i, count = 1, off = 0;
+       int ret;
+
+       if (safe_realloc(&table, sizeof(char *)) < 0)
+               return NULL;
+
+       table[0] = NULL;
+
+       while (1) {
+               ret = get_token(str + off, &token);
+               if (ret == -ENOENT)
+                       break;
+               else if (ret < 0)
+                       goto fail;
+
+               off += ret;
+               count++;
+               if (safe_realloc(&table, sizeof(char *) * count) < 0)
+                       goto fail;
+               table[count - 2] = token;
+               table[count - 1] = NULL;
+       }
+
+       if (add_empty && (off != strlen(str) || strlen(str) == 0)) {
+               token = ec_strdup("");
+               if (token == NULL)
+                       goto fail;
+
+               count++;
+               if (safe_realloc(&table, sizeof(char *) * count) < 0)
+                       goto fail;
+               table[count - 2] = token;
+               table[count - 1] = NULL;
+       }
+
+       return table;
+
+ fail:
+       for (i = 0; i < count; i++)
+               ec_free(table[i]);
+       ec_free(table);
+       return NULL;
+}
+
+static struct ec_parsed_tk *ec_tk_shlex_parse(const struct ec_tk *gen_tk,
+       const char *str)
+{
+       struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk;
+       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+       unsigned int i;
+       char **tokens, **t;
+
+       parsed_tk = ec_parsed_tk_new(gen_tk);
+       if (parsed_tk == NULL)
+               return NULL;
+
+       tokens = tokenize(str, 0);
+       if (tokens == NULL)
+               goto fail;
+
+       t = &tokens[0];
+       for (i = 0, t = &tokens[0]; i < tk->len; i++, t++) {
+               if (*t == NULL)
+                       goto fail;
+
+               child_parsed_tk = ec_tk_parse(tk->table[i], *t);
+               if (child_parsed_tk == NULL)
+                       goto fail;
+
+               ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+               if (strlen(child_parsed_tk->str) == 0)
+                       t--;
+               else if (strlen(child_parsed_tk->str) != strlen(*t))
+                       goto fail;
+       }
+
+       /* check it was the last token */
+       if (*t != NULL)
+               goto fail;
+
+       if (tokens != NULL) {
+               for (t = &tokens[0]; *t != NULL; t++)
+                       ec_free(*t);
+               ec_free(tokens);
+               tokens = NULL;
+       }
+
+       parsed_tk->str = ec_strdup(str);
+
+       return parsed_tk;
+
+ fail:
+       if (tokens != NULL) {
+               for (t = &tokens[0]; *t != NULL; t++)
+                       ec_free(*t);
+               ec_free(tokens);
+       }
+       ec_parsed_tk_free(parsed_tk);
+
+       return NULL;
+}
+
+static struct ec_completed_tk *ec_tk_shlex_complete(const struct ec_tk *gen_tk,
+       const char *str)
+{
+       struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk;
+       struct ec_completed_tk *completed_tk, *child_completed_tk = NULL;
+       struct ec_parsed_tk *child_parsed_tk;
+       unsigned int i;
+       char **tokens, **t;
+
+       tokens = tokenize(str, 1);
+       if (tokens == NULL)
+               goto fail;
+
+       printf("complete <%s>\n", str);
+       for (t = &tokens[0]; *t != NULL; t++)
+               printf("  token <%s> %p\n", *t, *t);
+
+       t = &tokens[0];
+
+       completed_tk = ec_completed_tk_new();
+       if (completed_tk == NULL)
+               return NULL;
+
+       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);
+
+       return completed_tk;
+
+ fail:
+       if (tokens != NULL) {
+               for (t = &tokens[0]; *t != NULL; t++)
+                       ec_free(*t);
+               ec_free(tokens);
+       }
+       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;
+       unsigned int i;
+
+       for (i = 0; i < tk->len; i++)
+               ec_tk_free(tk->table[i]);
+       ec_free(tk->table);
+}
+
+static struct ec_tk_ops ec_tk_shlex_ops = {
+       .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_shlex *tk = NULL;
+
+       tk = (struct ec_tk_shlex *)ec_tk_new(id, &ec_tk_shlex_ops, sizeof(*tk));
+       if (tk == NULL)
+               return NULL;
+
+       tk->table = NULL;
+       tk->len = 0;
+
+       return &tk->gen;
+}
+
+struct ec_tk *ec_tk_shlex_new_list(const char *id, ...)
+{
+       struct ec_tk_shlex *tk = NULL;
+       struct ec_tk *child;
+       va_list ap;
+
+       va_start(ap, id);
+
+       tk = (struct ec_tk_shlex *)ec_tk_shlex_new(id);
+       if (tk == NULL)
+               goto fail;
+
+       for (child = va_arg(ap, struct ec_tk *);
+            child != EC_TK_ENDLIST;
+            child = va_arg(ap, struct ec_tk *)) {
+               if (child == NULL)
+                       goto fail;
+
+               ec_tk_shlex_add(&tk->gen, child);
+       }
+
+       va_end(ap);
+       return &tk->gen;
+
+fail:
+       ec_tk_free(&tk->gen); /* will also free children */
+       va_end(ap);
+       return NULL;
+}
+
+int ec_tk_shlex_add(struct ec_tk *gen_tk, struct ec_tk *child)
+{
+       struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk;
+       struct ec_tk **table;
+
+       // XXX check tk type
+
+       assert(tk != NULL);
+       assert(child != NULL);
+
+       table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table));
+       if (table == NULL)
+               return -1;
+
+       tk->table = table;
+       table[tk->len] = child;
+       tk->len ++;
+
+       return 0;
+}
+
+static int ec_tk_shlex_testcase(void)
+{
+       struct ec_tk *tk;
+       int ret = 0;
+
+       tk = ec_tk_shlex_new_list(NULL,
+               ec_tk_str_new(NULL, "foo"),
+               ec_tk_option_new(NULL, ec_tk_str_new(NULL, "toto")),
+               ec_tk_str_new(NULL, "bar"),
+               EC_TK_ENDLIST);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo bar", "foo bar");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, " \"foo\" \"bar\"",
+               " \"foo\" \"bar\"");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo toto bar", "foo toto bar");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, " foo   bar ", " foo   bar ");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo bar xxx", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo barxxx", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, " \"foo \" \"bar\"", NULL);
+       ec_tk_free(tk);
+
+       /* test completion */
+       tk = ec_tk_shlex_new_list(NULL,
+               ec_tk_str_new(NULL, "foo"),
+               ec_tk_option_new(NULL, ec_tk_str_new(NULL, "toto")),
+               ec_tk_str_new(NULL, "bar"),
+               ec_tk_str_new(NULL, "titi"),
+               EC_TK_ENDLIST);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, " ", "foo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "foo ",
+               "bar", "toto", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo t", "oto");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo b", "ar");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo bar", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo bar ", "titi");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo toto bar ", "titi");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo barx", "");
+       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
new file mode 100644 (file)
index 0000000..52bc6a7
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 <sys/queue.h>
+
+#include <ecoli_tk.h>
+
+struct ec_tk_shlex {
+       struct ec_tk gen;
+       struct ec_tk **table;
+       unsigned int len;
+};
+
+struct ec_tk *ec_tk_shlex_new(const char *id);
+
+/* list must be terminated with EC_TK_ENDLIST */
+struct ec_tk *ec_tk_shlex_new_list(const char *id, ...);
+
+int ec_tk_shlex_add(struct ec_tk *tk, struct ec_tk *child);
+
+#endif
index 20facf8a5c0e76599a9c3fd62b12a2d6184d4401..6c69b3bc4e0f0a10c1d5d7082ab9720a06be1396 100644 (file)
@@ -30,6 +30,8 @@
 #include <string.h>
 #include <ctype.h>
 
+#include <ecoli_log.h>
+#include <ecoli_test.h>
 #include <ecoli_malloc.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_space.h>
@@ -64,3 +66,40 @@ struct ec_tk *ec_tk_space_new(const char *id)
        return ec_tk_new(id, &ec_tk_space_ops, sizeof(struct ec_tk_space));
 }
 
+static int ec_tk_space_testcase(void)
+{
+       struct ec_tk *tk;
+       int ret = 0;
+
+       tk = ec_tk_space_new(NULL);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, " ", " ");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "     ", "    ");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "  foo", "  ");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo ", NULL);
+       ec_tk_free(tk);
+
+       /* test completion */
+       tk = ec_tk_space_new(NULL);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, " ", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
+       ec_tk_free(tk);
+
+       return ret;
+}
+
+static struct ec_test ec_tk_space_test = {
+       .name = "tk_space",
+       .test = ec_tk_space_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_space_test);
index ad5d1191984696961a133b62b73cf5f5c709dc87..61953ef4381e5660924e68f0c04e5c8189c88682 100644 (file)
@@ -61,17 +61,20 @@ static struct ec_completed_tk *ec_tk_str_complete(const struct ec_tk *gen_tk,
        struct ec_completed_tk_elt *completed_tk_elt;
        size_t n;
 
+       completed_tk = ec_completed_tk_new();
+       if (completed_tk == NULL)
+               return NULL;
+
+       /* check the string has the same beginning than the token */
        for (n = 0; n < tk->len; n++) {
                if (str[n] != tk->string[n])
                        break;
        }
 
        if (str[n] != '\0')
-               return NULL;
-
-       completed_tk = ec_completed_tk_new();
-       if (completed_tk == NULL)
-               return NULL;
+               return completed_tk;
+       if (tk->string[n] == '\0')
+               return completed_tk;
 
        completed_tk_elt = ec_completed_tk_elt_new(gen_tk, tk->string + n,
                tk->string);
@@ -127,7 +130,6 @@ static int ec_tk_str_testcase(void)
        struct ec_tk *tk;
        int ret = 0;
 
-       /* all inputs starting with foo should match */
        tk = ec_tk_str_new(NULL, "foo");
        if (tk == NULL) {
                ec_log(EC_LOG_ERR, "cannot create tk\n");
@@ -140,7 +142,6 @@ static int ec_tk_str_testcase(void)
        ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
        ec_tk_free(tk);
 
-       /* all inputs starting with foo should match */
        tk = ec_tk_str_new(NULL, "Здравствуйте");
        if (tk == NULL) {
                ec_log(EC_LOG_ERR, "cannot create tk\n");
@@ -171,7 +172,7 @@ static int ec_tk_str_testcase(void)
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
        ec_tk_free(tk);
 
        return ret;
diff --git a/lib/main-readline.c b/lib/main-readline.c
new file mode 100644 (file)
index 0000000..3bdc703
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_seq.h>
+#include <ecoli_tk_space.h>
+#include <ecoli_tk_or.h>
+
+static struct ec_tk *commands;
+
+/* Set to a character describing the type of completion being attempted by
+   rl_complete_internal; available for use by application completion
+   functions. */
+extern int rl_completion_type;
+/* Set to the last key used to invoke one of the completion functions */
+extern int rl_completion_invoking_key;
+
+int my_complete(int x, int y)
+{
+       (void)x;
+       (void)y;
+
+       return 0;
+}
+
+char *my_completion_entry(const char *s, int state)
+{
+       static struct ec_completed_tk *c;
+       static const struct ec_completed_tk_elt *elt;
+
+       (void)s;
+
+       if (state == 0) {
+               char *start;
+
+               if (c != NULL)
+                       ec_completed_tk_free(c);
+
+               start = strdup(rl_line_buffer);
+               if (start == NULL)
+                       return NULL;
+               start[rl_point] = '\0';
+
+               c = ec_tk_complete(commands, start);
+               ec_completed_tk_iter_start(c);
+       }
+
+       elt = ec_completed_tk_iter_next(c);
+       if (elt == NULL)
+               return NULL;
+
+       return strdup(elt->full);
+}
+
+char **my_attempted_completion(const char *text, int start, int end)
+{
+       (void)start;
+       (void)end;
+       // XXX when it returns NULL, it completes with a file
+       return rl_completion_matches(text, my_completion_entry);
+}
+
+int main(void)
+{
+       struct ec_parsed_tk *p;
+//     const char *name;
+       char *line;
+
+       commands = ec_tk_seq_new_list(NULL,
+               ec_tk_str_new(NULL, "hello"),
+               ec_tk_space_new(NULL),
+               ec_tk_or_new_list("name",
+                       ec_tk_str_new(NULL, "john"),
+                       ec_tk_str_new(NULL, "mike"),
+                       EC_TK_ENDLIST),
+               EC_TK_ENDLIST);
+       if (commands == NULL) {
+               printf("cannot create token\n");
+               return 1;
+       }
+
+       //rl_bind_key('\t', my_complete);
+
+       //rl_completion_entry_function = my_completion_entry;
+       rl_attempted_completion_function = my_attempted_completion;
+
+       while (1) {
+               line = readline("> ");
+               if (line == NULL)
+                       break;
+
+               // XXX need a "parse_all"
+               p = ec_tk_parse(commands, line);
+               ec_parsed_tk_dump(stdout, p);
+               add_history(line);
+               ec_parsed_tk_free(p);
+       }
+
+
+       ec_tk_free(commands);
+       return 0;
+}