From: Jan Moskyto Matejka Date: Wed, 31 May 2017 13:04:27 +0000 (+0200) Subject: Merge branch 'cli-completion' into bash-completion X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5e9fe3e55787cb844b6828b6ebc72efb7d564ea;p=thirdparty%2Fbird.git Merge branch 'cli-completion' into bash-completion --- b5e9fe3e55787cb844b6828b6ebc72efb7d564ea diff --cc client/client.c index b4e2c6051,cab5601e6..741cae5b4 --- a/client/client.c +++ b/client/client.c @@@ -47,9 -50,9 +50,11 @@@ static byte server_read_buf[SERVER_READ static byte *server_read_pos = server_read_buf; int init = 1; /* During intial sequence */ - int busy = 1; /* Executing BIRD command */ + int busy = 0; /* Executing BIRD command */ int interactive; /* Whether stdin is terminal */ +int complete = 0; + int welcomed = 0; /* Was welcome message with BIRD version printed out? */ ++int symread = 0; /* During symbol receipt */ static int num_lines, skip_input; int term_lns, term_cls; @@@ -119,12 -114,7 +127,12 @@@ parse_args(int argc, char **argv tmp += strlen(tmp); *tmp++ = ' '; } - tmp[-1] = 0; /* Put ending null-terminator */ + + if (complete) { + strcpy(tmp, comp_now); + tmp += strlen(comp_now); + } else - tmp[-1] = 0; ++ tmp[-1] = 0; /* Drop last space character */ once = 1; interactive = 0; @@@ -199,7 -230,7 +248,7 @@@ init_commands(void return; } -- if (init_cmd) ++ if (init_cmd && !complete) { /* First transition - client received hello from BIRD and there is waiting initial command */ @@@ -208,17 -239,24 +257,30 @@@ return; } -- if (once) ++ if (once && !complete) { /* Initial command is finished and we want to exit */ cleanup(); exit(0); } - input_init(); + init_list(&symbols); + longest_symbol_len = 1; /* Be careful, it's used as denominator! */ + + /* In symbol list is a BIRD version for welcome message too */ - if (interactive) - retrieve_symbols(); ++ if (interactive || complete) ++ { ++ retrieve_symbols(); ++ symread = 1; ++ } + - input_init(); ++ if (!complete) ++ { ++ input_init(); -- term_lns = (term_lns > 0) ? term_lns : 25; -- term_cls = (term_cls > 0) ? term_cls : 80; ++ term_lns = (term_lns > 0) ? term_lns : 25; ++ term_cls = (term_cls > 0) ? term_cls : 80; ++ } init = 0; } @@@ -279,8 -317,57 +341,59 @@@ server_connect(void DIE("fcntl"); } + list * + cli_get_symbol_list(void) + { + return &symbols; + } - #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) + uint + cli_get_symbol_maxlen(void) + { + return longest_symbol_len; + } + + static void + process_internal_message(int reply_code, const char *name) + { + u32 flag = 0; + + switch (reply_code) + { + case RC_BIRD_VERSION_NUM: - if (interactive && !welcomed) ++ if (interactive && !welcomed && !complete) + { + welcomed = 1; + printf("BIRD %s ready.\n", name); + } + return; + + case RC_NOTIFY: + if (interactive) + retrieve_symbols(); + return; + + /* Symbols */ + case RC_CONSTANT_NAME: flag = CLI_SF_CONSTANT; break; + case RC_VARIABLE_NAME: flag = CLI_SF_VARIABLE; break; + case RC_FILTER_NAME: flag = CLI_SF_FILTER; break; + case RC_FUNCTION_NAME: flag = CLI_SF_FUNCTION; break; + case RC_PROTOCOL_NAME: flag = CLI_SF_PROTOCOL; break; + case RC_TABLE_NAME: flag = CLI_SF_TABLE; break; + case RC_TEMPLATE_NAME: flag = CLI_SF_TEMPLATE; break; + case RC_INTERFACE_NAME: flag = CLI_SF_INTERFACE; break; + default: + printf("Undefined %d: %s", reply_code, name); + return; + } + - if (flag && name && *name) ++ if (flag && name && *name) { ++ symread = 1; + add_to_symbols(flag, name); ++ } + } + -#define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) ++#define PRINTF(LEN, PARGS...) do { if (!skip_input && !complete) len = printf(PARGS); } while(0) static void server_got_reply(char *x) @@@ -296,8 -386,14 +412,15 @@@ sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && (x[4] == ' ' || x[4] == '-')) { - if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); + if (code >= 3000 && code < 4000) + { + process_internal_message(code, x+5); + } + else if (code) + { + PRINTF(len, "%s\n", verbose ? x : x+5); - } ++ } else if (symread) ++ symread = 0; if (x[4] == ' ') { @@@ -370,11 -511,11 +538,17 @@@ select_loop(void if (!init) input_notify(!busy); ++ if (!init && !symread && complete) ++ { ++ do_complete(init_cmd); ++ exit(0); ++ } ++ fd_set select_fds; FD_ZERO(&select_fds); FD_SET(server_fd, &select_fds); -- if (!busy) ++ if (!busy && !complete) FD_SET(0, &select_fds); rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); @@@ -459,10 -600,6 +633,14 @@@ main(int argc, char **argv interactive = isatty(0); parse_args(argc, argv); cmd_build_tree(); + - if (complete) ++ /* Shell completion of first word */ ++ if (complete && !strchr(init_cmd, ' ')) ++ { ++ init_list(&symbols); + return do_complete(init_cmd); ++ } + server_connect(); select_loop(); return 0; diff --cc client/client.h index 28b541697,b4a45cc5c..5d3ee8763 --- a/client/client.h +++ b/client/client.h @@@ -6,7 -6,10 +6,10 @@@ * Can be freely distributed and used under the terms of the GNU GPL. */ + #ifndef _BIRD_CLIENT_H_ + #define _BIRD_CLIENT_H_ + -extern int init, busy, interactive; +extern int init, busy, interactive, complete; extern int term_lns, term_cls; /* birdc.c / birdcl.c */ @@@ -30,16 -29,47 +33,52 @@@ void submit_command(char *cmd_raw) /* commands.c */ void cmd_build_tree(void); -void cmd_help(char *cmd, int len); -int cmd_complete(char *cmd, int len, char *buf, int again); +void cmd_help(const char *cmd, int len); +int cmd_complete(const char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); -/* client.c */ +/* complete.c */ + +void complete_init(int argc, char **argv); +int do_complete(char *cmd); +#define COMPLETE_ARGC 3 +extern const char *comp_now, *comp_last; + /* Client Symbol Flags: Types */ + #define CLI_SF_CONSTANT (1 << 0) + #define CLI_SF_VARIABLE (1 << 1) + #define CLI_SF_FILTER (1 << 2) + #define CLI_SF_FUNCTION (1 << 3) + #define CLI_SF_PROTOCOL (1 << 4) + #define CLI_SF_TABLE (1 << 5) + #define CLI_SF_TEMPLATE (1 << 6) + #define CLI_SF_INTERFACE (1 << 7) + + #define CLI_SF_OPTIONAL (1 << 8) /* This node is optional not mandatory */ + #define CLI_SF_PARAMETER (1 << 9) /* A parameter/word will follow after this node */ + + /* Client Symbol Flags: Keywords */ + #define CLI_SF_KW_ALL (1 << 10) + #define CLI_SF_KW_OFF (1 << 11) + + + struct cli_symbol + { + node n; + const char *name; + uint len; + u32 flags; /* CLI_SF_* */ + }; + + void submit_command(char *cmd_raw); + /* die() with system error messages */ #define DIE(x, y...) die(x ": %s", ##y, strerror(errno)) + + void retrieve_symbols(void); + void add_keywords_to_symbols(void); + list *cli_get_symbol_list(void); + uint cli_get_symbol_maxlen(void); + void simple_input_read(void); + + #endif diff --cc client/commands.c index 84fcc89d2,ae313d7f8..5ea3f3ff0 --- a/client/commands.c +++ b/client/commands.c @@@ -100,7 -109,7 +109,7 @@@ cmd_display_help(struct cmd_info *c1, s } static struct cmd_node * - cmd_find_abbrev(struct cmd_node *root, const char *cmd, int len, int *pambiguous) -cmd_find_abbrev(struct cmd_node *root, char *cmd, uint len, int *pambiguous) ++cmd_find_abbrev(struct cmd_node *root, const char *cmd, uint len, int *pambiguous) { struct cmd_node *m, *best = NULL, *best2 = NULL; @@@ -127,16 -136,21 +136,27 @@@ } static void - cmd_list_ambiguous(struct cmd_node *root, const char *cmd, int len) -cmd_list_ambiguous(struct cmd_node *root, char *cmd, uint len) ++cmd_list_ambiguous(struct cmd_node *root, const char *cmd, uint len) { struct cmd_node *m; for(m=root->son; m; m=m->sibling) if (m->len > len && !memcmp(m->token, cmd, len)) - cmd_display_help(m->help, m->cmd); + if (complete) + printf("%s\n", m->token); + else + cmd_display_help(m->help, m->cmd); + - struct cli_symbol *sym; + list *syms = cli_get_symbol_list(); ++ if (!syms) ++ return; ++ ++ struct cli_symbol *sym; + WALK_LIST(sym, *syms) + { + if ((sym->flags & root->flags) && sym->len > len && memcmp(sym->name, cmd, len) == 0) + printf("%s\n", sym->name); + } } void @@@ -173,14 -187,43 +193,43 @@@ cmd_help(const char *cmd, int len cmd_display_help(m->help, m->cmd); } + /* + * Return length of common prefix of all matches, + * Write common prefix string into buf + */ + static int + cmd_merge_match_with_others(int max_common_len, const char *token_name, int token_len, char *buf, int from) + { + if (max_common_len < 0) + { + /* For a case that we'll have exactly one match */ + strcpy(buf, token_name + from); + max_common_len = token_len - from; + } + else + { + int i = 0; + while (i < max_common_len && i < token_len - from && buf[i] == token_name[from+i]) + i++; + max_common_len = i; + } + return max_common_len; + } + + /* + * Return length of common prefix of all matches, + * Write count of all matches into pcount, + * Write common prefix string into buf + */ static int - cmd_find_common_match(struct cmd_node *root, const char *cmd, int len, int *pcount, char *buf) -cmd_find_common_match(struct cmd_node *root, char *cmd, uint len, int *pcount, char *buf) ++cmd_find_common_match(struct cmd_node *root, const char *cmd, uint len, int *pcount, char *buf) { struct cmd_node *m; - int best, best_prio, i; + int max_common_len; + int best_prio; *pcount = 0; - best = -1; + max_common_len = -1; best_prio = -1; for(m=root->son; m; m=m->sibling) { @@@ -193,35 -236,41 +242,44 @@@ if (best_prio < m->prio) { *pcount = 0; - best = -1; + max_common_len = -1; } + if (max_common_len < 0) + best_prio = m->prio; + (*pcount)++; - if (best < 0) - { - strcpy(buf, m->token + len); - best = m->len - len; - best_prio = m->prio; - } - else - { - i = 0; - while (i < best && i < m->len - len && buf[i] == m->token[len+i]) - i++; - best = i; - } + max_common_len = cmd_merge_match_with_others(max_common_len, m->token, m->len, buf, len); } - return best; + + list *syms = cli_get_symbol_list(); ++ if (!syms) ++ return max_common_len; ++ + struct cli_symbol *sym; + WALK_LIST(sym, *syms) + { + if (!(sym->flags & root->flags)) + continue; + + if (sym->len < len || memcmp(sym->name, cmd, len)) + continue; + + (*pcount)++; + max_common_len = cmd_merge_match_with_others(max_common_len, sym->name, sym->len, buf, len); + } + + return max_common_len; } int -cmd_complete(char *cmd, int len, char *buf, int again) +cmd_complete(const char *cmd, int len, char *buf, int again) { - char *start = cmd; - char *end = cmd + len; - char *fin; + const char *start = cmd; + const char *end = cmd + len; + const char *fin; - struct cmd_node *n, *m; + struct cmd_node *n, *m = NULL; - char *z; + const char *z; int ambig, cnt = 0, common; /* Find the last word we want to complete */ @@@ -271,7 -345,7 +356,7 @@@ buf[common] = 0; return 1; } -- if (common > 0) ++ if (!complete && (common > 0)) { buf[common] = 0; return 1; diff --cc client/complete.c index 559f3c65f,000000000..f30cdd989 mode 100644,000000..100644 --- a/client/complete.c +++ b/client/complete.c @@@ -1,123 -1,0 +1,59 @@@ +/* + * BIRD Client Bash Expansion + * + * (c) 2017 Jan Moskyto Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + + +#include +#include +#include + +#include "nest/bird.h" +#include "client/client.h" + +static int comp_type, comp_cword; +const char *comp_now, *comp_last; + +void complete_init(int argc, char **argv) { + /* In argv, there are: + * $NOW + * $COMP_TYPE + * $COMP_CWORD + * $COMP_POINT + * ${COMP_WORDS[@]} + */ + + comp_now = argv[0]; + + if (argc < COMPLETE_ARGC) + die("Not enough args."); + + if (sscanf(argv[1], "%d", &comp_type) != 1) + die("Strange COMP_TYPE=\"%s\".", argv[1]); + + if (sscanf(argv[2], "%d", &comp_cword) != 1) + die("Strange COMP_CWORD=\"%s\".", argv[2]); + + if (comp_cword + COMPLETE_ARGC >= argc) + die("COMP_CWORD=%d points after end of arg list.", comp_cword); + + comp_last = argv[COMPLETE_ARGC + comp_cword]; + return; +} + +int do_complete(char *cmd) { + if ((*cmd == 0) && (comp_type == 63)) + printf("-s\n-l\n-v\n-r\n"); + + char buf[256]; - int res = cmd_complete(cmd, strlen(cmd), buf, (comp_type == 63)); ++ int res = cmd_complete(cmd, strlen(cmd), buf, 1); + if (res == 1) + printf("%s%s\n", comp_now, buf); + + + return 0; +} + - #if 0 - - /* Environment and input check */ - if (!comp_line || !index(comp_line, ' ')) - die("Environment variable COMP_LINE not found."); - - /* Drop the command name */ - comp_line = index(comp_line, ' ') + 1; - - /* StrTok copy */ - char *clt = strdup(comp_line); - char *tok = strtok(clt, " "); - do { - if (!tok) - break; - if (!tok[0]) - goto next; - - if (want_socket) { - opt_s = tok; - goto next; - } - - if (tok[0] == '-') - switch(tok[1]) { - case 's': - if (tok[2]) - opt_s = tok+2; - else - want_socket = 1; - goto next; - case 'v': - opt_v++; - goto next; - case 'r': - opt_r++; - goto next; - case 'l': - opt_l++; - goto next; - default: - return 0; - } - - next: - tok = strtok(NULL, " "); - } while (tok); - - - - fprintf(stderr, "KEY \"%s\"\nLINE \"%s\"\nPOINT \"%s\"\nTYPE \"%s\"\n", - comp_key, comp_line, comp_point, comp_type); - - char buf[256]; - int result = cmd_complete(comp_line, atoi(comp_point), buf, 0); - - if (result < 0) - return 0; - - puts(buf); - - return 0; - } - #endif diff --cc nest/cli.c index aceb5770c,af37c5975..70d1042fc --- a/nest/cli.c +++ b/nest/cli.c @@@ -196,13 -199,13 +199,6 @@@ cli_copy_message(cli *c q[-1] = '\n'; } --static void --cli_hello(cli *c) --{ -- cli_printf(c, 1, "BIRD " BIRD_VERSION " ready."); -- c->cont = NULL; --} -- static void cli_free_out(cli *c) { @@@ -312,7 -314,7 +307,7 @@@ cli_new(void *priv c->event = ev_new(p); c->event->hook = cli_event; c->event->data = c; -- c->cont = cli_hello; ++ c->cont = NULL; c->parser_pool = lp_new_default(c->pool); c->show_pool = lp_new_default(c->pool); c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);