X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fmain-readline.c;h=2c81c53c0f69e76d413910bdc1ede305077844cd;hb=dec2d7fa17ae1e8b3d1dec8e396d057757baf4f3;hp=7432f3c57ffc47a782ac499643bc8477033ada18;hpb=a5446c41e019dadff4138e778470fef063666310;p=protos%2Flibecoli.git diff --git a/lib/main-readline.c b/lib/main-readline.c index 7432f3c..2c81c53 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -1,87 +1,94 @@ -/* - * 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 -#include -#include -#include - -static struct ec_tk *commands; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ec_node *commands; static char *my_completion_entry(const char *s, int state) { - static struct ec_completed_tk *c; - static struct ec_completed_tk_iter *iter; - static const struct ec_completed_tk_elt *elt; - 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_tk_free(c); - + ec_comp_free(c); line = strdup(rl_line_buffer); if (line == NULL) return NULL; line[rl_point] = '\0'; - c = ec_tk_complete(commands, line); + c = ec_node_complete(commands, line); free(line); if (c == NULL) return NULL; - ec_completed_tk_iter_free(iter); - iter = ec_completed_tk_iter_new(c, EC_MATCH); + ec_comp_iter_free(iter); + iter = ec_comp_iter(c, EC_COMP_FULL | EC_COMP_PARTIAL); if (iter == NULL) return NULL; } - elt = ec_completed_tk_iter_next(iter); - if (elt == NULL) + item = ec_comp_iter_next(iter); + if (item == NULL) return NULL; - if (asprintf(&out_string, "%s%s", s, elt->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) @@ -96,19 +103,32 @@ static char **my_attempted_completion(const char *text, int start, int end) } /* this function builds the help string */ -static char *get_tk_help(const struct ec_tk *tk) +static char *get_node_help(const struct ec_comp_item *item) { - const struct ec_tk *tk2; + const struct ec_comp_group *grp; + const struct ec_parse *state; + const struct ec_node *node; 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"); + const char *node_help = NULL; + const char *node_desc = NULL; + + 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) + node_desc = ec_node_desc(node); + } - if (tk_help == NULL) - tk_help = ""; + if (node_help == NULL) + node_help = "-"; + if (node_desc == NULL) + return NULL; - if (asprintf(&help, "%-20s %s", ec_tk_desc(tk), tk_help) < 0) + if (asprintf(&help, "%-20s %s", node_desc, node_help) < 0) return NULL; return help; @@ -116,114 +136,206 @@ static char *get_tk_help(const struct ec_tk *tk) 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; + 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; - line[rl_point] = '\0'; + goto fail; - c = ec_tk_complete(commands, line); + /* check if the current line matches */ + p = ec_node_parse(commands, line); + if (ec_parse_matches(p)) + match = 1; + ec_parse_free(p); + p = NULL; + + /* complete at current cursor position */ + line[rl_point] = '\0'; + c = ec_node_complete(commands, line); free(line); + line = NULL; if (c == NULL) - return 1; - //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; + goto fail; - iter = ec_completed_tk_iter_new(c, EC_MATCH | EC_NO_MATCH); + /* 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 */ - 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); - } + helps = calloc(match + 1, sizeof(char *)); + if (helps == NULL) + goto fail; + if (match) + helps[1] = ""; - ec_completed_tk_free(c); + while ((item = ec_comp_iter_next(iter)) != NULL) { + char **tmp; - rl_display_match_list(helps, count, 1000); + /* keep one help per group, skip other items */ + grp = ec_comp_item_get_grp(item); + if (grp == prev_grp) + continue; + + prev_grp = grp; + + 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; } static int create_commands(void) { - struct ec_tk *cmdlist = NULL, *cmd = NULL; + struct ec_node *cmdlist = NULL, *cmd = NULL; - cmdlist = ec_tk_or_new(NULL); + cmdlist = ec_node("or", EC_NO_ID); if (cmdlist == NULL) goto fail; - cmd = ec_tk_seq(NULL, - ec_tk_str(NULL, "hello"), - ec_tk_or(NULL, - ec_tk_or("name", - ec_tk_str(NULL, "john"), - ec_tk_str(NULL, "johnny"), - ec_tk_str(NULL, "mike"), - EC_TK_ENDLIST - ), - ec_tk_int("int", 0, 10, 10), - EC_TK_ENDLIST + + 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(EC_NO_ID, "johnny"), + ec_node_str(EC_NO_ID, "mike") ), - EC_TK_ENDLIST + ec_node_option(EC_NO_ID, ec_node_int("int", 0, 10, 10)) ); - ec_keyval_set(ec_tk_attrs(cmd), "help", "say hello to someone", NULL); - ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "name")), + if (cmd == NULL) + goto fail; + ec_keyval_set(ec_node_attrs(cmd), "help", + "say hello to someone several times", NULL); + ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "john")), + "help", "specific help for john", NULL); + ec_keyval_set(ec_node_attrs(ec_node_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) + ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "int")), + "help", "an integer (0-10)", NULL); + if (ec_node_or_add(cmdlist, cmd) < 0) + goto fail; + + + 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) + goto fail; + ec_keyval_set(ec_node_attrs(cmd), "help", + "say good morning to someone several times", NULL); + 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 (0-10)", NULL); + if (ec_node_or_add(cmdlist, cmd) < 0) + goto fail; + + + cmd = EC_NODE_CMD(EC_NO_ID, + "buy potatoes,carrots,pumpkins"); + if (cmd == NULL) + goto fail; + ec_keyval_set(ec_node_attrs(cmd), "help", + "buy some vegetables", NULL); + if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; - cmd = ec_tk_seq(NULL, - ec_tk_str(NULL, "bye"), - EC_TK_ENDLIST + + cmd = EC_NODE_CMD(EC_NO_ID, "eat vegetables", + ec_node_many("vegetables", + 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; + ec_keyval_set(ec_node_attrs(cmd), "help", + "eat vegetables (take some more potatoes)", NULL); + if (ec_node_or_add(cmdlist, cmd) < 0) + goto fail; + + + 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(EC_NO_ID, + ec_node_str(EC_NO_ID, "load"), + ec_node("file", EC_NO_ID) ); - ec_keyval_set(ec_tk_attrs(cmd), "help", "say bye to someone", NULL); - if (ec_tk_or_add(cmdlist, cmd) < 0) + ec_keyval_set(ec_node_attrs(cmd), "help", "load a file", NULL); + if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; - commands = ec_tk_shlex_new(NULL, cmdlist); + + commands = ec_node_sh_lex(EC_NO_ID, cmdlist); if (commands == NULL) goto fail; return 0; fail: - fprintf(stderr, "cannot initialize tokens\n"); - ec_tk_free(cmd); - ec_tk_free(cmdlist); + fprintf(stderr, "cannot initialize nodes\n"); + ec_node_free(cmdlist); return -1; } int main(void) { - struct ec_parsed_tk *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; @@ -236,14 +348,14 @@ int main(void) if (line == NULL) break; - p = ec_tk_parse(commands, line); - ec_parsed_tk_dump(stdout, p); + p = ec_node_parse(commands, line); + ec_parse_dump(stdout, p); add_history(line); - ec_parsed_tk_free(p); + ec_parse_free(p); } - ec_tk_free(commands); + ec_node_free(commands); return 0; }