summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure.ac3
-rw-r--r--lib/Makefile.am5
-rw-r--r--lib/command.c8
-rw-r--r--lib/command.h13
-rw-r--r--lib/command_lex.l13
-rw-r--r--lib/command_match.c36
-rw-r--r--lib/command_parse.y222
-rw-r--r--lib/grammar_sandbox.c242
-rw-r--r--lib/grammar_sandbox.h56
-rw-r--r--lib/grammar_sandbox_main.c64
-rw-r--r--tests/testcli.refout8
-rw-r--r--tools/permutations.c2
-rw-r--r--zebra/zebra_fpm_dt.c2
13 files changed, 353 insertions, 321 deletions
diff --git a/configure.ac b/configure.ac
index 6ce5d0cc2c..550ac4f926 100755
--- a/configure.ac
+++ b/configure.ac
@@ -1459,6 +1459,7 @@ case "x${quagga_ac_bison_version}" in
x2.7*)
BISON_OPENBRACE='"'
BISON_CLOSEBRACE='"'
+ BISON_VERBOSE=''
AC_MSG_RESULT([$quagga_ac_bison_version - 2.7 or older])
;;
x2.*|x1.*)
@@ -1474,11 +1475,13 @@ case "x${quagga_ac_bison_version}" in
*)
BISON_OPENBRACE='{'
BISON_CLOSEBRACE='}'
+ BISON_VERBOSE='-Dparse.error=verbose'
AC_MSG_RESULT([$quagga_ac_bison_version - 3.0 or newer])
;;
esac
AC_SUBST(BISON_OPENBRACE)
AC_SUBST(BISON_CLOSEBRACE)
+AC_SUBST(BISON_VERBOSE)
if $quagga_ac_bison_missing; then
YACC="$SHELL $missing_dir/missing bison -y"
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 5dd38ee45a..ac1935d731 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -3,7 +3,7 @@
AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
AM_CFLAGS = $(WERROR)
DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
-AM_YFLAGS = -d -Dapi.prefix=@BISON_OPENBRACE@cmd_yy@BISON_CLOSEBRACE@
+AM_YFLAGS = -d -Dapi.prefix=@BISON_OPENBRACE@cmd_yy@BISON_CLOSEBRACE@ @BISON_VERBOSE@
command_lex.h: command_lex.c
@if test ! -f $@; then rm -f command_lex.c; else :; fi
@@ -26,6 +26,7 @@ libzebra_la_SOURCES = \
imsg-buffer.c imsg.c skiplist.c \
qobj.c wheel.c \
event_counter.c \
+ grammar_sandbox.c \
strlcpy.c \
strlcat.c
@@ -54,7 +55,7 @@ noinst_HEADERS = \
noinst_PROGRAMS = grammar_sandbox
-grammar_sandbox_SOURCES = grammar_sandbox.c
+grammar_sandbox_SOURCES = grammar_sandbox_main.c
grammar_sandbox_LDADD = libzebra.la
EXTRA_DIST = \
diff --git a/lib/command.c b/lib/command.c
index 781f627bb5..f75a924fb6 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -526,7 +526,7 @@ compare_completions (const void *fst, const void *snd)
* @param completions linked list of cmd_token
* @return deduplicated and sorted vector with
*/
-static vector
+vector
completions_to_vec (struct list *completions)
{
vector comps = vector_init (VECTOR_MIN_SIZE);
@@ -1275,7 +1275,7 @@ permute (struct graph_node *start, struct vty *vty)
for (ALL_LIST_ELEMENTS_RO (position,ln,gnn))
{
struct cmd_token *tt = gnn->data;
- if (tt->type < SELECTOR_TKN)
+ if (tt->type < SPECIAL_TKN)
vty_out (vty, " %s", tt->text);
}
if (gn == start)
@@ -2405,6 +2405,10 @@ cmd_init (int terminal)
vrf_install_commands ();
}
srandom(time(NULL));
+
+#ifdef DEV_BUILD
+ grammar_sandbox_init();
+#endif
}
struct cmd_token *
diff --git a/lib/command.h b/lib/command.h
index 1e490cc4cd..ff1b1a0e83 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -177,11 +177,12 @@ enum cmd_token_type
IPV6_PREFIX_TKN, // IPV6 network prefixes
/* plumbing types */
- SELECTOR_TKN, // marks beginning of selector
- OPTION_TKN, // marks beginning of option
- NUL_TKN, // dummy token
+ FORK_TKN, // marks subgraph beginning
+ JOIN_TKN, // marks subgraph end
START_TKN, // first token in line
END_TKN, // last token in line
+
+ SPECIAL_TKN = FORK_TKN,
};
/* Command attributes */
@@ -203,6 +204,8 @@ struct cmd_token
char *desc; // token description
long long min, max; // for ranges
char *arg; // user input that matches this token
+
+ struct graph_node *forkjoin; // paired FORK/JOIN for JOIN/FORK
};
/* Structure of command element. */
@@ -420,12 +423,16 @@ extern void cmd_terminate (void);
extern void cmd_exit (struct vty *vty);
extern int cmd_list_cmds (struct vty *vty, int do_permute);
+/* NOT safe for general use; call this only if DEV_BUILD! */
+extern void grammar_sandbox_init (void);
+
/* memory management for cmd_token */
extern struct cmd_token *new_cmd_token (enum cmd_token_type, u_char attr,
const char *text, const char *desc);
extern void del_cmd_token (struct cmd_token *);
extern struct cmd_token *copy_cmd_token (struct cmd_token *);
+extern vector completions_to_vec (struct list *completions);
extern void command_parse_format (struct graph *graph, struct cmd_element *cmd);
/* Export typical functions. */
diff --git a/lib/command_lex.l b/lib/command_lex.l
index d767926263..2a241abbec 100644
--- a/lib/command_lex.l
+++ b/lib/command_lex.l
@@ -24,6 +24,12 @@
%{
#include "command_parse.h"
+
+#define YY_USER_ACTION yylloc->last_column += yyleng;
+#define LOC_STEP do { if (yylloc) { \
+ yylloc->first_column = yylloc->last_column; \
+ yylloc->first_line = yylloc->last_line; \
+ } } while(0)
%}
WORD (\-|\+)?[a-z0-9\*][-+_a-zA-Z0-9\*]*
@@ -45,9 +51,14 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\)
%option prefix="cmd_yy"
%option reentrant
%option bison-bridge
+%option bison-locations
%%
-[ /t] /* ignore whitespace */;
+%{
+ LOC_STEP;
+%}
+
+[ \t]+ LOC_STEP /* ignore whitespace */;
{WORD} {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return WORD;}
{IPV4} {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return IPV4;}
{IPV4_PREFIX} {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return IPV4_PREFIX;}
diff --git a/lib/command_match.c b/lib/command_match.c
index aa58313134..bbd9cd091d 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -458,7 +458,7 @@ command_complete (struct graph *graph,
/**
* Adds all children that are reachable by one parser hop to the given list.
- * NUL_TKN, SELECTOR_TKN, and OPTION_TKN nodes are treated as transparent.
+ * special tokens except END_TKN are treated as transparent.
*
* @param[in] list to add the nexthops to
* @param[in] node to start calculating nexthops from
@@ -489,26 +489,24 @@ add_nexthops (struct list *list, struct graph_node *node,
if (j != stackpos)
continue;
}
- switch (token->type)
+ if (token->type >= SPECIAL_TKN && token->type != END_TKN)
{
- case OPTION_TKN:
- case SELECTOR_TKN:
- case NUL_TKN:
- added += add_nexthops (list, child, stack, stackpos);
- break;
- default:
- if (stack)
- {
- nextstack = XMALLOC (MTYPE_CMD_MATCHSTACK,
- (stackpos + 1) * sizeof(struct graph_node *));
- nextstack[0] = child;
- memcpy(nextstack + 1, stack, stackpos * sizeof(struct graph_node *));
+ added += add_nexthops (list, child, stack, stackpos);
+ }
+ else
+ {
+ if (stack)
+ {
+ nextstack = XMALLOC (MTYPE_CMD_MATCHSTACK,
+ (stackpos + 1) * sizeof(struct graph_node *));
+ nextstack[0] = child;
+ memcpy(nextstack + 1, stack, stackpos * sizeof(struct graph_node *));
- listnode_add (list, nextstack);
- }
- else
- listnode_add (list, child);
- added++;
+ listnode_add (list, nextstack);
+ }
+ else
+ listnode_add (list, child);
+ added++;
}
}
diff --git a/lib/command_parse.y b/lib/command_parse.y
index 7f98704f90..e9d36ca41c 100644
--- a/lib/command_parse.y
+++ b/lib/command_parse.y
@@ -27,6 +27,8 @@
#define YYDEBUG 1
%}
+%locations
+/* define parse.error verbose */
%define api.pure full
/* define api.prefix {cmd_yy} */
@@ -49,14 +51,20 @@
#include "graph.h"
#define YYSTYPE CMD_YYSTYPE
+ #define YYLTYPE CMD_YYLTYPE
struct parser_ctx;
+
+ /* subgraph semantic value */
+ struct subgraph {
+ struct graph_node *start, *end;
+ };
}
%union {
long long number;
char *string;
struct graph_node *node;
- struct subgraph *subgraph;
+ struct subgraph subgraph;
}
%code provides {
@@ -94,25 +102,16 @@
%type <node> literal_token
%type <node> placeholder_token
%type <node> simple_token
-%type <subgraph> option
-%type <subgraph> option_token
-%type <subgraph> option_token_seq
%type <subgraph> selector
%type <subgraph> selector_token
%type <subgraph> selector_token_seq
%type <subgraph> selector_seq_seq
-%type <subgraph> compound_token
%code {
/* bison declarations */
void
- cmd_yyerror (struct parser_ctx *ctx, char const *msg);
-
- /* subgraph semantic value */
- struct subgraph {
- struct graph_node *start, *end;
- };
+ cmd_yyerror (CMD_YYLTYPE *locp, struct parser_ctx *ctx, char const *msg);
/* helper functions for parser */
static const char *
@@ -134,7 +133,7 @@
const char *doc);
static void
- terminate_graph (struct parser_ctx *ctx,
+ terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
struct graph_node *);
static void
@@ -173,7 +172,7 @@ start:
cmd_token_seq
{
// tack on the command element
- terminate_graph (ctx, ctx->currnode);
+ terminate_graph (&@1, ctx, ctx->currnode);
}
| cmd_token_seq placeholder_token '.' '.' '.'
{
@@ -187,7 +186,7 @@ start:
add_edge_dedup (ctx->currnode, ctx->currnode);
// tack on the command element
- terminate_graph (ctx, ctx->currnode);
+ terminate_graph (&@1, ctx, ctx->currnode);
}
;
@@ -202,11 +201,10 @@ cmd_token:
if ((ctx->currnode = add_edge_dedup (ctx->currnode, $1)) != $1)
graph_delete_node (ctx->graph, $1);
}
-| compound_token
+| selector
{
- graph_add_edge (ctx->currnode, $1->start);
- ctx->currnode = $1->end;
- free ($1);
+ graph_add_edge (ctx->currnode, $1.start);
+ ctx->currnode = $1.end;
}
;
@@ -215,11 +213,6 @@ simple_token:
| placeholder_token
;
-compound_token:
- selector
-| option
-;
-
literal_token: WORD
{
$$ = new_token_node (ctx, WORD_TKN, $1, doc_next(ctx));
@@ -265,7 +258,7 @@ placeholder_token:
token->max = strtoll (yylval.string, &yylval.string, 10);
// validate range
- if (token->min > token->max) cmd_yyerror (ctx, "Invalid range.");
+ if (token->min > token->max) cmd_yyerror (&@1, ctx, "Invalid range.");
free ($1);
}
@@ -273,141 +266,66 @@ placeholder_token:
/* <selector|set> productions */
selector: '<' selector_seq_seq '>'
{
- $$ = malloc (sizeof (struct subgraph));
- $$->start = new_token_node (ctx, SELECTOR_TKN, NULL, NULL);
- $$->end = new_token_node (ctx, NUL_TKN, NULL, NULL);
- for (unsigned int i = 0; i < vector_active ($2->start->to); i++)
- {
- struct graph_node *sn = vector_slot ($2->start->to, i),
- *en = vector_slot ($2->end->from, i);
- graph_add_edge ($$->start, sn);
- graph_add_edge (en, $$->end);
- }
- graph_delete_node (ctx->graph, $2->start);
- graph_delete_node (ctx->graph, $2->end);
- free ($2);
+ $$ = $2;
};
selector_seq_seq:
selector_seq_seq '|' selector_token_seq
{
- $$ = malloc (sizeof (struct subgraph));
- $$->start = graph_new_node (ctx->graph, NULL, NULL);
- $$->end = graph_new_node (ctx->graph, NULL, NULL);
-
- // link in last sequence
- graph_add_edge ($$->start, $3->start);
- graph_add_edge ($3->end, $$->end);
-
- for (unsigned int i = 0; i < vector_active ($1->start->to); i++)
- {
- struct graph_node *sn = vector_slot ($1->start->to, i),
- *en = vector_slot ($1->end->from, i);
- graph_add_edge ($$->start, sn);
- graph_add_edge (en, $$->end);
- }
- graph_delete_node (ctx->graph, $1->start);
- graph_delete_node (ctx->graph, $1->end);
- free ($1);
- free ($3);
+ $$ = $1;
+ graph_add_edge ($$.start, $3.start);
+ graph_add_edge ($3.end, $$.end);
}
-| selector_token_seq '|' selector_token_seq
+| selector_token_seq
{
- $$ = malloc (sizeof (struct subgraph));
- $$->start = graph_new_node (ctx->graph, NULL, NULL);
- $$->end = graph_new_node (ctx->graph, NULL, NULL);
- graph_add_edge ($$->start, $1->start);
- graph_add_edge ($1->end, $$->end);
- graph_add_edge ($$->start, $3->start);
- graph_add_edge ($3->end, $$->end);
- free ($1);
- free ($3);
+ $$.start = new_token_node (ctx, FORK_TKN, NULL, NULL);
+ $$.end = new_token_node (ctx, JOIN_TKN, NULL, NULL);
+ ((struct cmd_token *)$$.start->data)->forkjoin = $$.end;
+ ((struct cmd_token *)$$.end->data)->forkjoin = $$.start;
+
+ graph_add_edge ($$.start, $1.start);
+ graph_add_edge ($1.end, $$.end);
}
;
/* {keyword} productions */
selector: '{' selector_seq_seq '}'
{
- $$ = malloc (sizeof (struct subgraph));
- $$->start = new_token_node (ctx, SELECTOR_TKN, NULL, NULL);
- $$->end = new_token_node (ctx, NUL_TKN, NULL, NULL);
- graph_add_edge ($$->start, $$->end);
- for (unsigned int i = 0; i < vector_active ($2->start->to); i++)
- {
- struct graph_node *sn = vector_slot ($2->start->to, i),
- *en = vector_slot ($2->end->from, i);
- graph_add_edge ($$->start, sn);
- graph_add_edge (en, $$->start);
- }
- graph_delete_node (ctx->graph, $2->start);
- graph_delete_node (ctx->graph, $2->end);
- free ($2);
+ $$ = $2;
+ graph_add_edge ($$.end, $$.start);
+ /* there is intentionally no start->end link, for two reasons:
+ * 1) this allows "at least 1 of" semantics, which are otherwise impossible
+ * 2) this would add a start->end->start loop in the graph that the current
+ * loop-avoidal fails to handle
+ * just use [{a|b}] if neccessary, that will work perfectly fine, and reason
+ * #1 is good enough to keep it this way. */
};
-selector_token_seq:
- simple_token
-{
- $$ = malloc (sizeof (struct subgraph));
- $$->start = $$->end = $1;
-}
-| selector_token_seq selector_token
-{
- $$ = malloc (sizeof (struct subgraph));
- graph_add_edge ($1->end, $2->start);
- $$->start = $1->start;
- $$->end = $2->end;
- free ($1);
- free ($2);
-}
-;
-
selector_token:
simple_token
{
- $$ = malloc (sizeof (struct subgraph));
- $$->start = $$->end = $1;
+ $$.start = $$.end = $1;
}
-| option
| selector
;
-/* [option] productions */
-option: '[' option_token_seq ']'
+selector_token_seq:
+ selector_token_seq selector_token
{
- // make a new option
- $$ = malloc (sizeof (struct subgraph));
- $$->start = new_token_node (ctx, OPTION_TKN, NULL, NULL);
- $$->end = new_token_node (ctx, NUL_TKN, NULL, NULL);
- // add a path through the sequence to the end
- graph_add_edge ($$->start, $2->start);
- graph_add_edge ($2->end, $$->end);
- // add a path directly from the start to the end
- graph_add_edge ($$->start, $$->end);
- free ($2);
+ graph_add_edge ($1.end, $2.start);
+ $$.start = $1.start;
+ $$.end = $2.end;
}
+| selector_token
;
-option_token_seq:
- option_token
-| option_token_seq option_token
-{
- $$ = malloc (sizeof (struct subgraph));
- graph_add_edge ($1->end, $2->start);
- $$->start = $1->start;
- $$->end = $2->end;
- free ($1);
- free ($2);
-}
-;
-
-option_token:
- simple_token
+/* [option] productions */
+selector: '[' selector_seq_seq ']'
{
- $$ = malloc (sizeof (struct subgraph));
- $$->start = $$->end = $1;
+ $$ = $2;
+ graph_add_edge ($$.start, $$.end);
}
-| compound_token
;
%%
@@ -437,11 +355,39 @@ command_parse_format (struct graph *graph, struct cmd_element *cmd)
/* parser helper functions */
void
-yyerror (struct parser_ctx *ctx, char const *msg)
+yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg)
{
+ char *tmpstr = strdup(ctx->el->string);
+ char *line, *eol;
+ char spacing[256];
+ int lineno = 0;
+
zlog_err ("%s: FATAL parse error: %s", __func__, msg);
- zlog_err ("while parsing this command definition: \n\t%s\n", ctx->el->string);
- //exit(EXIT_FAILURE);
+ zlog_err ("%s: %d:%d-%d of this command definition:", __func__, loc->first_line, loc->first_column, loc->last_column);
+
+ line = tmpstr;
+ do {
+ lineno++;
+ eol = strchr(line, '\n');
+ if (eol)
+ *eol++ = '\0';
+
+ zlog_err ("%s: | %s", __func__, line);
+ if (lineno == loc->first_line && lineno == loc->last_line
+ && loc->first_column < (int)sizeof(spacing) - 1
+ && loc->last_column < (int)sizeof(spacing) - 1) {
+
+ int len = loc->last_column - loc->first_column;
+ if (len == 0)
+ len = 1;
+
+ memset(spacing, ' ', loc->first_column - 1);
+ memset(spacing + loc->first_column - 1, '^', len);
+ spacing[loc->first_column - 1 + len] = '\0';
+ zlog_err ("%s: | %s", __func__, spacing);
+ }
+ } while ((line = eol));
+ free(tmpstr);
}
static void
@@ -456,7 +402,8 @@ cleanup (struct parser_ctx *ctx)
}
static void
-terminate_graph (struct parser_ctx *ctx, struct graph_node *finalnode)
+terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
+ struct graph_node *finalnode)
{
// end of graph should look like this
// * -> finalnode -> END_TKN -> cmd_element
@@ -467,7 +414,7 @@ terminate_graph (struct parser_ctx *ctx, struct graph_node *finalnode)
graph_new_node (ctx->graph, element, NULL);
if (node_adjacent (finalnode, end_token_node))
- cmd_yyerror (ctx, "Duplicate command.");
+ cmd_yyerror (locp, ctx, "Duplicate command.");
graph_add_edge (finalnode, end_token_node);
graph_add_edge (end_token_node, end_element_node);
@@ -572,8 +519,7 @@ cmp_token (struct cmd_token *first, struct cmd_token *second)
* cases; ultimately this forks the graph, but the matcher can handle
* this regardless
*/
- case SELECTOR_TKN:
- case OPTION_TKN:
+ case FORK_TKN:
return 0;
/* end nodes are always considered equal, since each node may only
@@ -581,7 +527,7 @@ cmp_token (struct cmd_token *first, struct cmd_token *second)
*/
case START_TKN:
case END_TKN:
- case NUL_TKN:
+ case JOIN_TKN:
default:
break;
}
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
index 0239ca44ac..315bd4d59c 100644
--- a/lib/grammar_sandbox.c
+++ b/lib/grammar_sandbox.c
@@ -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.
*
@@ -45,15 +40,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,
@@ -240,17 +235,77 @@ 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",
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;
}
+extern vector cmdvec;
+
+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 +313,12 @@ 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_init_graph_cmd);
+ install_element (ENABLE_NODE, &grammar_access_cmd);
}
#define item(x) { x, #x }
@@ -275,9 +332,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 +348,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 +402,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)
+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)
{
- struct cmd_token *first = *(struct cmd_token **) fst,
- *secnd = *(struct cmd_token **) snd;
- return strcmp (first->text, secnd->text);
-}
+ // print this node
+ char tokennum[32];
+ struct cmd_token *tok = start->data;
+ const char *color;
-vector
-completions_to_vec (struct list *completions)
-{
- vector comps = vector_init (VECTOR_MIN_SIZE);
-
- 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);
}
diff --git a/lib/grammar_sandbox.h b/lib/grammar_sandbox.h
deleted file mode 100644
index 5da0b05d09..0000000000
--- a/lib/grammar_sandbox.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef _GRAMMAR_SANDBOX_H
-#define _GRAMMAR_SANDBOX_H
-
-/**
- * Houses functionality for testing shim as well as code that should go into
- * command.h and command.c during integration.
- */
-#include "memory.h"
-
-#define CMD_CR_TEXT "<cr>"
-
-void
-grammar_sandbox_init (void);
-
-/**
- * Types for tokens.
- *
- * The type determines what kind of data the token can match (in the
- * matching use case) or hold (in the argv use case).
- */
-enum cmd_token_type_t
-{
- WORD_TKN, // words
- NUMBER_TKN, // integral numbers
- VARIABLE_TKN, // almost anything
- RANGE_TKN, // integer range
- IPV4_TKN, // IPV4 addresses
- IPV4_PREFIX_TKN, // IPV4 network prefixes
- IPV6_TKN, // IPV6 prefixes
- IPV6_PREFIX_TKN, // IPV6 network prefixes
-
- /* plumbing types */
- SELECTOR_TKN, // marks beginning of selector
- OPTION_TKN, // marks beginning of option
- NUL_TKN, // dummy token
- START_TKN, // first token in line
- END_TKN, // last token in line
-};
-
-/**
- * Token struct.
- */
-struct cmd_token_t
-{
- enum cmd_token_type_t type; // token type
-
- char *text; // token text
- char *desc; // token description
-
- long long value; // for numeric types
- long long min, max; // for ranges
-
- char *arg; // user input that matches this token
-};
-
-#endif /* _GRAMMAR_SANDBOX_H */
diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c
new file mode 100644
index 0000000000..5deef406c1
--- /dev/null
+++ b/lib/grammar_sandbox_main.c
@@ -0,0 +1,64 @@
+/*
+ * Testing shim and API examples for the new CLI backend.
+ *
+ * Minimal main() to run grammar_sandbox standalone.
+ * [split off grammar_sandbox.c 2017-01-23]
+ * --
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Copyright (C) 2017 David Lamparter for NetDEF, Inc.
+ *
+ * This file is part of FreeRangeRouting (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FRR; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "command.h"
+#include "memory_vty.h"
+
+static void vty_do_exit(void)
+{
+ printf ("\nend.\n");
+ exit (0);
+}
+
+struct thread_master *master;
+
+int main(int argc, char **argv)
+{
+ 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 ();
+
+ vty_stdio (vty_do_exit);
+
+ /* Fetch next active thread. */
+ while (thread_fetch (master, &thread))
+ thread_call (&thread);
+
+ /* Not reached. */
+ exit (0);
+}
diff --git a/tests/testcli.refout b/tests/testcli.refout
index 088cbdfec4..8b438baee2 100644
--- a/tests/testcli.refout
+++ b/tests/testcli.refout
@@ -188,15 +188,11 @@ test# pat c c x
% [NONE] Unknown command: pat c c x
test#
test# pat d
-cmd8 with 2 args.
-[00]: pat
-[01]: d
+% Command incomplete.
test# pat d
bar baz foo
test# pat d
-cmd8 with 2 args.
-[00]: pat
-[01]: d
+% Command incomplete.
test# pat d foo 1.2.3.4
cmd8 with 4 args.
[00]: pat
diff --git a/tools/permutations.c b/tools/permutations.c
index 8db51ee037..0ca980b259 100644
--- a/tools/permutations.c
+++ b/tools/permutations.c
@@ -70,7 +70,7 @@ permute (struct graph_node *start)
for (ALL_LIST_ELEMENTS_RO (position,ln,gnn))
{
struct cmd_token *tt = gnn->data;
- if (tt->type < SELECTOR_TKN)
+ if (tt->type < SPECIAL_TKN)
fprintf (stdout, "%s ", tt->text);
}
fprintf (stdout, "\n");
diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c
index bd171c89b2..715e250a66 100644
--- a/zebra/zebra_fpm_dt.c
+++ b/zebra/zebra_fpm_dt.c
@@ -42,6 +42,8 @@
#include "vrf.h"
#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/zebra_vrf.h"
#include "zebra_fpm_private.h"