]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: cli: autocomplete variables
authorDavid Lamparter <equinox@opensourcerouting.org>
Sat, 19 Nov 2016 10:57:08 +0000 (11:57 +0100)
committerQuentin Young <qlyoung@users.noreply.github.com>
Mon, 15 May 2017 14:27:43 +0000 (10:27 -0400)
Shows known values in the appropriate naming domain when the user hits
<?> or <Tab>.  This patch only works in the telnet CLI, the next patch
adds vtysh support.

Included completions:
- interface names
- route-map names
- prefix-list names

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
lib/command.c
lib/command.h
lib/command_graph.h
lib/if.c
lib/plist.c
lib/routemap.c
lib/vty.c

index 0e19a3dfeefc3c85dc97f330fc8ac8146ddaf72e..5b4c63fa95b29268706a295aecc4482497da33dc 100644 (file)
@@ -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);
 
index 4531ec9a172ed1f960878b5bfa65d95054057f14..1aca8b4ae13c8974f243c7c2b4c6ed8408d80439 100644 (file)
@@ -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 */
index 11cea9bd4289f7c509d9afa19ab115df4e328073..595508d5ce70461908f37c3584499b40b7a021a9 100644 (file)
@@ -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
 {
index ecb74631685f826266d35f01338c77c2d4e1392f..3fbf2df6a0017d9761835b711d46f17b6137ea60 100644 (file)
--- 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
index 3714969696ce7d294b90ba99125ab5e4eb37561a..8f59c0c0582fba9c80e74c4fa3b1d5c1a9471b54 100644 (file)
@@ -3156,6 +3156,40 @@ config_write_prefix_ipv4 (struct vty *vty)
   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)
 {
@@ -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 ();
 }
index cd34ffaae5349567bc277c7b1537eafb99dc4f43..482155987d2fb7889d8ce124e680cf2660470e4d 100644 (file)
@@ -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);
 
index a8e54a57de48f1a1b3d46d19b9f6808cdcaa8eb5..83dc0106fb5c687a9583b62f3e48731def23af84 100644 (file)
--- 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,