DEFINE_MTYPE(LIB, STRVEC, "String vector")
DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
+#define item(x) \
+ { \
+ x, #x \
+ }
+
+/* clang-format off */
+const struct message tokennames[] = {
+ item(WORD_TKN),
+ item(VARIABLE_TKN),
+ item(RANGE_TKN),
+ item(IPV4_TKN),
+ item(IPV4_PREFIX_TKN),
+ item(IPV6_TKN),
+ item(IPV6_PREFIX_TKN),
+ item(MAC_TKN),
+ item(MAC_PREFIX_TKN),
+ item(FORK_TKN),
+ item(JOIN_TKN),
+ item(START_TKN),
+ item(END_TKN),
+ {0},
+};
+
const char *node_names[] = {
"auth", // AUTH_NODE,
"view", // VIEW_NODE,
"bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE
*/
};
+/* clang-format on */
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
return cmd_list_cmds(vty, argc == 3);
}
+DEFUN_HIDDEN(show_cli_graph,
+ show_cli_graph_cmd,
+ "show cli graph",
+ SHOW_STR
+ "CLI reflection\n"
+ "Dump current command space as DOT graph\n")
+{
+ struct cmd_node *cn = vector_slot(cmdvec, vty->node);
+ char *dot = cmd_graph_dump_dot(cn->cmdgraph);
+
+ vty_out(vty, "%s\n", dot);
+ XFREE(MTYPE_TMP, dot);
+ return CMD_SUCCESS;
+}
+
static int vty_write_config(struct vty *vty)
{
size_t i;
install_element(node, &config_end_cmd);
install_element(node, &config_help_cmd);
install_element(node, &config_list_cmd);
+ install_element(node, &show_cli_graph_cmd);
install_element(node, &find_cmd);
install_element(node, &config_write_cmd);
};
extern vector cmdvec;
+extern const struct message tokennames[];
extern const char *node_names[];
/* Node which has some commands and prompt string and configuration
#include <zebra.h>
#include "command_graph.h"
+#include "command.h"
+#include "log.h"
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
DEFINE_MTYPE_STATIC(LIB, CMD_DESC, "Command Token Text")
cmd_node_names(start, NULL, NULL);
}
+
+#ifndef BUILDING_CLIPPY
+
+void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf)
+{
+ static bool wasend;
+
+ char nbuf[512];
+ struct cmd_token *tok = gn->data;
+ const char *color;
+
+ if (wasend == true) {
+ wasend = false;
+ return;
+ }
+
+ if (tok->type == END_TKN) {
+ wasend = true;
+ return;
+ }
+
+ snprintf(nbuf, sizeof(nbuf), " n%p [ shape=box, label=<", gn);
+ buffer_putstr(buf, nbuf);
+ snprintf(nbuf, sizeof(nbuf), "<b>%s</b>",
+ lookup_msg(tokennames, tok->type, NULL));
+ buffer_putstr(buf, nbuf);
+ if (tok->attr == CMD_ATTR_DEPRECATED)
+ buffer_putstr(buf, " (d)");
+ else if (tok->attr == CMD_ATTR_HIDDEN)
+ buffer_putstr(buf, " (h)");
+ if (tok->text) {
+ if (tok->type == WORD_TKN)
+ snprintf(
+ nbuf, sizeof(nbuf),
+ "<br/>\"<font color=\"#0055ff\" point-size=\"11\"><b>%s</b></font>\"",
+ tok->text);
+ else
+ snprintf(nbuf, sizeof(nbuf), "<br/>%s", tok->text);
+ buffer_putstr(buf, nbuf);
+ }
+
+ 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;
+ }
+ snprintf(nbuf, sizeof(nbuf),
+ ">, style = filled, fillcolor = \"%s\" ];\n", color);
+ buffer_putstr(buf, nbuf);
+
+ for (unsigned int i = 0; i < vector_active(gn->to); i++) {
+ struct graph_node *adj = vector_slot(gn->to, i);
+
+ if (((struct cmd_token *)adj->data)->type == END_TKN) {
+ snprintf(nbuf, sizeof(nbuf), " n%p -> end%p;\n", gn,
+ adj);
+ buffer_putstr(buf, nbuf);
+ snprintf(
+ nbuf, sizeof(nbuf),
+ " end%p [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n",
+ adj);
+ } else
+ snprintf(nbuf, sizeof(nbuf), " n%p -> n%p;\n", gn,
+ adj);
+
+ buffer_putstr(buf, nbuf);
+ }
+}
+
+char *cmd_graph_dump_dot(struct graph *cmdgraph)
+{
+ struct graph_node *start = vector_slot(cmdgraph->nodes, 0);
+
+ return graph_dump_dot(cmdgraph, start, cmd_graph_node_print_cb);
+}
+
+#endif /* BUILDING_CLIPPY */
extern void cmd_graph_names(struct graph *graph);
extern void cmd_graph_merge(struct graph *old, struct graph *new,
int direction);
+/*
+ * Print callback for DOT dumping.
+ *
+ * See graph.h for more details.
+ */
+extern void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf);
+/*
+ * Dump command graph to DOT.
+ *
+ * cmdgraph
+ * A command graph to dump
+ *
+ * Returns:
+ * String allocated with MTYPE_TMP representing this graph
+ */
+char *cmd_graph_dump_dot(struct graph *cmdgraph);
#endif /* _FRR_COMMAND_GRAPH_H */
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 **);
/** shim interface commands **/
".dot filename\n")
{
check_nodegraph();
-
- struct graph_node *stack[CMD_ARGC_MAX];
- struct graph_node *visited[CMD_ARGC_MAX * CMD_ARGC_MAX];
- size_t vpos = 0;
-
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");
+ char *dot = cmd_graph_dump_dot(nodegraph);
+
+ fprintf(ofd, "%s", dot);
fclose(ofd);
+ XFREE(MTYPE_TMP, dot);
+
return CMD_SUCCESS;
}
install_element(ENABLE_NODE, &grammar_access_cmd);
}
-#define item(x) { x, #x }
-struct message tokennames[] = {item(WORD_TKN), // words
- item(VARIABLE_TKN), // almost anything
- item(RANGE_TKN), // integer range
- item(IPV4_TKN), // IPV4 addresses
- item(IPV4_PREFIX_TKN), // IPV4 network prefixes
- item(IPV6_TKN), // IPV6 prefixes
- item(IPV6_PREFIX_TKN), // IPV6 network prefixes
- item(MAC_TKN), // MAC address
- item(MAC_PREFIX_TKN), // MAC address w/ mask
-
- /* plumbing types */
- item(FORK_TKN),
- item(JOIN_TKN),
- item(START_TKN), // first token in line
- item(END_TKN), // last token in line
- {0}};
-
/**
* Pretty-prints a graph, assuming it is a tree.
*
vty_out(vty, "\n");
}
-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) == CMD_ARGC_MAX * CMD_ARGC_MAX)
- return;
-
- snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
- fprintf(ofd, " n%p [ shape=box, label=<", start);
-
- fprintf(ofd, "<b>%s</b>", lookup_msg(tokennames, tok->type, NULL));
- 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 == CMD_ARGC_MAX)
- 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%p -> n%p;\n", start, start);
- } else if (((struct cmd_token *)adj->data)->type == END_TKN) {
- // struct cmd_token *et = adj->data;
- fprintf(ofd, " n%p -> end%p;\n", start, adj);
- fprintf(ofd,
- " end%p [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n",
- adj);
- } else {
- fprintf(ofd, " n%p -> n%p;\n", start, 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)
{
#include "ns.h"
#include "vrf.h"
#include "libfrr.h"
+#include "command_graph.h"
DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy")
return CMD_SUCCESS;
}
+DEFUN_HIDDEN(show_cli_graph_vtysh,
+ show_cli_graph_vtysh_cmd,
+ "show cli graph",
+ SHOW_STR
+ "CLI reflection\n"
+ "Dump current command space as DOT graph\n")
+{
+ struct cmd_node *cn = vector_slot(cmdvec, vty->node);
+ char *dot = cmd_graph_dump_dot(cn->cmdgraph);
+
+ vty_out(vty, "%s\n", dot);
+ XFREE(MTYPE_TMP, dot);
+ return CMD_SUCCESS;
+}
+
static void vtysh_install_default(enum node_type node)
{
install_element(node, &config_list_cmd);
install_element(node, &find_cmd);
+ install_element(node, &show_cli_graph_vtysh_cmd);
install_element(node, &vtysh_output_file_cmd);
install_element(node, &no_vtysh_output_file_cmd);
}