X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fmain-readline.c;h=2c81c53c0f69e76d413910bdc1ede305077844cd;hb=dec2d7fa17ae1e8b3d1dec8e396d057757baf4f3;hp=8fecad423a5c15658cdbaea0056883f9769ecbb8;hpb=7284aef1f5f33a170cbd04aee7ed0a43608982d6;p=protos%2Flibecoli.git diff --git a/lib/main-readline.c b/lib/main-readline.c index 8fecad4..2c81c53 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -1,41 +1,20 @@ -/* - * 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. +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2016, Olivier MATZ */ #define _GNU_SOURCE /* for asprintf */ #include #include +#include #include #include #include +#include #include -#include -#include +#include +#include #include #include #include @@ -53,21 +32,23 @@ static struct ec_node *commands; 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 struct ec_comp *c; + static struct ec_comp_iter *iter; + const struct ec_comp_item *item; + enum ec_comp_type item_type; + const char *item_str, *item_display; (void)s; - /* don't append a quote */ + /* Don't append a quote. Note: there are still some bugs when + * completing a quoted token. */ rl_completion_suppress_quote = 1; - rl_basic_quote_characters = ""; + rl_completer_quote_characters = "\"'"; if (state == 0) { char *line; - ec_completed_free(c); - + ec_comp_free(c); line = strdup(rl_line_buffer); if (line == NULL) return NULL; @@ -78,33 +59,36 @@ static char *my_completion_entry(const char *s, int state) if (c == NULL) return NULL; - ec_completed_iter_free(iter); - iter = ec_completed_iter(c, EC_MATCH | EC_PARTIAL_MATCH); + ec_comp_iter_free(iter); + iter = ec_comp_iter(c, EC_COMP_FULL | EC_COMP_PARTIAL); if (iter == NULL) return NULL; } - item = ec_completed_iter_next(iter); + item = ec_comp_iter_next(iter); if (item == NULL) return NULL; - if (c->count_match == 1) { + item_str = ec_comp_item_get_str(item); + if (c->count_full == 1) { /* don't add the trailing space for partial completions */ if (state == 0) { - if (item->type == EC_MATCH) + item_type = ec_comp_item_get_type(item); + if (item_type == EC_COMP_FULL) rl_completion_suppress_append = 0; else rl_completion_suppress_append = 1; } - return strdup(item->str); + return strdup(item_str); } else if (rl_completion_type == '?') { /* on second try only show the display part */ - return strdup(item->display); + item_display = ec_comp_item_get_display(item); + return strdup(item_display); } - return strdup(item->str); + return strdup(item_str); } static char **my_attempted_completion(const char *text, int start, int end) @@ -119,21 +103,20 @@ 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_node *compnode) +static char *get_node_help(const struct ec_comp_item *item) { - const struct ec_completed_item *item; + const struct ec_comp_group *grp; + const struct ec_parse *state; const struct ec_node *node; char *help = NULL; const char *node_help = NULL; const char *node_desc = NULL; - size_t i; - - /* Since we display only one help per node, only look at the first item - * to get the path. The objective is to retrieve the most precise - * help for this node. */ - item = TAILQ_FIRST(&compnode->items); - for (i = 0; i < item->pathlen; i++) { - node = item->path[i]; + + grp = ec_comp_item_get_grp(item); + state = grp->state; + for (state = grp->state; state != NULL; + state = ec_parse_get_parent(state)) { + node = ec_parse_get_node(state); if (node_help == NULL) node_help = ec_keyval_get(ec_node_attrs(node), "help"); if (node_desc == NULL) @@ -141,7 +124,7 @@ static char *get_node_help(const struct ec_completed_node *compnode) } if (node_help == NULL) - node_help = ""; + node_help = "-"; if (node_desc == NULL) return NULL; @@ -153,86 +136,111 @@ static char *get_node_help(const struct ec_completed_node *compnode) static int show_help(int ignore, int invoking_key) { - const struct ec_completed_node *compnode; - struct ec_completed *c; - struct ec_parsed *p; - char *line; - unsigned int count, i; + struct ec_comp_iter *iter = NULL; + const struct ec_comp_group *grp, *prev_grp = NULL; + const struct ec_comp_item *item; + struct ec_comp *c = NULL; + struct ec_parse *p = NULL; + char *line = NULL; + unsigned int count; char **helps = NULL; int match = 0; + int cols; (void)ignore; (void)invoking_key; line = strdup(rl_line_buffer); if (line == NULL) - return 1; + goto fail; /* check if the current line matches */ p = ec_node_parse(commands, line); - if (ec_parsed_matches(p)) + if (ec_parse_matches(p)) match = 1; - ec_parsed_free(p); + ec_parse_free(p); + p = NULL; /* complete at current cursor position */ line[rl_point] = '\0'; c = ec_node_complete(commands, line); - //ec_completed_dump(stdout, c); free(line); + line = NULL; if (c == NULL) - return 1; + goto fail; /* let's display one contextual help per node */ count = 0; - TAILQ_FOREACH(compnode, &c->nodes, next) { - if (TAILQ_EMPTY(&compnode->items)) - continue; - count++; - } + iter = ec_comp_iter(c, + EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL); + if (iter == NULL) + goto fail; - helps = calloc(count + match + 1, sizeof(char *)); + /* strangely, rl_display_match_list() expects first index at 1 */ + helps = calloc(match + 1, sizeof(char *)); if (helps == NULL) - return 1; - + goto fail; if (match) helps[1] = ""; - /* strangely, rl_display_match_list() expects first index at 1 */ - i = match + 1; - TAILQ_FOREACH(compnode, &c->nodes, next) { - if (TAILQ_EMPTY(&compnode->items)) + while ((item = ec_comp_iter_next(iter)) != NULL) { + char **tmp; + + /* keep one help per group, skip other items */ + grp = ec_comp_item_get_grp(item); + if (grp == prev_grp) continue; - helps[i++] = get_node_help(compnode); - } - ec_completed_free(c); + prev_grp = grp; - rl_display_match_list(helps, count + match, 1000); /* XXX 1000 */ + tmp = realloc(helps, (count + match + 2) * sizeof(char *)); + if (tmp == NULL) + goto fail; + helps = tmp; + helps[count + match + 1] = get_node_help(item); + count++; + } + ec_comp_iter_free(iter); + ec_comp_free(c); + /* ensure not more than 1 entry per line */ + rl_get_screen_size(NULL, &cols); + rl_display_match_list(helps, count + match, cols); rl_forced_update_display(); return 0; - // free helps[n] XXX on error ? +fail: + ec_comp_iter_free(iter); + ec_parse_free(p); + free(line); + ec_comp_free(c); + if (helps != NULL) { + while (count--) + free(helps[count + match + 1]); + } + free(helps); + + return 1; } static int create_commands(void) { struct ec_node *cmdlist = NULL, *cmd = NULL; - cmdlist = ec_node("or", NULL); + cmdlist = ec_node("or", EC_NO_ID); if (cmdlist == NULL) goto fail; - cmd = EC_NODE_SEQ(NULL, - ec_node_str(NULL, "hello"), + cmd = EC_NODE_SEQ(EC_NO_ID, + ec_node_str(EC_NO_ID, "hello"), EC_NODE_OR("name", ec_node_str("john", "john"), - ec_node_str(NULL, "johnny"), - ec_node_str(NULL, "mike") + ec_node_str(EC_NO_ID, "johnny"), + ec_node_str(EC_NO_ID, "mike") ), - ec_node_option(NULL, ec_node_int("int", 0, 10, 10)) + ec_node_option(EC_NO_ID, ec_node_int("int", 0, 10, 10)) ); if (cmd == NULL) goto fail; @@ -248,7 +256,7 @@ static int create_commands(void) goto fail; - cmd = EC_NODE_CMD(NULL, "good morning name [count]", + cmd = EC_NODE_CMD(EC_NO_ID, "good morning name [count]", EC_NODE_CMD("name", "bob|bobby|michael"), ec_node_int("count", 0, 10, 10)); if (cmd == NULL) @@ -258,12 +266,12 @@ static int create_commands(void) ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", "the person to greet", NULL); ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "count")), "help", - "how many times to greet", NULL); + "how many times to greet (0-10)", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; - cmd = EC_NODE_CMD(NULL, + cmd = EC_NODE_CMD(EC_NO_ID, "buy potatoes,carrots,pumpkins"); if (cmd == NULL) goto fail; @@ -273,14 +281,14 @@ static int create_commands(void) goto fail; - cmd = EC_NODE_CMD(NULL, "eat vegetables", + cmd = EC_NODE_CMD(EC_NO_ID, "eat vegetables", ec_node_many("vegetables", - EC_NODE_OR(NULL, - ec_node_str(NULL, "potatoes"), - ec_node_once(NULL, - ec_node_str(NULL, "carrots")), - ec_node_once(NULL, - ec_node_str(NULL, "pumpkins"))), + EC_NODE_OR(EC_NO_ID, + ec_node_str(EC_NO_ID, "potatoes"), + ec_node_once(EC_NO_ID, + ec_node_str(EC_NO_ID, "carrots")), + ec_node_once(EC_NO_ID, + ec_node_str(EC_NO_ID, "pumpkins"))), 1, 0)); if (cmd == NULL) goto fail; @@ -290,24 +298,24 @@ static int create_commands(void) goto fail; - cmd = EC_NODE_SEQ(NULL, - ec_node_str(NULL, "bye") + cmd = EC_NODE_SEQ(EC_NO_ID, + ec_node_str(EC_NO_ID, "bye") ); ec_keyval_set(ec_node_attrs(cmd), "help", "say bye", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; - cmd = EC_NODE_SEQ(NULL, - ec_node_str(NULL, "load"), - ec_node("file", NULL) + cmd = EC_NODE_SEQ(EC_NO_ID, + ec_node_str(EC_NO_ID, "load"), + ec_node("file", EC_NO_ID) ); 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); + commands = ec_node_sh_lex(EC_NO_ID, cmdlist); if (commands == NULL) goto fail; @@ -321,9 +329,14 @@ static int create_commands(void) int main(void) { - struct ec_parsed *p; + struct ec_parse *p; char *line; + if (ec_init() < 0) { + fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); + return 1; + } + if (create_commands() < 0) return 1; @@ -336,9 +349,9 @@ int main(void) break; p = ec_node_parse(commands, line); - ec_parsed_dump(stdout, p); + ec_parse_dump(stdout, p); add_history(line); - ec_parsed_free(p); + ec_parse_free(p); }