summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/command.c62
-rw-r--r--lib/command.h9
-rw-r--r--lib/command_graph.h2
-rw-r--r--lib/if.c32
-rw-r--r--lib/plist.c36
-rw-r--r--lib/routemap.c28
-rw-r--r--lib/vty.c26
7 files changed, 187 insertions, 8 deletions
diff --git a/lib/command.c b/lib/command.c
index 0e19a3dfee..5b4c63fa95 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -46,6 +46,7 @@
DEFINE_MTYPE( LIB, HOST, "Host config")
DEFINE_MTYPE( LIB, STRVEC, "String vector")
+DEFINE_MTYPE( LIB, COMPLETION, "Completion item")
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
@@ -678,6 +679,54 @@ cmd_describe_command (vector vline, struct vty *vty, int *status)
return cmd_complete_command_real (vline, vty, status);
}
+static struct list *varhandlers = NULL;
+
+void
+cmd_variable_complete (struct cmd_token *token, const char *arg, vector comps)
+{
+ struct listnode *ln;
+ const struct cmd_variable_handler *cvh;
+ size_t i, argsz;
+ vector tmpcomps;
+
+ tmpcomps = arg ? vector_init (VECTOR_MIN_SIZE) : comps;
+
+ for (ALL_LIST_ELEMENTS_RO(varhandlers, ln, cvh))
+ {
+ if (cvh->tokenname && strcmp(cvh->tokenname, token->text))
+ continue;
+ if (cvh->varname && (!token->varname || strcmp(cvh->varname, token->varname)))
+ continue;
+ cvh->completions(tmpcomps, token);
+ break;
+ }
+
+ if (!arg)
+ return;
+
+ argsz = strlen(arg);
+ for (i = vector_active(tmpcomps); i; i--)
+ {
+ char *item = vector_slot(tmpcomps, i - 1);
+ if (strlen(item) >= argsz
+ && !strncmp(item, arg, argsz))
+ vector_set(comps, item);
+ else
+ XFREE(MTYPE_COMPLETION, item);
+ }
+ vector_free(tmpcomps);
+}
+
+void
+cmd_variable_handler_register (const struct cmd_variable_handler *cvh)
+{
+ if (!varhandlers)
+ return;
+
+ for (; cvh->completions; cvh++)
+ listnode_add(varhandlers, (void *)cvh);
+}
+
/**
* Generate possible tab-completions for the given input. This function only
* returns results that would result in a valid command if used as Readline
@@ -719,7 +768,12 @@ cmd_complete_command (vector vline, struct vty *vty, int *status)
{
struct cmd_token *token = vector_slot (initial_comps, i);
if (token->type == WORD_TKN)
- vector_set (comps, token);
+ vector_set (comps, XSTRDUP (MTYPE_COMPLETION, token->text));
+ else if (IS_VARYING_TOKEN(token->type))
+ {
+ const char *ref = vector_lookup(vline, vector_active (vline) - 1);
+ cmd_variable_complete (token, ref, comps);
+ }
}
vector_free (initial_comps);
@@ -741,9 +795,7 @@ cmd_complete_command (vector vline, struct vty *vty, int *status)
unsigned int i;
for (i = 0; i < vector_active (comps); i++)
{
- struct cmd_token *token = vector_slot (comps, i);
- ret[i] = XSTRDUP (MTYPE_TMP, token->text);
- vector_unset (comps, i);
+ ret[i] = vector_slot (comps, i);
}
// set the last element to NULL, because this array is used in
// a Readline completion_generator function which expects NULL
@@ -2394,6 +2446,8 @@ cmd_init (int terminal)
{
qobj_init ();
+ varhandlers = list_new ();
+
/* Allocate initial top vector of commands. */
cmdvec = vector_init (VECTOR_MIN_SIZE);
diff --git a/lib/command.h b/lib/command.h
index 4531ec9a17..1aca8b4ae1 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -32,6 +32,7 @@
#include "command_graph.h"
DECLARE_MTYPE(HOST)
+DECLARE_MTYPE(COMPLETION)
/* for test-commands.c */
DECLARE_MTYPE(STRVEC)
@@ -391,4 +392,12 @@ extern int cmd_banner_motd_file (const char *);
/* struct host global, ick */
extern struct host host;
+struct cmd_variable_handler {
+ const char *tokenname, *varname;
+ void (*completions)(vector out, struct cmd_token *token);
+};
+
+extern void cmd_variable_complete (struct cmd_token *token, const char *arg, vector comps);
+extern void cmd_variable_handler_register (const struct cmd_variable_handler *cvh);
+
#endif /* _ZEBRA_COMMAND_H */
diff --git a/lib/command_graph.h b/lib/command_graph.h
index 11cea9bd42..595508d5ce 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -61,6 +61,8 @@ enum cmd_token_type
SPECIAL_TKN = FORK_TKN,
};
+#define IS_VARYING_TOKEN(x) ((x) >= VARIABLE_TKN && (x) < FORK_TKN)
+
/* Command attributes */
enum
{
diff --git a/lib/if.c b/lib/if.c
index ecb7463168..3fbf2df6a0 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -1126,6 +1126,36 @@ ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex)
}
#endif /* ifaddr_ipv4_table */
+static void if_autocomplete(vector comps, struct cmd_token *token)
+{
+ struct interface *ifp;
+ struct listnode *ln;
+ struct vrf *vrf = NULL;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ {
+ for (ALL_LIST_ELEMENTS_RO(vrf->iflist, ln, ifp))
+ vector_set (comps, XSTRDUP (MTYPE_COMPLETION, ifp->name));
+ }
+
+}
+
+static const struct cmd_variable_handler if_var_handlers[] = {
+ {
+ /* "interface NAME" */
+ .varname = "interface",
+ .completions = if_autocomplete
+ }, {
+ .tokenname = "IFNAME",
+ .completions = if_autocomplete
+ }, {
+ .tokenname = "INTERFACE",
+ .completions = if_autocomplete
+ }, {
+ .completions = NULL
+ }
+};
+
/* Initialize interface list. */
void
if_init (struct list **intf_list)
@@ -1136,6 +1166,8 @@ if_init (struct list **intf_list)
#endif /* ifaddr_ipv4_table */
(*intf_list)->cmp = (int (*)(void *, void *))if_cmp_func;
+
+ cmd_variable_handler_register(if_var_handlers);
}
void
diff --git a/lib/plist.c b/lib/plist.c
index 3714969696..8f59c0c058 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -3157,6 +3157,40 @@ config_write_prefix_ipv4 (struct vty *vty)
}
static void
+plist_autocomplete_afi (afi_t afi, vector comps, struct cmd_token *token)
+{
+ struct prefix_list *plist;
+ struct prefix_master *master;
+
+ master = prefix_master_get (afi, 0);
+ if (master == NULL)
+ return;
+
+ for (plist = master->str.head; plist; plist = plist->next)
+ vector_set (comps, XSTRDUP (MTYPE_COMPLETION, plist->name));
+ for (plist = master->num.head; plist; plist = plist->next)
+ vector_set (comps, XSTRDUP (MTYPE_COMPLETION, plist->name));
+}
+
+static void
+plist_autocomplete(vector comps, struct cmd_token *token)
+{
+ plist_autocomplete_afi (AFI_IP, comps, token);
+ plist_autocomplete_afi (AFI_IP6, comps, token);
+}
+
+static const struct cmd_variable_handler plist_var_handlers[] = {
+ {
+ /* "prefix-list WORD" */
+ .varname = "prefix_list",
+ .completions = plist_autocomplete
+ }, {
+ .completions = NULL
+ }
+};
+
+
+static void
prefix_list_init_ipv4 (void)
{
install_node (&prefix_node, config_write_prefix_ipv4);
@@ -3275,6 +3309,8 @@ prefix_list_init_ipv6 (void)
void
prefix_list_init ()
{
+ cmd_variable_handler_register(plist_var_handlers);
+
prefix_list_init_ipv4 ();
prefix_list_init_ipv6 ();
}
diff --git a/lib/routemap.c b/lib/routemap.c
index cd34ffaae5..482155987d 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -2001,7 +2001,7 @@ DEFUN (match_interface,
DEFUN (no_match_interface,
no_match_interface_cmd,
- "no match interface [INTERFACE]",
+ "no match interface [WORD]",
NO_STR
MATCH_STR
"Match first hop interface of route\n"
@@ -2958,6 +2958,30 @@ route_map_finish (void)
route_map_master_hash = NULL;
}
+static void rmap_autocomplete(vector comps, struct cmd_token *token)
+{
+ struct route_map *map;
+
+ for (map = route_map_master.head; map; map = map->next)
+ vector_set (comps, XSTRDUP (MTYPE_COMPLETION, map->name));
+}
+
+static const struct cmd_variable_handler rmap_var_handlers[] = {
+ {
+ /* "route-map WORD" */
+ .varname = "route_map",
+ .completions = rmap_autocomplete
+ }, {
+ .tokenname = "ROUTEMAP_NAME",
+ .completions = rmap_autocomplete
+ }, {
+ .tokenname = "RMAP_NAME",
+ .completions = rmap_autocomplete
+ }, {
+ .completions = NULL
+ }
+};
+
/* Initialization of route map vector. */
void
route_map_init (void)
@@ -2973,6 +2997,8 @@ route_map_init (void)
route_map_dep_hash[i] = hash_create(route_map_dep_hash_make_key,
route_map_dep_hash_cmp);
+ cmd_variable_handler_register(rmap_var_handlers);
+
/* Install route map top node. */
install_node (&rmap_node, route_map_config_write);
diff --git a/lib/vty.c b/lib/vty.c
index a8e54a57de..83dc0106fb 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -955,14 +955,14 @@ vty_complete_command (struct vty *vty)
vty_backward_pure_word (vty);
vty_insert_word_overwrite (vty, matched[0]);
vty_self_insert (vty, ' ');
- XFREE (MTYPE_TMP, matched[0]);
+ XFREE (MTYPE_COMPLETION, matched[0]);
break;
case CMD_COMPLETE_MATCH:
vty_prompt (vty);
vty_redraw_line (vty);
vty_backward_pure_word (vty);
vty_insert_word_overwrite (vty, matched[0]);
- XFREE (MTYPE_TMP, matched[0]);
+ XFREE (MTYPE_COMPLETION, matched[0]);
break;
case CMD_COMPLETE_LIST_MATCH:
for (i = 0; matched[i] != NULL; i++)
@@ -970,7 +970,7 @@ vty_complete_command (struct vty *vty)
if (i != 0 && ((i % 6) == 0))
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "%-10s ", matched[i]);
- XFREE (MTYPE_TMP, matched[i]);
+ XFREE (MTYPE_COMPLETION, matched[i]);
}
vty_out (vty, "%s", VTY_NEWLINE);
@@ -1109,6 +1109,26 @@ vty_describe_command (struct vty *vty)
else
vty_describe_fold (vty, width, desc_width, token);
+ 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)
+ {
+ vty_out(vty, " ");
+ for (size_t j = 0; j < vector_active (varcomps); j++)
+ {
+ char *item = vector_slot (varcomps, j);
+ vty_out(vty, " %s", item);
+ XFREE(MTYPE_COMPLETION, item);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ vector_free(varcomps);
+ }
#if 0
vty_out (vty, " %-*s %s%s", width
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,