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 \
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 \
+++ /dev/null
-/*
- * Command DFA module.
- * Provides a DFA data structure and associated functions for manipulating it.
- * Used to match user command line input.
- *
- * @author Quentin Young <qlyoung@cumulusnetworks.com>
- */
-
-#include <zebra.h>
-#include "cmdtree.h"
-#include "memory.h"
-
-struct graph_node *
-add_node(struct graph_node *parent, struct graph_node *child)
-{
- struct graph_node *p_child;
-
- for (unsigned int i = 0; i < vector_active(parent->children); i++)
- {
- p_child = vector_slot(parent->children, i);
- if (cmp_node(child, p_child))
- return p_child;
- }
- vector_set(parent->children, child);
- return child;
-}
-
-int
-cmp_node(struct graph_node *first, struct graph_node *second)
-{
- // compare types
- if (first->type != second->type) return 0;
-
- switch (first->type) {
- case WORD_GN: // words and variables are equal if their
- case VARIABLE_GN: // text value is equal
- if (first->text && second->text) {
- if (strcmp(first->text, second->text)) return 0;
- }
- else if (first->text != second->text) return 0;
- break;
- case RANGE_GN: // ranges are equal if their bounds are equal
- if (first->min != second->min || first->max != second->max)
- return 0;
- break;
- case NUMBER_GN: // numbers are equal if their values are equal
- if (first->value != second->value) return 0;
- break;
- /* selectors and options should be equal if all paths are equal,
- * but the graph isomorphism problem is not solvable in polynomial
- * time so we consider selectors and options inequal in all cases
- */
- case SELECTOR_GN:
- case OPTION_GN:
- return 0;
- default:
- break;
- }
-
- return 1;
-}
-
-struct graph_node *
-new_node(enum graph_node_type type)
-{
- struct graph_node *node = malloc(sizeof(struct graph_node));
- node->type = type;
- node->children = vector_init(VECTOR_MIN_SIZE);
- node->is_leaf = 0;
- node->is_root = 0;
- node->end = NULL;
- node->text = NULL;
- node->value = 0;
- node->min = 0;
- node->max = 0;
- node->func = NULL;
-
- return node;
-}
-
-void
-walk_graph(struct graph_node *start, int level)
-{
- // print this node
- switch (start->type) {
- case WORD_GN:
- case IPV4_GN:
- case IPV4_PREFIX_GN:
- case IPV6_GN:
- case IPV6_PREFIX_GN:
- case VARIABLE_GN:
- case RANGE_GN:
- fprintf(stderr, "%s", start->text);
- break;
- case NUMBER_GN:
- fprintf(stderr, "%d", start->value);
- break;
- case SELECTOR_GN:
- fprintf(stderr, "<>");
- break;
- case OPTION_GN:
- fprintf(stderr, "[]");
- break;
- case NUL_GN:
- fprintf(stderr, "NUL");
- break;
- default:
- fprintf(stderr, "ERROR");
- }
- fprintf(stderr, "[%d] ", vector_active(start->children));
-
- if (vector_active(start->children))
- for (unsigned int i = 0; i < vector_active(start->children); i++)
- {
- struct graph_node *r = vector_slot(start->children, i);
- if (!r) {
- fprintf(stderr, "Child seems null?\n");
- break;
- }
- else {
- if (vector_active(start->children) > 1) {
- fprintf(stderr, "\n");
- for (int i = 0; i < level+1; i++)
- fprintf(stderr, " ");
- walk_graph(r, level+1);
- }
- else
- walk_graph(r, level);
- }
- }
- if (level == 0)
- fprintf(stderr, "\n");
-}
+++ /dev/null
-#include "vty.h"
-#include "vector.h"
-
-enum graph_node_type
-{
- WORD_GN,
- IPV4_GN,
- IPV4_PREFIX_GN,
- IPV6_GN,
- IPV6_PREFIX_GN,
- VARIABLE_GN,
- RANGE_GN,
- NUMBER_GN,
- SELECTOR_GN,
- OPTION_GN,
- NUL_GN
-};
-
-struct graph_node
-{
- enum graph_node_type type;
- vector children;
- int is_root; // true if first token in command
- int is_leaf; // true if last token in command
- struct graph_node * end; // pointer to end for selector & option
-
- int (*func)(struct vty *, int, const char *[]);
-
- /* various data fields for nodes */
- char* text; // for words and variables
- int value; // for numbers
- int min, max; // for ranges
-};
-
-/*
- * Adds a child to a node.
- * If the node already has the exact same child, nothing is done. This is
- * decided with cmp_node.
- *
- * @param[in] parent node
- * @param[in] child node
- * @return the new child, or the existing child if the parent already has the
- * new child
- */
-extern struct graph_node *
-add_node(struct graph_node *, struct graph_node *);
-
-/*
- * Compares two nodes for parsing equivalence.
- * Equivalence in this case means that a single user input token
- * should be able to unambiguously match one of the two nodes.
- * For example, two nodes which have all fields equal except their
- * function pointers would be considered equal.
- *
- * @param[in] first node to compare
- * @param[in] second node to compare
- * @return 1 if equal, zero otherwise.
- */
-extern int
-cmp_node(struct graph_node *, struct graph_node *);
-
-/*
- * Create a new node.
- * Initializes all fields to default values and sets the node type.
- *
- * @param[in] node type
- * @return pointer to the newly allocated node
- */
-extern struct graph_node *
-new_node(enum graph_node_type);
-
-/**
- * Walks a command DFA, printing structure to stdout.
- * For debugging.
- *
- * @param[in] start node of graph to walk
- * @param[in] graph depth for recursion, caller passes 0
- */
-extern void
-walk_graph(struct graph_node *, int);
#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;
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);
--- /dev/null
+/*
+ * Command DFA module.
+ * Provides a DFA data structure and associated functions for manipulating it.
+ * Used to match user command line input.
+ *
+ * @author Quentin Young <qlyoung@cumulusnetworks.com>
+ */
+
+#include <zebra.h>
+#include "command_graph.h"
+#include "memory.h"
+
+struct graph_node *
+add_node(struct graph_node *parent, struct graph_node *child)
+{
+ struct graph_node *p_child;
+
+ for (unsigned int i = 0; i < vector_active(parent->children); i++)
+ {
+ p_child = vector_slot(parent->children, i);
+ if (cmp_node(child, p_child))
+ return p_child;
+ }
+ vector_set(parent->children, child);
+ return child;
+}
+
+int
+cmp_node(struct graph_node *first, struct graph_node *second)
+{
+ // compare types
+ if (first->type != second->type) return 0;
+
+ switch (first->type) {
+ case WORD_GN: // words and variables are equal if their
+ case VARIABLE_GN: // text value is equal
+ if (first->text && second->text) {
+ if (strcmp(first->text, second->text)) return 0;
+ }
+ else if (first->text != second->text) return 0;
+ break;
+ case RANGE_GN: // ranges are equal if their bounds are equal
+ if (first->min != second->min || first->max != second->max)
+ return 0;
+ break;
+ case NUMBER_GN: // numbers are equal if their values are equal
+ if (first->value != second->value) return 0;
+ break;
+ /* selectors and options should be equal if all paths are equal,
+ * but the graph isomorphism problem is not solvable in polynomial
+ * time so we consider selectors and options inequal in all cases
+ */
+ case SELECTOR_GN:
+ case OPTION_GN:
+ return 0;
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+struct graph_node *
+new_node(enum graph_node_type type)
+{
+ struct graph_node *node = malloc(sizeof(struct graph_node));
+ node->type = type;
+ node->children = vector_init(VECTOR_MIN_SIZE);
+ node->is_leaf = 0;
+ node->is_root = 0;
+ node->end = NULL;
+ node->text = NULL;
+ node->value = 0;
+ node->min = 0;
+ node->max = 0;
+ node->func = NULL;
+
+ return node;
+}
+
+void
+walk_graph(struct graph_node *start, int level)
+{
+ // print this node
+ switch (start->type) {
+ case WORD_GN:
+ case IPV4_GN:
+ case IPV4_PREFIX_GN:
+ case IPV6_GN:
+ case IPV6_PREFIX_GN:
+ case VARIABLE_GN:
+ case RANGE_GN:
+ fprintf(stderr, "%s", start->text);
+ break;
+ case NUMBER_GN:
+ fprintf(stderr, "%d", start->value);
+ break;
+ case SELECTOR_GN:
+ fprintf(stderr, "<>");
+ break;
+ case OPTION_GN:
+ fprintf(stderr, "[]");
+ break;
+ case NUL_GN:
+ fprintf(stderr, "NUL");
+ break;
+ default:
+ fprintf(stderr, "ERROR");
+ }
+ fprintf(stderr, "[%d] ", vector_active(start->children));
+
+ if (vector_active(start->children))
+ for (unsigned int i = 0; i < vector_active(start->children); i++)
+ {
+ struct graph_node *r = vector_slot(start->children, i);
+ if (!r) {
+ fprintf(stderr, "Child seems null?\n");
+ break;
+ }
+ else {
+ if (vector_active(start->children) > 1) {
+ fprintf(stderr, "\n");
+ for (int i = 0; i < level+1; i++)
+ fprintf(stderr, " ");
+ walk_graph(r, level+1);
+ }
+ else
+ walk_graph(r, level);
+ }
+ }
+ if (level == 0)
+ fprintf(stderr, "\n");
+}
--- /dev/null
+#ifndef COMMAND_GRAPH_H
+#define COMMAND_GRAPH_H
+
+#include "vty.h"
+#include "vector.h"
+
+enum graph_node_type
+{
+ WORD_GN,
+ IPV4_GN,
+ IPV4_PREFIX_GN,
+ IPV6_GN,
+ IPV6_PREFIX_GN,
+ VARIABLE_GN,
+ RANGE_GN,
+ NUMBER_GN,
+ SELECTOR_GN,
+ OPTION_GN,
+ NUL_GN
+};
+
+struct graph_node
+{
+ enum graph_node_type type;
+ vector children;
+ int is_root; // true if first token in command
+ int is_leaf; // true if last token in command
+ struct graph_node * end; // pointer to end for selector & option
+
+ int (*func)(struct vty *, int, const char *[]);
+
+ /* various data fields for nodes */
+ char* text; // for words and variables
+ int value; // for numbers
+ int min, max; // for ranges
+};
+
+/*
+ * Adds a child to a node.
+ * If the node already has the exact same child, nothing is done. This is
+ * decided with cmp_node.
+ *
+ * @param[in] parent node
+ * @param[in] child node
+ * @return the new child, or the existing child if the parent already has the
+ * new child
+ */
+extern struct graph_node *
+add_node(struct graph_node *, struct graph_node *);
+
+/*
+ * Compares two nodes for parsing equivalence.
+ * Equivalence in this case means that a single user input token
+ * should be able to unambiguously match one of the two nodes.
+ * For example, two nodes which have all fields equal except their
+ * function pointers would be considered equal.
+ *
+ * @param[in] first node to compare
+ * @param[in] second node to compare
+ * @return 1 if equal, zero otherwise.
+ */
+extern int
+cmp_node(struct graph_node *, struct graph_node *);
+
+/*
+ * Create a new node.
+ * Initializes all fields to default values and sets the node type.
+ *
+ * @param[in] node type
+ * @return pointer to the newly allocated node
+ */
+extern struct graph_node *
+new_node(enum graph_node_type);
+
+/**
+ * Walks a command DFA, printing structure to stdout.
+ * For debugging.
+ *
+ * @param[in] start node of graph to walk
+ * @param[in] graph depth for recursion, caller passes 0
+ */
+extern void
+walk_graph(struct graph_node *, int);
+
+#endif
--- /dev/null
+#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;
+}
--- /dev/null
+#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
+/*
+ * 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 *);
#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
$$ = 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;
}
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);
#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;
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;
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;
}
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);
}
--- /dev/null
+void
+grammar_sandbox_init(void);
#include "bgpd/bgp_vty.h"
#include "vrf.h"
+#include "lib/grammar_sandbox.h"
+
/* Struct VTY. */
struct vty *vty;
install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd);
install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd);
+ /* grammar sandbox */
+ grammar_sandbox_init();
}