lib: Break up functions, begin matcher

Moved test hook out of command.c into vtysh.c,
renamed graph modules, added matching code

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2016-07-19 21:14:27 +00:00
parent 340a2b4ac0
commit 9d0662e009
10 changed files with 310 additions and 25 deletions

View File

@ -11,7 +11,7 @@ libzebra_la_LDFLAGS = -version-info 0:0:0
libzebra_la_SOURCES = \
network.c pid_output.c getopt.c getopt1.c daemon.c \
checksum.c vector.c linklist.c vty.c \
cmdtree.c command_parse.y command_lex.l \
command_graph.c command_parse.y command_lex.l command_match.c grammar_sandbox.c \
command.c \
sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
@ -28,7 +28,7 @@ libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@
pkginclude_HEADERS = \
buffer.h checksum.h filter.h getopt.h hash.h \
if.h linklist.h log.h \
cmdtree.h \
command_graph.h command_match.h grammar_sandbox.h \
command.h \
memory.h network.h prefix.h routemap.h distribute.h sockunion.h \
str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \

View File

@ -34,8 +34,6 @@ Boston, MA 02111-1307, USA. */
#include "workqueue.h"
#include "vrf.h"
#include "grammar_sandbox.c"
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
vector cmdvec = NULL;
@ -4077,10 +4075,6 @@ cmd_init (int terminal)
install_element (ENABLE_NODE, &show_version_cmd);
install_element (ENABLE_NODE, &show_commandtree_cmd);
/**/
grammar_sandbox_init();
/**/
if (terminal)
{
install_element (ENABLE_NODE, &config_terminal_length_cmd);

View File

@ -7,7 +7,7 @@
*/
#include <zebra.h>
#include "cmdtree.h"
#include "command_graph.h"
#include "memory.h"
struct graph_node *

View File

@ -1,3 +1,6 @@
#ifndef COMMAND_GRAPH_H
#define COMMAND_GRAPH_H
#include "vty.h"
#include "vector.h"
@ -78,3 +81,5 @@ new_node(enum graph_node_type);
*/
extern void
walk_graph(struct graph_node *, int);
#endif

174
lib/command_match.c Normal file
View File

@ -0,0 +1,174 @@
#include <zebra.h>
#include "memory.h"
#include "command_match.h"
enum match_type
match_command (struct graph_node *start, enum filter_type filter, const char *input)
{
// match input on DFA
return exact_match;
}
#define IPV4_ADDR_STR "0123456789."
#define IPV4_PREFIX_STR "0123456789./"
enum match_type
cmd_ipv4_match (const char *str)
{
struct sockaddr_in sin_dummy;
if (str == NULL)
return partly_match;
if (strspn (str, IPV4_ADDR_STR) != strlen (str))
return no_match;
if (inet_pton(AF_INET, str, &sin_dummy.sin_addr) != 1)
return no_match;
return exact_match;
}
enum match_type
cmd_ipv4_prefix_match (const char *str)
{
struct sockaddr_in sin_dummy;
const char *delim = "/\0";
char *dupe, *prefix, *mask, *context, *endptr;
int nmask = -1;
if (str == NULL)
return partly_match;
if (strspn (str, IPV4_PREFIX_STR) != strlen (str))
return no_match;
/* tokenize to address + mask */
dupe = XMALLOC(MTYPE_TMP, strlen(str)+1);
strncpy(dupe, str, strlen(str)+1);
prefix = strtok_r(dupe, delim, &context);
mask = strtok_r(NULL, delim, &context);
if (!mask)
return partly_match;
/* validate prefix */
if (inet_pton(AF_INET, prefix, &sin_dummy.sin_addr) != 1)
return no_match;
/* validate mask */
nmask = strtol (mask, &endptr, 10);
if (*endptr != '\0' || nmask < 0 || nmask > 32)
return no_match;
XFREE(MTYPE_TMP, dupe);
return exact_match;
}
#define IPV6_ADDR_STR "0123456789abcdefABCDEF:."
#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./"
#ifdef HAVE_IPV6
enum match_type
cmd_ipv6_match (const char *str)
{
struct sockaddr_in6 sin6_dummy;
int ret;
if (str == NULL)
return partly_match;
if (strspn (str, IPV6_ADDR_STR) != strlen (str))
return no_match;
ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
if (ret == 1)
return exact_match;
return no_match;
}
enum match_type
cmd_ipv6_prefix_match (const char *str)
{
struct sockaddr_in6 sin6_dummy;
const char *delim = "/\0";
char *dupe, *prefix, *mask, *context, *endptr;
int nmask = -1;
if (str == NULL)
return partly_match;
if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
return no_match;
/* tokenize to address + mask */
dupe = XMALLOC(MTYPE_TMP, strlen(str)+1);
strncpy(dupe, str, strlen(str)+1);
prefix = strtok_r(dupe, delim, &context);
mask = strtok_r(NULL, delim, &context);
if (!mask)
return partly_match;
/* validate prefix */
if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1)
return no_match;
/* validate mask */
nmask = strtol (mask, &endptr, 10);
if (*endptr != '\0' || nmask < 0 || nmask > 128)
return no_match;
XFREE(MTYPE_TMP, dupe);
return exact_match;
}
#endif
enum match_type
cmd_range_match (struct graph_node *rangenode, const char *str)
{
char *endptr = NULL;
signed long long val;
if (str == NULL)
return 1;
val = strtoll (str, &endptr, 10);
if (*endptr != '\0')
return 0;
val = llabs(val);
if (val < rangenode->min || val > rangenode->max)
return no_match;
else
return exact_match;
}
enum match_type
cmd_word_match(struct graph_node *wordnode,
enum filter_type filter,
const char *word)
{
if (filter == FILTER_RELAXED)
if (!word || !strlen(word))
return partly_match;
if (!word)
return no_match;
if (filter == FILTER_RELAXED && !strncmp(wordnode->text, word, strlen(word)))
{
if (!strcmp(wordnode->text, word))
return exact_match;
return partly_match;
}
if (filter == FILTER_STRICT && !strcmp(wordnode->text, word))
return exact_match;
return no_match;
}

69
lib/command_match.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef COMMAND_MATCH_H
#define COMMAND_MATCH_H
#include "command_graph.h"
/**
* Filter types. These tell the parser whether to allow
* partial matching on tokens.
*/
enum filter_type
{
FILTER_RELAXED,
FILTER_STRICT
};
/**
* Command matcher result value.
*/
enum matcher_rv
{
MATCHER_OK,
MATCHER_COMPLETE,
MATCHER_INCOMPLETE,
MATCHER_NO_MATCH,
MATCHER_AMBIGUOUS,
MATCHER_EXCEED_ARGC_MAX
};
/* Completion match types. */
enum match_type
{
no_match,
partly_match,
exact_match
};
/**
* Defines which matcher_rv values constitute
* an error. Should be used against matcher_rv
* return values to do basic error checking.
*/
#define MATCHER_ERROR(matcher_rv) \
( (matcher_rv) == MATCHER_INCOMPLETE \
|| (matcher_rv) == MATCHER_NO_MATCH \
|| (matcher_rv) == MATCHER_AMBIGUOUS \
|| (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
)
enum match_type
cmd_ipv4_match (const char *);
enum match_type
cmd_ipv4_prefix_match (const char *);
enum match_type
cmd_ipv6_match (const char *);
enum match_type
cmd_ipv6_prefix_match (const char *);
enum match_type
cmd_range_match (struct graph_node *, const char *str);
enum match_type
cmd_word_match (struct graph_node *, enum filter_type, const char *);
enum match_type
match_command (struct graph_node *, enum filter_type, const char *);
#endif

View File

@ -1,5 +1,15 @@
/*
* Command format string parser.
*
* Turns a command definition into a DFA that together with the functions
* provided in command_match.c may be used to map command line input to a
* function.
*
* @author Quentin Young <qlyoung@cumulusnetworks.com>
*/
%{
#include "cmdtree.h"
#include "command_graph.h"
extern int yylex(void);
extern void yyerror(const char *);
@ -8,16 +18,22 @@ extern void yyerror(const char *);
#define YYDEBUG 1
%}
%code provides {
extern struct graph_node *cmd_parse_format_new(const char *, const char *, struct graph_node *);
extern void set_buffer_string(const char *);
extern struct
graph_node *cmd_parse_format(const char *,
const char *,
struct graph_node *);
extern void
set_buffer_string(const char *);
}
/* valid types for tokens */
%union{
int integer;
char *string;
struct graph_node *node;
}
/* some helpful state variables */
%{
struct graph_node *startnode, // command root node
*currnode, // current node
@ -66,7 +82,7 @@ sentence_root: WORD
$$ = new_node(WORD_GN);
$$->text = strdup(yylval.string);
fprintf(stderr, ">>>>>>>> YYLVAL.STRING: %s\n", yylval.string);
fprintf(stderr, ">>>>>>>> TEXT: %s\n", $$->text);
fprintf(stderr, ">>>>>>>> TEXT: %s\n", $$->text);
currnode = $$;
currnode->is_root = 1;
@ -270,7 +286,7 @@ void yyerror(char const *message) {
}
struct graph_node *
cmd_parse_format_new(const char *string, const char *desc, struct graph_node *start)
cmd_parse_format(const char *string, const char *desc, struct graph_node *start)
{
fprintf(stderr, "parsing: %s\n", string);

View File

@ -1,17 +1,14 @@
#include "command.h"
#include "command_graph.h"
#include "command_parse.h"
#include "cmdtree.h"
#include "command_match.h"
#define GRAMMAR_STR "CLI grammar sandbox\n"
struct graph_node * nodegraph;
DEFUN (grammar_test,
grammar_test_cmd,
"grammar parse .COMMAND",
GRAMMAR_STR
"command to pass to new parser\n")
{
/*
char* combine_vararg(char* argv, int argc) {
size_t linesize = 0;
for (int i = 0; i < argc; i++)
linesize += strlen(argv[i]) + 1;
@ -24,8 +21,19 @@ DEFUN (grammar_test,
strcat(cat, " ");
}
//struct graph_node *result = new_node(NUL_GN);
cmd_parse_format_new((const char*) cat, "lol", nodegraph);
return cat;
}
*/
DEFUN (grammar_test,
grammar_test_cmd,
"grammar parse .COMMAND",
GRAMMAR_STR
"command to pass to new parser\n")
{
const char* command = argv_concat(argv, argc, 0);
cmd_parse_format(command, "lol", nodegraph);
walk_graph(nodegraph, 0);
return CMD_SUCCESS;
@ -37,8 +45,20 @@ DEFUN (grammar_test_show,
GRAMMAR_STR
"print current accumulated DFA\n")
{
walk_graph(nodegraph, 0);
return CMD_SUCCESS;
walk_graph(nodegraph, 0);
return CMD_SUCCESS;
}
DEFUN (grammar_test_match,
grammar_test_match_cmd,
"grammar match .COMMAND",
GRAMMAR_STR
"attempt to match input on DFA\n"
"command to match")
{
const char* command = argv_concat(argv, argc, 0);
match_command(nodegraph, FILTER_STRICT, command);
return CMD_SUCCESS;
}
@ -48,4 +68,5 @@ void grammar_sandbox_init() {
nodegraph = new_node(NUL_GN);
install_element (ENABLE_NODE, &grammar_test_cmd);
install_element (ENABLE_NODE, &grammar_test_show_cmd);
install_element (ENABLE_NODE, &grammar_test_match_cmd);
}

2
lib/grammar_sandbox.h Normal file
View File

@ -0,0 +1,2 @@
void
grammar_sandbox_init(void);

View File

@ -42,6 +42,8 @@
#include "bgpd/bgp_vty.h"
#include "vrf.h"
#include "lib/grammar_sandbox.h"
/* Struct VTY. */
struct vty *vty;
@ -3076,4 +3078,6 @@ vtysh_init_vty (void)
install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd);
install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd);
/* grammar sandbox */
grammar_sandbox_init();
}