From: Olivier Matz Date: Wed, 2 Nov 2016 08:04:48 +0000 (+0100) Subject: completion X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=0ae8acfbbe4820ccf222c49d8c49aa45202f4f18;p=protos%2Flibecoli.git completion --- diff --git a/lib/build/test b/lib/build/test index 2913edb..8ef7e3e 100755 Binary files a/lib/build/test and b/lib/build/test differ diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index 3f1c4fd..f6521da 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -71,12 +71,12 @@ int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input, int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input, const char *expected) { - struct ec_completed_tk *p; + struct ec_completed_tk *c; const char *s; int ret = -1; - p = ec_tk_complete(tk, input); - s = ec_completed_tk_smallest_start(p); + c = ec_tk_complete(tk, input); + s = ec_completed_tk_smallest_start(c); if (s == NULL && expected == NULL) ret = 0; else if (s != NULL && expected != NULL && @@ -91,11 +91,61 @@ int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input, "tk should complete with <%s> but completes with <%s>\n", expected, s); - ec_completed_tk_free(p); + ec_completed_tk_free(c); return ret; } +int ec_test_check_tk_complete_list(const struct ec_tk *tk, + const char *input, ...) +{ + struct ec_completed_tk *c = NULL; + struct ec_completed_tk_elt *elt; + const char *s; + int ret = -1; + unsigned int count = 0; + va_list ap; + + va_start(ap, input); + + c = ec_tk_complete(tk, input); + if (c == NULL) + goto out; + + for (s = va_arg(ap, const char *); + s != EC_TK_ENDLIST; + s = va_arg(ap, const char *)) { + if (s == NULL) + goto out; + + count++; + TAILQ_FOREACH(elt, &c->elts, next) { + if (strcmp(elt->add, s) == 0) + break; + } + + if (elt == NULL) { + ec_log(EC_LOG_ERR, + "completion <%s> not in list\n", s); + goto out; + } + } + + if (count != ec_completed_tk_count(c)) { + ec_log(EC_LOG_ERR, + "nb_completion (%d) does not match (%d)\n", + count, ec_completed_tk_count(c)); + goto out; + } + + ret = 0; + +out: + ec_completed_tk_free(c); + va_end(ap); + return ret; +} + 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); @@ -108,10 +158,15 @@ struct debug_alloc_hdr { unsigned int cookie; }; +struct debug_alloc_ftr { + unsigned int cookie; +} __attribute__((packed)); + static void *debug_malloc(size_t size, const char *file, unsigned int line) { struct debug_alloc_hdr *hdr; - size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int); + struct debug_alloc_ftr *ftr; + size_t new_size = size + sizeof(*hdr) + sizeof(*ftr); void *ret; hdr = malloc(new_size); @@ -124,6 +179,9 @@ static void *debug_malloc(size_t size, const char *file, unsigned int line) 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; } ec_log(EC_LOG_INFO, "%s:%d: info: malloc(%zd) -> %p\n", @@ -135,6 +193,7 @@ static void *debug_malloc(size_t size, const char *file, unsigned int line) static void debug_free(void *ptr, const char *file, unsigned int line) { struct debug_alloc_hdr *hdr, *h; + struct debug_alloc_ftr *ftr; (void)file; (void)line; @@ -151,6 +210,13 @@ static void debug_free(void *ptr, const char *file, unsigned int line) abort(); } + ftr = (ptr + hdr->size); + if (ftr->cookie != 0x12345678) { + ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n", + file, line, ptr); + abort(); + } + TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) { if (h == hdr) break; @@ -168,14 +234,24 @@ static void debug_free(void *ptr, const char *file, unsigned int line) void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line) { - struct debug_alloc_hdr *hdr = (ptr - sizeof(*hdr)); - struct debug_alloc_hdr *h; + struct debug_alloc_hdr *hdr, *h; + struct debug_alloc_ftr *ftr; size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int); void *ret; if (ptr != NULL) { + hdr = (ptr - sizeof(*hdr)); if (hdr->cookie != 0x12345678) { - ec_log(EC_LOG_ERR, "%s:%d: error: realloc(%p): bad start cookie\n", + ec_log(EC_LOG_ERR, + "%s:%d: error: realloc(%p): bad start cookie\n", + file, line, ptr); + abort(); + } + + ftr = (ptr + hdr->size); + if (ftr->cookie != 0x12345678) { + ec_log(EC_LOG_ERR, + "%s:%d: error: realloc(%p): bad end cookie\n", file, line, ptr); abort(); } @@ -204,7 +280,7 @@ void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line) if (hdr == NULL) ret = NULL; else - ret= hdr + 1; + ret = hdr + 1; } if (hdr != NULL) { @@ -213,6 +289,9 @@ void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line) hdr->size = size; 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; } ec_log(EC_LOG_INFO, "%s:%d: info: realloc(%p, %zd) -> %p\n", @@ -243,7 +322,7 @@ int ec_test_all(void) TAILQ_INIT(&debug_alloc_hdr_list); /* register a new malloc to trac memleaks */ - if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) { + if (0 && ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) { ec_log(EC_LOG_ERR, "cannot register new malloc\n"); return -1; } diff --git a/lib/ecoli_test.h b/lib/ecoli_test.h index 61d9950..45d1d4e 100644 --- a/lib/ecoli_test.h +++ b/lib/ecoli_test.h @@ -30,6 +30,7 @@ #include +#include #include #define EC_REGISTER_TEST(t) \ @@ -66,13 +67,12 @@ void ec_test_register(struct ec_test *test); int ec_test_all(void); -// XXX could be a macro that display file:line int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input, const char *expected); #define TEST_ERR() \ - printf("%s:%d: error: test failed\n", \ - __FILE__, __LINE__); \ + 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); \ @@ -91,4 +91,14 @@ int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input, 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; \ +}) + #endif diff --git a/lib/ecoli_tk.c b/lib/ecoli_tk.c index e814b68..cdb064e 100644 --- a/lib/ecoli_tk.c +++ b/lib/ecoli_tk.c @@ -70,11 +70,15 @@ void ec_tk_free(struct ec_tk *tk) ec_free(tk); } -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) { struct ec_parsed_tk *parsed_tk; - parsed_tk = token->ops->parse(token, str); + /* by default, it does not match anything */ + if (tk->ops->parse == NULL) + return NULL; + + parsed_tk = tk->ops->parse(tk, str); return parsed_tk; } @@ -174,16 +178,6 @@ const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk) return parsed_tk->str; } -struct ec_completed_tk *ec_tk_complete(const struct ec_tk *token, - const char *str) -{ - struct ec_completed_tk *completed_tk; - - completed_tk = token->ops->complete(token, str); - - return completed_tk; -} - struct ec_completed_tk *ec_completed_tk_new(void) { struct ec_completed_tk *completed_tk = NULL; @@ -208,16 +202,41 @@ struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk, return NULL; elt->tk = tk; - elt->add = ec_strdup(add); - elt->full = ec_strdup(full); - if (elt->add == NULL || elt->full == NULL) { - ec_completed_tk_elt_free(elt); - return NULL; + if (add != NULL) { + elt->add = ec_strdup(add); + if (elt->add == NULL) { + ec_completed_tk_elt_free(elt); + return NULL; + } + } + if (full != NULL) { + elt->full = ec_strdup(full); + if (elt->full == NULL) { + ec_completed_tk_elt_free(elt); + return NULL; + } } return elt; } +/* XXX define when to use ec_tk_complete() or tk->complete() + * (same for parse) + * suggestion: tk->op() is internal, user calls the function + */ +struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk, + const char *str) +{ + struct ec_completed_tk *completed_tk; + + if (tk->ops->complete == NULL) + return ec_completed_tk_new(); + + completed_tk = tk->ops->complete(tk, str); + + return completed_tk; +} + /* count the number of identical chars at the beginning of 2 strings */ static size_t strcmp_count(const char *s1, const char *s2) { @@ -305,8 +324,10 @@ void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk) fprintf(out, "completion: count=%u smallest_start=<%s>\n", completed_tk->count, completed_tk->smallest_start); - TAILQ_FOREACH(elt, &completed_tk->elts, next) - fprintf(out, "add=<%s> full=<%s>\n", elt->add, elt->full); + TAILQ_FOREACH(elt, &completed_tk->elts, next) { + fprintf(out, "add=<%s>, full=<%s>, tk=%p\n", + elt->add, elt->full, elt->tk); + } } const char *ec_completed_tk_smallest_start( @@ -320,14 +341,8 @@ const char *ec_completed_tk_smallest_start( unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk) { - struct ec_completed_tk_elt *elt; - unsigned int count = 0; - if (completed_tk == NULL) return 0; - TAILQ_FOREACH(elt, &completed_tk->elts, next) - count++; - - return count; + return completed_tk->count; } diff --git a/lib/ecoli_tk.h b/lib/ecoli_tk.h index 490c5e1..cc0a208 100644 --- a/lib/ecoli_tk.h +++ b/lib/ecoli_tk.h @@ -69,6 +69,10 @@ struct ec_parsed_tk { char *str; }; +/* 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); struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *token, const char *str); void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk, @@ -98,17 +102,16 @@ struct ec_completed_tk { }; /* - * return NULL if it does not match the beginning of the token - * return "" if it matches but does not know how to complete - * return "xyz" if it knows how to complete + * 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, const char *str); 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); -void ec_completed_tk_add_elt( - struct ec_completed_tk *completed_tk, struct ec_completed_tk_elt *elt); +void ec_completed_tk_add_elt(struct ec_completed_tk *completed_tk, + struct ec_completed_tk_elt *elt); void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt); struct ec_completed_tk *ec_completed_tk_merge( struct ec_completed_tk *completed_tk1, @@ -117,6 +120,7 @@ void ec_completed_tk_free(struct ec_completed_tk *completed_tk); void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk); +/* can return NULL */ const char *ec_completed_tk_smallest_start( const struct ec_completed_tk *completed_tk); diff --git a/lib/ecoli_tk_empty.c b/lib/ecoli_tk_empty.c index 61d29b5..e3e24f9 100644 --- a/lib/ecoli_tk_empty.c +++ b/lib/ecoli_tk_empty.c @@ -29,10 +29,13 @@ #include #include +#include +#include +#include #include #include -static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, +static struct ec_parsed_tk *ec_tk_empty_parse(const struct ec_tk *gen_tk, const char *str) { struct ec_parsed_tk *parsed_tk; @@ -42,17 +45,52 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, return NULL; (void)str; - parsed_tk->str = strdup(""); + parsed_tk->str = ec_strdup(""); return parsed_tk; } -static struct ec_tk_ops empty_ops = { - .parse = parse, +static struct ec_tk_ops ec_tk_empty_ops = { + .parse = ec_tk_empty_parse, }; struct ec_tk *ec_tk_empty_new(const char *id) { - return ec_tk_new(id, &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) +{ + struct ec_tk *tk; + int ret = 0; + + /* all inputs match */ + tk = ec_tk_empty_new(NULL); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + 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, "", ""); + ec_tk_free(tk); + + /* never completes */ + tk = ec_tk_empty_new(NULL); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + return -1; + } + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", NULL); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", NULL); + ec_tk_free(tk); + + return ret; +} + +static struct ec_test ec_tk_empty_test = { + .name = "tk_empty", + .test = ec_tk_empty_testcase, +}; + +EC_REGISTER_TEST(ec_tk_empty_test); diff --git a/lib/ecoli_tk_int.c b/lib/ecoli_tk_int.c index 6296b8b..8aeb6bc 100644 --- a/lib/ecoli_tk_int.c +++ b/lib/ecoli_tk_int.c @@ -61,7 +61,7 @@ static size_t parse_llint(struct ec_tk_int *tk, const char *str, return endptr - str; } -static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, +static struct ec_parsed_tk *ec_tk_int_parse(const struct ec_tk *gen_tk, const char *str) { struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk; @@ -82,8 +82,8 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, return parsed_tk; } -static struct ec_tk_ops int_ops = { - .parse = parse, +static struct ec_tk_ops ec_tk_int_ops = { + .parse = ec_tk_int_parse, }; struct ec_tk *ec_tk_int_new(const char *id, long long int min, @@ -91,7 +91,7 @@ struct ec_tk *ec_tk_int_new(const char *id, long long int min, { struct ec_tk_int *tk = NULL; - tk = (struct ec_tk_int *)ec_tk_new(id, &int_ops, sizeof(*tk)); + tk = (struct ec_tk_int *)ec_tk_new(id, &ec_tk_int_ops, sizeof(*tk)); if (tk == NULL) return NULL; @@ -115,7 +115,7 @@ long long ec_tk_int_getval(struct ec_tk *gen_tk, const char *str) return val; } -static int testcase(void) +static int ec_tk_int_testcase(void) { struct ec_parsed_tk *p; struct ec_tk *tk; @@ -192,24 +192,23 @@ static int testcase(void) ret |= EC_TEST_CHECK_TK_PARSE(tk, "1", NULL); ec_tk_free(tk); - /* /\* test completion *\/ */ - /* tk = ec_tk_int_new(NULL, "foo"); */ - /* if (tk == NULL) { */ - /* ec_log(EC_LOG_ERR, "cannot create tk\n"); */ - /* return -1; */ - /* } */ - /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo"); */ - /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo"); */ - /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", ""); */ - /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL); */ - /* ec_tk_free(tk); */ + /* test completion */ + tk = ec_tk_int_new(NULL, 0, 10, 0); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + return -1; + } + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", NULL); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "1", NULL); + ec_tk_free(tk); return ret; } -static struct ec_test test = { +static struct ec_test ec_tk_int_test = { .name = "tk_int", - .test = testcase, + .test = ec_tk_int_testcase, }; -EC_REGISTER_TEST(test); +EC_REGISTER_TEST(ec_tk_int_test); diff --git a/lib/ecoli_tk_or.c b/lib/ecoli_tk_or.c index b498dcb..4c782dd 100644 --- a/lib/ecoli_tk_or.c +++ b/lib/ecoli_tk_or.c @@ -38,7 +38,7 @@ #include #include -static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, +static struct ec_parsed_tk *ec_tk_or_parse(const struct ec_tk *gen_tk, const char *str) { struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; @@ -70,7 +70,7 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, return NULL; } -static struct ec_completed_tk *complete(const struct ec_tk *gen_tk, +static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk, const char *str) { struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; @@ -90,7 +90,7 @@ static struct ec_completed_tk *complete(const struct ec_tk *gen_tk, return completed_tk; } -static void free_priv(struct ec_tk *gen_tk) +static void ec_tk_or_free_priv(struct ec_tk *gen_tk) { struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; unsigned int i; @@ -100,17 +100,17 @@ static void free_priv(struct ec_tk *gen_tk) ec_free(tk->table); } -static struct ec_tk_ops or_ops = { - .parse = parse, - .complete = complete, - .free_priv = free_priv, +static struct ec_tk_ops ec_tk_or_ops = { + .parse = ec_tk_or_parse, + .complete = ec_tk_or_complete, + .free_priv = ec_tk_or_free_priv, }; struct ec_tk *ec_tk_or_new(const char *id) { struct ec_tk_or *tk = NULL; - tk = (struct ec_tk_or *)ec_tk_new(id, &or_ops, sizeof(*tk)); + tk = (struct ec_tk_or *)ec_tk_new(id, &ec_tk_or_ops, sizeof(*tk)); if (tk == NULL) return NULL; @@ -169,7 +169,7 @@ int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child) return 0; } -static int testcase(void) +static int ec_tk_or_testcase(void) { struct ec_tk *tk; int ret = 0; @@ -207,14 +207,22 @@ static int testcase(void) ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "t", ""); ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "to", "to"); ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL); + ret |= EC_TEST_CHECK_TK_COMPLETE_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); ec_tk_free(tk); return ret; } -static struct ec_test test = { +static struct ec_test ec_tk_or_test = { .name = "tk_or", - .test = testcase, + .test = ec_tk_or_testcase, }; -EC_REGISTER_TEST(test); +EC_REGISTER_TEST(ec_tk_or_test); diff --git a/lib/ecoli_tk_seq.c b/lib/ecoli_tk_seq.c index 4c182c4..fb1b434 100644 --- a/lib/ecoli_tk_seq.c +++ b/lib/ecoli_tk_seq.c @@ -32,12 +32,15 @@ #include #include +#include +#include #include +#include #include // XXX to handle the quote, it will be done in tk_shseq // it will unquote the string and parse each token separately -static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, +static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk, const char *str) { struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; @@ -67,7 +70,34 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, return NULL; } -static void free_priv(struct ec_tk *gen_tk) +static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk, + const char *str) +{ + struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; + struct ec_completed_tk *completed_tk; + struct ec_parsed_tk *parsed_tk; + size_t len = 0; + unsigned int i; + + if (tk->len == 0) + return ec_completed_tk_new(); + + /* parse the first tokens */ + for (i = 0; i < tk->len - 1; i++) { + parsed_tk = ec_tk_parse(tk->table[i], str + len); + if (parsed_tk == NULL) + break; + + len += strlen(parsed_tk->str); + ec_parsed_tk_free(parsed_tk); + } + + completed_tk = ec_tk_complete(tk->table[i], str + len); + + return completed_tk; +} + +static void ec_tk_seq_free_priv(struct ec_tk *gen_tk) { struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; unsigned int i; @@ -77,16 +107,17 @@ static void free_priv(struct ec_tk *gen_tk) ec_free(tk->table); } -static struct ec_tk_ops seq_ops = { - .parse = parse, - .free_priv = free_priv, +static struct ec_tk_ops ec_tk_seq_ops = { + .parse = ec_tk_seq_parse, + .complete = ec_tk_seq_complete, + .free_priv = ec_tk_seq_free_priv, }; struct ec_tk *ec_tk_seq_new(const char *id) { struct ec_tk_seq *tk = NULL; - tk = (struct ec_tk_seq *)ec_tk_new(id, &seq_ops, sizeof(*tk)); + tk = (struct ec_tk_seq *)ec_tk_new(id, &ec_tk_seq_ops, sizeof(*tk)); if (tk == NULL) return NULL; @@ -131,10 +162,12 @@ int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child) struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; struct ec_tk **table; + // XXX check tk type + assert(tk != NULL); assert(child != NULL); - table = ec_realloc(tk->table, tk->len + 1); + table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table)); if (table == NULL) return -1; @@ -145,3 +178,51 @@ int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child) return 0; } +static int ec_tk_seq_testcase(void) +{ + struct ec_tk *tk; + int ret = 0; + + /* all inputs starting with foo should match */ + tk = ec_tk_seq_new_list(NULL, + ec_tk_str_new(NULL, "foo"), + ec_tk_str_new(NULL, "bar"), + EC_TK_ENDLIST); + if (tk == NULL) { + 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); + ec_tk_free(tk); + + /* test completion */ + tk = ec_tk_seq_new_list(NULL, + ec_tk_str_new(NULL, "foo"), + 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_COMPLETE(tk, "", "foo"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "bar"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foob", "ar"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobar", ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobarx", NULL); + ec_tk_free(tk); + + return ret; +} + +static struct ec_test ec_tk_seq_test = { + .name = "tk_seq", + .test = ec_tk_seq_testcase, +}; + +EC_REGISTER_TEST(ec_tk_seq_test); diff --git a/lib/ecoli_tk_space.c b/lib/ecoli_tk_space.c index adc57c4..20facf8 100644 --- a/lib/ecoli_tk_space.c +++ b/lib/ecoli_tk_space.c @@ -34,7 +34,7 @@ #include #include -static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, +static struct ec_parsed_tk *ec_tk_space_parse(const struct ec_tk *gen_tk, const char *str) { struct ec_parsed_tk *parsed_tk; @@ -55,12 +55,12 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, return parsed_tk; } -static struct ec_tk_ops space_ops = { - .parse = parse, +static struct ec_tk_ops ec_tk_space_ops = { + .parse = ec_tk_space_parse, }; struct ec_tk *ec_tk_space_new(const char *id) { - return ec_tk_new(id, &space_ops, sizeof(struct ec_tk_space)); + return ec_tk_new(id, &ec_tk_space_ops, sizeof(struct ec_tk_space)); } diff --git a/lib/ecoli_tk_str.c b/lib/ecoli_tk_str.c index f7b18fb..ad5d119 100644 --- a/lib/ecoli_tk_str.c +++ b/lib/ecoli_tk_str.c @@ -35,7 +35,7 @@ #include #include -static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, +static struct ec_parsed_tk *ec_tk_str_parse(const struct ec_tk *gen_tk, const char *str) { struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk; @@ -53,7 +53,7 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk, return parsed_tk; } -static struct ec_completed_tk *complete(const struct ec_tk *gen_tk, +static struct ec_completed_tk *ec_tk_str_complete(const struct ec_tk *gen_tk, const char *str) { struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk; @@ -85,17 +85,17 @@ static struct ec_completed_tk *complete(const struct ec_tk *gen_tk, return completed_tk; } -static void free_priv(struct ec_tk *gen_tk) +static void ec_tk_str_free_priv(struct ec_tk *gen_tk) { struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk; ec_free(tk->string); } -static struct ec_tk_ops str_ops = { - .parse = parse, - .complete = complete, - .free_priv = free_priv, +static struct ec_tk_ops ec_tk_str_ops = { + .parse = ec_tk_str_parse, + .complete = ec_tk_str_complete, + .free_priv = ec_tk_str_free_priv, }; struct ec_tk *ec_tk_str_new(const char *id, const char *str) @@ -103,7 +103,7 @@ struct ec_tk *ec_tk_str_new(const char *id, const char *str) struct ec_tk_str *tk = NULL; char *s = NULL; - tk = (struct ec_tk_str *)ec_tk_new(id, &str_ops, sizeof(*tk)); + tk = (struct ec_tk_str *)ec_tk_new(id, &ec_tk_str_ops, sizeof(*tk)); if (tk == NULL) goto fail; @@ -122,7 +122,7 @@ fail: return NULL; } -static int testcase(void) +static int ec_tk_str_testcase(void) { struct ec_tk *tk; int ret = 0; @@ -177,9 +177,9 @@ static int testcase(void) return ret; } -static struct ec_test test = { +static struct ec_test ec_tk_str_test = { .name = "tk_str", - .test = testcase, + .test = ec_tk_str_testcase, }; -EC_REGISTER_TEST(test); +EC_REGISTER_TEST(ec_tk_str_test);