summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command_graph.c66
-rw-r--r--lib/command_graph.h7
-rw-r--r--lib/command_match.c181
-rw-r--r--lib/command_match.h7
-rw-r--r--lib/command_parse.y4
-rw-r--r--lib/grammar_sandbox.c40
6 files changed, 249 insertions, 56 deletions
diff --git a/lib/command_graph.c b/lib/command_graph.c
index 9b91eaf242..4e99884dc8 100644
--- a/lib/command_graph.c
+++ b/lib/command_graph.c
@@ -78,11 +78,19 @@ new_node(enum graph_node_type type)
return node;
}
-void
-walk_graph(struct graph_node *start, int level)
+const char *
+describe_node(struct graph_node *node)
{
+ const char *desc = NULL;
+ char num[21];
+
+ if (node == NULL) {
+ desc = "(null node)";
+ return desc;
+ }
+
// print this node
- switch (start->type) {
+ switch (node->type) {
case WORD_GN:
case IPV4_GN:
case IPV4_PREFIX_GN:
@@ -90,44 +98,46 @@ walk_graph(struct graph_node *start, int level)
case IPV6_PREFIX_GN:
case VARIABLE_GN:
case RANGE_GN:
- fprintf(stderr, "%s", start->text);
+ desc = node->text;
break;
case NUMBER_GN:
- fprintf(stderr, "%d", start->value);
+ sprintf(num, "%d", node->value);
break;
case SELECTOR_GN:
- fprintf(stderr, "<>");
+ desc = "<>";
break;
case OPTION_GN:
- fprintf(stderr, "[]");
+ desc = "[]";
break;
case NUL_GN:
- fprintf(stderr, "NUL");
+ desc = "NUL";
break;
default:
- fprintf(stderr, "ERROR");
+ desc = "ERROR";
}
- fprintf(stderr, "[%d] ", vector_active(start->children));
+ return desc;
+}
- 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);
+
+void
+walk_graph(struct graph_node *start, int level)
+{
+ // print this node
+ fprintf(stderr, "%s[%d] ", describe_node(start), vector_active(start->children));
+
+ if (vector_active(start->children)) {
+ if (vector_active(start->children) == 1)
+ walk_graph(vector_slot(start->children, 0), level);
+ else {
+ fprintf(stderr, "\n");
+ for (unsigned int i = 0; i < vector_active(start->children); i++) {
+ struct graph_node *r = vector_slot(start->children, i);
+ for (int j = 0; j < level+1; j++)
+ fprintf(stderr, " ");
+ walk_graph(r, level+1);
}
}
- if (level == 0)
+ }
+ else
fprintf(stderr, "\n");
}
diff --git a/lib/command_graph.h b/lib/command_graph.h
index 8d23577d37..081ecaac4c 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -82,4 +82,11 @@ new_node(enum graph_node_type);
extern void
walk_graph(struct graph_node *, int);
+/**
+ * Returns a string representation of the given node.
+ * @param[in] the node to describe
+ * @return pointer to description string
+ */
+extern const char *
+describe_node(struct graph_node *);
#endif
diff --git a/lib/command_match.c b/lib/command_match.c
index bcc28bdc5b..f7e2789253 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -1,12 +1,187 @@
#include <zebra.h>
#include "memory.h"
+#include "vector.h"
#include "command_match.h"
-enum match_type
+static enum match_type
+match_token (struct graph_node *node, char *token, enum filter_type filter)
+{
+ switch (node->type) {
+ case WORD_GN:
+ return cmd_word_match (node, filter, token);
+ case IPV4_GN:
+ return cmd_ipv4_match (token);
+ case IPV4_PREFIX_GN:
+ return cmd_ipv4_prefix_match (token);
+ case IPV6_GN:
+ return cmd_ipv6_match (token);
+ case IPV6_PREFIX_GN:
+ return cmd_ipv6_prefix_match (token);
+ case RANGE_GN:
+ return cmd_range_match (node, token);
+ case NUMBER_GN:
+ return node->value == atoi(token);
+ case VARIABLE_GN:
+ default:
+ return no_match;
+ }
+}
+
+/* Breaking up string into each command piece. I assume given
+ character is separated by a space character. Return value is a
+ vector which includes char ** data element. */
+static vector
+cmd_make_strvec (const char *string)
+{
+ const char *cp, *start;
+ char *token;
+ int strlen;
+ vector strvec;
+
+ if (string == NULL)
+ return NULL;
+
+ cp = string;
+
+ /* Skip white spaces. */
+ while (isspace ((int) *cp) && *cp != '\0')
+ cp++;
+
+ /* Return if there is only white spaces */
+ if (*cp == '\0')
+ return NULL;
+
+ if (*cp == '!' || *cp == '#')
+ return NULL;
+
+ /* Prepare return vector. */
+ strvec = vector_init (VECTOR_MIN_SIZE);
+
+ /* Copy each command piece and set into vector. */
+ while (1)
+ {
+ start = cp;
+ while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
+ *cp != '\0')
+ cp++;
+ strlen = cp - start;
+ token = XMALLOC (MTYPE_STRVEC, strlen + 1);
+ memcpy (token, start, strlen);
+ *(token + strlen) = '\0';
+ vector_set (strvec, token);
+
+ while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
+ *cp != '\0')
+ cp++;
+
+ if (*cp == '\0')
+ return strvec;
+ }
+}
+
+/**
+ * Adds all children that are reachable by one parser hop
+ * to the given list. NUL_GN, SELECTOR_GN, and OPTION_GN
+ * nodes are treated as though their children are attached
+ * to their parent.
+ *
+ * @param[out] l the list to add the children to
+ * @param[in] node the node to get the children of
+ * @return the number of children added to the list
+ */
+static int
+add_nexthops(struct list *l, struct graph_node *node)
+{
+ int added = 0;
+ struct graph_node *child;
+ for (unsigned int i = 0; i < vector_active(node->children); i++)
+ {
+ child = vector_slot(node->children, i);
+ if (child->type == OPTION_GN || child->type == SELECTOR_GN || child->type == NUL_GN)
+ added += add_nexthops(l, child);
+ else {
+ listnode_add(l, child);
+ added++;
+ }
+ }
+ return added;
+}
+
+/**
+ * Compiles matches or next-hops for a given line of user input.
+ *
+ * Given a string of input and a start node for a matching DFA, runs the input
+ * against the DFA until the input is exhausted, there are no possible
+ * transitions, or both.
+ * If there are no further state transitions available, one of two scenarios is possible:
+ * - The end of input has been reached. This indicates a valid command.
+ * - The end of input has not yet been reached. The input does not match any command.
+ * If there are further transitions available, one of two scenarios is possible:
+ * - The current input is a valid prefix to a longer command
+ * - The current input matches a command
+ * - The current input matches a command, and is also a valid prefix to a longer command
+ *
+ * Any other states indicate a programming error.
+ *
+ * @param[in] start the start node of the DFA to match against
+ * @param[in] filter the filtering method
+ * @param[in] input the input string
+ * @return an array with two lists. The first list is
+ */
+struct list**
match_command (struct graph_node *start, enum filter_type filter, const char *input)
{
- // match input on DFA
- return exact_match;
+ // break command
+ vector command = cmd_make_strvec (input);
+
+ // pointer to next input token to match
+ char *token;
+
+ struct list *current = list_new(), // current nodes to match input token against
+ *matched = list_new(), // current nodes that match the input token
+ *next = list_new(); // possible next hops to current input token
+
+ // pointers used for iterating lists
+ struct graph_node *cnode;
+ struct listnode *node;
+
+ // add all children of start node to list
+ add_nexthops(next, start);
+
+ unsigned int idx;
+ for (idx = 0; idx < vector_active(command) && next->count > 0; idx++)
+ {
+ list_free (current);
+ current = next;
+ next = list_new();
+
+ token = vector_slot(command, idx);
+
+ list_delete_all_node(matched);
+
+ for (ALL_LIST_ELEMENTS_RO(current,node,cnode))
+ {
+ if (match_token(cnode, token, filter) == exact_match) {
+ listnode_add(matched, cnode);
+ add_nexthops(next, cnode);
+ }
+ }
+ }
+
+ /* Variable summary
+ * -----------------------------------------------------------------
+ * token = last input token processed
+ * idx = index in `command` of last token processed
+ * current = set of all transitions from the previous input token
+ * matched = set of all nodes reachable with current input
+ * next = set of all nodes reachable from all nodes in `matched`
+ */
+
+ struct list **result = malloc( 2 * sizeof(struct list *) );
+ result[0] = matched;
+ result[1] = next;
+
+ return result;
}
diff --git a/lib/command_match.h b/lib/command_match.h
index cddeb08af7..4fc0995940 100644
--- a/lib/command_match.h
+++ b/lib/command_match.h
@@ -2,6 +2,7 @@
#define COMMAND_MATCH_H
#include "command_graph.h"
+#include "linklist.h"
/**
* Filter types. These tell the parser whether to allow
@@ -27,11 +28,11 @@ enum matcher_rv
};
/* Completion match types. */
-enum match_type
+enum match_type
{
no_match,
partly_match,
- exact_match
+ exact_match
};
/**
* Defines which matcher_rv values constitute
@@ -63,7 +64,7 @@ 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
+struct list**
match_command (struct graph_node *, enum filter_type, const char *);
#endif
diff --git a/lib/command_parse.y b/lib/command_parse.y
index 80487af7cd..9c1b3fff24 100644
--- a/lib/command_parse.y
+++ b/lib/command_parse.y
@@ -19,9 +19,7 @@ extern void yyerror(const char *);
%}
%code provides {
extern struct
-graph_node *cmd_parse_format(const char *,
- const char *,
- struct graph_node *);
+graph_node *cmd_parse_format(const char *, const char *, struct graph_node *);
extern void
set_buffer_string(const char *);
}
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
index 1e6a76c69c..fab78f3e21 100644
--- a/lib/grammar_sandbox.c
+++ b/lib/grammar_sandbox.c
@@ -2,29 +2,12 @@
#include "command_graph.h"
#include "command_parse.h"
#include "command_match.h"
+#include "linklist.h"
#define GRAMMAR_STR "CLI grammar sandbox\n"
struct graph_node * nodegraph;
-/*
-char* combine_vararg(char* argv, int argc) {
- size_t linesize = 0;
- for (int i = 0; i < argc; i++)
- linesize += strlen(argv[i]) + 1;
-
- char* cat = malloc(linesize);
- cat[0] = '\0';
- for (int i = 0; i < argc; i++) {
- strcat(cat, argv[i]);
- if (i != argc)
- strcat(cat, " ");
- }
-
- return cat;
-}
-*/
-
DEFUN (grammar_test,
grammar_test_cmd,
"grammar parse .COMMAND",
@@ -57,7 +40,26 @@ DEFUN (grammar_test_match,
"command to match")
{
const char* command = argv_concat(argv, argc, 0);
- match_command(nodegraph, FILTER_STRICT, command);
+ struct list **result = match_command(nodegraph, FILTER_STRICT, command);
+ struct list *matched = result[0];
+ struct list *next = result[1];
+
+ if (matched->count == 0) // the last token tried yielded no matches
+ fprintf(stderr, "%% Unknown command\n");
+ else
+ {
+ fprintf(stderr, "%% Matched full input, possible completions:\n");
+ struct listnode *node;
+ struct graph_node *cnode;
+ // iterate through currently matched nodes to see if any are leaves
+ for (ALL_LIST_ELEMENTS_RO(matched,node,cnode))
+ if (cnode->is_leaf)
+ fprintf(stderr, "<cr>\n");
+ // print possible next hops, if any
+ for (ALL_LIST_ELEMENTS_RO(next,node,cnode))
+ fprintf(stderr, "%s\n",describe_node(cnode));
+ }
+
return CMD_SUCCESS;
}