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. */
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
{
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);
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
{
qobj_init ();
+ varhandlers = list_new ();
+
/* Allocate initial top vector of commands. */
cmdvec = vector_init (VECTOR_MIN_SIZE);
#include "command_graph.h"
DECLARE_MTYPE(HOST)
+DECLARE_MTYPE(COMPLETION)
/* for test-commands.c */
DECLARE_MTYPE(STRVEC)
/* 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 */
SPECIAL_TKN = FORK_TKN,
};
+#define IS_VARYING_TOKEN(x) ((x) >= VARIABLE_TKN && (x) < FORK_TKN)
+
/* Command attributes */
enum
{
}
#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)
#endif /* ifaddr_ipv4_table */
(*intf_list)->cmp = (int (*)(void *, void *))if_cmp_func;
+
+ cmd_variable_handler_register(if_var_handlers);
}
void
return config_write_prefix_afi (AFI_IP, 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)
{
void
prefix_list_init ()
{
+ cmd_variable_handler_register(plist_var_handlers);
+
prefix_list_init_ipv4 ();
prefix_list_init_ipv6 ();
}
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"
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)
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);
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++)
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);
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,