]> git.puffer.fish Git - matthieu/frr.git/commitdiff
vtysh: autocomplete variables
authorDavid Lamparter <equinox@opensourcerouting.org>
Wed, 10 May 2017 14:38:48 +0000 (16:38 +0200)
committerQuentin Young <qlyoung@users.noreply.github.com>
Mon, 15 May 2017 14:27:43 +0000 (10:27 -0400)
This asks the connected daemons for their variable completions through a
hidden CLI command.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
lib/command.c
vtysh/vtysh.c
vtysh/vtysh.h
vtysh/vtysh_config.c

index 5b4c63fa95b29268706a295aecc4482497da33dc..cc597952e4abed09ccfb67c5bddce5152bb19975 100644 (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)
index c28d57cff256f703c10de28723e988c12e00062f..fcae717374173cda1513bcaec53931d28b82969d 100644 (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);
index ad45abcdf33185b7eee6bdbe50453cde0feef6a3..57151aceedf974e14c2e968358848de0f66141e0 100644 (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 *);
 
index 2a84847aaa798d56f3fe2231f590023a21f5bee5..aa003c752804d01d2c8b9d57aa82760205932d0e 100644 (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 ();
 }