cont
authorOlivier Matz <zer0@droids-corp.org>
Sat, 19 Nov 2016 21:47:27 +0000 (22:47 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Sat, 19 Nov 2016 21:47:27 +0000 (22:47 +0100)
29 files changed:
lib/Makefile
lib/ecoli_malloc.c
lib/ecoli_malloc.h
lib/ecoli_strvec.c [new file with mode: 0644]
lib/ecoli_strvec.h [new file with mode: 0644]
lib/ecoli_test.c
lib/ecoli_test.h
lib/ecoli_tk.c
lib/ecoli_tk.h
lib/ecoli_tk_empty.c
lib/ecoli_tk_empty.h
lib/ecoli_tk_int.c
lib/ecoli_tk_int.h
lib/ecoli_tk_many.c [new file with mode: 0644]
lib/ecoli_tk_many.h [new file with mode: 0644]
lib/ecoli_tk_option.c
lib/ecoli_tk_option.h
lib/ecoli_tk_or.c
lib/ecoli_tk_or.h
lib/ecoli_tk_seq.c
lib/ecoli_tk_seq.h
lib/ecoli_tk_shlex.c
lib/ecoli_tk_shlex.h
lib/ecoli_tk_space.c
lib/ecoli_tk_space.h
lib/ecoli_tk_str.c
lib/ecoli_tk_str.h
lib/main-readline.c
lib/main.c

index 42a969e..28fce46 100644 (file)
@@ -33,13 +33,15 @@ O ?= build/
 CFLAGS  = -g -O0 -Wall -Werror -W -fPIC
 CFLAGS += -I.
 
-srcs := ecoli_tk.c ecoli_tk_str.c ecoli_tk_seq.c
+srcs := ecoli_tk.c ecoli_malloc.c ecoli_log.c ecoli_strvec.c
+srcs += 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 ecoli_tk_option.c
+srcs += ecoli_tk_option.c ecoli_tk_many.c
 srcs += ecoli_tk_shlex.c
 shlib-y-$(O)libecoli.so := $(srcs)
 
+ldflags-$(O)test = -rdynamic
 exe-y-$(O)test = $(srcs) main.c
 
 ldflags-$(O)readline = -lreadline
index 0bdbf21..0871247 100644 (file)
@@ -80,7 +80,7 @@ void *__ec_calloc(size_t nmemb, size_t size, const char *file,
        if (ptr == NULL)
                return NULL;
 
-       memset(ptr, 0, size);
+       memset(ptr, 0, total);
        return ptr;
 }
 
index 4c49649..f5bdc39 100644 (file)
@@ -29,6 +29,8 @@
 #define ECOLI_MALLOC_
 
 #include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
 
 typedef void *(*ec_malloc_t)(size_t size, const char *file, unsigned int line);
 typedef void (*ec_free_t)(void *ptr, const char *file, unsigned int line);
diff --git a/lib/ecoli_strvec.c b/lib/ecoli_strvec.c
new file mode 100644 (file)
index 0000000..17a10c8
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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 <sys/types.h>
+#include <stdlib.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
+
+struct ec_strvec *ec_strvec_new(void)
+{
+       struct ec_strvec *strvec;
+
+       strvec = ec_calloc(1, sizeof(*strvec));
+       if (strvec == NULL)
+               return NULL;
+
+       return strvec;
+}
+
+int ec_strvec_add(struct ec_strvec *strvec, const char *s)
+{
+       char **new_vec;
+
+       new_vec = ec_realloc(strvec->vec,
+               sizeof(*strvec->vec) * (strvec->len + 1));
+       if (new_vec == NULL)
+               return -1;
+
+       strvec->vec = new_vec;
+       strvec->vec[strvec->len] = ec_strdup(s);
+       if (strvec->vec[strvec->len] == NULL)
+               return -1;
+
+       strvec->len++;
+       return 0;
+}
+
+struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t len)
+{
+       struct ec_strvec *copy = NULL;
+       size_t i, veclen;
+
+       copy = ec_strvec_new();
+       if (copy == NULL)
+               goto fail;
+
+       if (len == 0)
+               return copy;
+
+       veclen = ec_strvec_len(strvec);
+       if (len > veclen)
+               len = veclen;
+       copy->vec = ec_calloc(len, sizeof(*copy->vec));
+       if (copy->vec == NULL)
+               goto fail;
+
+       for (i = 0; i < len; i++) {
+               copy->vec[i] = ec_strdup(strvec->vec[i]);
+               if (copy->vec[i] == NULL)
+                       goto fail;
+               copy->len++;
+       }
+
+       return copy;
+
+fail:
+       ec_strvec_free(copy);
+       return NULL;
+}
+
+struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec)
+{
+       return ec_strvec_ndup(strvec, ec_strvec_len(strvec));
+}
+
+void ec_strvec_free(struct ec_strvec *strvec)
+{
+       size_t i;
+
+       if (strvec == NULL)
+               return;
+
+       for (i = 0; i < ec_strvec_len(strvec); i++)
+               ec_free(ec_strvec_val(strvec, i));
+
+       ec_free(strvec->vec);
+       ec_free(strvec);
+}
+
+size_t ec_strvec_len(const struct ec_strvec *strvec)
+{
+       return strvec->len;
+}
+
+char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
+{
+       if (strvec == NULL || idx >= strvec->len)
+               return NULL;
+
+       return strvec->vec[idx];
+}
+
+int ec_strvec_slice(struct ec_strvec *strvec, const struct ec_strvec *from,
+       size_t off)
+{
+       if (off > from->len)
+               return -1;
+
+       strvec->len = from->len - off;
+       strvec->vec = &from->vec[off];
+
+       return 0;
+}
+
+void ec_strvec_dump(const struct ec_strvec *strvec, FILE *out)
+{
+       size_t i;
+
+       if (strvec == NULL) {
+               fprintf(out, "empty strvec\n");
+               return;
+       }
+
+       fprintf(out, "strvec:\n");
+       for (i = 0; i < ec_strvec_len(strvec); i++)
+               fprintf(out, "  %zd: %s\n", i, strvec->vec[i]);
+}
diff --git a/lib/ecoli_strvec.h b/lib/ecoli_strvec.h
new file mode 100644 (file)
index 0000000..a606177
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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_STRVEC_
+#define ECOLI_STRVEC_
+
+#include <stdio.h>
+
+struct ec_strvec {
+       size_t len;
+       char **vec;
+};
+
+struct ec_strvec *ec_strvec_new(void);
+int ec_strvec_add(struct ec_strvec *strvec, const char *s);
+struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec);
+struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t len);
+void ec_strvec_free(struct ec_strvec *strvec);
+size_t ec_strvec_len(const struct ec_strvec *strvec);
+char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx);
+int ec_strvec_slice(struct ec_strvec *strvec, const struct ec_strvec *from,
+       size_t off);
+void ec_strvec_dump(const struct ec_strvec *strvec, FILE *out);
+
+#endif
index 3cce2da..1408f5a 100644 (file)
@@ -33,6 +33,7 @@
 #include <ecoli_log.h>
 #include <ecoli_malloc.h>
 #include <ecoli_test.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 
 static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list);
@@ -43,70 +44,72 @@ void ec_test_register(struct ec_test *test)
        TAILQ_INSERT_TAIL(&test_list, test, next);
 }
 
-int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
-       const char *expected)
+int ec_test_check_tk_parse(const struct ec_tk *tk, int expected, ...)
 {
        struct ec_parsed_tk *p;
+       struct ec_strvec *vec = NULL;
        const char *s;
-       int ret = -1;
-
-       p = ec_tk_parse(tk, input);
-       s = ec_parsed_tk_to_string(p);
-       if (s == NULL && expected == NULL)
-               ret = 0;
-       else if (s != NULL && expected != NULL &&
-               !strcmp(s, expected))
-               ret = 0;
-
-       if (expected == NULL && ret != 0)
-               ec_log(EC_LOG_ERR, "tk should not match but matches <%s>\n", s);
-       if (expected != NULL && ret != 0)
-               ec_log(EC_LOG_ERR, "tk should match <%s> but matches <%s>\n",
-                       expected, s);
+       int ret = -1, match;
+       va_list ap;
 
-       ec_parsed_tk_free(p);
+       va_start(ap, expected);
 
-       return ret;
-}
+       /* build a string vector */
+       vec = ec_strvec_new();
+       if (vec == NULL)
+               goto out;
 
-int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
-       const char *expected)
-{
-       struct ec_completed_tk *c;
-       const char *s;
-       int ret = -1;
+       for (s = va_arg(ap, const char *);
+            s != EC_TK_ENDLIST;
+            s = va_arg(ap, const char *)) {
+               if (s == NULL)
+                       goto out;
 
-       assert(expected != NULL);
+               if (ec_strvec_add(vec, s) < 0)
+                       goto out;
+       }
 
-       c = ec_tk_complete(tk, input);
-       s = ec_completed_tk_smallest_start(c);
-       if (!strcmp(s, expected))
+       p = ec_tk_parse_tokens(tk, vec);
+       /* XXX only for debug */
+       ec_parsed_tk_dump(stdout, p);
+       if (p == NULL) {
+               ec_log(EC_LOG_ERR, "parsed_tk is NULL\n");
+       }
+       if (ec_parsed_tk_matches(p))
+               match = ec_parsed_tk_len(p);
+       else
+               match = -1;
+       if (expected == match) {
                ret = 0;
-
-       if (ret != 0)
+       } else {
                ec_log(EC_LOG_ERR,
-                       "should complete with <%s> but completes with <%s>\n",
-                       expected, s);
+                       "tk parsed len (%d) does not match expected (%d)\n",
+                       match, expected);
+       }
 
-       ec_completed_tk_free(c);
+       ec_parsed_tk_free(p);
 
+out:
+       ec_strvec_free(vec);
+       va_end(ap);
        return ret;
 }
 
-int ec_test_check_tk_complete_list(const struct ec_tk *tk,
-       const char *input, ...)
+int ec_test_check_tk_complete(const struct ec_tk *tk, ...)
 {
        struct ec_completed_tk *c = NULL;
        struct ec_completed_tk_elt *elt;
-       const char *s;
-       int ret = -1;
+       struct ec_strvec *vec = NULL;
+       const char *s, *expected;
+       int ret = 0;
        unsigned int count = 0;
        va_list ap;
 
-       va_start(ap, input);
+       va_start(ap, tk);
 
-       c = ec_tk_complete(tk, input);
-       if (c == NULL)
+       /* build a string vector */
+       vec = ec_strvec_new();
+       if (vec == NULL)
                goto out;
 
        for (s = va_arg(ap, const char *);
@@ -115,30 +118,58 @@ int ec_test_check_tk_complete_list(const struct ec_tk *tk,
                if (s == NULL)
                        goto out;
 
+               if (ec_strvec_add(vec, s) < 0)
+                       goto out;
+       }
+
+       c = ec_tk_complete_tokens(tk, vec);
+       if (c == NULL) {
+               ret = -1;
+               goto out;
+       }
+
+       for (s = va_arg(ap, const char *);
+            s != EC_TK_ENDLIST;
+            s = va_arg(ap, const char *)) {
+               if (s == NULL) {
+                       ret = -1;
+                       goto out;
+               }
+
                count++;
                TAILQ_FOREACH(elt, &c->elts, next) {
-                       if (strcmp(elt->add, s) == 0)
+                       /* only check matching completions */
+                       if (elt->add != NULL && strcmp(elt->add, s) == 0)
                                break;
                }
 
                if (elt == NULL) {
                        ec_log(EC_LOG_ERR,
                                "completion <%s> not in list\n", s);
-                       goto out;
+                       ret = -1;
                }
        }
 
-       if (count != ec_completed_tk_count(c)) {
+       if (count != ec_completed_tk_count_match(c)) {
                ec_log(EC_LOG_ERR,
                        "nb_completion (%d) does not match (%d)\n",
-                       count, ec_completed_tk_count(c));
+                       count, ec_completed_tk_count_match(c));
                ec_completed_tk_dump(stdout, c);
-               goto out;
+               ret = -1;
        }
 
-       ret = 0;
+
+       expected = va_arg(ap, const char *);
+       s = ec_completed_tk_smallest_start(c);
+       if (strcmp(s, expected)) {
+               ret = -1;
+               ec_log(EC_LOG_ERR,
+                       "should complete with <%s> but completes with <%s>\n",
+                       expected, s);
+       }
 
 out:
+       ec_strvec_free(vec);
        ec_completed_tk_free(c);
        va_end(ap);
        return ret;
@@ -150,6 +181,8 @@ int ec_test_all(void)
        int ret = 0;
 
        TAILQ_FOREACH(test, &test_list, next) {
+               ec_log(EC_LOG_INFO, "== starting test %-20s\n", test->name);
+
                if (test->test() == 0) {
                        ec_log(EC_LOG_INFO, "== test %-20s success\n",
                                test->name);
index 45d1d4e..c1246c4 100644 (file)
@@ -67,38 +67,27 @@ void ec_test_register(struct ec_test *test);
 
 int ec_test_all(void);
 
-int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
-       const char *expected);
+/* expected == -1 means no match */
+int ec_test_check_tk_parse(const struct ec_tk *tk, int expected, ...);
 
 #define TEST_ERR()                                                     \
        ec_log(EC_LOG_ERR, "%s:%d: error: test failed\n",               \
                __FILE__, __LINE__);                                    \
 
-#define EC_TEST_CHECK_TK_PARSE(tk, input, expected) ({                 \
-       int ret = ec_test_check_tk_parse(tk, input, expected);          \
-       if (ret                                                       \
+#define EC_TEST_CHECK_TK_PARSE(tk, input, expected...) ({              \
+       int ret_ = ec_test_check_tk_parse(tk, input, expected);         \
+       if (ret_)                                                       \
                TEST_ERR();                                             \
-       ret                                                           \
+       ret_;                                                           \
 })
 
-int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
-       const char *expected);
+int ec_test_check_tk_complete(const struct ec_tk *tk, ...);
 
-#define EC_TEST_CHECK_TK_COMPLETE(tk, input, expected) ({              \
-       int ret = ec_test_check_tk_complete(tk, input, expected);       \
-       if (ret                                                       \
+#define EC_TEST_CHECK_TK_COMPLETE(tk, args...) ({                      \
+       int ret_ = ec_test_check_tk_complete(tk, args);                 \
+       if (ret_)                                                       \
                TEST_ERR();                                             \
-       ret;                                                            \
-})
-
-int ec_test_check_tk_complete_list(const struct ec_tk *tk,
-       const char *input, ...);
-
-#define EC_TEST_CHECK_TK_COMPLETE_LIST(tk, input, expected...) ({      \
-       int ret = ec_test_check_tk_complete_list(tk, input, expected);  \
-       if (ret)                                                        \
-               TEST_ERR();                                             \
-       ret;                                                            \
+       ret_;                                                           \
 })
 
 #endif
index 8f22fbd..e21df55 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <errno.h>
 
 #include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 
 struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
@@ -72,27 +74,53 @@ void ec_tk_free(struct ec_tk *tk)
 
 struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
 {
+       struct ec_strvec *strvec = NULL;
        struct ec_parsed_tk *parsed_tk;
 
-       /* by default, it does not match anything */
-       if (tk->ops->parse == NULL)
+       errno = ENOMEM;
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
+
+       if (ec_strvec_add(strvec, str) < 0)
+               goto fail;
+
+       parsed_tk = ec_tk_parse_tokens(tk, strvec);
+       if (parsed_tk == NULL)
+               goto fail;
+
+       ec_strvec_free(strvec);
+       return parsed_tk;
+
+ fail:
+       ec_strvec_free(strvec);
+       return NULL;
+}
+
+struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *tk,
+       const struct ec_strvec *strvec)
+{
+       struct ec_parsed_tk *parsed_tk;
+
+       if (tk->ops->parse == NULL) {
+               errno = ENOTSUP;
                return NULL;
+       }
 
-       parsed_tk = tk->ops->parse(tk, str);
+       parsed_tk = tk->ops->parse(tk, strvec);
 
        return parsed_tk;
 }
 
 
-struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk)
+struct ec_parsed_tk *ec_parsed_tk_new(void)
 {
-       struct ec_parsed_tk *parsed_tk;
+       struct ec_parsed_tk *parsed_tk = NULL;
 
        parsed_tk = ec_calloc(1, sizeof(*parsed_tk));
        if (parsed_tk == NULL)
                goto fail;
 
-       parsed_tk->tk = tk;
        TAILQ_INIT(&parsed_tk->children);
 
        return parsed_tk;
@@ -101,7 +129,14 @@ struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk)
        return NULL;
 }
 
-void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
+void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
+       const struct ec_tk *tk, struct ec_strvec *strvec)
+{
+       parsed_tk->tk = tk;
+       parsed_tk->strvec = strvec;
+}
+
+void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk)
 {
        struct ec_parsed_tk *child;
 
@@ -113,22 +148,37 @@ void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
                TAILQ_REMOVE(&parsed_tk->children, child, next);
                ec_parsed_tk_free(child);
        }
-       ec_free(parsed_tk->str);
+}
+
+void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
+{
+       if (parsed_tk == NULL)
+               return;
+
+       ec_parsed_tk_free_children(parsed_tk);
+       ec_strvec_free(parsed_tk->strvec);
        ec_free(parsed_tk);
 }
 
-static void __ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk,
-       size_t indent)
+static void __ec_parsed_tk_dump(FILE *out,
+       const struct ec_parsed_tk *parsed_tk, size_t indent)
 {
        struct ec_parsed_tk *child;
        size_t i;
-       const char *s;
+       const char *s, *id = "None", *typename = "None";
 
        /* XXX enhance */
        for (i = 0; i < indent; i++)
                fprintf(out, " ");
+
        s = ec_parsed_tk_to_string(parsed_tk);
-       fprintf(out, "id=%s, s=<%s>\n", parsed_tk->tk->id, s);
+       if (parsed_tk->tk != NULL) {
+               if (parsed_tk->tk->id != NULL)
+                       id = parsed_tk->tk->id;
+               typename = parsed_tk->tk->ops->typename;
+       }
+
+       fprintf(out, "tk_type=%s, id=%s, s=<%s>\n", typename, id, s);
 
        TAILQ_FOREACH(child, &parsed_tk->children, next)
                __ec_parsed_tk_dump(out, child, indent + 2);
@@ -137,6 +187,10 @@ static void __ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk,
 void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
 {
        if (parsed_tk == NULL) {
+               fprintf(out, "parsed_tk is NULL, error in parse\n");
+               return;
+       }
+       if (!ec_parsed_tk_matches(parsed_tk)) {
                fprintf(out, "no match\n");
                return;
        }
@@ -158,7 +212,9 @@ struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
        if (parsed_tk == NULL)
                return NULL;
 
-       if (parsed_tk->tk->id != NULL && !strcmp(parsed_tk->tk->id, id))
+       if (parsed_tk->tk != NULL &&
+                       parsed_tk->tk->id != NULL &&
+                       !strcmp(parsed_tk->tk->id, id))
                return parsed_tk;
 
        TAILQ_FOREACH(child, &parsed_tk->children, next) {
@@ -170,12 +226,34 @@ struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
        return NULL;
 }
 
+/* XXX return NUL if it matches several tokens?
+   or add a parameter to join() the tokens ? */
 const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk)
 {
-       if (parsed_tk == NULL)
+       if (parsed_tk == NULL || parsed_tk->strvec == NULL)
                return NULL;
 
-       return parsed_tk->str;
+       return ec_strvec_val(parsed_tk->strvec, 0);
+}
+
+/* number of parsed tokens */
+size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk)
+{
+       if (parsed_tk == NULL || parsed_tk->strvec == NULL)
+               return 0;
+
+       return ec_strvec_len(parsed_tk->strvec);
+}
+
+size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk)
+{
+       if (parsed_tk == NULL)
+               return 0;
+
+       if (parsed_tk->tk == NULL && TAILQ_EMPTY(&parsed_tk->children))
+               return 0;
+
+       return 1;
 }
 
 struct ec_completed_tk *ec_completed_tk_new(void)
@@ -187,13 +265,13 @@ struct ec_completed_tk *ec_completed_tk_new(void)
                return NULL;
 
        TAILQ_INIT(&completed_tk->elts);
-       completed_tk->count = 0;
+       completed_tk->count_match = 0;
 
        return completed_tk;
 }
 
 struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
-       const char *add, const char *full)
+       const char *add)
 {
        struct ec_completed_tk_elt *elt = NULL;
 
@@ -209,13 +287,6 @@ struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
                        return NULL;
                }
        }
-       if (full != NULL) {
-               elt->full = ec_strdup(full);
-               if (elt->full == NULL) {
-                       ec_completed_tk_elt_free(elt);
-                       return NULL;
-               }
-       }
 
        return elt;
 }
@@ -227,17 +298,60 @@ struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
  */
 struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
        const char *str)
+{
+       struct ec_strvec *strvec = NULL;
+       struct ec_completed_tk *completed_tk;
+
+       errno = ENOMEM;
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
+
+       if (ec_strvec_add(strvec, str) < 0)
+               goto fail;
+
+       completed_tk = ec_tk_complete_tokens(tk, strvec);
+       if (completed_tk == NULL)
+               goto fail;
+
+       ec_strvec_free(strvec);
+       return completed_tk;
+
+ fail:
+       ec_strvec_free(strvec);
+       return NULL;
+}
+
+/* default completion function: return a no-match element */
+struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
+       const struct ec_strvec *strvec)
 {
        struct ec_completed_tk *completed_tk;
+       struct ec_completed_tk_elt *completed_tk_elt;
+
+       (void)strvec;
+
+       completed_tk = ec_completed_tk_new();
+       if (completed_tk == NULL)
+               return NULL;
 
-       if (tk->ops->complete == NULL)
-               return ec_completed_tk_new();
+       completed_tk_elt = ec_completed_tk_elt_new(gen_tk, NULL);
+       if (completed_tk_elt == NULL) {
+               ec_completed_tk_free(completed_tk);
+               return NULL;
+       }
 
-       completed_tk = tk->ops->complete(tk, str);
+       ec_completed_tk_add_elt(completed_tk, completed_tk_elt);
 
        return completed_tk;
 }
 
+struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
+       const struct ec_strvec *strvec)
+{
+       return tk->ops->complete(tk, strvec);
+}
+
 /* count the number of identical chars at the beginning of 2 strings */
 static size_t strcmp_count(const char *s1, const char *s2)
 {
@@ -256,6 +370,8 @@ void ec_completed_tk_add_elt(
 
        TAILQ_INSERT_TAIL(&completed_tk->elts, elt, next);
        completed_tk->count++;
+       if (elt->add != NULL)
+               completed_tk->count_match++;
        if (elt->add != NULL) {
                if (completed_tk->smallest_start == NULL) {
                        completed_tk->smallest_start = ec_strdup(elt->add);
@@ -270,7 +386,6 @@ void ec_completed_tk_add_elt(
 void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt)
 {
        ec_free(elt->add);
-       ec_free(elt->full);
        ec_free(elt);
 }
 
@@ -316,12 +431,13 @@ void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk)
                return;
        }
 
-       fprintf(out, "completion: count=%u smallest_start=<%s>\n",
-               completed_tk->count, completed_tk->smallest_start);
+       fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n",
+               completed_tk->count, completed_tk->count_match,
+               completed_tk->smallest_start);
 
        TAILQ_FOREACH(elt, &completed_tk->elts, next) {
-               fprintf(out, "add=<%s>, full=<%s>, tk=%p\n",
-                       elt->add, elt->full, elt->tk);
+               fprintf(out, "add=<%s>, tk=%p, tk_type=%s\n",
+                       elt->add, elt->tk, elt->tk->ops->typename);
        }
 }
 
@@ -334,33 +450,62 @@ const char *ec_completed_tk_smallest_start(
        return completed_tk->smallest_start;
 }
 
-unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk)
+unsigned int ec_completed_tk_count_match(
+       const struct ec_completed_tk *completed_tk)
 {
        if (completed_tk == NULL)
                return 0;
 
-       return completed_tk->count;
+       return completed_tk->count_match;
 }
 
-void ec_completed_tk_iter_start(struct ec_completed_tk *completed_tk)
+struct ec_completed_tk_iter *
+ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk,
+       enum ec_completed_tk_filter_flags flags)
 {
-       if (completed_tk == NULL)
-               return;
+       struct ec_completed_tk_iter *iter;
+
+       iter = ec_calloc(1, sizeof(*iter));
+       if (iter == NULL)
+               return NULL;
 
-       completed_tk->cur = NULL;
+       iter->completed_tk = completed_tk;
+       iter->flags = flags;
+       iter->cur = NULL;
+
+       return iter;
 }
 
 const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
-       struct ec_completed_tk *completed_tk)
+       struct ec_completed_tk_iter *iter)
 {
-       if (completed_tk == NULL)
+       if (iter->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);
-       }
+       do {
+               if (iter->cur == NULL) {
+                       iter->cur = TAILQ_FIRST(&iter->completed_tk->elts);
+               } else {
+                       iter->cur = TAILQ_NEXT(iter->cur, next);
+               }
+
+               if (iter->cur == NULL)
+                       break;
 
-       return completed_tk->cur;
+               if (iter->cur->add == NULL &&
+                               (iter->flags & ITER_NO_MATCH))
+                       break;
+
+               if (iter->cur->add != NULL &&
+                               (iter->flags & ITER_MATCH))
+                       break;
+
+       } while (iter->cur != NULL);
+
+       return iter->cur;
+}
+
+void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter)
+{
+       ec_free(iter);
 }
index 72aee30..e243c08 100644 (file)
 
 #include <sys/queue.h>
 #include <sys/types.h>
-
 #include <stdio.h>
 
 #define EC_TK_ENDLIST ((void *)1)
 
 struct ec_tk;
 struct ec_parsed_tk;
+struct ec_strvec;
 
 typedef struct ec_parsed_tk *(*ec_tk_parse_t)(const struct ec_tk *tk,
-       const char *str);
+       const struct ec_strvec *strvec);
 typedef struct ec_completed_tk *(*ec_tk_complete_t)(const struct ec_tk *tk,
-       const char *str);
+       const struct ec_strvec *strvec);
 typedef void (*ec_tk_free_priv_t)(struct ec_tk *);
 
 struct ec_tk_ops {
+       const char *typename;
        ec_tk_parse_t parse;
        ec_tk_complete_t complete;
        ec_tk_free_priv_t free_priv;
@@ -62,21 +63,38 @@ void ec_tk_free(struct ec_tk *tk);
 
 TAILQ_HEAD(ec_parsed_tk_list, ec_parsed_tk);
 
+/*
+  tk == NULL + empty children list means "no match"
+*/
 struct ec_parsed_tk {
-       struct ec_parsed_tk_list children;
        TAILQ_ENTRY(ec_parsed_tk) next;
+       struct ec_parsed_tk_list children;
        const struct ec_tk *tk;
-       char *str;
+       struct ec_strvec *strvec;
 };
 
+struct ec_parsed_tk *ec_parsed_tk_new(void);
+
+void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
+       const struct ec_tk *tk, struct ec_strvec *strvec);
+
 /* XXX we could use a cache to store possible completions or match: the
  * cache would be per-node, and would be reset for each call to parse()
  * or complete() ? */
-
-struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk);
+/* a NULL return value is an error, with errno set
+  ENOTSUP: no ->parse() operation
+*/
 struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *token, const char *str);
+
+/* mostly internal to tokens */
+/* XXX it should not reset cache
+ * ... not sure... it is used by tests */
+struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *token,
+       const struct ec_strvec *strvec);
+
 void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
        struct ec_parsed_tk *child);
+void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk);
 void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk);
 void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk);
 
@@ -84,12 +102,13 @@ struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
        const char *id);
 
 const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk);
+size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk);
+size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk);
 
 struct ec_completed_tk_elt {
        TAILQ_ENTRY(ec_completed_tk_elt) next;
        const struct ec_tk *tk;
        char *add;
-       char *full;
 };
 
 TAILQ_HEAD(ec_completed_tk_elt_list, ec_completed_tk_elt);
@@ -97,8 +116,8 @@ 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;
+       unsigned count_match;
        char *smallest_start;
 };
 
@@ -108,9 +127,11 @@ struct ec_completed_tk {
  */
 struct ec_completed_tk *ec_tk_complete(const struct ec_tk *token,
        const char *str);
+struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *token,
+       const struct ec_strvec *strvec);
 struct ec_completed_tk *ec_completed_tk_new(void);
 struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
-       const char *add, const char *full);
+       const char *add);
 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);
@@ -119,15 +140,33 @@ void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
 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);
+struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
+       const struct ec_strvec *strvec);
 
 /* 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);
+unsigned int ec_completed_tk_count_match(
+       const struct ec_completed_tk *completed_tk);
+
+enum ec_completed_tk_filter_flags {
+       ITER_MATCH = 1,
+       ITER_NO_MATCH,
+};
+
+struct ec_completed_tk_iter {
+       enum ec_completed_tk_filter_flags flags;
+       const struct ec_completed_tk *completed_tk;
+       const struct ec_completed_tk_elt *cur;
+};
+
+struct ec_completed_tk_iter *
+ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk,
+       enum ec_completed_tk_filter_flags flags);
 
-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);
+       struct ec_completed_tk_iter *iter);
 
+void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter);
 #endif
index bc180d7..196041c 100644 (file)
 #include <ecoli_malloc.h>
 #include <ecoli_log.h>
 #include <ecoli_test.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_empty.h>
 
+struct ec_tk_empty {
+       struct ec_tk gen;
+};
+
 static struct ec_parsed_tk *ec_tk_empty_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_parsed_tk *parsed_tk;
+       struct ec_strvec *match_strvec;
+
+       (void)strvec;
 
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
 
-       (void)str;
-       parsed_tk->str = ec_strdup("");
+       match_strvec = ec_strvec_new();
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
 
        return parsed_tk;
+
+ fail:
+       ec_parsed_tk_free(parsed_tk);
+       return NULL;
 }
 
 static struct ec_tk_ops ec_tk_empty_ops = {
+       .typename = "empty",
        .parse = ec_tk_empty_parse,
+       .complete = ec_tk_default_complete,
 };
 
 struct ec_tk *ec_tk_empty_new(const char *id)
 {
-       return ec_tk_new(id, &ec_tk_empty_ops, sizeof(struct ec_tk_empty));
+       return ec_tk_new(id, &ec_tk_empty_ops,
+               sizeof(struct ec_tk_empty));
 }
 
 static int ec_tk_empty_testcase(void)
@@ -69,9 +87,9 @@ static int ec_tk_empty_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, " foo", "");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "", "");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foo", "bar", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* never completes */
@@ -80,8 +98,14 @@ 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, "", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "foo", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
        ec_tk_free(tk);
 
        return ret;
index 4e22420..37c0eef 100644 (file)
 #ifndef ECOLI_TK_EMPTY_
 #define ECOLI_TK_EMPTY_
 
-#include <ecoli_tk.h>
-
-struct ec_tk_empty {
-       struct ec_tk gen;
-};
-
 struct ec_tk *ec_tk_empty_new(const char *id);
 
 #endif
index a6cc12d..f4cca45 100644 (file)
 
 #include <ecoli_log.h>
 #include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_int.h>
 #include <ecoli_test.h>
 
-static size_t parse_llint(struct ec_tk_int *tk, const char *str,
+struct ec_tk_int {
+       struct ec_tk gen;
+       long long int min;
+       long long int max;
+       unsigned int base;
+};
+
+static int parse_llint(struct ec_tk_int *tk, const char *str,
        long long *val)
 {
        char *endptr;
@@ -46,44 +54,57 @@ static size_t parse_llint(struct ec_tk_int *tk, const char *str,
        errno = 0;
        *val = strtoll(str, &endptr, tk->base);
 
-       /* starts with a space */
-       if (isspace(str[0]))
-               return 0;
-
        /* out of range */
        if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
                        (errno != 0 && *val == 0))
-               return 0;
+               return -1;
 
        if (*val < tk->min || *val > tk->max)
-               return 0;
+               return -1;
+
+       if (*endptr != 0)
+               return -1;
 
-       return endptr - str;
+       return 0;
 }
 
 static struct ec_parsed_tk *ec_tk_int_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk;
        struct ec_parsed_tk *parsed_tk;
+       struct ec_strvec *match_strvec;
+       const char *str;
        long long val;
-       size_t len;
 
-       len = parse_llint(tk, str, &val);
-       if (len == 0)
-               return NULL;
-
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
+
+       if (ec_strvec_len(strvec) == 0)
+               return parsed_tk;
 
-       parsed_tk->str = ec_strndup(str, len);
+       str = ec_strvec_val(strvec, 0);
+       if (parse_llint(tk, str, &val) < 0)
+               return parsed_tk;
+
+       match_strvec = ec_strvec_ndup(strvec, 1);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
 
        return parsed_tk;
+
+ fail:
+       ec_parsed_tk_free(parsed_tk);
+       return NULL;
 }
 
 static struct ec_tk_ops ec_tk_int_ops = {
+       .typename = "int",
        .parse = ec_tk_int_parse,
+       .complete = ec_tk_default_complete,
 };
 
 struct ec_tk *ec_tk_int_new(const char *id, long long int min,
@@ -127,12 +148,12 @@ static int ec_tk_int_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "256", "256");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x100", "0x100");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", NULL);
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x101", NULL);
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, " 1", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "256", "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0x100", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 1", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-1", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x101", EC_TK_ENDLIST);
 
        p = ec_tk_parse(tk, "0");
        s = ec_parsed_tk_to_string(p);
@@ -160,13 +181,11 @@ static int ec_tk_int_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", "-1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "7fffffffffffffff",
-               "7fffffffffffffff");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x7fffffffffffffff",
-               "0x7fffffffffffffff");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "-2", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-1", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "7fffffffffffffff", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0x7fffffffffffffff", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-2", EC_TK_ENDLIST);
 
        p = ec_tk_parse(tk, "10");
        s = ec_parsed_tk_to_string(p);
@@ -184,12 +203,12 @@ static int ec_tk_int_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", "-1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "-9223372036854775808",
-               "-9223372036854775808");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x0", "0");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "1", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-1", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-9223372036854775808",
+               EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x0", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "1", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* test completion */
@@ -198,9 +217,18 @@ 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, "", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "1", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "x", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "1", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
        ec_tk_free(tk);
 
        return ret;
index e210279..147e2d0 100644 (file)
 
 #include <ecoli_tk.h>
 
-struct ec_tk_int {
-       struct ec_tk gen;
-       long long int min;
-       long long int max;
-       unsigned int base;
-};
-
 struct ec_tk *ec_tk_int_new(const char *id, long long int min,
        long long int max, unsigned int base);
 long long ec_tk_int_getval(struct ec_tk *tk, const char *str);
diff --git a/lib/ecoli_tk_many.c b/lib/ecoli_tk_many.c
new file mode 100644 (file)
index 0000000..6d0b501
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * 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_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_many.h>
+
+struct ec_tk_many {
+       struct ec_tk gen;
+       unsigned int min;
+       unsigned int max;
+       struct ec_tk *child;
+};
+
+static struct ec_parsed_tk *ec_tk_many_parse(const struct ec_tk *gen_tk,
+       const struct ec_strvec *strvec)
+{
+       struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
+       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+       struct ec_strvec *match_strvec;
+       struct ec_strvec childvec;
+       size_t off = 0, len, count;
+
+       parsed_tk = ec_parsed_tk_new();
+       if (parsed_tk == NULL)
+               goto fail;
+
+       for (count = 0; tk->max == 0 || count < tk->max; count++) {
+               if (ec_strvec_slice(&childvec, strvec, off) < 0)
+                       goto fail;
+
+               child_parsed_tk = ec_tk_parse_tokens(tk->child, &childvec);
+               if (child_parsed_tk == NULL)
+                       goto fail;
+
+               if (!ec_parsed_tk_matches(child_parsed_tk)) {
+                       ec_parsed_tk_free(child_parsed_tk);
+                       break;
+               }
+
+               ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+
+               /* it matches "no token", no need to continue */
+               len = ec_parsed_tk_len(child_parsed_tk);
+               if (len == 0) {
+                       ec_parsed_tk_free(child_parsed_tk);
+                       break;
+               }
+
+               off += len;
+       }
+
+       if (count < tk->min) {
+               ec_parsed_tk_free_children(parsed_tk);
+               return parsed_tk;
+       }
+
+       match_strvec = ec_strvec_ndup(strvec, off);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
+
+       return parsed_tk;
+
+ fail:
+       ec_parsed_tk_free(parsed_tk);
+       return NULL;
+}
+
+#if 0
+static struct ec_completed_tk *ec_tk_many_complete(const struct ec_tk *gen_tk,
+       const struct ec_strvec *strvec)
+{
+       struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
+       struct ec_completed_tk *completed_tk, *child_completed_tk;
+       struct ec_strvec childvec;
+       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 completed_tk;
+
+       for (i = 0; i < tk->len; i++) {
+               if (ec_strvec_slice(&childvec, strvec, len) < 0)
+                       return completed_tk; /* XXX fail ? */
+
+               child_completed_tk = ec_tk_complete_tokens(tk->table[i],
+                       &childvec);
+               if (child_completed_tk == NULL) {
+                       ec_completed_tk_free(completed_tk);
+                       return NULL;
+               }
+               ec_completed_tk_merge(completed_tk, child_completed_tk);
+
+               parsed_tk = ec_tk_parse_tokens(tk->table[i], &childvec);
+               if (parsed_tk == NULL)
+                       goto fail;
+               if (!ec_parsed_tk_matches(parsed_tk)) {
+                       ec_parsed_tk_free(parsed_tk);
+                       break;
+               }
+
+               len += ec_strvec_len(parsed_tk->strvec);
+               ec_parsed_tk_free(parsed_tk);
+       }
+
+       return completed_tk;
+
+fail:
+       /* XXX */
+       return NULL;
+}
+#endif
+
+static void ec_tk_many_free_priv(struct ec_tk *gen_tk)
+{
+       struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
+
+       ec_tk_free(tk->child);
+}
+
+static struct ec_tk_ops ec_tk_many_ops = {
+       .typename = "many",
+       .parse = ec_tk_many_parse,
+       .complete = ec_tk_default_complete,
+//XXX  .complete = ec_tk_many_complete,
+       .free_priv = ec_tk_many_free_priv,
+};
+
+struct ec_tk *ec_tk_many_new(const char *id, struct ec_tk *child,
+       unsigned int min, unsigned int max)
+{
+       struct ec_tk_many *tk = NULL;
+
+       if (child == NULL)
+               return NULL;
+
+       tk = (struct ec_tk_many *)ec_tk_new(id, &ec_tk_many_ops,
+               sizeof(*tk));
+       if (tk == NULL) {
+               ec_tk_free(child);
+               return NULL;
+       }
+
+       tk->child = child;
+       tk->min = min;
+       tk->max = max;
+
+       return &tk->gen;
+}
+
+static int ec_tk_many_testcase(void)
+{
+       struct ec_tk *tk;
+       int ret = 0;
+
+       tk = ec_tk_many_new(NULL, ec_tk_str_new(NULL, "foo"), 0, 0);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar",
+               EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, EC_TK_ENDLIST);
+       ec_tk_free(tk);
+
+       tk = ec_tk_many_new(NULL, ec_tk_str_new(NULL, "foo"), 1, 0);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar",
+               EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, EC_TK_ENDLIST);
+       ec_tk_free(tk);
+
+       tk = ec_tk_many_new(NULL, ec_tk_str_new(NULL, "foo"), 1, 2);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar",
+               EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "foo",
+               EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, EC_TK_ENDLIST);
+       ec_tk_free(tk);
+
+       /* test completion */
+       /* XXX */
+
+       return ret;
+}
+
+static struct ec_test ec_tk_many_test = {
+       .name = "many",
+       .test = ec_tk_many_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_many_test);
diff --git a/lib/ecoli_tk_many.h b/lib/ecoli_tk_many.h
new file mode 100644 (file)
index 0000000..ed32075
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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_MANY_
+#define ECOLI_TK_MANY_
+
+/*
+ * if min == max == 0, there is no limit
+ */
+struct ec_tk *ec_tk_many_new(const char *id, struct ec_tk *child,
+       unsigned int min, unsigned int max);
+
+#endif
index 2b4e4cb..5427e19 100644 (file)
 
 #include <ecoli_malloc.h>
 #include <ecoli_log.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_option.h>
 #include <ecoli_tk_str.h>
 #include <ecoli_test.h>
 
+struct ec_tk_option {
+       struct ec_tk gen;
+       struct ec_tk *child;
+};
+
 static struct ec_parsed_tk *ec_tk_option_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
-       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+       struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk;
+       struct ec_strvec *match_strvec;
 
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
 
-       child_parsed_tk = ec_tk_parse(tk->child, str);
-       if (child_parsed_tk != NULL) {
+       child_parsed_tk = ec_tk_parse_tokens(tk->child, strvec);
+       if (child_parsed_tk == NULL)
+               goto fail;
+
+       if (ec_parsed_tk_matches(child_parsed_tk)) {
                ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
-               parsed_tk->str = ec_strndup(child_parsed_tk->str,
-                       strlen(child_parsed_tk->str));
+               match_strvec = ec_strvec_dup(child_parsed_tk->strvec);
        } else {
-               parsed_tk->str = ec_strdup("");
+               ec_parsed_tk_free(child_parsed_tk);
+               match_strvec = ec_strvec_new();
        }
 
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
+
        return parsed_tk;
+
+ fail:
+       ec_parsed_tk_free(parsed_tk);
+       return NULL;
 }
 
 static struct ec_completed_tk *ec_tk_option_complete(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
 
-       return ec_tk_complete(tk->child, str);
+       return ec_tk_complete_tokens(tk->child, strvec);
 }
 
 static void ec_tk_option_free_priv(struct ec_tk *gen_tk)
@@ -76,6 +95,7 @@ static void ec_tk_option_free_priv(struct ec_tk *gen_tk)
 }
 
 static struct ec_tk_ops ec_tk_option_ops = {
+       .typename = "option",
        .parse = ec_tk_option_parse,
        .complete = ec_tk_option_complete,
        .free_priv = ec_tk_option_free_priv,
@@ -90,8 +110,10 @@ struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *child)
 
        tk = (struct ec_tk_option *)ec_tk_new(id, &ec_tk_option_ops,
                sizeof(*tk));
-       if (tk == NULL)
+       if (tk == NULL) {
+               ec_tk_free(child);
                return NULL;
+       }
 
        tk->child = child;
 
@@ -108,9 +130,10 @@ static int ec_tk_option_testcase(void)
                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", "");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* test completion */
@@ -119,11 +142,18 @@ static int ec_tk_option_testcase(void)
                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);
+       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,
+               "b", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
        ec_tk_free(tk);
 
        return ret;
index cadcc05..338749d 100644 (file)
 
 #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 900ee23..2438679 100644 (file)
 
 #include <ecoli_malloc.h>
 #include <ecoli_log.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_or.h>
 #include <ecoli_tk_str.h>
 #include <ecoli_test.h>
 
+struct ec_tk_or {
+       struct ec_tk gen;
+       struct ec_tk **table;
+       unsigned int len;
+};
+
 static struct ec_parsed_tk *ec_tk_or_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
-       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+       struct ec_parsed_tk *parsed_tk, *child_parsed_tk = NULL;
+       struct ec_strvec *match_strvec;
        unsigned int i;
 
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
 
        for (i = 0; i < tk->len; i++) {
-               child_parsed_tk = ec_tk_parse(tk->table[i], str);
-               if (child_parsed_tk != NULL)
+               child_parsed_tk = ec_tk_parse_tokens(tk->table[i], strvec);
+               if (child_parsed_tk == NULL)
+                       goto fail;
+               if (ec_parsed_tk_matches(child_parsed_tk))
                        break;
+               ec_parsed_tk_free(child_parsed_tk);
+               child_parsed_tk = NULL;
        }
 
-       if (child_parsed_tk == NULL)
+       /* no match */
+       if (i == tk->len)
+               return parsed_tk;
+
+       match_strvec = ec_strvec_dup(child_parsed_tk->strvec);
+       if (match_strvec == NULL)
                goto fail;
 
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
        ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
 
-       parsed_tk->str = ec_strndup(child_parsed_tk->str,
-               strlen(child_parsed_tk->str));
-
        return parsed_tk;
 
  fail:
+       ec_parsed_tk_free(child_parsed_tk);
        ec_parsed_tk_free(parsed_tk);
        return NULL;
 }
 
 static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
        struct ec_completed_tk *completed_tk, *child_completed_tk;
@@ -82,9 +98,10 @@ static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk,
                return NULL;
 
        for (n = 0; n < tk->len; n++) {
-               child_completed_tk = ec_tk_complete(tk->table[n], str);
+               child_completed_tk = ec_tk_complete_tokens(tk->table[n],
+                       strvec);
 
-               if (child_completed_tk == NULL)
+               if (child_completed_tk == NULL) // XXX fail instead?
                        continue;
 
                ec_completed_tk_merge(completed_tk, child_completed_tk);
@@ -104,6 +121,7 @@ static void ec_tk_or_free_priv(struct ec_tk *gen_tk)
 }
 
 static struct ec_tk_ops ec_tk_or_ops = {
+       .typename = "or",
        .parse = ec_tk_or_parse,
        .complete = ec_tk_or_complete,
        .free_priv = ec_tk_or_free_priv,
@@ -128,6 +146,7 @@ struct ec_tk *ec_tk_or_new_list(const char *id, ...)
        struct ec_tk_or *tk = NULL;
        struct ec_tk *child;
        va_list ap;
+       int fail = 0;
 
        va_start(ap, id);
 
@@ -138,12 +157,15 @@ struct ec_tk *ec_tk_or_new_list(const char *id, ...)
        for (child = va_arg(ap, struct ec_tk *);
             child != EC_TK_ENDLIST;
             child = va_arg(ap, struct ec_tk *)) {
-               if (child == NULL)
-                       goto fail;
+               /* on error, don't quit the loop to avoid leaks */
 
-               ec_tk_or_add(&tk->gen, child);
+               if (child == NULL || ec_tk_or_add(&tk->gen, child) < 0)
+                       fail = 1;
        }
 
+       if (fail == 1)
+               goto fail;
+
        va_end(ap);
        return &tk->gen;
 
@@ -185,10 +207,13 @@ static int ec_tk_or_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "fooxxx", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "oo", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " ", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "toto", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* test completion */
@@ -203,20 +228,34 @@ static int ec_tk_or_testcase(void)
                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, "f", "oo");
-       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", "");
-       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",
-               "oo", EC_TK_ENDLIST);
-       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "b",
-               "ar", "ar2", EC_TK_ENDLIST);
-       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "t",
-               "oto", "iti", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "", EC_TK_ENDLIST,
+               "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "f", EC_TK_ENDLIST,
+               "oo", EC_TK_ENDLIST,
+               "oo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "b", EC_TK_ENDLIST,
+               "ar", "ar2", EC_TK_ENDLIST,
+               "ar");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "bar", EC_TK_ENDLIST,
+               "", "2", EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "t", EC_TK_ENDLIST,
+               "oto", "iti", EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "to", EC_TK_ENDLIST,
+               "to", EC_TK_ENDLIST,
+               "to");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "x", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
        ec_tk_free(tk);
 
        return ret;
index a04c6a3..07d4cea 100644 (file)
 #ifndef ECOLI_TK_OR_
 #define ECOLI_TK_OR_
 
-#include <sys/queue.h>
-
 #include <ecoli_tk.h>
 
-struct ec_tk_or {
-       struct ec_tk gen;
-       struct ec_tk **table;
-       unsigned int len;
-};
-
 struct ec_tk *ec_tk_or_new(const char *id);
 
 /* list must be terminated with EC_TK_ENDLIST */
index 7f840c0..d7bf998 100644 (file)
 #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_seq.h>
 
+struct ec_tk_seq {
+       struct ec_tk gen;
+       struct ec_tk **table;
+       unsigned int len;
+};
+
 static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
        struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+       struct ec_strvec *match_strvec;
+       struct ec_strvec childvec;
        size_t len = 0;
        unsigned int i;
 
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
 
        for (i = 0; i < tk->len; i++) {
-               child_parsed_tk = ec_tk_parse(tk->table[i], str + len);
+               if (ec_strvec_slice(&childvec, strvec, len) < 0)
+                       goto fail;
+
+               child_parsed_tk = ec_tk_parse_tokens(tk->table[i], &childvec);
                if (child_parsed_tk == NULL)
                        goto fail;
+               if (!ec_parsed_tk_matches(child_parsed_tk)) {
+                       ec_parsed_tk_free(child_parsed_tk);
+                       ec_parsed_tk_free_children(parsed_tk);
+                       return parsed_tk;
+               }
 
-               len += strlen(child_parsed_tk->str);
                ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+               len += ec_parsed_tk_len(child_parsed_tk);
        }
 
-       parsed_tk->str = ec_strndup(str, len);
+       match_strvec = ec_strvec_ndup(strvec, len);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
 
        return parsed_tk;
 
@@ -70,10 +91,11 @@ static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
 }
 
 static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
        struct ec_completed_tk *completed_tk, *child_completed_tk;
+       struct ec_strvec childvec;
        struct ec_parsed_tk *parsed_tk;
        size_t len = 0;
        unsigned int i;
@@ -85,23 +107,35 @@ static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
        if (tk->len == 0)
                return completed_tk;
 
-       for (i = 0; i < tk->len; i++) {
-               child_completed_tk = ec_tk_complete(tk->table[i], str + len);
+       for (i = 0; i < tk->len && len < ec_strvec_len(strvec); i++) {
+               if (ec_strvec_slice(&childvec, strvec, len) < 0)
+                       goto fail;
+
+               child_completed_tk = ec_tk_complete_tokens(tk->table[i],
+                       &childvec);
                if (child_completed_tk == NULL) {
                        ec_completed_tk_free(completed_tk);
                        return NULL;
                }
                ec_completed_tk_merge(completed_tk, child_completed_tk);
 
-               parsed_tk = ec_tk_parse(tk->table[i], str + len);
+               parsed_tk = ec_tk_parse_tokens(tk->table[i], &childvec);
                if (parsed_tk == NULL)
+                       goto fail;
+               if (!ec_parsed_tk_matches(parsed_tk)) {
+                       ec_parsed_tk_free(parsed_tk);
                        break;
+               }
 
-               len += strlen(parsed_tk->str);
+               len += ec_strvec_len(parsed_tk->strvec);
                ec_parsed_tk_free(parsed_tk);
        }
 
        return completed_tk;
+
+fail:
+       /* XXX */
+       return NULL;
 }
 
 static void ec_tk_seq_free_priv(struct ec_tk *gen_tk)
@@ -115,6 +149,7 @@ static void ec_tk_seq_free_priv(struct ec_tk *gen_tk)
 }
 
 static struct ec_tk_ops ec_tk_seq_ops = {
+       .typename = "seq",
        .parse = ec_tk_seq_parse,
        .complete = ec_tk_seq_complete,
        .free_priv = ec_tk_seq_free_priv,
@@ -139,6 +174,7 @@ struct ec_tk *ec_tk_seq_new_list(const char *id, ...)
        struct ec_tk_seq *tk = NULL;
        struct ec_tk *child;
        va_list ap;
+       int fail = 0;
 
        va_start(ap, id);
 
@@ -149,12 +185,15 @@ struct ec_tk *ec_tk_seq_new_list(const char *id, ...)
        for (child = va_arg(ap, struct ec_tk *);
             child != EC_TK_ENDLIST;
             child = va_arg(ap, struct ec_tk *)) {
-               if (child == NULL)
-                       goto fail;
+               /* on error, don't quit the loop to avoid leaks */
 
-               ec_tk_seq_add(&tk->gen, child);
+               if (child == NULL || ec_tk_seq_add(&tk->gen, child) < 0)
+                       fail = 1;
        }
 
+       if (fail == 1)
+               goto fail;
+
        va_end(ap);
        return &tk->gen;
 
@@ -198,11 +237,14 @@ static int ec_tk_seq_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foobar", "foobar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foobarxxx", "foobar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, " foobar", NULL);
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", "toto",
+               EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", "barx", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar", "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", "foo", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* test completion */
@@ -215,16 +257,42 @@ static int ec_tk_seq_testcase(void)
                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, "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", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobarx", "");
+       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,
+               "x", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "foobarx", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
        ec_tk_free(tk);
 
        return ret;
index 5c7bfed..2385da2 100644 (file)
 #ifndef ECOLI_TK_SEQ_
 #define ECOLI_TK_SEQ_
 
-#include <sys/queue.h>
-
 #include <ecoli_tk.h>
 
-struct ec_tk_seq {
-       struct ec_tk gen;
-       struct ec_tk **table;
-       unsigned int len;
-};
-
 struct ec_tk *ec_tk_seq_new(const char *id);
 
 /* list must be terminated with EC_TK_ENDLIST */
index 3384557..ba6d20a 100644 (file)
 #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>
 
-static int isend(char c)
+struct ec_tk_shlex {
+       struct ec_tk gen;
+       struct ec_tk *child;
+};
+
+static size_t eat_spaces(const char *str)
 {
-       if (c == '\0' || c == '#' || c == '\n' || c == '\r')
-               return 1;
-       return 0;
+       size_t i = 0;
+
+       /* skip spaces */
+       while (isblank(str[i]))
+               i++;
+
+       return i;
 }
 
-/* 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)
+/*
+ * 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 = 0, d = 0, dstlen;
-       int quoted = 0;
+       unsigned s = 1, d = 0;
+       char quote = str[0];
        char *dst;
+       int closed = 0;
 
-       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;
+       dst = ec_malloc(n);
+       if (dst == NULL) {
+               errno = ENOMEM;
+               return NULL;
        }
 
        /* 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++] = '"';
+       while (s < n && d < n && str[s] != '\0') {
+               if (str[s] == '\\' && str[s+1] == quote) {
+                       dst[d++] = quote;
                        s += 2;
                        continue;
                }
-               if (src[s] == '\\' && src[s+1] == '\\') {
+               if (str[s] == '\\' && str[s+1] == '\\') {
                        dst[d++] = '\\';
                        s += 2;
                        continue;
                }
-               if (src[s] == '"') {
+               if (str[s] == quote) {
                        s++;
-                       quoted = !quoted;
-                       continue;
+                       closed = 1;
+                       break;
                }
-               dst[d++] = src[s++];
+               dst[d++] = str[s++];
        }
 
-       /* not enough room in dst buffer */
-       if (d >= dstlen) {
+       /* not enough room in dst buffer (should not happen) */
+       if (d >= n) {
                ec_free(dst);
-               return -EMSGSIZE;
+               errno = EMSGSIZE;
+               return NULL;
        }
 
-       /* end of string during quote */
-       if (quoted) {
-               ec_free(dst);
-               return -EINVAL;
+       /* 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';
-       *p_dst = dst;
-       return s;
+
+       return dst;
 }
 
-static int safe_realloc(void *arg, size_t size)
+static size_t eat_quoted_str(const char *str)
 {
-       void **pptr = arg;
-       void *new_ptr = ec_realloc(*pptr, size);
+       size_t i = 0;
+       char quote = str[0];
 
-       if (new_ptr == NULL)
-               return -1;
-       *pptr = new_ptr;
-       return 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 char **tokenize(const char *str, int add_empty)
+static size_t eat_str(const char *str)
 {
-       char **table = NULL, *token;
-       unsigned i, count = 1, off = 0;
-       int ret;
+       size_t i = 0;
 
-       if (safe_realloc(&table, sizeof(char *)) < 0)
-               return NULL;
+       /* skip spaces */
+       while (!isblank(str[i]) && str[i] != '\0')
+               i++;
 
-       table[0] = NULL;
+       return i;
+}
 
-       while (1) {
-               ret = get_token(str + off, &token);
-               if (ret == -ENOENT)
-                       break;
-               else if (ret < 0)
-                       goto fail;
+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;
 
-               off += ret;
-               count++;
-               if (safe_realloc(&table, sizeof(char *) * count) < 0)
-                       goto fail;
-               table[count - 2] = token;
-               table[count - 1] = NULL;
-       }
+//     printf("str=%s\n", str);
 
-       if (add_empty && (off != strlen(str) || strlen(str) == 0)) {
-               token = ec_strdup("");
-               if (token == NULL)
-                       goto fail;
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
 
-               count++;
-               if (safe_realloc(&table, sizeof(char *) * count) < 0)
+       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;
+               }
+
+//             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;
-               table[count - 2] = token;
-               table[count - 1] = NULL;
        }
 
-       return table;
+       return strvec;
 
  fail:
-       for (i = 0; i < count; i++)
-               ec_free(table[i]);
-       ec_free(table);
+       ec_free(word);
+       ec_free(concat);
+       ec_strvec_free(strvec);
        return NULL;
 }
 
-/* XXX broken: how to support that:
-   shlex(
-     str("toto"),
-     many(str("titi")),
-   )
-
-   that would match:
-     toto
-     toto titi
-     toto titi titi ...
-
-     --> maybe we should not try to create/match the spaces automatically
-
-     it would become:
-
-   shlex(
-     option(space()),   auto?
-     str("toto"),
-     many(
-       space(),
-       str("titi coin"),
-     ),
-     option(space()),   auto?
-   )
-
-   -> the goal of shlex would only be to unquote
-   -> the creation of auto-spaces would be in another token shcmd
-
-   cmd = shcmd_new()
-   shcmd_add_tk("ip", tk_ip_new())
-   shcmd_set_syntax("show <ip>")
-
-
- */
 static struct ec_parsed_tk *ec_tk_shlex_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        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;
+       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(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        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;
+       if (ec_strvec_len(strvec) == 0)
+               return parsed_tk;
 
-               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;
-       }
+       str = ec_strvec_val(strvec, 0);
+       new_vec = tokenize(str, 0, 0, NULL);
+       if (new_vec == NULL)
+               goto fail;
 
-       /* check it was the last token */
-       if (*t != NULL)
+       child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec);
+       if (child_parsed_tk == NULL)
                goto fail;
 
-       if (tokens != NULL) {
-               for (t = &tokens[0]; *t != NULL; t++)
-                       ec_free(*t);
-               ec_free(tokens);
-               tokens = NULL;
+       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;
 
-       parsed_tk->str = ec_strdup(str);
+       ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+       match_strvec = ec_strvec_ndup(strvec, 1);
+       if (match_strvec == NULL)
+               goto fail;
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
 
        return parsed_tk;
 
  fail:
-       if (tokens != NULL) {
-               for (t = &tokens[0]; *t != NULL; t++)
-                       ec_free(*t);
-               ec_free(tokens);
-       }
+       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 char *str)
+       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_parsed_tk *child_parsed_tk;
-       unsigned int i;
-       char **tokens, **t;
+       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;
 
-       tokens = tokenize(str, 1);
-       if (tokens == 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;
 
-       printf("complete <%s>\n", str);
-       for (t = &tokens[0]; *t != NULL; t++)
-               printf("  token <%s> %p\n", *t, *t);
+//     ec_strvec_dump(new_vec, stdout);
 
-       t = &tokens[0];
+       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;
 
-       completed_tk = ec_completed_tk_new();
-       if (completed_tk == NULL)
-               return NULL;
 
+#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);
@@ -329,99 +355,45 @@ static struct ec_completed_tk *ec_tk_shlex_complete(const struct ec_tk *gen_tk,
        }
 
        ec_completed_tk_dump(stdout, completed_tk);
-
-       return completed_tk;
+#endif
 
  fail:
-       if (tokens != NULL) {
-               for (t = &tokens[0]; *t != NULL; t++)
-                       ec_free(*t);
-               ec_free(tokens);
-       }
+       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;
-       unsigned int i;
 
-       for (i = 0; i < tk->len; i++)
-               ec_tk_free(tk->table[i]);
-       ec_free(tk->table);
+       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 *ec_tk_shlex_new(const char *id, struct ec_tk *child)
 {
        struct ec_tk_shlex *tk = NULL;
 
-       tk = (struct ec_tk_shlex *)ec_tk_new(id, &ec_tk_shlex_ops, sizeof(*tk));
-       if (tk == NULL)
+       if (child == 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);
+       tk = (struct ec_tk_shlex *)ec_tk_new(id, &ec_tk_shlex_ops,
+               sizeof(*tk));
+       if (tk == NULL) {
+               ec_tk_free(child);
+               return NULL;
        }
 
-       va_end(ap);
-       return &tk->gen;
+       tk->child = child;
 
-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;
+       return &tk->gen;
 }
 
 static int ec_tk_shlex_testcase(void)
@@ -429,52 +401,93 @@ 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);
+       tk = ec_tk_shlex_new(NULL,
+               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) {
                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);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "  foo   bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "  'foo' \"bar\"", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "  'f'oo 'toto' bar",
+               EC_TK_ENDLIST);
        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);
+       tk = ec_tk_shlex_new(NULL,
+               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_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);
+       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;
 }
 
index 52bc6a7..5aa2246 100644 (file)
 #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);
+struct ec_tk *ec_tk_shlex_new(const char *id, struct ec_tk *child);
 
 #endif
index 6c69b3b..8ec1650 100644 (file)
 #include <ecoli_log.h>
 #include <ecoli_test.h>
 #include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_space.h>
 
+struct ec_tk_space {
+       struct ec_tk gen;
+};
+
 static struct ec_parsed_tk *ec_tk_space_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
-       struct ec_parsed_tk *parsed_tk;
+       struct ec_parsed_tk *parsed_tk = NULL;
+       struct ec_strvec *match_strvec;
+       const char *str;
        size_t len = 0;
 
-       if (!isspace(str[0]))
-               return NULL;
-
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
 
+       if (ec_strvec_len(strvec) == 0)
+               return parsed_tk;
+
+       str = ec_strvec_val(strvec, 0);
        while (isspace(str[len]))
                len++;
+       if (len == 0 || len != strlen(str))
+               return parsed_tk;
+
+       match_strvec = ec_strvec_ndup(strvec, 1);
+       if (match_strvec == NULL)
+               goto fail;
 
-       parsed_tk->str = ec_strndup(str, len);
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
 
        return parsed_tk;
+
+ fail:
+       ec_parsed_tk_free(parsed_tk);
+       return NULL;
 }
 
 static struct ec_tk_ops ec_tk_space_ops = {
+       .typename = "space",
        .parse = ec_tk_space_parse,
+       .complete = ec_tk_default_complete,
 };
 
 struct ec_tk *ec_tk_space_new(const char *id)
@@ -76,11 +96,11 @@ static int ec_tk_space_testcase(void)
                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);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " ", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " ", "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo ", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* test completion */
@@ -89,9 +109,18 @@ static int ec_tk_space_testcase(void)
                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", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               " ", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "foo", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
        ec_tk_free(tk);
 
        return ret;
index 6327c8a..08aa48f 100644 (file)
 
 #include <ecoli_tk.h>
 
-struct ec_tk_space {
-       struct ec_tk gen;
-};
-
 struct ec_tk *ec_tk_space_new(const char *id);
 
 #endif
index 61953ef..4fe3062 100644 (file)
 #include <ecoli_log.h>
 #include <ecoli_malloc.h>
 #include <ecoli_test.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_str.h>
 
+struct ec_tk_str {
+       struct ec_tk gen;
+       char *string;
+       unsigned len;
+};
+
 static struct ec_parsed_tk *ec_tk_str_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
-       struct ec_parsed_tk *parsed_tk;
+       struct ec_strvec *match_strvec;
+       struct ec_parsed_tk *parsed_tk = NULL;
+       const char *str;
 
-       if (strncmp(str, tk->string, tk->len) != 0)
-               return NULL;
-
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
+
+       if (ec_strvec_len(strvec) == 0)
+               return parsed_tk;
 
-       parsed_tk->str = ec_strndup(str, tk->len);
+       str = ec_strvec_val(strvec, 0);
+       if (strcmp(str, tk->string) != 0)
+               return parsed_tk;
+
+       match_strvec = ec_strvec_ndup(strvec, 1);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
 
        return parsed_tk;
+
+ fail:
+       ec_parsed_tk_free(parsed_tk);
+       return NULL;
 }
 
 static struct ec_completed_tk *ec_tk_str_complete(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
        struct ec_completed_tk *completed_tk;
        struct ec_completed_tk_elt *completed_tk_elt;
-       size_t n;
+       const char *str, *add;
+       size_t n = 0;
 
        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 completed_tk;
-       if (tk->string[n] == '\0')
+       if (ec_strvec_len(strvec) > 1)
                return completed_tk;
 
-       completed_tk_elt = ec_completed_tk_elt_new(gen_tk, tk->string + n,
-               tk->string);
+       if (ec_strvec_len(strvec) == 1) {
+               str = ec_strvec_val(strvec, 0);
+               for (n = 0; n < tk->len; n++) {
+                       if (str[n] != tk->string[n])
+                               break;
+               }
+
+               if (str[n] != '\0')
+                       add = NULL;
+               else
+                       add = tk->string + n;
+       }
+
+       completed_tk_elt = ec_completed_tk_elt_new(gen_tk, add);
        if (completed_tk_elt == NULL) {
                ec_completed_tk_free(completed_tk);
                return NULL;
@@ -95,7 +121,8 @@ static void ec_tk_str_free_priv(struct ec_tk *gen_tk)
        ec_free(tk->string);
 }
 
-static struct ec_tk_ops ec_tk_str_ops = {
+static const struct ec_tk_ops ec_tk_str_ops = {
+       .typename = "str",
        .parse = ec_tk_str_parse,
        .complete = ec_tk_str_complete,
        .free_priv = ec_tk_str_free_priv,
@@ -135,11 +162,11 @@ static int ec_tk_str_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foobar", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, " foo", NULL);
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "", NULL);
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foobar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        tk = ec_tk_str_new(NULL, "Здравствуйте");
@@ -147,10 +174,11 @@ static int ec_tk_str_testcase(void)
                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, "Здравствуйте John!", "Здравствуйте");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "Здравствуйте", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "Здравствуйте",
+               "John!", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* an empty token string always matches */
@@ -159,8 +187,9 @@ static int ec_tk_str_testcase(void)
                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", "");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "", "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* test completion */
@@ -169,10 +198,22 @@ static int ec_tk_str_testcase(void)
                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, "foo", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
+       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,
+               "x", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
        ec_tk_free(tk);
 
        return ret;
index e7c0759..00632ec 100644 (file)
 
 #include <ecoli_tk.h>
 
-struct ec_tk_str {
-       struct ec_tk gen;
-       char *string;
-       unsigned len;
-};
-
 struct ec_tk *ec_tk_str_new(const char *id, const char *str);
 
 #endif
index 3bdc703..379c36e 100644 (file)
@@ -25,6 +25,7 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#define _GNU_SOURCE /* for asprintf */
 #include <stdlib.h>
 #include <stdio.h>
 #include <assert.h>
 #include <ecoli_tk_seq.h>
 #include <ecoli_tk_space.h>
 #include <ecoli_tk_or.h>
+#include <ecoli_tk_shlex.h>
+#include <ecoli_tk_int.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 char *my_completion_entry(const char *s, int state)
 {
        static struct ec_completed_tk *c;
+       static struct ec_completed_tk_iter *iter;
        static const struct ec_completed_tk_elt *elt;
+       char *out_string;
 
-       (void)s;
 
        if (state == 0) {
-               char *start;
+               char *line;
+
+               ec_completed_tk_free(c);
 
-               if (c != NULL)
-                       ec_completed_tk_free(c);
+               line = strdup(rl_line_buffer);
+               if (line == NULL)
+                       return NULL;
+               line[rl_point] = '\0';
 
-               start = strdup(rl_line_buffer);
-               if (start == NULL)
+               c = ec_tk_complete(commands, line);
+               free(line);
+               if (c == NULL)
                        return NULL;
-               start[rl_point] = '\0';
 
-               c = ec_tk_complete(commands, start);
-               ec_completed_tk_iter_start(c);
+               ec_completed_tk_iter_free(iter);
+               iter = ec_completed_tk_iter_new(c, ITER_MATCH);
+               if (iter == NULL)
+                       return NULL;
        }
 
-       elt = ec_completed_tk_iter_next(c);
+       elt = ec_completed_tk_iter_next(iter);
        if (elt == NULL)
                return NULL;
 
-       return strdup(elt->full);
+       if (asprintf(&out_string, "%s%s", s, elt->add) < 0)
+               return NULL;
+
+       return out_string;
 }
 
-char **my_attempted_completion(const char *text, int start, int end)
+static 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
+
+       /* remove default file completion */
+       rl_attempted_completion_over = 1;
+
        return rl_completion_matches(text, my_completion_entry);
 }
 
+
+static int show_help(int ignore, int invoking_key)
+{
+       struct ec_completed_tk *c;
+       char *line;
+
+       (void)ignore;
+       (void)invoking_key;
+
+       printf("\nhelp:\n");
+       line = strdup(rl_line_buffer);
+       if (line == NULL)
+               return 1;
+       line[rl_point] = '\0';
+
+       c = ec_tk_complete(commands, line);
+       free(line);
+       if (c == NULL)
+               return 1;
+       ec_completed_tk_dump(stdout, c);
+       ec_completed_tk_free(c);
+
+       rl_forced_update_display();
+
+       return 0;
+}
+
 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);
+       rl_bind_key('?', show_help);
+
+       commands = ec_tk_shlex_new(NULL,
+               ec_tk_seq_new_list(NULL,
+                       ec_tk_str_new(NULL, "hello"),
+                       ec_tk_or_new_list("name",
+                               ec_tk_str_new(NULL, "john"),
+                               ec_tk_str_new(NULL, "johnny"),
+                               ec_tk_str_new(NULL, "mike"),
+                               ec_tk_int_new(NULL, 0, 10, 10),
+                               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) {
@@ -120,7 +152,6 @@ int main(void)
                if (line == NULL)
                        break;
 
-               // XXX need a "parse_all"
                p = ec_tk_parse(commands, line);
                ec_parsed_tk_dump(stdout, p);
                add_history(line);
index a2d101b..d3d35b3 100644 (file)
 #include <stdio.h>
 #include <assert.h>
 #include <getopt.h>
+#include <execinfo.h>
 
+#include <ecoli_log.h>
 #include <ecoli_test.h>
 #include <ecoli_malloc.h>
 
+#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
+               ((size_t)(!(sizeof(x) % sizeof(0[x])))))
+
+static int log_level = EC_LOG_INFO;
+static int alloc_fail_proba = 0;
+
 static const char ec_short_options[] =
-       "h" /* help */
+       "h"  /* help */
+       "l:" /* log-level */
+       "r:" /* random-alloc-fail */
        ;
 
-enum {
-       /* long options */
-       EC_OPT_LONG_MIN_NUM = 256,
 #define EC_OPT_HELP "help"
-       EC_OPT_HELP_NUM,
-};
+#define EC_OPT_LOG_LEVEL "log-level"
+#define EC_OPT_RANDOM_ALLOC_FAIL "random-alloc-fail"
 
 static const struct option ec_long_options[] = {
-       {EC_OPT_HELP, 1, NULL, EC_OPT_HELP_NUM},
+       {EC_OPT_HELP, 1, NULL, 'h'},
+       {EC_OPT_LOG_LEVEL, 1, NULL, 'l'},
+       {EC_OPT_RANDOM_ALLOC_FAIL, 1, NULL, 'r'},
        {NULL, 0, NULL, 0}
 };
 
 static void usage(const char *prgname)
 {
        printf("%s [options]\n"
-                  "  --"EC_OPT_HELP": show this help\n"
+               "  -h\n"
+               "  --"EC_OPT_HELP"\n"
+               "      Show this help.\n"
+               "  -l <level>\n"
+               "  --"EC_OPT_LOG_LEVEL"=<level>\n"
+               "      Set log level (0 = no log, 6 = verbose).\n"
+               "  -r <probability>\n"
+               "  --"EC_OPT_RANDOM_ALLOC_FAIL"=<probability>\n"
+               "      Cause malloc to fail randomly. This helps to debug\n"
+               "      leaks or crashes in error cases. The probability is\n"
+               "      between 0 and 100.\n"
                , prgname);
 }
 
+static int
+parse_int(const char *s, int min, int max, int *ret, unsigned int base)
+{
+       char *end = NULL;
+       long long n;
+
+       n = strtoll(s, &end, base);
+       if ((s[0] == '\0') || (end == NULL) || (*end != '\0'))
+               return -1;
+       if (n < min)
+               return -1;
+       if (n > max)
+               return -1;
+
+       *ret = n;
+       return 0;
+}
+
 static void parse_args(int argc, char **argv)
 {
        int opt;
@@ -65,10 +102,27 @@ static void parse_args(int argc, char **argv)
 
                switch (opt) {
                case 'h': /* help */
-               case EC_OPT_HELP_NUM:
                        usage(argv[0]);
                        exit(0);
 
+               case 'l': /* log-level */
+                       if (parse_int(optarg, EC_LOG_EMERG,
+                                       EC_LOG_DEBUG, &log_level, 10) < 0) {
+                               printf("Invalid log value\n");
+                               usage(argv[0]);
+                               exit(1);
+                       }
+                       break;
+
+               case 'r': /* random-alloc-fail */
+                       if (parse_int(optarg, 0, 100, &alloc_fail_proba,
+                                       10) < 0) {
+                               printf("Invalid probability value\n");
+                               usage(argv[0]);
+                               exit(1);
+                       }
+                       break;
+
                default:
                        usage(argv[0]);
                        exit(1);
@@ -80,11 +134,14 @@ TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr);
 static struct debug_alloc_hdr_list debug_alloc_hdr_list =
        TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list);
 
+#define STACK_SZ 16
 struct debug_alloc_hdr {
        TAILQ_ENTRY(debug_alloc_hdr) next;
        const char *file;
        unsigned int line;
        size_t size;
+       void *stack[STACK_SZ];
+       int stacklen;
        unsigned int cookie;
 };
 
@@ -99,22 +156,28 @@ static void *debug_malloc(size_t size, const char *file, unsigned int line)
        size_t new_size = size + sizeof(*hdr) + sizeof(*ftr);
        void *ret;
 
-       hdr = malloc(new_size);
+
+       if (alloc_fail_proba != 0 && (random() % 100) < alloc_fail_proba)
+               hdr = NULL;
+       else
+               hdr = malloc(new_size);
+
        if (hdr == NULL) {
                ret = NULL;
        } else {
                hdr->file = file;
                hdr->line = line;
                hdr->size = size;
+               hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
                hdr->cookie = 0x12345678;
                TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
                ret = hdr + 1;
                ftr = (struct debug_alloc_ftr *)(
                        (char *)hdr + size + sizeof(*hdr));
-               ftr->cookie = 0x12345678;
+               ftr->cookie = 0x87654321;
        }
 
-       ec_log(EC_LOG_INFO, "%s:%d: info: malloc(%zd) -> %p\n",
+       ec_log(EC_LOG_DEBUG, "%s:%d: info: malloc(%zd) -> %p\n",
                file, line, size, ret);
 
        return ret;
@@ -128,7 +191,7 @@ static void debug_free(void *ptr, const char *file, unsigned int line)
        (void)file;
        (void)line;
 
-       ec_log(EC_LOG_INFO, "%s:%d: info: free(%p)\n", file, line, ptr);
+       ec_log(EC_LOG_DEBUG, "%s:%d: info: free(%p)\n", file, line, ptr);
 
        if (ptr == NULL)
                return;
@@ -141,7 +204,7 @@ static void debug_free(void *ptr, const char *file, unsigned int line)
        }
 
        ftr = (ptr + hdr->size);
-       if (ftr->cookie != 0x12345678) {
+       if (ftr->cookie != 0x87654321) {
                ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
                        file, line, ptr);
                abort();
@@ -162,7 +225,8 @@ static void debug_free(void *ptr, const char *file, unsigned int line)
        free(hdr);
 }
 
-void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
+static void *debug_realloc(void *ptr, size_t size, const char *file,
+       unsigned int line)
 {
        struct debug_alloc_hdr *hdr, *h;
        struct debug_alloc_ftr *ftr;
@@ -179,7 +243,7 @@ void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
                }
 
                ftr = (ptr + hdr->size);
-               if (ftr->cookie != 0x12345678) {
+               if (ftr->cookie != 0x87654321) {
                        ec_log(EC_LOG_ERR,
                                "%s:%d: error: realloc(%p): bad end cookie\n",
                                file, line, ptr);
@@ -217,37 +281,71 @@ void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
                hdr->file = file;
                hdr->line = line;
                hdr->size = size;
+               hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
                hdr->cookie = 0x12345678;
                TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
                ftr = (struct debug_alloc_ftr *)(
                        (char *)hdr + size + sizeof(*hdr));
-               ftr->cookie = 0x12345678;
+               ftr->cookie = 0x87654321;
        }
 
-       ec_log(EC_LOG_INFO, "%s:%d: info: realloc(%p, %zd) -> %p\n",
+       ec_log(EC_LOG_DEBUG, "%s:%d: info: realloc(%p, %zd) -> %p\n",
                file, line, ptr, size, ret);
 
        return ret;
 }
 
-void debug_alloc_dump(void)
+static int debug_alloc_dump_leaks(void)
 {
        struct debug_alloc_hdr *hdr;
+       int i;
+       char **buffer;
+
+       if (TAILQ_EMPTY(&debug_alloc_hdr_list))
+               return 0;
 
        TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
-               ec_log(EC_LOG_ERR, "%s:%d: error: memory leak size=%zd ptr=%p\n",
+               ec_log(EC_LOG_ERR,
+                       "%s:%d: error: memory leak size=%zd ptr=%p\n",
                        hdr->file, hdr->line, hdr->size, hdr + 1);
+               buffer = backtrace_symbols(hdr->stack, hdr->stacklen);
+               if (buffer == NULL) {
+                       for (i = 0; i < hdr->stacklen; i++)
+                               ec_log(EC_LOG_ERR, "  %p\n", hdr->stack[i]);
+               } else {
+                       for (i = 0; i < hdr->stacklen; i++)
+                               ec_log(EC_LOG_ERR, "  %s\n",
+                                       buffer ? buffer[i] : "unknown");
+               }
+               free(buffer);
        }
+
+       ec_log(EC_LOG_ERR,
+               "  missing static syms, use: addr2line -f -e <prog> <addr>\n");
+
+       return -1;
+}
+
+static int debug_log(unsigned int level, void *opaque, const char *str)
+{
+       (void)opaque;
+
+       if (level > (unsigned int)log_level)
+               return 0;
+
+       return printf("%s", str);
 }
 
 int main(int argc, char **argv)
 {
-       int ret;
+       int ret, leaks;
 
        parse_args(argc, argv);
 
-       TAILQ_INIT(&debug_alloc_hdr_list);
+       ec_log_register(debug_log, NULL);
+
        /* register a new malloc to track memleaks */
+       TAILQ_INIT(&debug_alloc_hdr_list);
        if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
                ec_log(EC_LOG_ERR, "cannot register new malloc\n");
                return -1;
@@ -256,10 +354,17 @@ int main(int argc, char **argv)
        ret = ec_test_all();
 
        ec_malloc_unregister();
-       debug_alloc_dump();
+       leaks = debug_alloc_dump_leaks();
 
-       if (ret != 0)
+       if (alloc_fail_proba == 0 && ret != 0) {
                printf("tests failed\n");
+               return 1;
+       } else if (alloc_fail_proba != 0 && leaks != 0) {
+               printf("tests failed (memory leak)\n");
+               return 1;
+       }
+
+       printf("\ntests ok\n");
 
        return 0;
 }