*
* 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.
*
#include "command.h"
#include "memory_vty.h"
#include "graph.h"
+#include "linklist.h"
#include "command_match.h"
#define GRAMMAR_STR "CLI grammar sandbox\n"
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,
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;
}
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;
// free resources
list_delete (completions);
cmd_free_strvec (command);
- free (cmdstr);
+ XFREE (MTYPE_TMP, cmdstr);
return CMD_SUCCESS;
}
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;
// free resources
cmd_free_strvec (command);
- free (cmdstr);
+ XFREE (MTYPE_TMP, cmdstr);
return CMD_SUCCESS;
}
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);
// 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 }
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 }
*/
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];
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);
}