vtysh: autocomplete variables

This asks the connected daemons for their variable completions through a
hidden CLI command.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
David Lamparter 2017-05-10 16:38:48 +02:00 committed by Quentin Young
parent 70d44c5cd4
commit 7f059ea614
4 changed files with 102 additions and 16 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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 *);

View File

@ -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 ();
}