]> git.proxmox.com Git - mirror_frr.git/blobdiff - lib/grammar_sandbox.c
Merge branch 'frr/pull/236' ("tools: frr-reload.py needs to treat "mpls" as a single...
[mirror_frr.git] / lib / grammar_sandbox.c
index 0239ca44ac9c84b6ddb3bd6af4e289019dd0fed2..e3a7c979fcc176e1d82adfca6933966501f0a36c 100644 (file)
@@ -3,11 +3,6 @@
  *
  * This unit defines a number of commands in the old engine that can
  * be used to test and interact with the new engine.
- *
- * This shim should be removed upon integration. It is currently hooked in
- * vtysh/vtysh.c. It has no header, vtysh.c merely includes this entire unit
- * since it clutters up the makefiles less and this is only a temporary shim.
- *
  * --
  * Copyright (C) 2016 Cumulus Networks, Inc.
  *
@@ -32,6 +27,7 @@
 #include "command.h"
 #include "memory_vty.h"
 #include "graph.h"
+#include "linklist.h"
 #include "command_match.h"
 
 #define GRAMMAR_STR "CLI grammar sandbox\n"
@@ -45,15 +41,15 @@ void
 grammar_sandbox_init (void);
 void
 pretty_print_graph (struct vty *vty, struct graph_node *, int, int, struct graph_node **, size_t);
+static void
+pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start,
+                  struct graph_node **stack, size_t stackpos,
+                  struct graph_node **visited, size_t *visitpos);
 void
 init_cmdgraph (struct vty *, struct graph **);
-vector
-completions_to_vec (struct list *);
-int
-compare_completions (const void *, const void *);
 
 /** shim interface commands **/
-struct graph *nodegraph;
+struct graph *nodegraph = NULL, *nodegraph_free = NULL;
 
 DEFUN (grammar_test,
        grammar_test_cmd,
@@ -73,7 +69,12 @@ DEFUN (grammar_test,
   cmd->func = NULL;
 
   // parse the command and install it into the command graph
-  command_parse_format (nodegraph, cmd);
+  struct graph *graph = graph_new();
+  struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+  graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token);
+
+  command_parse_format (graph, cmd);
+  cmd_merge_graphs (nodegraph, graph, +1);
 
   return CMD_SUCCESS;
 }
@@ -91,6 +92,11 @@ DEFUN (grammar_test_complete,
     return CMD_SUCCESS;
 
   vector command = cmd_make_strvec (cmdstr);
+  if (!command)
+    {
+      XFREE (MTYPE_TMP, cmdstr);
+      return CMD_SUCCESS;
+    }
 
   // generate completions of user input
   struct list *completions;
@@ -126,7 +132,7 @@ DEFUN (grammar_test_complete,
   // free resources
   list_delete (completions);
   cmd_free_strvec (command);
-  free (cmdstr);
+  XFREE (MTYPE_TMP, cmdstr);
 
   return CMD_SUCCESS;
 }
@@ -143,7 +149,14 @@ DEFUN (grammar_test_match,
     return CMD_SUCCESS;
 
   char *cmdstr = argv_concat(argv, argc, idx_command);
+  if (!cmdstr)
+    return CMD_SUCCESS;
   vector command = cmd_make_strvec (cmdstr);
+  if (!command)
+    {
+       XFREE (MTYPE_TMP, cmdstr);
+       return CMD_SUCCESS;
+    }
 
   struct list *argvv = NULL;
   const struct cmd_element *element = NULL;
@@ -182,7 +195,7 @@ DEFUN (grammar_test_match,
 
   // free resources
   cmd_free_strvec (command);
-  free (cmdstr);
+  XFREE (MTYPE_TMP, cmdstr);
 
   return CMD_SUCCESS;
 }
@@ -240,17 +253,226 @@ DEFUN (grammar_test_show,
   return CMD_SUCCESS;
 }
 
+DEFUN (grammar_test_dot,
+       grammar_test_dot_cmd,
+       "grammar dotfile OUTNAME",
+       GRAMMAR_STR
+       "print current graph for dot\n"
+       ".dot filename\n")
+{
+  struct graph_node *stack[MAXDEPTH];
+  struct graph_node *visited[MAXDEPTH*MAXDEPTH];
+  size_t vpos = 0;
+
+  if (!nodegraph) {
+    vty_out(vty, "nodegraph uninitialized\r\n");
+    return CMD_SUCCESS;
+  }
+  FILE *ofd = fopen(argv[2]->arg, "w");
+  if (!ofd) {
+    vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno));
+    return CMD_SUCCESS;
+  }
+
+  fprintf(ofd, "digraph {\n  graph [ rankdir = LR ];\n  node [ fontname = \"Fira Mono\", fontsize = 9 ];\n\n");
+  pretty_print_dot (ofd, 0,
+                    vector_slot (nodegraph->nodes, 0),
+                    stack, 0, visited, &vpos);
+  fprintf(ofd, "}\n");
+  fclose(ofd);
+  return CMD_SUCCESS;
+}
+
+struct cmd_permute_item
+{
+  char *cmd;
+  struct cmd_element *el;
+};
+
+static void
+cmd_permute_free (void *arg)
+{
+  struct cmd_permute_item *i = arg;
+  XFREE (MTYPE_TMP, i->cmd);
+  XFREE (MTYPE_TMP, i);
+}
+
+static int
+cmd_permute_cmp (void *a, void *b)
+{
+  struct cmd_permute_item *aa = a, *bb = b;
+  return strcmp (aa->cmd, bb->cmd);
+}
+
+static void
+cmd_graph_permute (struct list *out, struct graph_node **stack,
+                   size_t stackpos, char *cmd)
+{
+  struct graph_node *gn = stack[stackpos];
+  struct cmd_token *tok = gn->data;
+  char *appendp = cmd + strlen(cmd);
+  size_t i, j;
+
+  if (tok->type < SPECIAL_TKN)
+    {
+      sprintf (appendp, "%s ", tok->text);
+      appendp += strlen (appendp);
+    }
+  else if (tok->type == END_TKN)
+    {
+      struct cmd_permute_item *i = XMALLOC (MTYPE_TMP, sizeof (*i));
+      i->el = ((struct graph_node *)vector_slot (gn->to, 0))->data;
+      i->cmd = XSTRDUP (MTYPE_TMP, cmd);
+      i->cmd[strlen(cmd) - 1] = '\0';
+      listnode_add_sort (out, i);
+      return;
+    }
+
+  if (++stackpos == MAXDEPTH)
+    return;
+
+  for (i = 0; i < vector_active (gn->to); i++)
+    {
+      struct graph_node *gnext = vector_slot (gn->to, i);
+      for (j = 0; j < stackpos; j++)
+        if (stack[j] == gnext)
+          break;
+      if (j != stackpos)
+        continue;
+
+      stack[stackpos] = gnext;
+      *appendp = '\0';
+      cmd_graph_permute (out, stack, stackpos, cmd);
+    }
+}
+
+static struct list *
+cmd_graph_permutations (struct graph *graph)
+{
+  char accumulate[2048] = "";
+  struct graph_node *stack[MAXDEPTH];
+
+  struct list *rv = list_new ();
+  rv->cmp = cmd_permute_cmp;
+  rv->del = cmd_permute_free;
+  stack[0] = vector_slot (graph->nodes, 0);
+  cmd_graph_permute (rv, stack, 0, accumulate);
+  return rv;
+}
+
+extern vector cmdvec;
+
+DEFUN (grammar_findambig,
+       grammar_findambig_cmd,
+       "grammar find-ambiguous [{printall|nodescan}]",
+       GRAMMAR_STR
+       "Find ambiguous commands\n"
+       "Print all permutations\n"
+       "Scan all nodes\n")
+{
+  struct list *commands;
+  struct cmd_permute_item *prev = NULL, *cur = NULL;
+  struct listnode *ln;
+  int i, printall, scan, scannode = 0;
+  int ambig = 0;
+
+  i = 0;
+  printall = argv_find (argv, argc, "printall", &i);
+  i = 0;
+  scan = argv_find (argv, argc, "nodescan", &i);
+
+  if (scan && nodegraph_free)
+    {
+      graph_delete_graph (nodegraph_free);
+      nodegraph_free = NULL;
+    }
+
+  if (!scan && !nodegraph)
+    {
+      vty_out(vty, "nodegraph uninitialized\r\n");
+      return CMD_WARNING;
+    }
+
+  do {
+    if (scan)
+      {
+        struct cmd_node *cnode = vector_slot (cmdvec, scannode++);
+        if (!cnode)
+          continue;
+        nodegraph = cnode->cmdgraph;
+        if (!nodegraph)
+          continue;
+        vty_out (vty, "scanning node %d%s", scannode - 1, VTY_NEWLINE);
+      }
+
+    commands = cmd_graph_permutations (nodegraph);
+    prev = NULL;
+    for (ALL_LIST_ELEMENTS_RO (commands, ln, cur))
+      {
+        int same = prev && !strcmp (prev->cmd, cur->cmd);
+        if (printall && !same)
+          vty_out (vty, "'%s'%s", cur->cmd, VTY_NEWLINE);
+        if (same)
+          {
+            vty_out (vty, "'%s' AMBIGUOUS:%s", cur->cmd, VTY_NEWLINE);
+            vty_out (vty, "  %s%s   '%s'%s", prev->el->name, VTY_NEWLINE, prev->el->string, VTY_NEWLINE);
+            vty_out (vty, "  %s%s   '%s'%s", cur->el->name,  VTY_NEWLINE, cur->el->string,  VTY_NEWLINE);
+            vty_out (vty, "%s", VTY_NEWLINE);
+            ambig++;
+          }
+        prev = cur;
+      }
+    list_delete (commands);
+
+    vty_out (vty, "%s", VTY_NEWLINE);
+  } while (scan && scannode < LINK_PARAMS_NODE);
+
+  vty_out (vty, "%d ambiguous commands found.%s", ambig, VTY_NEWLINE);
+
+  if (scan)
+    nodegraph = NULL;
+  return ambig == 0 ? CMD_SUCCESS : CMD_WARNING;
+}
+
 DEFUN (grammar_init_graph,
        grammar_init_graph_cmd,
        "grammar init",
        GRAMMAR_STR
        "(re)initialize graph\n")
 {
-  graph_delete_graph (nodegraph);
+  if (nodegraph_free)
+    graph_delete_graph (nodegraph_free);
+  nodegraph_free = NULL;
+
   init_cmdgraph (vty, &nodegraph);
   return CMD_SUCCESS;
 }
 
+DEFUN (grammar_access,
+       grammar_access_cmd,
+       "grammar access (0-65535)",
+       GRAMMAR_STR
+       "access node graph\n"
+       "node number\n")
+{
+  if (nodegraph_free)
+    graph_delete_graph (nodegraph_free);
+  nodegraph_free = NULL;
+
+  struct cmd_node *cnode;
+
+  cnode = vector_slot (cmdvec, atoi (argv[2]->arg));
+  if (!cnode)
+    {
+      vty_out (vty, "%% no such node%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vty_out (vty, "node %d%s", (int)cnode->node, VTY_NEWLINE);
+  nodegraph = cnode->cmdgraph;
+  return CMD_SUCCESS;
+}
+
 /* this is called in vtysh.c to set up the testing shim */
 void grammar_sandbox_init(void) {
   init_cmdgraph (NULL, &nodegraph);
@@ -258,10 +480,13 @@ void grammar_sandbox_init(void) {
   // install all enable elements
   install_element (ENABLE_NODE, &grammar_test_cmd);
   install_element (ENABLE_NODE, &grammar_test_show_cmd);
+  install_element (ENABLE_NODE, &grammar_test_dot_cmd);
   install_element (ENABLE_NODE, &grammar_test_match_cmd);
   install_element (ENABLE_NODE, &grammar_test_complete_cmd);
   install_element (ENABLE_NODE, &grammar_test_doc_cmd);
+  install_element (ENABLE_NODE, &grammar_findambig_cmd);
   install_element (ENABLE_NODE, &grammar_init_graph_cmd);
+  install_element (ENABLE_NODE, &grammar_access_cmd);
 }
 
 #define item(x) { x, #x }
@@ -275,9 +500,8 @@ struct message tokennames[] = {
   item(IPV6_PREFIX_TKN),  // IPV6 network prefixes
 
   /* plumbing types */
-  item(SELECTOR_TKN),     // marks beginning of selector
-  item(OPTION_TKN),       // marks beginning of option
-  item(NUL_TKN),          // dummy token
+  item(FORK_TKN),
+  item(JOIN_TKN),
   item(START_TKN),        // first token in line
   item(END_TKN),          // last token in line
   { 0, NULL }
@@ -292,7 +516,7 @@ size_t tokennames_max = array_size(tokennames);
  */
 void
 pretty_print_graph (struct vty *vty, struct graph_node *start, int level,
-               int desc, struct graph_node **stack, size_t stackpos)
+                    int desc, struct graph_node **stack, size_t stackpos)
 {
   // print this node
   char tokennum[32];
@@ -346,92 +570,92 @@ pretty_print_graph (struct vty *vty, struct graph_node *start, int level,
     vty_out(vty, "%s", VTY_NEWLINE);
 }
 
-/** stuff that should go in command.c + command.h */
-void
-init_cmdgraph (struct vty *vty, struct graph **graph)
-{
-  // initialize graph, add start noe
-  *graph = graph_new ();
-  struct cmd_token *token = new_cmd_token (START_TKN, 0, NULL, NULL);
-  graph_new_node (*graph, token, (void (*)(void *)) &del_cmd_token);
-  if (vty)
-    vty_out (vty, "initialized graph%s", VTY_NEWLINE);
-}
-
-int
-compare_completions (const void *fst, const void *snd)
-{
-  struct cmd_token *first = *(struct cmd_token **) fst,
-                     *secnd = *(struct cmd_token **) snd;
-  return strcmp (first->text, secnd->text);
-}
-
-vector
-completions_to_vec (struct list *completions)
+static void
+pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start,
+                  struct graph_node **stack, size_t stackpos,
+                  struct graph_node **visited, size_t *visitpos)
 {
-  vector comps = vector_init (VECTOR_MIN_SIZE);
+  // print this node
+  char tokennum[32];
+  struct cmd_token *tok = start->data;
+  const char *color;
 
-  struct listnode *ln;
-  struct cmd_token *token;
-  unsigned int i, exists;
-  for (ALL_LIST_ELEMENTS_RO(completions,ln,token))
-  {
-    // linear search for token in completions vector
-    exists = 0;
-    for (i = 0; i < vector_active (comps) && !exists; i++)
-    {
-      struct cmd_token *curr = vector_slot (comps, i);
-      exists = !strcmp (curr->text, token->text) &&
-               !strcmp (curr->desc, token->desc);
-    }
+  for (size_t i = 0; i < (*visitpos); i++)
+    if (visited[i] == start)
+      return;
+  visited[(*visitpos)++] = start;
+  if ((*visitpos) == MAXDEPTH*MAXDEPTH)
+    return;
 
-    if (!exists)
-      vector_set (comps, copy_cmd_token (token));
+  snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
+  fprintf(ofd, "  n%016llx [ shape=box, label=<", (unsigned long long)start);
+
+  fprintf(ofd, "<b>%s</b>", LOOKUP_DEF(tokennames, tok->type, tokennum));
+  if (tok->attr == CMD_ATTR_DEPRECATED)
+    fprintf(ofd, " (d)");
+  else if (tok->attr == CMD_ATTR_HIDDEN)
+    fprintf(ofd, " (h)");
+  if (tok->text) {
+    if (tok->type == WORD_TKN)
+      fprintf(ofd, "<br/>\"<font color=\"#0055ff\" point-size=\"11\"><b>%s</b></font>\"", tok->text);
+    else
+      fprintf(ofd, "<br/>%s", tok->text);
   }
+/*  if (desc)
+    fprintf(ofd, " ?'%s'", tok->desc); */
+  switch (tok->type) {
+  case START_TKN:      color = "#ccffcc"; break;
+  case FORK_TKN:       color = "#aaddff"; break;
+  case JOIN_TKN:       color = "#ddaaff"; break;
+  case WORD_TKN:       color = "#ffffff"; break;
+  default:             color = "#ffffff"; break;
+  }
+  fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color);
 
-  // sort completions
-  qsort (comps->index,
-         vector_active (comps),
-         sizeof (void *),
-         &compare_completions);
-
-  return comps;
-}
+  if (stackpos == MAXDEPTH)
+    return;
+  stack[stackpos++] = start;
 
-static void vty_do_exit(void)
-{
-  printf ("\nend.\n");
-  exit (0);
+  for (unsigned int i = 0; i < vector_active (start->to); i++)
+    {
+      struct graph_node *adj = vector_slot (start->to, i);
+      // if this node is a vararg, just print *
+      if (adj == start) {
+        fprintf(ofd, "  n%016llx -> n%016llx;\n",
+                    (unsigned long long)start,
+                    (unsigned long long)start);
+      } else if (((struct cmd_token *)adj->data)->type == END_TKN) {
+        //struct cmd_token *et = adj->data;
+        fprintf(ofd, "  n%016llx -> end%016llx;\n",
+                    (unsigned long long)start,
+                    (unsigned long long)adj);
+        fprintf(ofd, "  end%016llx [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n",
+                    (unsigned long long)adj);
+      } else {
+        fprintf(ofd, "  n%016llx -> n%016llx;\n",
+                    (unsigned long long)start,
+                    (unsigned long long)adj);
+        size_t k;
+        for (k = 0; k < stackpos; k++)
+          if (stack[k] == adj)
+            break;
+        if (k == stackpos) {
+          pretty_print_dot (ofd, opts, adj, stack, stackpos, visited, visitpos);
+        }
+      }
+   }
 }
 
-struct thread_master *master;
 
-int main(int argc, char **argv)
+/** stuff that should go in command.c + command.h */
+void
+init_cmdgraph (struct vty *vty, struct graph **graph)
 {
-  struct thread thread;
-
-  master = thread_master_create ();
-
-  zlog_default = openzlog ("grammar_sandbox", ZLOG_NONE, 0,
-                           LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
-  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
-  zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG);
-  zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
-
-  /* Library inits. */
-  cmd_init (1);
-  host.name = strdup ("test");
-
-  vty_init (master);
-  memory_init ();
-  grammar_sandbox_init();
-
-  vty_stdio (vty_do_exit);
-
-  /* Fetch next active thread. */
-  while (thread_fetch (master, &thread))
-    thread_call (&thread);
-
-  /* Not reached. */
-  exit (0);
+  // initialize graph, add start noe
+  *graph = graph_new ();
+  nodegraph_free = *graph;
+  struct cmd_token *token = new_cmd_token (START_TKN, 0, NULL, NULL);
+  graph_new_node (*graph, token, (void (*)(void *)) &del_cmd_token);
+  if (vty)
+    vty_out (vty, "initialized graph%s", VTY_NEWLINE);
 }