From: David Lamparter Date: Wed, 10 May 2017 14:38:48 +0000 (+0200) Subject: vtysh: autocomplete variables X-Git-Tag: reindent-master-before~164 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=7f059ea614bf310015bcb4e9b8dd7b1f6c6e072a;p=matthieu%2Ffrr.git vtysh: autocomplete variables This asks the connected daemons for their variable completions through a hidden CLI command. Signed-off-by: David Lamparter --- diff --git a/lib/command.c b/lib/command.c index 5b4c63fa95..cc597952e4 100644 --- a/lib/command.c +++ b/lib/command.c @@ -727,6 +727,38 @@ cmd_variable_handler_register (const struct cmd_variable_handler *cvh) listnode_add(varhandlers, (void *)cvh); } +DEFUN_HIDDEN (autocomplete, + autocomplete_cmd, + "autocomplete TYPE TEXT VARNAME", + "Autocompletion handler (internal, for vtysh)\n" + "cmd_token->type\n" + "cmd_token->text\n" + "cmd_token->varname\n") +{ + struct cmd_token tok; + vector comps = vector_init(32); + size_t i; + + memset(&tok, 0, sizeof(tok)); + tok.type = atoi(argv[1]->arg); + tok.text = argv[2]->arg; + tok.varname = argv[3]->arg; + if (!strcmp(tok.varname, "-")) + tok.varname = NULL; + + cmd_variable_complete(&tok, NULL, comps); + + for (i = 0; i < vector_active(comps); i++) + { + char *text = vector_slot(comps, i); + vty_out(vty, "%s\n", text); + XFREE(MTYPE_COMPLETION, text); + } + + vector_free(comps); + return CMD_SUCCESS; +} + /** * Generate possible tab-completions for the given input. This function only * returns results that would result in a valid command if used as Readline @@ -2434,6 +2466,8 @@ install_default (enum node_type node) install_element (node, &config_write_cmd); install_element (node, &show_running_config_cmd); + + install_element (node, &autocomplete_cmd); } /* Initialize command interface. Install basic nodes and commands. @@ -2483,6 +2517,7 @@ cmd_init (int terminal) install_element (VIEW_NODE, &show_logging_cmd); install_element (VIEW_NODE, &show_commandtree_cmd); install_element (VIEW_NODE, &echo_cmd); + install_element (VIEW_NODE, &autocomplete_cmd); } if (terminal) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c28d57cff2..fcae717374 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -107,12 +107,9 @@ begins_with(const char *str, const char *prefix) return strncmp(str, prefix, lenprefix) == 0; } -/* NB: multiplexed function: - * if fp == NULL, this calls vtysh_config_parse_line - * if fp != NULL, this prints lines to fp - */ static int -vtysh_client_run (struct vtysh_client *vclient, const char *line, FILE *fp) +vtysh_client_run (struct vtysh_client *vclient, const char *line, FILE *fp, + void (*callback)(void *, const char *), void *cbarg) { int ret; char stackbuf[4096]; @@ -178,8 +175,8 @@ vtysh_client_run (struct vtysh_client *vclient, const char *line, FILE *fp) fputs (buf, fp); fputc ('\n', fp); } - else - vtysh_config_parse_line (buf); + if (callback) + callback(cbarg, buf); if (eol == end) /* \n\0\0\0 */ @@ -223,14 +220,15 @@ out: static int vtysh_client_run_all (struct vtysh_client *head_client, const char *line, - int continue_on_err, FILE *fp) + int continue_on_err, FILE *fp, + void (*callback)(void *, const char *), void *cbarg) { struct vtysh_client *client; int rc, rc_all = CMD_SUCCESS; for (client = head_client; client; client = client->next) { - rc = vtysh_client_run(client, line, fp); + rc = vtysh_client_run(client, line, fp, callback, cbarg); if (rc != CMD_SUCCESS) { if (!continue_on_err) @@ -245,13 +243,13 @@ static int vtysh_client_execute (struct vtysh_client *head_client, const char *line, FILE *fp) { - return vtysh_client_run_all (head_client, line, 0, fp); + return vtysh_client_run_all (head_client, line, 0, fp, NULL, NULL); } static void vtysh_client_config (struct vtysh_client *head_client, char *line) { - vtysh_client_run_all (head_client, line, 1, NULL); + vtysh_client_run_all (head_client, line, 1, NULL, vtysh_config_parse_line, NULL); } void @@ -796,6 +794,27 @@ vtysh_rl_describe (void) width, token->text, token->desc); + + if (IS_VARYING_TOKEN(token->type)) + { + const char *ref = vector_slot(vline, vector_active(vline) - 1); + + vector varcomps = vector_init (VECTOR_MIN_SIZE); + cmd_variable_complete (token, ref, varcomps); + + if (vector_active (varcomps) > 0) + { + fprintf(stdout, " "); + for (size_t j = 0; j < vector_active (varcomps); j++) + { + char *item = vector_slot (varcomps, j); + fprintf (stdout, " %s", item); + XFREE (MTYPE_COMPLETION, item); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + vector_free (varcomps); + } } cmd_free_strvec (vline); @@ -838,6 +857,7 @@ command_generator (const char *text, int state) } if (matched && matched[index]) + /* this is free()'d by readline, but we leak 1 count of MTYPE_COMPLETION */ return matched[index++]; XFREE (MTYPE_TMP, matched); @@ -3193,6 +3213,36 @@ vtysh_prompt (void) return buf; } +static void vtysh_ac_line(void *arg, const char *line) +{ + vector comps = arg; + size_t i; + for (i = 0; i < vector_active(comps); i++) + if (!strcmp(line, (char *)vector_slot(comps, i))) + return; + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, line)); +} + +static void vtysh_autocomplete(vector comps, struct cmd_token *token) +{ + char accmd[256]; + size_t i; + + snprintf(accmd, sizeof(accmd), "autocomplete %d %s %s", token->type, + token->text, token->varname ? token->varname : "-"); + + for (i = 0; i < array_size(vtysh_client); i++) + vtysh_client_run_all (&vtysh_client[i], accmd, 1, NULL, + vtysh_ac_line, comps); +} + +static const struct cmd_variable_handler vtysh_var_handler = { + /* match all */ + .tokenname = NULL, + .varname = NULL, + .completions = vtysh_autocomplete +}; + void vtysh_init_vty (void) { @@ -3203,6 +3253,7 @@ vtysh_init_vty (void) /* Initialize commands. */ cmd_init (0); + cmd_variable_handler_register(&vtysh_var_handler); /* Install nodes. */ install_node (&bgp_node, NULL); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index ad45abcdf3..57151aceed 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -85,7 +85,7 @@ int vtysh_mark_file(const char *filename); int vtysh_read_config (const char *); int vtysh_write_config_integrated (void); -void vtysh_config_parse_line (const char *); +void vtysh_config_parse_line (void *, const char *); void vtysh_config_dump (FILE *); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 2a84847aaa..aa003c7528 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -145,7 +145,7 @@ config_add_line_uniq (struct list *config, const char *line) } void -vtysh_config_parse_line (const char *line) +vtysh_config_parse_line (void *arg, const char *line) { char c; static struct config *config = NULL; @@ -418,12 +418,12 @@ vtysh_config_write () if (host.name) { sprintf (line, "hostname %s", host.name); - vtysh_config_parse_line(line); + vtysh_config_parse_line(NULL, line); } if (vtysh_write_integrated == WRITE_INTEGRATED_NO) - vtysh_config_parse_line ("no service integrated-vtysh-config"); + vtysh_config_parse_line (NULL, "no service integrated-vtysh-config"); if (vtysh_write_integrated == WRITE_INTEGRATED_YES) - vtysh_config_parse_line ("service integrated-vtysh-config"); + vtysh_config_parse_line (NULL, "service integrated-vtysh-config"); user_config_write (); }