mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-05 10:08:41 +00:00
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:
commit
bcfb39acd7
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||
|
@ -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;
|
||||
|
61
configure.ac
61
configure.ac
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
%{
|
||||
#include "command_parse.h"
|
||||
#define YYSTYPE CMD_YYSTYPE
|
||||
%}
|
||||
|
||||
WORD (\-|\+)?[a-z\*][-+_a-zA-Z0-9\*]*
|
||||
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
177
m4/ax_compare_version.m4
Normal 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
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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#
|
||||
|
Loading…
Reference in New Issue
Block a user