continue !
authorOlivier Matz <zer0@droids-corp.org>
Thu, 1 Dec 2016 19:15:09 +0000 (20:15 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 1 Dec 2016 19:15:09 +0000 (20:15 +0100)
16 files changed:
lib/Makefile
lib/build/test
lib/ecoli_keyval.c [new file with mode: 0644]
lib/ecoli_keyval.h [new file with mode: 0644]
lib/ecoli_malloc.c
lib/ecoli_malloc.h
lib/ecoli_strvec.c
lib/ecoli_test.c
lib/ecoli_test.h
lib/ecoli_tk.c
lib/ecoli_tk.h
lib/ecoli_tk_int.c
lib/ecoli_tk_or.c
lib/ecoli_tk_seq.c
lib/ecoli_tk_str.c
lib/main-readline.c

index 28fce46..9bbd73b 100644 (file)
@@ -34,6 +34,7 @@ CFLAGS  = -g -O0 -Wall -Werror -W -fPIC
 CFLAGS += -I.
 
 srcs := ecoli_tk.c ecoli_malloc.c ecoli_log.c ecoli_strvec.c
+srcs += ecoli_keyval.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
@@ -44,7 +45,7 @@ shlib-y-$(O)libecoli.so := $(srcs)
 ldflags-$(O)test = -rdynamic
 exe-y-$(O)test = $(srcs) main.c
 
-ldflags-$(O)readline = -lreadline
+ldflags-$(O)readline = -lreadline -ltermcap
 exe-y-$(O)readline = $(srcs) main-readline.c
 
 include $(ECOLI)/mk/ecoli-post.mk
index 3e62c75..e389ed2 100755 (executable)
Binary files a/lib/build/test and b/lib/build/test differ
diff --git a/lib/ecoli_keyval.c b/lib/ecoli_keyval.c
new file mode 100644 (file)
index 0000000..8275715
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * 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 <assert.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_keyval.h>
+
+struct ec_keyval {
+       size_t len;
+       struct ec_keyval_elt *vec;
+};
+
+struct ec_keyval_elt {
+       char *key;
+       void *val;
+       ec_keyval_elt_free_t free;
+};
+
+struct ec_keyval *ec_keyval_new(void)
+{
+       struct ec_keyval *keyval;
+
+       keyval = ec_calloc(1, sizeof(*keyval));
+       if (keyval == NULL)
+               return NULL;
+
+       return keyval;
+}
+
+static struct ec_keyval_elt *ec_keyval_lookup(const struct ec_keyval *keyval,
+       const char *key)
+{
+       struct ec_keyval_elt *elt;
+       size_t i;
+
+       if (keyval == NULL || keyval->vec == NULL)
+               return NULL;
+
+       for (i = 0; i < ec_keyval_len(keyval); i++) {
+               elt = &keyval->vec[i];
+               if (strcmp(elt->key, key) == 0)
+                       return elt;
+       }
+
+       return NULL;
+}
+
+static void ec_keyval_elt_free(struct ec_keyval_elt *elt)
+{
+       if (elt == NULL)
+               return;
+
+       ec_free(elt->key);
+       if (elt->free != NULL)
+               elt->free(elt->val);
+}
+
+void *ec_keyval_get(const struct ec_keyval *keyval, const char *key)
+{
+       struct ec_keyval_elt *elt;
+
+       elt = ec_keyval_lookup(keyval, key);
+       if (elt == NULL)
+               return NULL;
+
+       return elt->val;
+}
+
+int ec_keyval_del(struct ec_keyval *keyval, const char *key)
+{
+       struct ec_keyval_elt *elt;
+       struct ec_keyval_elt *last = &keyval->vec[keyval->len - 1];
+
+       elt = ec_keyval_lookup(keyval, key);
+       if (elt == NULL)
+               return -ENOENT;
+
+       ec_keyval_elt_free(elt);
+
+       if (elt != last)
+               memcpy(elt, last, sizeof(*elt));
+
+       keyval->len--;
+
+       return 0;
+}
+
+int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
+       ec_keyval_elt_free_t free_cb)
+{
+       struct ec_keyval_elt *new_vec;
+       struct ec_keyval_elt *elt;
+
+       assert(keyval != NULL);
+       assert(key != NULL);
+
+       ec_keyval_del(keyval, key);
+
+       new_vec = ec_realloc(keyval->vec,
+               sizeof(*keyval->vec) * (keyval->len + 1));
+       if (new_vec == NULL)
+               return -ENOMEM;
+
+       keyval->vec = new_vec;
+
+       elt = &new_vec[keyval->len];
+       elt->key = ec_strdup(key);
+       if (elt->key == NULL)
+               return -ENOMEM;
+
+       elt->val = val;
+       elt->free = free_cb;
+       keyval->len++;
+
+       return 0;
+}
+
+void ec_keyval_free(struct ec_keyval *keyval)
+{
+       struct ec_keyval_elt *elt;
+       size_t i;
+
+       if (keyval == NULL)
+               return;
+
+       for (i = 0; i < ec_keyval_len(keyval); i++) {
+               elt = &keyval->vec[i];
+               ec_keyval_elt_free(elt);
+       }
+       ec_free(keyval->vec);
+       ec_free(keyval);
+}
+
+size_t ec_keyval_len(const struct ec_keyval *keyval)
+{
+       return keyval->len;
+}
+
+void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out)
+{
+       size_t i;
+
+       if (keyval == NULL) {
+               fprintf(out, "empty keyval\n");
+               return;
+       }
+
+       fprintf(out, "keyval:\n");
+       for (i = 0; i < ec_keyval_len(keyval); i++) {
+               fprintf(out, "  %s: %p\n",
+                       keyval->vec[i].key,
+                       keyval->vec[i].val);
+       }
+}
+
+static int ec_keyval_testcase(void)
+{
+       struct ec_keyval *keyval;
+       char *val;
+
+       keyval = ec_keyval_new();
+       if (keyval == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create keyval\n");
+               return -1;
+       }
+
+       EC_TEST_ASSERT(ec_keyval_len(keyval) == 0);
+       ec_keyval_set(keyval, "key1", "val1", NULL);
+       ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2);
+       EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
+
+       val = ec_keyval_get(keyval, "key1");
+       EC_TEST_ASSERT(val != NULL && !strcmp(val, "val1"));
+       val = ec_keyval_get(keyval, "key2");
+       EC_TEST_ASSERT(val != NULL && !strcmp(val, "val2"));
+       val = ec_keyval_get(keyval, "key3");
+       EC_TEST_ASSERT(val == NULL);
+
+       ec_keyval_set(keyval, "key1", "another_val1", NULL);
+       ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2);
+       EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
+
+       val = ec_keyval_get(keyval, "key1");
+       EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val1"));
+       val = ec_keyval_get(keyval, "key2");
+       EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val2"));
+
+       ec_keyval_del(keyval, "key1");
+       EC_TEST_ASSERT(ec_keyval_len(keyval) == 1);
+
+       ec_keyval_free(keyval);
+
+       return 0;
+}
+
+static struct ec_test ec_keyval_test = {
+       .name = "keyval",
+       .test = ec_keyval_testcase,
+};
+
+EC_REGISTER_TEST(ec_keyval_test);
diff --git a/lib/ecoli_keyval.h b/lib/ecoli_keyval.h
new file mode 100644 (file)
index 0000000..f0076cf
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ECOLI_KEYVAL_
+#define ECOLI_KEYVAL_
+
+#include <stdio.h>
+
+typedef void (*ec_keyval_elt_free_t)(void *);
+
+struct ec_keyval;
+
+struct ec_keyval *ec_keyval_new(void);
+
+void *ec_keyval_get(const struct ec_keyval *keyval, const char *key);
+int ec_keyval_del(struct ec_keyval *keyval, const char *key);
+int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
+       ec_keyval_elt_free_t free_cb);
+
+void ec_keyval_free(struct ec_keyval *keyval);
+size_t ec_keyval_len(const struct ec_keyval *keyval);
+void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out);
+
+#endif
index 0871247..0768cab 100644 (file)
@@ -59,11 +59,21 @@ void *__ec_malloc(size_t size, const char *file, unsigned int line)
        return ec_malloc_handler.malloc(size, file, line);
 }
 
+void *ec_malloc2(size_t size)
+{
+       return __ec_malloc(size, __FILE__, __LINE__);
+}
+
 void __ec_free(void *ptr, const char *file, unsigned int line)
 {
        ec_malloc_handler.free(ptr, file, line);
 }
 
+void ec_free2(void *ptr)
+{
+       __ec_free(ptr, __FILE__, __LINE__);
+}
+
 void *__ec_calloc(size_t nmemb, size_t size, const char *file,
        unsigned int line)
 {
index f5bdc39..76b63e0 100644 (file)
@@ -59,6 +59,10 @@ char *__ec_strdup(const char *s, const char *file, unsigned int line);
 char *__ec_strndup(const char *s, size_t n, const char *file,
        unsigned int line);
 
+/* XXX rename into ec_malloc, and change macro to uppercase */
+void *ec_malloc2(size_t size);
+void ec_free2(void *ptr);
+
 /* we use macros here to ensure the file/line stay correct when
  * debugging the standard malloc with valgrind */
 
index 17a10c8..da15502 100644 (file)
@@ -150,3 +150,5 @@ void ec_strvec_dump(const struct ec_strvec *strvec, FILE *out)
        for (i = 0; i < ec_strvec_len(strvec); i++)
                fprintf(out, "  %zd: %s\n", i, strvec->vec[i]);
 }
+
+/* XXX test case */
index 1408f5a..4a7de26 100644 (file)
@@ -150,10 +150,10 @@ int ec_test_check_tk_complete(const struct ec_tk *tk, ...)
                }
        }
 
-       if (count != ec_completed_tk_count_match(c)) {
+       if (count != ec_completed_tk_count(c, EC_MATCH)) {
                ec_log(EC_LOG_ERR,
                        "nb_completion (%d) does not match (%d)\n",
-                       count, ec_completed_tk_count_match(c));
+                       count, ec_completed_tk_count(c, EC_MATCH));
                ec_completed_tk_dump(stdout, c);
                ret = -1;
        }
index c1246c4..40ddefa 100644 (file)
@@ -70,14 +70,20 @@ int ec_test_all(void);
 /* 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_ERR(fmt, ...)                                          \
+       ec_log(EC_LOG_ERR, "%s:%d: error: " fmt "\n",                   \
+               __FILE__, __LINE__, ##__VA_ARGS__);                     \
+
+#define EC_TEST_ASSERT(cond, args...)                                  \
+       do {                                                            \
+               if (!(cond))                                            \
+                       EC_TEST_ERR("assertion failure: " #cond);       \
+       } while (0)
 
 #define EC_TEST_CHECK_TK_PARSE(tk, input, expected...) ({              \
        int ret_ = ec_test_check_tk_parse(tk, input, expected);         \
        if (ret_)                                                       \
-               TEST_ERR();                                             \
+               EC_TEST_ERR("parse test failed");                       \
        ret_;                                                           \
 })
 
@@ -86,7 +92,7 @@ int ec_test_check_tk_complete(const struct ec_tk *tk, ...);
 #define EC_TEST_CHECK_TK_COMPLETE(tk, args...) ({                      \
        int ret_ = ec_test_check_tk_complete(tk, args);                 \
        if (ret_)                                                       \
-               TEST_ERR();                                             \
+               EC_TEST_ERR("complete test failed");                    \
        ret_;                                                           \
 })
 
index e21df55..fec8492 100644 (file)
 
 #include <ecoli_malloc.h>
 #include <ecoli_strvec.h>
+#include <ecoli_keyval.h>
 #include <ecoli_tk.h>
 
 struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
        size_t size)
 {
        struct ec_tk *tk = NULL;
+       char buf[256]; // XXX
 
        assert(size >= sizeof(*tk));
 
@@ -46,17 +48,30 @@ struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
        if (tk == NULL)
                goto fail;
 
+       TAILQ_INIT(&tk->children);
+       tk->ops = ops;
+
        if (id != NULL) {
                tk->id = ec_strdup(id);
                if (tk->id == NULL)
                        goto fail;
        }
 
-       tk->ops = ops;
+       snprintf(buf, sizeof(buf), "<%s>", ops->typename);
+       tk->desc = ec_strdup(buf); // XXX ec_asprintf ?
+       if (tk->desc == NULL)
+               goto fail;
+
+       tk->attrs = ec_keyval_new();
+       if (tk->attrs == NULL)
+               goto fail;
 
        return tk;
 
  fail:
+       ec_free(tk->attrs);
+       ec_free(tk->desc);
+       ec_free(tk->id);
        ec_tk_free(tk);
        return NULL;
 }
@@ -69,9 +84,43 @@ void ec_tk_free(struct ec_tk *tk)
        if (tk->ops != NULL && tk->ops->free_priv != NULL)
                tk->ops->free_priv(tk);
        ec_free(tk->id);
+       ec_free(tk->desc);
+       ec_free(tk->attrs);
        ec_free(tk);
 }
 
+struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id)
+{
+       struct ec_tk *child, *ret;
+       const char *tk_id = ec_tk_id(tk);
+
+       if (id != NULL && tk_id != NULL && !strcmp(tk_id, id))
+               return tk;
+
+       TAILQ_FOREACH(child, &tk->children, next) {
+               ret = ec_tk_find(child, id);
+               if (ret != NULL)
+                       return ret;
+       }
+
+       return NULL;
+}
+
+struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk)
+{
+       return tk->attrs;
+}
+
+const char *ec_tk_id(const struct ec_tk *tk)
+{
+       return tk->id;
+}
+
+struct ec_tk *ec_tk_parent(const struct ec_tk *tk)
+{
+       return tk->parent;
+}
+
 struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
 {
        struct ec_strvec *strvec = NULL;
@@ -178,6 +227,7 @@ static void __ec_parsed_tk_dump(FILE *out,
                typename = parsed_tk->tk->ops->typename;
        }
 
+       /* XXX we only display the first token */
        fprintf(out, "tk_type=%s, id=%s, s=<%s>\n", typename, id, s);
 
        TAILQ_FOREACH(child, &parsed_tk->children, next)
@@ -450,13 +500,21 @@ const char *ec_completed_tk_smallest_start(
        return completed_tk->smallest_start;
 }
 
-unsigned int ec_completed_tk_count_match(
-       const struct ec_completed_tk *completed_tk)
+unsigned int ec_completed_tk_count(
+       const struct ec_completed_tk *completed_tk,
+       enum ec_completed_tk_filter_flags flags)
 {
+       unsigned int count = 0;
+
        if (completed_tk == NULL)
-               return 0;
+               return count;
+
+       if (flags & EC_MATCH)
+               count += completed_tk->count_match;
+       if (flags & EC_NO_MATCH)
+               count += (completed_tk->count - completed_tk->count_match); //XXX
 
-       return completed_tk->count_match;
+       return count;
 }
 
 struct ec_completed_tk_iter *
@@ -493,11 +551,11 @@ const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
                        break;
 
                if (iter->cur->add == NULL &&
-                               (iter->flags & ITER_NO_MATCH))
+                               (iter->flags & EC_NO_MATCH))
                        break;
 
                if (iter->cur->add != NULL &&
-                               (iter->flags & ITER_MATCH))
+                               (iter->flags & EC_MATCH))
                        break;
 
        } while (iter->cur != NULL);
@@ -509,3 +567,11 @@ void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter)
 {
        ec_free(iter);
 }
+
+const char *ec_tk_desc(const struct ec_tk *tk)
+{
+       if (tk->ops->desc != NULL)
+               return tk->ops->desc(tk);
+
+       return tk->desc;
+}
index e243c08..5d1bafb 100644 (file)
 struct ec_tk;
 struct ec_parsed_tk;
 struct ec_strvec;
+struct ec_keyval;
 
 typedef struct ec_parsed_tk *(*ec_tk_parse_t)(const struct ec_tk *tk,
        const struct ec_strvec *strvec);
 typedef struct ec_completed_tk *(*ec_tk_complete_t)(const struct ec_tk *tk,
        const struct ec_strvec *strvec);
+typedef const char * (*ec_tk_desc_t)(const struct ec_tk *);
 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_desc_t desc;
        ec_tk_free_priv_t free_priv;
 };
 
+TAILQ_HEAD(ec_tk_list, ec_tk);
+
 struct ec_tk {
        const struct ec_tk_ops *ops;
        char *id;
+       char *desc;
+       struct ec_keyval *attrs;
+       /* XXX ensure parent and child are properly set in all nodes */
+       struct ec_tk *parent;
+
+       TAILQ_ENTRY(ec_tk) next;
+       struct ec_tk_list children;
 };
 
 struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
        size_t priv_size);
 void ec_tk_free(struct ec_tk *tk);
 
+/* XXX add more accessors */
+struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk);
+struct ec_tk *ec_tk_parent(const struct ec_tk *tk);
+const char *ec_tk_id(const struct ec_tk *tk);
+
+struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id);
+
+/* XXX split this file ? */
 
 TAILQ_HEAD(ec_parsed_tk_list, ec_parsed_tk);
 
@@ -84,12 +104,12 @@ void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_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);
+struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, 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,
+struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *tk,
        const struct ec_strvec *strvec);
 
 void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
@@ -125,9 +145,9 @@ struct ec_completed_tk {
  * return a completed_tk object filled with elts
  * return NULL on error (nomem?)
  */
-struct ec_completed_tk *ec_tk_complete(const struct ec_tk *token,
+struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
        const char *str);
-struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *token,
+struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
        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,
@@ -147,14 +167,15 @@ struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
 const char *ec_completed_tk_smallest_start(
        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,
+       EC_MATCH = 1,
+       EC_NO_MATCH = 2,
 };
 
+unsigned int ec_completed_tk_count(
+       const struct ec_completed_tk *completed_tk,
+       enum ec_completed_tk_filter_flags flags);
+
 struct ec_completed_tk_iter {
        enum ec_completed_tk_filter_flags flags;
        const struct ec_completed_tk *completed_tk;
@@ -169,4 +190,8 @@ const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
        struct ec_completed_tk_iter *iter);
 
 void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter);
+
+
+const char *ec_tk_desc(const struct ec_tk *tk);
+
 #endif
index f4cca45..57971ee 100644 (file)
@@ -157,22 +157,12 @@ static int ec_tk_int_testcase(void)
 
        p = ec_tk_parse(tk, "0");
        s = ec_parsed_tk_to_string(p);
-       if (s == NULL) {
-               TEST_ERR();
-       } else {
-               if (ec_tk_int_getval(tk, s) != 0)
-                       TEST_ERR();
-       }
+       EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 0);
        ec_parsed_tk_free(p);
 
        p = ec_tk_parse(tk, "10");
        s = ec_parsed_tk_to_string(p);
-       if (s == NULL) {
-               TEST_ERR();
-       } else {
-               if (ec_tk_int_getval(tk, s) != 10)
-                       TEST_ERR();
-       }
+       EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 10);
        ec_parsed_tk_free(p);
        ec_tk_free(tk);
 
@@ -189,12 +179,7 @@ static int ec_tk_int_testcase(void)
 
        p = ec_tk_parse(tk, "10");
        s = ec_parsed_tk_to_string(p);
-       if (s == NULL) {
-               TEST_ERR();
-       } else {
-               if (ec_tk_int_getval(tk, s) != 16)
-                       TEST_ERR();
-       }
+       EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 16);
        ec_parsed_tk_free(p);
        ec_tk_free(tk);
 
index 2438679..2889147 100644 (file)
@@ -191,6 +191,9 @@ int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child)
        table[tk->len] = child;
        tk->len ++;
 
+       child->parent = gen_tk;
+       TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+
        return 0;
 }
 
index d7bf998..e606a09 100644 (file)
@@ -221,6 +221,9 @@ int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child)
        table[tk->len] = child;
        tk->len ++;
 
+       child->parent = gen_tk;
+       TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+
        return 0;
 }
 
index 4fe3062..6c650d3 100644 (file)
@@ -114,6 +114,13 @@ static struct ec_completed_tk *ec_tk_str_complete(const struct ec_tk *gen_tk,
        return completed_tk;
 }
 
+static const char *ec_tk_str_desc(const struct ec_tk *gen_tk)
+{
+       struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
+
+       return tk->string;
+}
+
 static void ec_tk_str_free_priv(struct ec_tk *gen_tk)
 {
        struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
@@ -125,6 +132,7 @@ static const struct ec_tk_ops ec_tk_str_ops = {
        .typename = "str",
        .parse = ec_tk_str_parse,
        .complete = ec_tk_str_complete,
+       .desc = ec_tk_str_desc,
        .free_priv = ec_tk_str_free_priv,
 };
 
index 379c36e..8c5ffc5 100644 (file)
@@ -33,6 +33,9 @@
 #include <readline/readline.h>
 #include <readline/history.h>
 
+#include <ecoli_tk.h>
+#include <ecoli_keyval.h>
+
 #include <ecoli_tk_str.h>
 #include <ecoli_tk_seq.h>
 #include <ecoli_tk_space.h>
@@ -66,7 +69,7 @@ static char *my_completion_entry(const char *s, int state)
                        return NULL;
 
                ec_completed_tk_iter_free(iter);
-               iter = ec_completed_tk_iter_new(c, ITER_MATCH);
+               iter = ec_completed_tk_iter_new(c, EC_MATCH);
                if (iter == NULL)
                        return NULL;
        }
@@ -92,16 +95,37 @@ static char **my_attempted_completion(const char *text, int start, int end)
        return rl_completion_matches(text, my_completion_entry);
 }
 
+/* this function builds the help string */
+static char *get_tk_help(const struct ec_tk *tk)
+{
+       const struct ec_tk *tk2;
+       char *help = NULL;
+       char *tk_help = NULL;
+
+       for (tk2 = tk; tk2 != NULL && tk_help == NULL; tk2 = ec_tk_parent(tk2))
+               tk_help = ec_keyval_get(ec_tk_attrs(tk2), "help");
+
+       if (tk_help == NULL)
+               tk_help = "";
+
+       if (asprintf(&help, "%-20s %s", ec_tk_desc(tk), tk_help) < 0)
+               return NULL;
+
+       return help;
+}
 
 static int show_help(int ignore, int invoking_key)
 {
+       const struct ec_completed_tk_elt *elt;
+       struct ec_completed_tk_iter *iter;
        struct ec_completed_tk *c;
        char *line;
+       unsigned int count, i;
+       char **helps = NULL;
 
        (void)ignore;
        (void)invoking_key;
 
-       printf("\nhelp:\n");
        line = strdup(rl_line_buffer);
        if (line == NULL)
                return 1;
@@ -111,40 +135,100 @@ static int show_help(int ignore, int invoking_key)
        free(line);
        if (c == NULL)
                return 1;
-       ec_completed_tk_dump(stdout, c);
+       //ec_completed_tk_dump(stdout, c);
+
+       count = ec_completed_tk_count(c, EC_MATCH | EC_NO_MATCH);
+       helps = calloc(count + 1, sizeof(char *));
+       if (helps == NULL)
+               return 1;
+
+       iter = ec_completed_tk_iter_new(c, EC_MATCH | EC_NO_MATCH);
+       if (iter == NULL)
+               goto fail;
+
+       /* strangely, rl_display_match_list() expects first index at 1 */
+       for (i = 1, elt = ec_completed_tk_iter_next(iter);
+            i <= count && elt != NULL;
+            i++, elt = ec_completed_tk_iter_next(iter)) {
+               helps[i] = get_tk_help(elt->tk);
+       }
+
        ec_completed_tk_free(c);
 
+       rl_display_match_list(helps, count, 1000);
+
        rl_forced_update_display();
 
        return 0;
+
+fail:
+       free(helps);
+       // free helps[n] XXX
+       return 1;
 }
 
-int main(void)
+static int create_commands(void)
 {
-       struct ec_parsed_tk *p;
-//     const char *name;
-       char *line;
+       struct ec_tk *cmdlist = NULL, *cmd = NULL;
 
-       rl_bind_key('?', show_help);
+       cmdlist = ec_tk_or_new(NULL);
+       if (cmdlist == NULL)
+               goto fail;
 
-       commands = ec_tk_shlex_new(NULL,
-               ec_tk_seq_new_list(NULL,
-                       ec_tk_str_new(NULL, "hello"),
+       cmd = ec_tk_seq_new_list(NULL,
+               ec_tk_str_new(NULL, "hello"),
+               ec_tk_or_new_list(NULL,
                        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_int_new("int", 0, 10, 10),
                        EC_TK_ENDLIST
-               )
+               ),
+               EC_TK_ENDLIST
+       );
+       ec_keyval_set(ec_tk_attrs(cmd), "help", "say hello to someone", NULL);
+       ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "name")),
+               "help", "the name of the person", NULL);
+       ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "int")),
+               "help", "an integer", NULL);
+       if (ec_tk_or_add(cmdlist, cmd) < 0)
+               goto fail;
+
+       cmd = ec_tk_seq_new_list(NULL,
+               ec_tk_str_new(NULL, "bye"),
+               EC_TK_ENDLIST
        );
-       if (commands == NULL) {
-               printf("cannot create token\n");
+       ec_keyval_set(ec_tk_attrs(cmd), "help", "say bye to someone", NULL);
+       if (ec_tk_or_add(cmdlist, cmd) < 0)
+               goto fail;
+
+       commands = ec_tk_shlex_new(NULL, cmdlist);
+       if (commands == NULL)
+               goto fail;
+
+       return 0;
+
+ fail:
+       fprintf(stderr, "cannot initialize tokens\n");
+       ec_tk_free(cmd);
+       ec_tk_free(cmdlist);
+       return -1;
+}
+
+int main(void)
+{
+       struct ec_parsed_tk *p;
+//     const char *name;
+       char *line;
+
+
+       if (create_commands() < 0)
                return 1;
-       }
 
+       rl_bind_key('?', show_help);
        rl_attempted_completion_function = my_attempted_completion;
 
        while (1) {
@@ -161,4 +245,5 @@ int main(void)
 
        ec_tk_free(commands);
        return 0;
+
 }