From 499318ae3ca1322044b62814bb8891fc26d641fe Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 21 Sep 2017 16:07:03 +0200 Subject: [PATCH] save --- lib/Makefile | 4 +- lib/ecoli_completed.c | 139 ++++++++++++------- lib/ecoli_completed.h | 12 +- lib/ecoli_node_cmd.c | 20 ++- lib/ecoli_node_file.c | 295 ++++++++++++++++++++++++++++++++++++++++ lib/ecoli_node_file.h | 38 ++++++ lib/ecoli_node_sh_lex.c | 10 +- lib/ecoli_test.c | 3 +- lib/main-readline.c | 76 +++++++++-- lib/todo.txt | 21 +++ 10 files changed, 541 insertions(+), 77 deletions(-) create mode 100644 lib/ecoli_node_file.c create mode 100644 lib/ecoli_node_file.h diff --git a/lib/Makefile b/lib/Makefile index 29df719..8d44796 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -53,7 +53,7 @@ srcs += ecoli_node_cmd.c srcs += ecoli_node_empty.c srcs += ecoli_node_expr.c srcs += ecoli_node_expr_test.c -#srcs += ecoli_node_file.c +srcs += ecoli_node_file.c srcs += ecoli_node_int.c srcs += ecoli_node_many.c srcs += ecoli_node_once.c @@ -76,7 +76,7 @@ ldflags-$(O)test = -rdynamic exe-y-$(O)test = $(srcs) main.c ldflags-$(O)readline = -lreadline -ltermcap -#exe-y-$(O)readline = $(srcs) main-readline.c +exe-y-$(O)readline = $(srcs) main-readline.c include $(ECOLI)/mk/ecoli-post.mk diff --git a/lib/ecoli_completed.c b/lib/ecoli_completed.c index a600902..c9aff48 100644 --- a/lib/ecoli_completed.c +++ b/lib/ecoli_completed.c @@ -193,32 +193,26 @@ ec_completed_match(enum ec_completed_type type, struct ec_parsed *state, const struct ec_node *node, const char *add) { struct ec_completed_match *item = NULL; + struct ec_parsed *p; + size_t len; item = ec_calloc(1, sizeof(*item)); if (item == NULL) return NULL; - /* XXX can state be NULL? */ - if (state != NULL) { - struct ec_parsed *p; - size_t len; - - /* get path len */ - for (p = state, len = 0; p != NULL; - p = ec_parsed_get_parent(p), len++) - ; - - item->path = ec_calloc(len, sizeof(*item->path)); - if (item->path == NULL) - goto fail; - - item->pathlen = len; - - /* write path in array */ - for (p = state, len = 0; p != NULL; - p = ec_parsed_get_parent(p), len++) - item->path[len] = p->node; - } + /* get path len */ + for (p = state, len = 0; p != NULL; + p = ec_parsed_get_parent(p), len++) + ; + /* allocate room for path */ + item->path = ec_calloc(len, sizeof(*item->path)); + if (item->path == NULL) + goto fail; + item->pathlen = len; + /* write path in array */ + for (p = state, len = 0; p != NULL; + p = ec_parsed_get_parent(p), len++) + item->path[len] = p->node; item->type = type; item->node = node; @@ -240,16 +234,18 @@ fail: return NULL; } -int -ec_completed_add_match(struct ec_completed *completed, - struct ec_parsed *parsed_state, - const struct ec_node *node, const char *add) +static int +__ec_completed_add_match(enum ec_completed_type type, + struct ec_completed *completed, + struct ec_parsed *parsed_state, + const struct ec_node *node, const char *add) { struct ec_completed_node *compnode = NULL; - struct ec_completed_match *item = NULL; + struct ec_completed_match *match = NULL; int ret = -ENOMEM; size_t n; + /* find the compnode entry corresponding to this node */ TAILQ_FOREACH(compnode, &completed->nodes, next) { if (compnode->node == node) break; @@ -257,68 +253,97 @@ ec_completed_add_match(struct ec_completed *completed, if (compnode == NULL) return -ENOENT; - item = ec_completed_match(EC_MATCH, parsed_state, node, add); - if (item == NULL) + match = ec_completed_match(type, parsed_state, node, add); + if (match == NULL) goto fail; - if (item->add != NULL) { + if (match->add != NULL) { if (completed->smallest_start == NULL) { - completed->smallest_start = ec_strdup(item->add); + completed->smallest_start = ec_strdup(match->add); if (completed->smallest_start == NULL) goto fail; } else { - n = strcmp_count(item->add, + n = strcmp_count(match->add, completed->smallest_start); completed->smallest_start[n] = '\0'; } completed->count_match++; } - TAILQ_INSERT_TAIL(&compnode->matches, item, next); + TAILQ_INSERT_TAIL(&compnode->matches, match, next); completed->count++; return 0; fail: - ec_completed_match_free(item); + ec_completed_match_free(match); return ret; } +int +ec_completed_add_match(struct ec_completed *completed, + struct ec_parsed *parsed_state, + const struct ec_node *node, const char *add) +{ + return __ec_completed_add_match(EC_MATCH, completed, parsed_state, + node, add); +} + +int +ec_completed_add_no_match(struct ec_completed *completed, + struct ec_parsed *parsed_state, + const struct ec_node *node) +{ + return __ec_completed_add_match(EC_NO_MATCH, completed, parsed_state, + node, NULL); +} + +int +ec_completed_add_partial_match(struct ec_completed *completed, + struct ec_parsed *parsed_state, + const struct ec_node *node, const char *add) +{ + return __ec_completed_add_match(EC_PARTIAL_MATCH, completed, parsed_state, + node, add); +} + int ec_completed_add_node(struct ec_completed *completed, const struct ec_node *node) { - struct ec_completed_node *item = NULL; + struct ec_completed_node *compnode = NULL; - item = ec_completed_node(node); - if (item == NULL) + compnode = ec_completed_node(node); + if (compnode == NULL) return -ENOMEM; - TAILQ_INSERT_TAIL(&completed->nodes, item, next); + TAILQ_INSERT_TAIL(&completed->nodes, compnode, next); return 0; } -void ec_completed_match_free(struct ec_completed_match *item) +void ec_completed_match_free(struct ec_completed_match *match) { - ec_free(item->add); - ec_free(item->path); - ec_free(item); + ec_free(match->add); + ec_free(match->path); + ec_free(match); } -/* default completion function: return a no-item element */ +/* default completion function: return a no-match element */ int ec_node_default_complete(const struct ec_node *gen_node, struct ec_completed *completed, - struct ec_parsed *parsed, + struct ec_parsed *parsed_state, const struct ec_strvec *strvec) { - (void)gen_node; - (void)completed; - (void)parsed; + int ret; - if (ec_strvec_len(strvec) != 1) //XXX needed? + if (ec_strvec_len(strvec) != 1) return 0; + ret = ec_completed_add_no_match(completed, parsed_state, gen_node); + if (ret < 0) + return ret; + return 0; } @@ -363,7 +388,16 @@ void ec_completed_dump(FILE *out, const struct ec_completed *completed) fprintf(out, "node=%p, node_type=%s\n", compnode->node, compnode->node->type->name); TAILQ_FOREACH(item, &compnode->matches, next) { - fprintf(out, "add=<%s>\n", item->add); /* XXX comp type */ + const char *typestr; + + switch (item->type) { + case EC_NO_MATCH: typestr = "no-match"; break; + case EC_MATCH: typestr = "match"; break; + case EC_PARTIAL_MATCH: typestr = "partial-match"; break; + default: typestr = "unknown"; break; + } + + fprintf(out, " type=%s add=<%s>\n", typestr, item->add); } } } @@ -429,19 +463,22 @@ const struct ec_completed_match *ec_completed_iter_next( if (cur_node == NULL) { TAILQ_FOREACH(cur_node, &completed->nodes, next) { TAILQ_FOREACH(cur_match, &cur_node->matches, next) { - if (cur_match != NULL) + if (cur_match != NULL && + cur_match->type & iter->type) goto found; } } return NULL; } else { cur_match = TAILQ_NEXT(cur_match, next); - if (cur_match != NULL) + if (cur_match != NULL && + cur_match->type & iter->type) goto found; cur_node = TAILQ_NEXT(cur_node, next); while (cur_node != NULL) { cur_match = TAILQ_FIRST(&cur_node->matches); - if (cur_match != NULL) + if (cur_match != NULL && + cur_match->type & iter->type) goto found; cur_node = TAILQ_NEXT(cur_node, next); } diff --git a/lib/ecoli_completed.h b/lib/ecoli_completed.h index 6da00af..c81544a 100644 --- a/lib/ecoli_completed.h +++ b/lib/ecoli_completed.h @@ -37,7 +37,7 @@ struct ec_node; enum ec_completed_type { EC_NO_MATCH, EC_MATCH, - EC_PARTIAL, + EC_PARTIAL_MATCH, }; struct ec_completed_match { @@ -46,7 +46,7 @@ struct ec_completed_match { const struct ec_node *node; char *add; - /* reverse order: [0] = last parsed, [len-1] = root */ + /* reverse order: [0] = last, [len-1] = root */ const struct ec_node **path; size_t pathlen; }; @@ -88,12 +88,16 @@ struct ec_completed *ec_completed(void); int ec_completed_add_match(struct ec_completed *completed, struct ec_parsed *state, const struct ec_node *node, const char *add); -int ec_completed_add_node(struct ec_completed *completed, +int ec_completed_add_no_match(struct ec_completed *completed, + struct ec_parsed *parsed_state, const struct ec_node *node); -int ec_completed_add_partial(struct ec_completed *completed, +int ec_completed_add_partial_match(struct ec_completed *completed, struct ec_parsed *state, const struct ec_node *node, const char *add); +int ec_completed_add_node(struct ec_completed *completed, + const struct ec_node *node); + void ec_completed_match_free(struct ec_completed_match *item); void ec_completed_free(struct ec_completed *completed); void ec_completed_dump(FILE *out, diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c index 1f40b0f..01c2cd2 100644 --- a/lib/ecoli_node_cmd.c +++ b/lib/ecoli_node_cmd.c @@ -515,16 +515,28 @@ static int ec_node_cmd_testcase(void) ret |= EC_TEST_CHECK_PARSE(node, -1, "foo"); ec_node_free(node); - node = EC_NODE_CMD(NULL, "good morning bob|bobby|michael [count]", + node = EC_NODE_CMD(NULL, "good morning [count] bob|bobby|michael", ec_node_int("count", 0, 10, 10)); if (node == NULL) { ec_log(EC_LOG_ERR, "cannot create node\n"); return -1; } - ret |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "bob", "1"); - ec_node_free(node); + ret |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob"); + + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + "good", EC_NODE_ENDLIST, + "good"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "g", EC_NODE_ENDLIST, + "ood", EC_NODE_ENDLIST, + "ood"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "good", "morning", "", EC_NODE_ENDLIST, + "bob", "bobby", "michael", EC_NODE_ENDLIST, + ""); - // XXX completion + ec_node_free(node); return ret; } diff --git a/lib/ecoli_node_file.c b/lib/ecoli_node_file.c new file mode 100644 index 0000000..73a0174 --- /dev/null +++ b/lib/ecoli_node_file.c @@ -0,0 +1,295 @@ +/* + * 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. + */ + +#define _GNU_SOURCE /* for asprintf */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_file { + struct ec_node gen; +}; + +static int +ec_node_file_parse(const struct ec_node *gen_node, + struct ec_parsed *state, + const struct ec_strvec *strvec) +{ + (void)gen_node; + (void)state; + + if (ec_strvec_len(strvec) == 0) + return EC_PARSED_NOMATCH; + + return 1; +} + +/* + * Almost the same than dirname (3) and basename (3) except that + * it always returns a substring of the given path, which can + * be empty. + * - the behavior is different when the path finishes with a '/' + * - the argument is not modified + * - the output is allocated and must be freed with ec_free(). + * + * path dirname basename split_path + * /usr/lib /usr lib /usr/ lib + * /usr/ / usr /usr/ + * usr . usr usr + * / / / / + * . . . . + * .. . .. .. + */ +static int split_path(const char *path, char **dname_p, char **bname_p) +{ + char *last_slash; + size_t dirlen; + char *dname, *bname; + + *dname_p = NULL; + *bname_p = NULL; + + last_slash = strrchr(path, '/'); + if (last_slash == NULL) + dirlen = 0; + else + dirlen = last_slash - path + 1; + + dname = ec_strdup(path); + if (dname == NULL) + return -ENOMEM; + dname[dirlen] = '\0'; + + bname = ec_strdup(path + dirlen); + if (bname == NULL) { + ec_free(dname); + return -ENOMEM; + } + + *dname_p = dname; + *bname_p = bname; + + return 0; +} + +static int +ec_node_file_complete(const struct ec_node *gen_node, + struct ec_completed *completed, + struct ec_parsed *state, + const struct ec_strvec *strvec) +{ + struct stat st; + const char *path; + size_t bname_len; + struct dirent *de = NULL; + DIR *dir = NULL; + char *dname = NULL, *bname = NULL, *effective_dir; + char *add = NULL; + int ret; + int is_dir = 0; + + /* + * Example with this file tree: + * / + * ├── dir1 + * │   ├── file1 + * │   ├── file2 + * │   └── subdir + * │   └── file3 + * ├── dir2 + * │   └── file4 + * └── file5 + * + * Input Output completions + * / [dir1/, dir2/, file5] + * /d [dir1/, dir2/] + * /f [file5] + * /dir1/ [file1, file2, subdir/] + * + * + * + */ + + if (ec_strvec_len(strvec) != 1) + goto out; + + path = ec_strvec_val(strvec, 0); + ret = split_path(path, &dname, &bname); + if (ret < 0) { + ec_completed_free(completed); + completed = NULL; + goto out; + } + + if (strcmp(dname, "") == 0) + effective_dir = "."; + else + effective_dir = dname; + + ret = lstat(effective_dir, &st); + if (ret != 0) { + ret = -errno; + goto out; + } + if (!S_ISDIR(st.st_mode)) + goto out; + + dir = opendir(effective_dir); + if (dir == NULL) + goto out; + + bname_len = strlen(bname); + while (1) { + de = readdir(dir); + if (de == NULL) + goto out; + + if (strncmp(bname, de->d_name, bname_len)) + continue; + if (bname[0] != '.' && de->d_name[0] == '.') + continue; + + /* add '/' if it's a dir */ + if (de->d_type == DT_DIR) { + is_dir = 1; + } else if (de->d_type == DT_UNKNOWN) { // XXX todo + } else { + is_dir = 0; + } + + if (is_dir) { + if (asprintf(&add, "%s/", &de->d_name[bname_len]) < 0) { + ret = -errno; + goto out; + } + if (ec_completed_add_partial_match( + completed, state, gen_node, add) < 0) { + ec_completed_free(completed); + completed = NULL; + goto out; + } + } else { + if (asprintf(&add, "%s", &de->d_name[bname_len]) < 0) { + ret = -errno; + goto out; + } + if (ec_completed_add_match(completed, state, gen_node, + add) < 0) { + ec_completed_free(completed); + completed = NULL; + goto out; + } + } + } + ret = 0; + +out: + free(add); + ec_free(dname); + ec_free(bname); + if (dir != NULL) + closedir(dir); + + return ret; +} + +static struct ec_node_type ec_node_file_type = { + .name = "file", + .parse = ec_node_file_parse, + .complete = ec_node_file_complete, + .size = sizeof(struct ec_node_file), +}; + +EC_NODE_TYPE_REGISTER(ec_node_file_type); + +/* LCOV_EXCL_START */ +static int ec_node_file_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node("file", NULL); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + /* any string matches */ + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "/tmp/bar"); + ret |= EC_TEST_CHECK_PARSE(node, -1); + + /* test completion */ + ret |= EC_TEST_CHECK_COMPLETE(node, + EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "/", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "/tmp", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "/tmp/", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "/tmp/.", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} +/* LCOV_EXCL_STOP */ + +static struct ec_test ec_node_file_test = { + .name = "node_file", + .test = ec_node_file_testcase, +}; + +EC_TEST_REGISTER(ec_node_file_test); diff --git a/lib/ecoli_node_file.h b/lib/ecoli_node_file.h new file mode 100644 index 0000000..6a6d52a --- /dev/null +++ b/lib/ecoli_node_file.h @@ -0,0 +1,38 @@ +/* + * 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_NODE_FILE_ +#define ECOLI_NODE_FILE_ + +#include + +struct ec_node *ec_node_file(const char *id, const char *file); + +/* file is duplicated */ +int ec_node_file_set_str(struct ec_node *node, const char *file); + +#endif diff --git a/lib/ecoli_node_sh_lex.c b/lib/ecoli_node_sh_lex.c index 59b917f..897f922 100644 --- a/lib/ecoli_node_sh_lex.c +++ b/lib/ecoli_node_sh_lex.c @@ -141,8 +141,9 @@ static size_t eat_str(const char *str) { size_t i = 0; - /* skip spaces */ - while (!isblank(str[i]) && str[i] != '\0') + /* eat chars until we find a quote, space, or end of string */ + while (!isblank(str[i]) && str[i] != '\0' && + str[i] != '"' && str[i] != '\'') i++; return i; @@ -295,10 +296,14 @@ ec_node_sh_lex_complete(const struct ec_node *gen_node, return 0; str = ec_strvec_val(strvec, 0); +// printf("\nold:%s\n", str); new_vec = tokenize(str, 1, 1, &missing_quote); if (new_vec == NULL) goto fail; +// printf("new:%s\n", ec_strvec_val(new_vec, 0)); + // XXX: complete should add the quotes for !EC_PARTIAL: use another + // completed object ret = ec_node_complete_child(node->child, completed, parsed, new_vec); if (ret < 0) goto fail; @@ -370,6 +375,7 @@ static int ec_node_sh_lex_testcase(void) ret |= EC_TEST_CHECK_PARSE(node, 1, " foo bar"); ret |= EC_TEST_CHECK_PARSE(node, 1, " 'foo' \"bar\""); ret |= EC_TEST_CHECK_PARSE(node, 1, " 'f'oo 'toto' bar"); + ret |= EC_TEST_CHECK_PARSE(node, -1, " foo toto bar'"); ec_node_free(node); /* test completion */ diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index efe4c04..1e98c65 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -166,7 +166,8 @@ int ec_test_check_complete(struct ec_node *tk, ...) count, ec_completed_count(c, EC_MATCH)); ec_completed_dump(stdout, c); ret = -1; - } + } else + ec_completed_dump(stdout, c); //XXX /* check the expected smallest start */ expected = va_arg(ap, const char *); diff --git a/lib/main-readline.c b/lib/main-readline.c index b8c6459..b8c2f0b 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -47,6 +47,7 @@ #include #include #include +#include static struct ec_node *commands; @@ -54,9 +55,12 @@ static char *my_completion_entry(const char *s, int state) { static struct ec_completed *c; static struct ec_completed_iter *iter; - static const struct ec_completed_item *item; + static const struct ec_completed_match *item; char *out_string; + /* don't append a quote */ + rl_completion_suppress_quote = 1; + rl_basic_quote_characters = ""; if (state == 0) { char *line; @@ -74,16 +78,27 @@ static char *my_completion_entry(const char *s, int state) return NULL; ec_completed_iter_free(iter); - iter = ec_completed_iter(c, EC_MATCH); + iter = ec_completed_iter(c, EC_MATCH | EC_PARTIAL_MATCH); if (iter == NULL) return NULL; } - elt = ec_completed_iter_next(iter); - if (elt == NULL) + item = ec_completed_iter_next(iter); + if (item == NULL) return NULL; - if (asprintf(&out_string, "%s%s", s, elt->add) < 0) + /* don't add the trailing space for partial completions */ + if (state == 0) { + if (item->type == EC_MATCH) + rl_completion_suppress_append = 0; + else + rl_completion_suppress_append = 1; + } + + //XXX see printable_part() and rl_display_match_list() + //XXX or: if match count > 1, strip beginning (in node_file) + + if (asprintf(&out_string, "%s%s", s, item->add) < 0) // XXX ec_asprintf (check all in code) return NULL; return out_string; @@ -101,7 +116,7 @@ static char **my_attempted_completion(const char *text, int start, int end) } /* this function builds the help string */ -static char *get_node_help(const struct ec_completed_elt *elt) +static char *get_node_help(const struct ec_completed_match *elt) { const struct ec_node *node; char *help = NULL; @@ -130,8 +145,9 @@ static char *get_node_help(const struct ec_completed_elt *elt) static int show_help(int ignore, int invoking_key) { - const struct ec_completed_item *item; - struct ec_completed_iter *iter; + const struct ec_completed_node *compnode; +// const struct ec_completed_match *item; +// struct ec_completed_iter *iter; struct ec_completed *c; struct ec_parsed *p; char *line; @@ -160,7 +176,9 @@ static int show_help(int ignore, int invoking_key) if (c == NULL) return 1; - count = ec_completed_count(c, EC_MATCH | EC_NO_MATCH); +#if 0 // old method + count = ec_completed_count(c, EC_MATCH | EC_NO_MATCH | + EC_PARTIAL_MATCH); helps = calloc(count + match + 1, sizeof(char *)); if (helps == NULL) return 1; @@ -168,7 +186,8 @@ static int show_help(int ignore, int invoking_key) if (match) helps[1] = ""; - iter = ec_completed_iter(c, EC_MATCH | EC_NO_MATCH); + iter = ec_completed_iter(c, EC_MATCH | EC_NO_MATCH | + EC_PARTIAL_MATCH); if (iter == NULL) goto fail; @@ -178,7 +197,31 @@ static int show_help(int ignore, int invoking_key) i++, item = ec_completed_iter_next(iter)) { helps[i] = get_node_help(item); } +#else + count = 0; + TAILQ_FOREACH(compnode, &c->nodes, next) { + if (TAILQ_EMPTY(&compnode->matches)) + continue; + count++; + } + helps = calloc(count + match + 1, sizeof(char *)); + if (helps == NULL) + return 1; + + if (match) + helps[1] = ""; + + i = match + 1; + TAILQ_FOREACH(compnode, &c->nodes, next) { + if (TAILQ_EMPTY(&compnode->matches)) + continue; + // we should pass compnode instead + helps[i++] = get_node_help(TAILQ_FIRST(&compnode->matches)); //XXX + } +#endif + + ec_completed_dump(stdout, c); ec_completed_free(c); rl_display_match_list(helps, count + match, 1000); /* XXX 1000 */ @@ -187,7 +230,7 @@ static int show_help(int ignore, int invoking_key) return 0; -fail: +//fail: free(helps); // free helps[n] XXX return 1; @@ -275,6 +318,15 @@ static int create_commands(void) goto fail; + cmd = EC_NODE_SEQ(NULL, + ec_node_str(NULL, "load"), + ec_node("file", NULL) + ); + ec_keyval_set(ec_node_attrs(cmd), "help", "load a file", NULL); + if (ec_node_or_add(cmdlist, cmd) < 0) + goto fail; + + commands = ec_node_sh_lex(NULL, cmdlist); if (commands == NULL) goto fail; @@ -290,10 +342,8 @@ static int create_commands(void) int main(void) { struct ec_parsed *p; -// const char *name; char *line; - if (create_commands() < 0) return 1; diff --git a/lib/todo.txt b/lib/todo.txt index 7426228..37a61d7 100644 --- a/lib/todo.txt +++ b/lib/todo.txt @@ -9,7 +9,10 @@ X tk_re cleanup / rework ================ +- add_no_match +- add_partial_match - check XXX in code +- properly manage quotes in shlex X remove the _new() functions - iterate children nodes without chaining them - add a tk vector type: will be used in several nodes (ex: or, seq, ...) @@ -126,3 +129,21 @@ netconf example =============== - demonstration example that parses yang file and generate cli + + + +----------------------- + +readline: + +[tab] list possible completions (matches only) +[?] list what is expected, example: + +"command [foo] toto|titi|" + +help("command f") -> + foo (help of foo) + toto (help of toto) + titi (help of titi) + (help of int) + -- 2.39.5