X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fmain-readline.c;h=2c81c53c0f69e76d413910bdc1ede305077844cd;hb=51028779e0a8772091aec5ab96bcf2519cf2f1ad;hp=b147e8a3e2fca4dc9dbefb0f5590fa799b93e271;hpb=139916ef3060ed731bc50ea9b619cc265213588c;p=protos%2Flibecoli.git diff --git a/lib/main-readline.c b/lib/main-readline.c index b147e8a..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 @@ -47,22 +26,29 @@ #include #include #include +#include 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; - char *out_string; + 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. Note: there are still some bugs when + * completing a quoted token. */ + rl_completion_suppress_quote = 1; + 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; @@ -73,20 +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_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 (asprintf(&out_string, "%s%s", s, item->add) < 0) - return NULL; + 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) { + 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); + } else if (rl_completion_type == '?') { + /* on second try only show the display part */ + item_display = ec_comp_item_get_display(item); + return strdup(item_display); + } - return out_string; + return strdup(item_str); } static char **my_attempted_completion(const char *text, int start, int end) @@ -101,16 +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_item *item) +static char *get_node_help(const struct ec_comp_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; - 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) @@ -118,7 +124,7 @@ static char *get_node_help(const struct ec_completed_item *item) } if (node_help == NULL) - node_help = ""; + node_help = "-"; if (node_desc == NULL) return NULL; @@ -130,66 +136,91 @@ static char *get_node_help(const struct ec_completed_item *item) static int show_help(int ignore, int invoking_key) { - const struct ec_completed_item *item; - struct ec_completed_iter *iter; - 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; - count = ec_completed_count(c, EC_MATCH | EC_NO_MATCH); - helps = calloc(count + match + 1, sizeof(char *)); - if (helps == NULL) - return 1; + /* let's display one contextual help per node */ + count = 0; + iter = ec_comp_iter(c, + EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL); + if (iter == NULL) + goto fail; + /* strangely, rl_display_match_list() expects first index at 1 */ + helps = calloc(match + 1, sizeof(char *)); + if (helps == NULL) + goto fail; if (match) helps[1] = ""; - iter = ec_completed_iter(c, EC_MATCH | EC_NO_MATCH); - if (iter == NULL) - goto fail; + while ((item = ec_comp_iter_next(iter)) != NULL) { + char **tmp; - /* strangely, rl_display_match_list() expects first index at 1 */ - for (i = match + 1, item = ec_completed_iter_next(iter); - i < count + match + 1 && item != NULL; - i++, item = ec_completed_iter_next(iter)) { - helps[i] = get_node_help(item); - } + /* keep one help per group, skip other items */ + grp = ec_comp_item_get_grp(item); + if (grp == prev_grp) + continue; - 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; 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); - // free helps[n] XXX + return 1; } @@ -197,19 +228,19 @@ 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; @@ -220,12 +251,12 @@ static int create_commands(void) ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", "the name of the person", NULL); ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "int")), - "help", "an integer", NULL); + "help", "an integer (0-10)", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) 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) @@ -235,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; @@ -250,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; @@ -267,15 +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; - commands = ec_node_sh_lex(NULL, cmdlist); + 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(EC_NO_ID, cmdlist); if (commands == NULL) goto fail; @@ -289,10 +329,13 @@ static int create_commands(void) int main(void) { - struct ec_parsed *p; -// const char *name; + 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; @@ -306,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); }