]> git.puffer.fish Git - mirror/frr.git/commitdiff
grammar_sandbox: add .dot file writing capability
authorDavid Lamparter <equinox@opensourcerouting.org>
Mon, 5 Dec 2016 22:03:57 +0000 (23:03 +0100)
committerDavid Lamparter <equinox@opensourcerouting.org>
Mon, 23 Jan 2017 20:52:44 +0000 (21:52 +0100)
Writing a .dot graphviz file is very useful to get a graphical
representation of the command graph.

This code is intended solely for testing & development, it uses some
fixed-size array and may unexpectedly crash.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
lib/grammar_sandbox.c

index b5ac36c41fb54182dde1fea7c039ed1f0db12db8..98c75ca6e3f8ad2876d4932ce24adace093590ed 100644 (file)
@@ -45,6 +45,10 @@ 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
@@ -240,6 +244,36 @@ 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;
+}
+
 DEFUN (grammar_init_graph,
        grammar_init_graph_cmd,
        "grammar init",
@@ -258,6 +292,7 @@ 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);
@@ -345,6 +380,83 @@ pretty_print_graph (struct vty *vty, struct graph_node *start, int level,
     vty_out(vty, "%s", VTY_NEWLINE);
 }
 
+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)
+{
+  // print this node
+  char tokennum[32];
+  struct cmd_token *tok = start->data;
+  const char *color;
+
+  for (size_t i = 0; i < (*visitpos); i++)
+    if (visited[i] == start)
+      return;
+  visited[(*visitpos)++] = start;
+  if ((*visitpos) == MAXDEPTH*MAXDEPTH)
+    return;
+
+  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);
+
+  if (stackpos == MAXDEPTH)
+    return;
+  stack[stackpos++] = start;
+
+  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);
+        }
+      }
+   }
+}
+
+
 /** stuff that should go in command.c + command.h */
 void
 init_cmdgraph (struct vty *vty, struct graph **graph)