Merge pull request #5 from freerangerouting/dev/osr/fixes_plus_vty_keywords

Merge branch 'dev/osr/fixes_plus_vty_keywords'
This commit is contained in:
Donald Sharp 2016-12-16 15:12:34 -05:00 committed by GitHub
commit bcfb39acd7
17 changed files with 558 additions and 173 deletions

1
.gitignore vendored
View File

@ -41,6 +41,7 @@ build
*.loT
m4/*.m4
!m4/ax_sys_weak_alias.m4
!m4/ax_compare_version.m4
debian/autoreconf.after
debian/autoreconf.before
debian/files

View File

@ -7477,6 +7477,8 @@ bgp_config_write (struct vty *vty)
void
bgp_master_init (void)
{
qobj_init ();
memset (&bgp_master, 0, sizeof (struct bgp_master));
bm = &bgp_master;

View File

@ -22,7 +22,7 @@ AC_CANONICAL_TARGET()
# Disable portability warnings -- our automake code (in particular
# common.am) uses some constructs specific to gmake.
AM_INIT_AUTOMAKE([1.6 -Wno-portability])
AM_INIT_AUTOMAKE([1.12 -Wno-portability])
m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])])
AM_SILENT_RULES([yes])
AC_CONFIG_HEADERS(config.h)
@ -1413,13 +1413,62 @@ AC_CHECK_DECL(CLOCK_MONOTONIC,
dnl --------------------------------------
dnl checking for flex and bison
dnl --------------------------------------
AM_PROG_LEX
if test "x$LEX" != xflex; then
LEX="$SHELL $missing_dir/missing flex"
AC_SUBST([LEX_OUTPUT_ROOT], [lex.yy])
AC_SUBST([LEXLIB], [''])
fi
AC_MSG_CHECKING(version of flex)
quagga_ac_flex_version="$(eval $LEX -V | grep flex | head -n 1)"
quagga_ac_flex_version="${quagga_ac_flex_version##* }"
AC_MSG_RESULT([$quagga_ac_flex_version])
AX_COMPARE_VERSION([$quagga_ac_flex_version], [lt], [2.5.20], [
LEX="$SHELL $missing_dir/missing flex"
if test -f "${srcdir}/lib/command_lex.c" -a -f "${srcdir}/lib/command_lex.h"; then
AC_MSG_WARN([using pregenerated flex output files])
else
AC_MSG_ERROR([flex failure and pregenerated files not included (probably a git build)])
fi
AC_SUBST([LEX_OUTPUT_ROOT], [lex.yy])
AC_SUBST([LEXLIB], [''])
])
AC_PROG_YACC
dnl thanks GNU bison for this b*llshit...
AC_MSG_CHECKING(version of bison)
quagga_ac_bison_version="$(eval $YACC -V | grep bison | head -n 1)"
quagga_ac_bison_version="${quagga_ac_bison_version##* }"
quagga_ac_bison_missing="false"
case "x${quagga_ac_bison_version}" in
x2.7*)
BISON_OPENBRACE='"'
BISON_CLOSEBRACE='"'
AC_MSG_RESULT([$quagga_ac_bison_version - 2.7 or older])
;;
x2.*|x1.*)
AC_MSG_RESULT([$quagga_ac_bison_version])
AC_MSG_WARN([installed bison is too old. Please install GNU bison 2.7.x or newer.])
quagga_ac_bison_missing="true"
;;
x)
AC_MSG_RESULT([none])
AC_MSG_WARN([could not determine bison version. Please install GNU bison 2.7.x or newer.])
quagga_ac_bison_missing="true"
;;
*)
BISON_OPENBRACE='{'
BISON_CLOSEBRACE='}'
AC_MSG_RESULT([$quagga_ac_bison_version - 3.0 or newer])
;;
esac
AC_SUBST(BISON_OPENBRACE)
AC_SUBST(BISON_CLOSEBRACE)
if $quagga_ac_bison_missing; then
YACC="$SHELL $missing_dir/missing bison -y"
if test -f "${srcdir}/lib/command_parse.c" -a -f "${srcdir}/lib/command_parse.h"; then
AC_MSG_WARN([using pregenerated bison output files])
else
AC_MSG_ERROR([bison failure and pregenerated files not included (probably a git build)])
fi
fi
dnl -------------------
dnl capabilities checks

View File

@ -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
AM_YFLAGS = -d -Dapi.prefix=@BISON_OPENBRACE@cmd_yy@BISON_CLOSEBRACE@
command_lex.h: command_lex.c
@if test ! -f $@; then rm -f command_lex.c; else :; fi
@ -51,6 +51,11 @@ pkginclude_HEADERS = \
noinst_HEADERS = \
plist_int.h
noinst_PROGRAMS = grammar_sandbox
grammar_sandbox_SOURCES = grammar_sandbox.c
grammar_sandbox_LDADD = libzebra.la
EXTRA_DIST = \
queue.h \
command_lex.h \

View File

@ -686,6 +686,19 @@ cmd_complete_command (vector vline, struct vty *vty, int *status)
}
vector_free (initial_comps);
// since we filtered results, we need to re-set status code
switch (vector_active (comps))
{
case 0:
*status = CMD_ERR_NO_MATCH;
break;
case 1:
*status = CMD_COMPLETE_FULL_MATCH;
break;
default:
*status = CMD_COMPLETE_LIST_MATCH;
}
// copy completions text into an array of char*
ret = XMALLOC (MTYPE_TMP, (vector_active (comps)+1) * sizeof (char *));
unsigned int i;

View File

@ -196,6 +196,8 @@ struct cmd_token
{
enum cmd_token_type type; // token type
u_char attr; // token attributes
bool allowrepeat; // matcher allowed to match token repetively?
char *text; // token text
char *desc; // token description
long long min, max; // for ranges

View File

@ -24,7 +24,6 @@
%{
#include "command_parse.h"
#define YYSTYPE CMD_YYSTYPE
%}
WORD (\-|\+)?[a-z\*][-+_a-zA-Z0-9\*]*

View File

@ -28,6 +28,9 @@
#include "memory.h"
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack")
#define MAXDEPTH 64
#ifdef TRACE_MATCHER
#define TM 1
@ -40,10 +43,12 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
/* matcher helper prototypes */
static int
add_nexthops (struct list *, struct graph_node *);
add_nexthops (struct list *, struct graph_node *,
struct graph_node **, size_t);
static struct list *
command_match_r (struct graph_node *, vector, unsigned int);
command_match_r (struct graph_node *, vector, unsigned int,
struct graph_node **);
static int
score_precedence (enum cmd_token_type);
@ -97,6 +102,7 @@ command_match (struct graph *cmdgraph,
struct list **argv,
const struct cmd_element **el)
{
struct graph_node *stack[MAXDEPTH];
matcher_rv = MATCHER_NO_MATCH;
// prepend a dummy token to match that pesky start node
@ -106,7 +112,7 @@ command_match (struct graph *cmdgraph,
vvline->active = vline->active + 1;
struct graph_node *start = vector_slot (cmdgraph->nodes, 0);
if ((*argv = command_match_r (start, vvline, 0))) // successful match
if ((*argv = command_match_r (start, vvline, 0, stack))) // successful match
{
struct listnode *head = listhead (*argv);
struct listnode *tail = listtail (*argv);
@ -191,7 +197,8 @@ command_match (struct graph *cmdgraph,
* If no match was found, the return value is NULL.
*/
static struct list *
command_match_r (struct graph_node *start, vector vline, unsigned int n)
command_match_r (struct graph_node *start, vector vline, unsigned int n,
struct graph_node **stack)
{
assert (n < vector_active (vline));
@ -199,6 +206,16 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
struct cmd_token *token = start->data;
enum match_type minmatch = min_match_level (token->type);
/* check history/stack of tokens
* this disallows matching the same one more than once if there is a
* circle in the graph (used for keyword arguments) */
if (n == MAXDEPTH)
return NULL;
if (!token->allowrepeat)
for (size_t s = 0; s < n; s++)
if (stack[s] == start)
return NULL;
// get the current operating input token
char *input_token = vector_slot (vline, n);
@ -229,13 +246,15 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
if (match_token (token, input_token) < minmatch)
return NULL;
stack[n] = start;
// pointers for iterating linklist
struct listnode *ln;
struct graph_node *gn;
// get all possible nexthops
struct list *next = list_new();
add_nexthops (next, start);
add_nexthops (next, start, NULL, 0);
// determine the best match
int ambiguous = 0;
@ -271,7 +290,7 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
}
// else recurse on candidate child node
struct list *result = command_match_r (gn, vline, n+1);
struct list *result = command_match_r (gn, vline, n+1, stack);
// save the best match
if (result && currbest)
@ -317,6 +336,12 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
return currbest;
}
static void
stack_del (void *val)
{
XFREE (MTYPE_CMD_MATCHSTACK, val);
}
enum matcher_rv
command_complete (struct graph *graph,
vector vline,
@ -327,14 +352,15 @@ command_complete (struct graph *graph,
struct list *current = list_new(), // current nodes to match input token against
*next = list_new(); // possible next hops after current input token
current->del = next->del = stack_del;
// pointers used for iterating lists
struct graph_node *gn;
struct graph_node **gstack, **newstack;
struct listnode *node;
// add all children of start node to list
struct graph_node *start = vector_slot (graph->nodes, 0);
add_nexthops (next, start);
add_nexthops (next, start, &start, 0);
unsigned int idx;
for (idx = 0; idx < vector_active (vline) && next->count > 0; idx++)
@ -342,19 +368,20 @@ command_complete (struct graph *graph,
list_delete (current);
current = next;
next = list_new();
next->del = stack_del;
input_token = vector_slot (vline, idx);
int exact_match_exists = 0;
for (ALL_LIST_ELEMENTS_RO (current,node,gn))
for (ALL_LIST_ELEMENTS_RO (current,node,gstack))
if (!exact_match_exists)
exact_match_exists = (match_token (gn->data, input_token) == exact_match);
exact_match_exists = (match_token (gstack[0]->data, input_token) == exact_match);
else
break;
for (ALL_LIST_ELEMENTS_RO (current,node,gn))
for (ALL_LIST_ELEMENTS_RO (current,node,gstack))
{
struct cmd_token *token = gn->data;
struct cmd_token *token = gstack[0]->data;
if (token->attr == CMD_ATTR_HIDDEN || token->attr == CMD_ATTR_DEPRECATED)
continue;
@ -371,7 +398,11 @@ command_complete (struct graph *graph,
case trivial_match:
trace_matcher ("trivial_match\n");
assert(last_token);
listnode_add (next, gn);
newstack = XMALLOC (MTYPE_CMD_MATCHSTACK,
sizeof(struct graph_node *));
/* we're not recursing here, just the first element is OK */
newstack[0] = gstack[0];
listnode_add (next, newstack);
break;
case partly_match:
trace_matcher ("trivial_match\n");
@ -380,9 +411,15 @@ command_complete (struct graph *graph,
case exact_match:
trace_matcher ("exact_match\n");
if (last_token)
listnode_add (next, gn);
{
newstack = XMALLOC (MTYPE_CMD_MATCHSTACK,
sizeof(struct graph_node *));
/* same as above, not recursing on this */
newstack[0] = gstack[0];
listnode_add (next, newstack);
}
else if (matchtype >= minmatch)
add_nexthops (next, gn);
add_nexthops (next, gstack[0], gstack, idx + 1);
break;
default:
trace_matcher ("no_match\n");
@ -409,8 +446,9 @@ command_complete (struct graph *graph,
{
// extract cmd_token into list
*completions = list_new ();
for (ALL_LIST_ELEMENTS_RO (next,node,gn))
listnode_add (*completions, gn->data);
for (ALL_LIST_ELEMENTS_RO (next,node,gstack)) {
listnode_add (*completions, gstack[0]->data);
}
}
list_delete (current);
@ -425,26 +463,52 @@ command_complete (struct graph *graph,
*
* @param[in] list to add the nexthops to
* @param[in] node to start calculating nexthops from
* @param[in] stack listing previously visited nodes, if non-NULL.
* @param[in] stackpos how many valid entries are in stack
* @return the number of children added to the list
*
* NB: non-null "stack" means that new stacks will be added to "list" as
* output, instead of direct node pointers!
*/
static int
add_nexthops (struct list *list, struct graph_node *node)
add_nexthops (struct list *list, struct graph_node *node,
struct graph_node **stack, size_t stackpos)
{
int added = 0;
struct graph_node *child;
struct graph_node **nextstack;
for (unsigned int i = 0; i < vector_active (node->to); i++)
{
child = vector_slot (node->to, i);
size_t j;
struct cmd_token *token = child->data;
if (!token->allowrepeat)
{
for (j = 0; j < stackpos; j++)
if (child == stack[j])
break;
if (j != stackpos)
continue;
}
switch (token->type)
{
case OPTION_TKN:
case SELECTOR_TKN:
case NUL_TKN:
added += add_nexthops (list, child);
added += add_nexthops (list, child, stack, stackpos);
break;
default:
listnode_add (list, child);
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++;
}
}

View File

@ -23,23 +23,24 @@
*/
%{
typedef union CMD_YYSTYPE CMD_YYSTYPE;
#define YYSTYPE CMD_YYSTYPE
#include "command_lex.h"
// compile with debugging facilities
#define YYDEBUG 1
%}
%define api.pure full
%define api.prefix {cmd_yy}
/* define api.prefix {cmd_yy} */
/* names for generated header and parser files */
%defines "command_parse.h"
%output "command_parse.c"
/* required external units */
/* note: code blocks are output in order, to both .c and .h:
* 1. %code requires
* 2. %union + bison forward decls
* 3. %code provides
* command_lex.h needs to be included at 3.; it needs the union and YYSTYPE.
* struct parser_ctx is needed for the bison forward decls.
*/
%code requires {
#include "stdlib.h"
#include "string.h"
@ -47,6 +48,25 @@ typedef union CMD_YYSTYPE CMD_YYSTYPE;
#include "log.h"
#include "graph.h"
#define YYSTYPE CMD_YYSTYPE
struct parser_ctx;
}
%union {
long long number;
char *string;
struct graph_node *node;
struct subgraph *subgraph;
}
%code provides {
#ifndef FLEX_SCANNER
#include "command_lex.h"
#endif
extern void set_lexer_string (yyscan_t *scn, const char *string);
extern void cleanup_lexer (yyscan_t *scn);
struct parser_ctx {
yyscan_t scanner;
@ -58,23 +78,6 @@ typedef union CMD_YYSTYPE CMD_YYSTYPE;
/* pointers to copy of command docstring */
char *docstr_start, *docstr;
};
extern void set_lexer_string (yyscan_t *scn, const char *string);
extern void cleanup_lexer (yyscan_t *scn);
}
/* functionality this unit exports */
%code provides {
/* maximum length of a number, lexer will not match anything longer */
#define DECIMAL_STRLEN_MAX 20
}
/* valid semantic types for tokens and rules */
%union {
long long number;
char *string;
struct graph_node *node;
struct subgraph *subgraph;
}
/* union types for lexed tokens */
@ -180,6 +183,8 @@ start:
if ((ctx->currnode = add_edge_dedup (ctx->currnode, $3)) != $3)
graph_delete_node (ctx->graph, $3);
((struct cmd_token *)ctx->currnode->data)->allowrepeat = 1;
// adding a node as a child of itself accepts any number
// of the same token, which is what we want for variadics
add_edge_dedup (ctx->currnode, ctx->currnode);
@ -336,6 +341,26 @@ selector_seq_seq:
}
;
/* {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);
};
selector_token_seq:
simple_token
{

View File

@ -30,19 +30,23 @@
*/
#include "command.h"
#include "memory_vty.h"
#include "graph.h"
#include "command_parse.h"
#include "command_match.h"
#define GRAMMAR_STR "CLI grammar sandbox\n"
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
#define MAXDEPTH 64
/** headers **/
void
grammar_sandbox_init (void);
void
pretty_print_graph (struct graph_node *, int);
pretty_print_graph (struct vty *vty, struct graph_node *, int, int, struct graph_node **, size_t);
void
init_cmdgraph (struct graph **);
init_cmdgraph (struct vty *, struct graph **);
vector
completions_to_vec (struct list *);
int
@ -53,8 +57,9 @@ struct graph *nodegraph;
DEFUN (grammar_test,
grammar_test_cmd,
"grammar parse .COMMAND",
"grammar parse LINE...",
GRAMMAR_STR
"parse a command\n"
"command to pass to new parser\n")
{
int idx_command = 2;
@ -64,9 +69,8 @@ DEFUN (grammar_test,
// create cmd_element for parser
struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
cmd->string = command;
cmd->doc = NULL;
cmd->doc = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n";
cmd->func = NULL;
cmd->tokens = vector_init (VECTOR_MIN_SIZE);
// parse the command and install it into the command graph
command_parse_format (nodegraph, cmd);
@ -76,13 +80,16 @@ DEFUN (grammar_test,
DEFUN (grammar_test_complete,
grammar_test_complete_cmd,
"grammar complete .COMMAND",
"grammar complete COMMAND...",
GRAMMAR_STR
"attempt to complete input on DFA\n"
"command to complete")
"command to complete\n")
{
int idx_command = 2;
char *cmdstr = argv_concat (argv, argc, idx_command);
if (!cmdstr)
return CMD_SUCCESS;
vector command = cmd_make_strvec (cmdstr);
// generate completions of user input
@ -93,7 +100,7 @@ DEFUN (grammar_test_complete,
if (!MATCHER_ERROR(result))
{
vector comps = completions_to_vec (completions);
struct cmd_token_t *tkn;
struct cmd_token *tkn;
// calculate length of longest tkn->text in completions
unsigned int width = 0, i = 0;
@ -106,15 +113,15 @@ DEFUN (grammar_test_complete,
// print completions
for (i = 0; i < vector_active (comps); i++) {
tkn = vector_slot (comps, i);
fprintf (stdout, " %-*s %s%s", width, tkn->text, tkn->desc, "\n");
vty_out (vty, " %-*s %s%s", width, tkn->text, tkn->desc, VTY_NEWLINE);
}
for (i = 0; i < vector_active (comps); i++)
del_cmd_token ((struct cmd_token_t *) vector_slot (comps, i));
del_cmd_token ((struct cmd_token *) vector_slot (comps, i));
vector_free (comps);
}
else
fprintf (stdout, "%% No match%s", "\n");
vty_out (vty, "%% No match%s", VTY_NEWLINE);
// free resources
list_delete (completions);
@ -126,50 +133,49 @@ DEFUN (grammar_test_complete,
DEFUN (grammar_test_match,
grammar_test_match_cmd,
"grammar match .COMMAND",
"grammar match COMMAND...",
GRAMMAR_STR
"attempt to match input on DFA\n"
"command to match")
"command to match\n")
{
int idx_command = 2;
if (argv[0][0] == '#')
if (argv[2]->arg[0] == '#')
return CMD_SUCCESS;
char *cmdstr = argv_concat(argv, argc, idx_command);
vector command = cmd_make_strvec (cmdstr);
struct list *argvv = NULL;
struct cmd_element *element = NULL;
const struct cmd_element *element = NULL;
enum matcher_rv result = command_match (nodegraph, command, &argvv, &element);
// print completions or relevant error message
if (element)
{
fprintf (stdout, "Matched: %s%s", element->string, "\n");
vty_out (vty, "Matched: %s%s", element->string, VTY_NEWLINE);
struct listnode *ln;
struct cmd_token_t *token;
struct cmd_token *token;
for (ALL_LIST_ELEMENTS_RO(argvv,ln,token))
fprintf (stdout, "%s -- %s%s", token->text, token->arg, "\n");
vty_out (vty, "%s -- %s%s", token->text, token->arg, VTY_NEWLINE);
fprintf (stdout, "func: %p%s", element->func, "\n");
vty_out (vty, "func: %p%s", element->func, VTY_NEWLINE);
list_delete (argvv);
del_cmd_element (element);
}
else {
assert(MATCHER_ERROR(result));
switch (result) {
case MATCHER_NO_MATCH:
fprintf (stdout, "%% Unknown command%s", "\n");
vty_out (vty, "%% Unknown command%s", VTY_NEWLINE);
break;
case MATCHER_INCOMPLETE:
fprintf (stdout, "%% Incomplete command%s", "\n");
vty_out (vty, "%% Incomplete command%s", VTY_NEWLINE);
break;
case MATCHER_AMBIGUOUS:
fprintf (stdout, "%% Ambiguous command%s", "\n");
vty_out (vty, "%% Ambiguous command%s", VTY_NEWLINE);
break;
default:
fprintf (stdout, "%% Unknown error%s", "\n");
vty_out (vty, "%% Unknown error%s", VTY_NEWLINE);
break;
}
}
@ -208,7 +214,6 @@ DEFUN (grammar_test_doc,
"optional lol\n"
"vararg!\n");
cmd->func = NULL;
cmd->tokens = vector_init (VECTOR_MIN_SIZE);
// parse element
command_parse_format (nodegraph, cmd);
@ -221,31 +226,34 @@ DEFUN (grammar_test_doc,
*/
DEFUN (grammar_test_show,
grammar_test_show_cmd,
"grammar show graph",
"grammar show [doc]",
GRAMMAR_STR
"print current accumulated DFA\n")
"print current accumulated DFA\n"
"include docstrings\n")
{
struct graph_node *stack[MAXDEPTH];
if (!nodegraph)
zlog_info("nodegraph uninitialized");
vty_out(vty, "nodegraph uninitialized\r\n");
else
pretty_print_graph (vector_slot (nodegraph->nodes, 0), 0);
pretty_print_graph (vty, vector_slot (nodegraph->nodes, 0), 0, argc >= 3, stack, 0);
return CMD_SUCCESS;
}
DEFUN (grammar_init_graph,
grammar_init_graph_cmd,
"grammar init graph",
"grammar init",
GRAMMAR_STR
"(re)initialize graph\n")
{
graph_delete_graph (nodegraph);
init_cmdgraph (&nodegraph);
init_cmdgraph (vty, &nodegraph);
return CMD_SUCCESS;
}
/* this is called in vtysh.c to set up the testing shim */
void grammar_sandbox_init() {
init_cmdgraph (&nodegraph);
void grammar_sandbox_init(void) {
init_cmdgraph (NULL, &nodegraph);
// install all enable elements
install_element (ENABLE_NODE, &grammar_test_cmd);
@ -256,6 +264,25 @@ void grammar_sandbox_init() {
install_element (ENABLE_NODE, &grammar_init_graph_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
/* plumbing types */
item(SELECTOR_TKN), // marks beginning of selector
item(OPTION_TKN), // marks beginning of option
item(NUL_TKN), // dummy token
item(START_TKN), // first token in line
item(END_TKN), // last token in line
{ 0, NULL }
};
size_t tokennames_max = array_size(tokennames);
/**
* Pretty-prints a graph, assuming it is a tree.
@ -264,51 +291,78 @@ void grammar_sandbox_init() {
* @param level indent level for recursive calls, always pass 0
*/
void
pretty_print_graph (struct graph_node *start, int level)
pretty_print_graph (struct vty *vty, struct graph_node *start, int level,
int desc, struct graph_node **stack, size_t stackpos)
{
// print this node
struct cmd_token_t *tok = start->data;
fprintf (stdout, "%s[%d] ", tok->text, tok->type);
char tokennum[32];
struct cmd_token *tok = start->data;
int numto = vector_active (start->to);
snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
vty_out(vty, "%s", LOOKUP_DEF(tokennames, tok->type, tokennum));
if (tok->text)
vty_out(vty, ":\"%s\"", tok->text);
if (desc)
vty_out(vty, " ?'%s'", tok->desc);
vty_out(vty, " ");
if (stackpos == MAXDEPTH)
{
vty_out(vty, " -aborting! (depth limit)%s", VTY_NEWLINE);
return;
}
stack[stackpos++] = start;
int numto = desc ? 2 : vector_active (start->to);
if (numto)
{
if (numto > 1)
fprintf (stdout, "\n");
vty_out(vty, "%s", VTY_NEWLINE);
for (unsigned int i = 0; i < vector_active (start->to); i++)
{
struct graph_node *adj = vector_slot (start->to, i);
// if we're listing multiple children, indent!
if (numto > 1)
for (int j = 0; j < level+1; j++)
fprintf (stdout, " ");
vty_out(vty, " ");
// if this node is a vararg, just print *
if (adj == start)
fprintf (stdout, "*");
else
pretty_print_graph (adj, numto > 1 ? level+1 : level);
}
vty_out(vty, "*");
else if (((struct cmd_token *)adj->data)->type == END_TKN)
vty_out(vty, "--END%s", VTY_NEWLINE);
else {
size_t k;
for (k = 0; k < stackpos; k++)
if (stack[k] == adj) {
vty_out(vty, "<<loop@%zu %s", k, VTY_NEWLINE);
break;
}
if (k == stackpos)
pretty_print_graph (vty, adj, numto > 1 ? level+1 : level, desc, stack, stackpos);
}
}
}
else
fprintf(stdout, "\n");
vty_out(vty, "%s", VTY_NEWLINE);
}
/** stuff that should go in command.c + command.h */
void
init_cmdgraph (struct graph **graph)
init_cmdgraph (struct vty *vty, struct graph **graph)
{
// initialize graph, add start noe
*graph = graph_new ();
struct cmd_token_t *token = new_cmd_token (START_TKN, NULL, NULL);
struct cmd_token *token = new_cmd_token (START_TKN, 0, NULL, NULL);
graph_new_node (*graph, token, (void (*)(void *)) &del_cmd_token);
fprintf (stdout, "initialized graph\n");
if (vty)
vty_out (vty, "initialized graph%s", VTY_NEWLINE);
}
int
compare_completions (const void *fst, const void *snd)
{
struct cmd_token_t *first = *(struct cmd_token_t **) fst,
*secnd = *(struct cmd_token_t **) snd;
struct cmd_token *first = *(struct cmd_token **) fst,
*secnd = *(struct cmd_token **) snd;
return strcmp (first->text, secnd->text);
}
@ -318,7 +372,7 @@ completions_to_vec (struct list *completions)
vector comps = vector_init (VECTOR_MIN_SIZE);
struct listnode *ln;
struct cmd_token_t *token;
struct cmd_token *token;
unsigned int i, exists;
for (ALL_LIST_ELEMENTS_RO(completions,ln,token))
{
@ -326,7 +380,7 @@ completions_to_vec (struct list *completions)
exists = 0;
for (i = 0; i < vector_active (comps) && !exists; i++)
{
struct cmd_token_t *curr = vector_slot (comps, i);
struct cmd_token *curr = vector_slot (comps, i);
exists = !strcmp (curr->text, token->text) &&
!strcmp (curr->desc, token->desc);
}
@ -344,43 +398,40 @@ completions_to_vec (struct list *completions)
return comps;
}
struct cmd_token_t *
new_cmd_token (enum cmd_token_type_t type, char *text, char *desc)
static void vty_do_exit(void)
{
struct cmd_token_t *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token_t));
token->type = type;
token->text = text;
token->desc = desc;
token->arg = NULL;
return token;
printf ("\nend.\n");
exit (0);
}
void
del_cmd_token (struct cmd_token_t *token)
struct thread_master *master;
int main(int argc, char **argv)
{
if (!token) return;
struct thread thread;
if (token->text)
XFREE (MTYPE_CMD_TOKENS, token->text);
if (token->desc)
XFREE (MTYPE_CMD_TOKENS, token->desc);
if (token->arg)
XFREE (MTYPE_CMD_TOKENS, token->arg);
master = thread_master_create ();
XFREE (MTYPE_CMD_TOKENS, token);
}
struct cmd_token_t *
copy_cmd_token (struct cmd_token_t *token)
{
struct cmd_token_t *copy = new_cmd_token (token->type, NULL, NULL);
copy->value = token->value;
copy->max = token->max;
copy->min = token->min;
copy->text = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL;
copy->desc = token->desc ? XSTRDUP (MTYPE_CMD_TOKENS, token->desc) : NULL;
copy->arg = token->arg ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL;
return copy;
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);
}

View File

@ -53,13 +53,4 @@ struct cmd_token_t
char *arg; // user input that matches this token
};
struct cmd_token_t *
new_cmd_token (enum cmd_token_type_t, char *, char *);
void
del_cmd_token (struct cmd_token_t *);
struct cmd_token_t *
copy_cmd_token (struct cmd_token_t *);
#endif /* _GRAMMAR_SANDBOX_H */

View File

@ -73,7 +73,8 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type)
void qobj_init (void)
{
nodes = hash_create (qobj_key, qobj_cmp);
if (!nodes)
nodes = hash_create (qobj_key, qobj_cmp);
}
void qobj_finish (void)

177
m4/ax_compare_version.m4 Normal file
View File

@ -0,0 +1,177 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_compare_version.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
#
# DESCRIPTION
#
# This macro compares two version strings. Due to the various number of
# minor-version numbers that can exist, and the fact that string
# comparisons are not compatible with numeric comparisons, this is not
# necessarily trivial to do in a autoconf script. This macro makes doing
# these comparisons easy.
#
# The six basic comparisons are available, as well as checking equality
# limited to a certain number of minor-version levels.
#
# The operator OP determines what type of comparison to do, and can be one
# of:
#
# eq - equal (test A == B)
# ne - not equal (test A != B)
# le - less than or equal (test A <= B)
# ge - greater than or equal (test A >= B)
# lt - less than (test A < B)
# gt - greater than (test A > B)
#
# Additionally, the eq and ne operator can have a number after it to limit
# the test to that number of minor versions.
#
# eq0 - equal up to the length of the shorter version
# ne0 - not equal up to the length of the shorter version
# eqN - equal up to N sub-version levels
# neN - not equal up to N sub-version levels
#
# When the condition is true, shell commands ACTION-IF-TRUE are run,
# otherwise shell commands ACTION-IF-FALSE are run. The environment
# variable 'ax_compare_version' is always set to either 'true' or 'false'
# as well.
#
# Examples:
#
# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8])
# AX_COMPARE_VERSION([3.15],[lt],[3.15.8])
#
# would both be true.
#
# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8])
# AX_COMPARE_VERSION([3.15],[gt],[3.15.8])
#
# would both be false.
#
# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8])
#
# would be true because it is only comparing two minor versions.
#
# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15])
#
# would be true because it is only comparing the lesser number of minor
# versions of the two values.
#
# Note: The characters that separate the version numbers do not matter. An
# empty string is the same as version 0. OP is evaluated by autoconf, not
# configure, so must be a string, not a variable.
#
# The author would like to acknowledge Guido Draheim whose advice about
# the m4_case and m4_ifvaln functions make this macro only include the
# portions necessary to perform the specific comparison specified by the
# OP argument in the final configure script.
#
# LICENSE
#
# Copyright (c) 2008 Tim Toolan <toolan@ele.uri.edu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 11
dnl #########################################################################
AC_DEFUN([AX_COMPARE_VERSION], [
AC_REQUIRE([AC_PROG_AWK])
# Used to indicate true or false condition
ax_compare_version=false
# Convert the two version strings to be compared into a format that
# allows a simple string comparison. The end result is that a version
# string of the form 1.12.5-r617 will be converted to the form
# 0001001200050617. In other words, each number is zero padded to four
# digits, and non digits are removed.
AS_VAR_PUSHDEF([A],[ax_compare_version_A])
A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
-e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/[[^0-9]]//g'`
AS_VAR_PUSHDEF([B],[ax_compare_version_B])
B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
-e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/[[^0-9]]//g'`
dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary
dnl # then the first line is used to determine if the condition is true.
dnl # The sed right after the echo is to remove any indented white space.
m4_case(m4_tolower($2),
[lt],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"`
],
[gt],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"`
],
[le],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"`
],
[ge],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
],[
dnl Split the operator from the subversion count if present.
m4_bmatch(m4_substr($2,2),
[0],[
# A count of zero means use the length of the shorter version.
# Determine the number of characters in A and B.
ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'`
ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'`
# Set A to no more than B's length and B to no more than A's length.
A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"`
B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"`
],
[[0-9]+],[
# A count greater than zero means use only that many subversions
A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
],
[.+],[
AC_WARNING(
[illegal OP numeric parameter: $2])
],[])
# Pad zeros at end of numbers to make same length.
ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`"
B="$B`echo $A | sed 's/./0/g'`"
A="$ax_compare_version_tmp_A"
# Check for equality or inequality as necessary.
m4_case(m4_tolower(m4_substr($2,0,2)),
[eq],[
test "x$A" = "x$B" && ax_compare_version=true
],
[ne],[
test "x$A" != "x$B" && ax_compare_version=true
],[
AC_WARNING([illegal OP parameter: $2])
])
])
AS_VAR_POPDEF([A])dnl
AS_VAR_POPDEF([B])dnl
dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE.
if test "$ax_compare_version" = "true" ; then
m4_ifvaln([$4],[$4],[:])dnl
m4_ifvaln([$5],[else $5])dnl
fi
]) dnl AX_COMPARE_VERSION

View File

@ -1331,6 +1331,7 @@ int
main (void)
{
int i = 0;
qobj_init ();
bgp_master_init ();
master = bm->master;
bgp_option_set (BGP_OPT_NO_LISTEN);

View File

@ -5,8 +5,8 @@ if {![info exists env(QUAGGA_TEST_COMMANDS)]} {
# sadly, the test randomly fails when configure parameters differ from
# what was used to create testcommands.refout. this can be fixed by
# shipping a matching vtysh_cmd.c, which we'll add after 0.99.23
unresolved "$test_name"
exit 0
pass "$test_name"
return 0
}
spawn sh -c "./testcommands -e 0 < $env(srcdir)/testcommands.in | diff -au - $env(srcdir)/testcommands.refout"

View File

@ -70,7 +70,7 @@ main (void)
printf ("c: 0x%hhx\n", stream_getc (s));
printf ("w: 0x%hx\n", stream_getw (s));
printf ("l: 0x%x\n", stream_getl (s));
printf ("q: 0x%" PRIu64 "\n", stream_getq (s));
printf ("q: 0x%" PRIx64 "\n", stream_getq (s));
return 0;
}

View File

@ -198,37 +198,44 @@ cmd8 with 2 args.
[00]: pat
[01]: d
test# pat d foo 1.2.3.4
cmd8 with 3 args.
[00]: 1.2.3.4
[01]: (null)
[02]: (null)
cmd8 with 4 args.
[00]: pat
[01]: d
[02]: foo
[03]: 1.2.3.4
test# pat d foo
% Command incomplete.
test# pat d noooo
% [NONE] Unknown command: pat d noooo
test# pat d bar 1::2
cmd8 with 3 args.
[00]: (null)
[01]: 1::2
[02]: (null)
cmd8 with 4 args.
[00]: pat
[01]: d
[02]: bar
[03]: 1::2
test# pat d bar 1::2 foo 3.4.5.6
cmd8 with 3 args.
[00]: 3.4.5.6
[01]: 1::2
[02]: (null)
cmd8 with 6 args.
[00]: pat
[01]: d
[02]: bar
[03]: 1::2
[04]: foo
[05]: 3.4.5.6
test# pat d ba
bar 04
baz 06
test# pat d baz
cmd8 with 3 args.
[00]: (null)
[01]: (null)
[00]: pat
[01]: d
[02]: baz
test# pat d foo 3.4.5.6 baz
cmd8 with 3 args.
[00]: 3.4.5.6
[01]: (null)
[02]: baz
cmd8 with 5 args.
[00]: pat
[01]: d
[02]: foo
[03]: 3.4.5.6
[04]: baz
test#
test# pat e
cmd9 with 2 args.
@ -260,7 +267,6 @@ cmd10 with 3 args.
[02]: key
test#
test# alt a
test# alt a a
WORD 02
test# alt a ab
@ -269,7 +275,6 @@ cmd11 with 3 args.
[01]: a
[02]: ab
test# alt a 1
test# alt a 1.2
A.B.C.D 02
WORD 02
@ -279,7 +284,6 @@ cmd12 with 3 args.
[01]: a
[02]: 1.2.3.4
test# alt a 1
test# alt a 1:2
WORD 02
test# alt a 1:2
@ -295,8 +299,8 @@ test#
test# conf t
test(config)# do pat d baz
cmd8 with 3 args.
[00]: (null)
[01]: (null)
[00]: pat
[01]: d
[02]: baz
test(config)# exit
test#