diff options
| -rwxr-xr-x | configure.ac | 3 | ||||
| -rw-r--r-- | lib/Makefile.am | 5 | ||||
| -rw-r--r-- | lib/command.c | 8 | ||||
| -rw-r--r-- | lib/command.h | 13 | ||||
| -rw-r--r-- | lib/command_lex.l | 13 | ||||
| -rw-r--r-- | lib/command_match.c | 36 | ||||
| -rw-r--r-- | lib/command_parse.y | 222 | ||||
| -rw-r--r-- | lib/grammar_sandbox.c | 242 | ||||
| -rw-r--r-- | lib/grammar_sandbox.h | 56 | ||||
| -rw-r--r-- | lib/grammar_sandbox_main.c | 64 | ||||
| -rw-r--r-- | tests/testcli.refout | 8 | ||||
| -rw-r--r-- | tools/permutations.c | 2 | ||||
| -rw-r--r-- | zebra/zebra_fpm_dt.c | 2 |
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" |
