From: Olivier Matz Date: Thu, 1 Dec 2016 19:15:09 +0000 (+0100) Subject: continue ! X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=538ee5ebd89490777cf1022e89438bde7d3adcdb;p=protos%2Flibecoli.git continue ! --- diff --git a/lib/Makefile b/lib/Makefile index 28fce46..9bbd73b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/build/test b/lib/build/test index 3e62c75..e389ed2 100755 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 index 0000000..8275715 --- /dev/null +++ b/lib/ecoli_keyval.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +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 index 0000000..f0076cf --- /dev/null +++ b/lib/ecoli_keyval.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ECOLI_KEYVAL_ +#define ECOLI_KEYVAL_ + +#include + +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 diff --git a/lib/ecoli_malloc.c b/lib/ecoli_malloc.c index 0871247..0768cab 100644 --- a/lib/ecoli_malloc.c +++ b/lib/ecoli_malloc.c @@ -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) { diff --git a/lib/ecoli_malloc.h b/lib/ecoli_malloc.h index f5bdc39..76b63e0 100644 --- a/lib/ecoli_malloc.h +++ b/lib/ecoli_malloc.h @@ -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 */ diff --git a/lib/ecoli_strvec.c b/lib/ecoli_strvec.c index 17a10c8..da15502 100644 --- a/lib/ecoli_strvec.c +++ b/lib/ecoli_strvec.c @@ -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 */ diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index 1408f5a..4a7de26 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -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; } diff --git a/lib/ecoli_test.h b/lib/ecoli_test.h index c1246c4..40ddefa 100644 --- a/lib/ecoli_test.h +++ b/lib/ecoli_test.h @@ -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_; \ }) diff --git a/lib/ecoli_tk.c b/lib/ecoli_tk.c index e21df55..fec8492 100644 --- a/lib/ecoli_tk.c +++ b/lib/ecoli_tk.c @@ -33,12 +33,14 @@ #include #include +#include #include 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; +} diff --git a/lib/ecoli_tk.h b/lib/ecoli_tk.h index e243c08..5d1bafb 100644 --- a/lib/ecoli_tk.h +++ b/lib/ecoli_tk.h @@ -37,29 +37,49 @@ 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 diff --git a/lib/ecoli_tk_int.c b/lib/ecoli_tk_int.c index f4cca45..57971ee 100644 --- a/lib/ecoli_tk_int.c +++ b/lib/ecoli_tk_int.c @@ -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); diff --git a/lib/ecoli_tk_or.c b/lib/ecoli_tk_or.c index 2438679..2889147 100644 --- a/lib/ecoli_tk_or.c +++ b/lib/ecoli_tk_or.c @@ -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; } diff --git a/lib/ecoli_tk_seq.c b/lib/ecoli_tk_seq.c index d7bf998..e606a09 100644 --- a/lib/ecoli_tk_seq.c +++ b/lib/ecoli_tk_seq.c @@ -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; } diff --git a/lib/ecoli_tk_str.c b/lib/ecoli_tk_str.c index 4fe3062..6c650d3 100644 --- a/lib/ecoli_tk_str.c +++ b/lib/ecoli_tk_str.c @@ -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, }; diff --git a/lib/main-readline.c b/lib/main-readline.c index 379c36e..8c5ffc5 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -33,6 +33,9 @@ #include #include +#include +#include + #include #include #include @@ -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; + }