From: Quentin Young Date: Wed, 27 Jul 2016 01:35:46 +0000 (+0000) Subject: lib: Add matching and argv support X-Git-Tag: frr-3.0-branchpoint~129^2~280 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=eceb106640e0279ed89e476c25f37f3b2da860fc;p=matthieu%2Ffrr.git lib: Add matching and argv support Queries may be run against DFA's to find matching cmd_element, and argument lists may be constructed. Signed-off-by: Quentin Young --- diff --git a/lib/command_graph.c b/lib/command_graph.c index b81e35ac9a..38baa802c3 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -6,8 +6,8 @@ * @author Quentin Young */ -#include #include "command_graph.h" +#include #include "memory.h" struct graph_node * @@ -100,7 +100,7 @@ describe_node(struct graph_node *node, char* buffer, unsigned int bufsize) snprintf(buffer, bufsize, node->text); break; case NUMBER_GN: - snprintf(buffer, bufsize, "%d", node->value); + snprintf(buffer, bufsize, "%ld", node->value); break; case SELECTOR_GN: snprintf(buffer, bufsize, "<>"); diff --git a/lib/command_graph.h b/lib/command_graph.h index e57c078688..f0ded21262 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -1,8 +1,7 @@ #ifndef COMMAND_GRAPH_H #define COMMAND_GRAPH_H -#include "vty.h" -#include "vector.h" +#include "command.h" enum graph_node_type { @@ -22,18 +21,19 @@ enum graph_node_type struct graph_node { - enum graph_node_type type; - vector children; - int is_root; // true if first token in command + enum graph_node_type type;// data type this node matches or holds + int is_start; // whether this node is a start node + vector children; // this node's children struct graph_node * end; // pointer to end for SELECTOR_GN & OPTION_GN - // cmd_element struct pointer, only valid for END_GN - struct cmd_element *element; - - /* various data fields for nodes */ char* text; // for WORD_GN and VARIABLE_GN - long value; // for NUMBER_GN - long min, max; // for RANGE_GN + long value; // for NUMBER_GN + long min, max; // for RANGE_GN + + /* cmd_element struct pointer, only valid for END_GN */ + struct cmd_element *element; + /* used for passing arguments to command functions */ + char *arg; }; /* @@ -91,4 +91,11 @@ walk_graph(struct graph_node *, int); */ extern char * describe_node(struct graph_node *, char *, unsigned int); + +/** + * Frees the data associated with a graph_node. + * @param[out] pointer to graph_node to free + */ +void +free_node(struct graph_node *); #endif diff --git a/lib/command_match.c b/lib/command_match.c index fd51572652..dc44f1b824 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -1,56 +1,55 @@ +#include "command_match.h" +#include "command_parse.h" #include #include "memory.h" -#include "vector.h" -#include "command_match.h" -/* prototypes */ +/* matcher helper prototypes */ static int add_nexthops(struct list *, struct graph_node *); +static struct list * +match_build_argv_r (struct graph_node *start, vector vline, unsigned int n); + +/* token matcher prototypes */ +static enum match_type +match_ipv4 (const char *); + static enum match_type -cmd_ipv4_match (const char *); +match_ipv4_prefix (const char *); static enum match_type -cmd_ipv4_prefix_match (const char *); +match_ipv6 (const char *); static enum match_type -cmd_ipv6_match (const char *); +match_ipv6_prefix (const char *); static enum match_type -cmd_ipv6_prefix_match (const char *); +match_range (struct graph_node *, const char *str); static enum match_type -cmd_range_match (struct graph_node *, const char *str); +match_word (struct graph_node *, enum filter_type, const char *); static enum match_type -cmd_word_match (struct graph_node *, enum filter_type, const char *); +match_number (struct graph_node *, const char *); -static vector -cmd_make_strvec (const char *); +static enum match_type +match_variable (struct graph_node *, const char *); static enum match_type match_token (struct graph_node *, char *, enum filter_type); -static vector -cmd_make_strvec (const char *); - /* matching functions */ -/** - * Attempt to find an exact command match for a line of user input. - * - * @return cmd_element found, or NULL if there is no match. - */ -struct cmd_element -match_command (struct graph_node *start, vector *command, enum filter_type filter) +struct cmd_element * +match_command (struct graph_node *start, const char *line, enum filter_type filter) { // get all possible completions - struct list completions = match_command_complete (start, command, filter); + struct list *completions = match_command_complete (start, line, filter); // one of them should be END_GN if this command matches struct graph_node *gn; struct listnode *node; - for (ALL_LIST_ELEMENTS_RO(current,node,gn)) + for (ALL_LIST_ELEMENTS_RO(completions,node,gn)) { if (gn->type == END_GN) break; @@ -59,32 +58,11 @@ match_command (struct graph_node *start, vector *command, enum filter_type filte return gn ? gn->element : NULL; } -/** - * Compiles 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 or a mismatch is encountered. - * - * This function returns all valid next hops away from the current node. - * - If the input is a valid prefix to a longer command(s), the set of next - * hops determines what tokens are valid to follow the prefix. In other words, - * the returned list is a list of possible completions. - * - If the input matched a full command, exactly one of the next hops will be - * a node of type END_GN and its function pointer will be set. - * - If the input did not match any valid token sequence, the returned list - * will be empty (there are no transitions away from a nonexistent state). - * - * @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 pointer to linked list with all possible next hops from the last - * matched token. If this is empty, the input did not match any command. - */ struct list * -match_command_complete (struct graph_node *start, const char *input, enum filter_type filter) +match_command_complete (struct graph_node *start, const char *line, enum filter_type filter) { - // break command - vector command = cmd_make_strvec (input); + // vectorize command line + vector vline = cmd_make_strvec (line); // pointer to next input token to match char *token; @@ -101,13 +79,13 @@ match_command_complete (struct graph_node *start, const char *input, enum filter add_nexthops(next, start); unsigned int idx; - for (idx = 0; idx < vector_active(command) && next->count > 0; idx++) + for (idx = 0; idx < vector_active(vline) && next->count > 0; idx++) { list_free (current); current = next; next = list_new(); - token = vector_slot(command, idx); + token = vector_slot(vline, idx); list_delete_all_node(matched); @@ -131,14 +109,15 @@ match_command_complete (struct graph_node *start, const char *input, enum filter list_free (current); list_free (matched); + cmd_free_strvec(vline); + return next; } /** * 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. + * nodes are treated as transparent. * * @param[out] l the list to add the children to * @param[in] node the node to get the children of @@ -166,24 +145,73 @@ add_nexthops(struct list *l, struct graph_node *node) return added; } -/** - * Build the appropriate argv for a matched command. - * - * @param[in] command the command element - * @param[in] the input line matching this command - * @param[out] argv - * @return argc - * -int -match_build_argv (struct cmd_element *command, vector *input, char *argv) +struct list * +match_build_argv (const char *line, struct cmd_element *element) { - // build individual command graph + struct list *argv = NULL; + + // parse command struct graph_node *start = new_node(NUL_GN); - cmd_parse_format(start, command->string); - + parse_command_format(start, element); + + vector vline = cmd_make_strvec (line); + + for (unsigned int i = 0; i < vector_active(start->children); i++) + { + // call recursive builder on each starting child + argv = match_build_argv_r (vector_slot(start->children, i), vline, 0); + // if any of them succeed, return their argv (there should only be one) + if (argv) break; + } + + return argv; } -*/ +static struct list * +match_build_argv_r (struct graph_node *start, vector vline, unsigned int n) +{ + // if we don't match this node, die + if (match_token(start, vector_slot(vline, n), FILTER_STRICT) == no_match) + return NULL; + + // some stuffs we need + struct list *argv = list_new(); + struct graph_node *gn; + struct listnode *ln; + + // append current arg + start->arg = strdup(vector_slot(vline, n)); + listnode_add(argv, start); + + // get all possible nexthops + struct list *next = list_new(); + add_nexthops(next, start); + + // check if one of them is END_GN + for (ALL_LIST_ELEMENTS_RO(next,ln,gn)) + { + if (gn->type == END_GN){ + fprintf(stderr, "Hit END_GN while searching next set of node with text %s\n", start->text); + return argv; + } + } + + // if we have no more input, why even live? + if (n+1 >= vector_active(vline)) return NULL; + + // otherwise recurse on all nexthops + for (ALL_LIST_ELEMENTS_RO(next,ln,gn)) + { + for (unsigned int i = 0; i < n; i++) fprintf(stderr, "\t"); + fprintf(stderr, "Recursing on node %s for token %s\n", gn->text, (char*) vector_slot(vline, n+1)); + struct list *result = match_build_argv_r (gn, vline, n+1); + if (result != NULL) { + list_add_list (argv, result); + return argv; + } + } + return NULL; +} /* matching utility functions */ @@ -192,21 +220,22 @@ 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); + return match_word (node, filter, token); case IPV4_GN: - return cmd_ipv4_match (token); + return match_ipv4 (token); case IPV4_PREFIX_GN: - return cmd_ipv4_prefix_match (token); + return match_ipv4_prefix (token); case IPV6_GN: - return cmd_ipv6_match (token); + return match_ipv6 (token); case IPV6_PREFIX_GN: - return cmd_ipv6_prefix_match (token); + return match_ipv6_prefix (token); case RANGE_GN: - return cmd_range_match (node, token); + return match_range (node, token); case NUMBER_GN: - return cmd_number_match (node, token); + return match_number (node, token); case VARIABLE_GN: - return cmd_variable_match (node, token); + return match_variable (node, token); + case END_GN: default: return no_match; } @@ -216,7 +245,7 @@ match_token (struct graph_node *node, char *token, enum filter_type filter) #define IPV4_PREFIX_STR "0123456789./" static enum match_type -cmd_ipv4_match (const char *str) +match_ipv4 (const char *str) { struct sockaddr_in sin_dummy; @@ -233,7 +262,7 @@ cmd_ipv4_match (const char *str) } static enum match_type -cmd_ipv4_prefix_match (const char *str) +match_ipv4_prefix (const char *str) { struct sockaddr_in sin_dummy; const char *delim = "/\0"; @@ -274,7 +303,7 @@ cmd_ipv4_prefix_match (const char *str) #define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" static enum match_type -cmd_ipv6_match (const char *str) +match_ipv6 (const char *str) { struct sockaddr_in6 sin6_dummy; int ret; @@ -294,7 +323,7 @@ cmd_ipv6_match (const char *str) } static enum match_type -cmd_ipv6_prefix_match (const char *str) +match_ipv6_prefix (const char *str) { struct sockaddr_in6 sin6_dummy; const char *delim = "/\0"; @@ -332,10 +361,10 @@ cmd_ipv6_prefix_match (const char *str) #endif static enum match_type -cmd_range_match (struct graph_node *rangenode, const char *str) +match_range (struct graph_node *rangenode, const char *str) { char *endptr = NULL; - signed long long val; + signed long val; if (str == NULL) return 1; @@ -352,11 +381,10 @@ cmd_range_match (struct graph_node *rangenode, const char *str) } static enum match_type -cmd_word_match(struct graph_node *wordnode, - enum filter_type filter, - const char *word) +match_word(struct graph_node *wordnode, + enum filter_type filter, + const char *word) { - if (filter == FILTER_RELAXED) { if (!word || !strlen(word)) @@ -375,7 +403,8 @@ cmd_word_match(struct graph_node *wordnode, } } -cmd_number_match(struct graph_node *numnode, const char *word) +static enum match_type +match_number(struct graph_node *numnode, const char *word) { if (!strcmp("\0", word)) return no_match; char *endptr; @@ -384,57 +413,11 @@ cmd_number_match(struct graph_node *numnode, const char *word) return num == numnode->value ? exact_match : no_match; } -cmd_variable_match(struct graph_node *varnode, const char *word) -{ - // I guess this matches whatever? - return exact_match; -} +#define VARIABLE_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890" -static vector -cmd_make_strvec (const char *string) +static enum match_type +match_variable(struct graph_node *varnode, const char *word) { - 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; - } + return strlen(word) == strspn(word, VARIABLE_ALPHABET) && isalpha(word[0]) ? + exact_match : no_match; } diff --git a/lib/command_match.h b/lib/command_match.h index 695dda2827..24cd1287e6 100644 --- a/lib/command_match.h +++ b/lib/command_match.h @@ -1,22 +1,21 @@ #ifndef COMMAND_MATCH_H #define COMMAND_MATCH_H +#include "command.h" #include "command_graph.h" #include "linklist.h" -/** - * Filter types. These tell the parser whether to allow - * partial matching on tokens. - */ + +/** These definitions exist in command.c in + * the current engine but will be relocated + * here in the new engine*/ enum filter_type { FILTER_RELAXED, FILTER_STRICT }; -/** - * Command matcher result value. - */ +/* matcher result value. */ enum matcher_rv { MATCHER_OK, @@ -34,8 +33,8 @@ enum match_type partly_match, exact_match }; -/** - * Defines which matcher_rv values constitute + +/* Defines which matcher_rv values constitute * an error. Should be used against matcher_rv * return values to do basic error checking. */ @@ -46,7 +45,46 @@ enum match_type || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ ) +/** + * Attempt to find an exact command match for a line of user input. + * + * @return cmd_element found, or NULL if there is no match. + */ +struct cmd_element * +match_command (struct graph_node *, const char *, enum filter_type); + +/** + * Compiles 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 or a mismatch is encountered. + * + * This function returns all valid next hops away from the current node. + * - If the input is a valid prefix to a longer command(s), the set of next + * hops determines what tokens are valid to follow the prefix. In other words, + * the returned list is a list of possible completions. + * - If the input matched a full command, exactly one of the next hops will be + * a node of type END_GN and its function pointer will be set. + * - If the input did not match any valid token sequence, the returned list + * will be empty (there are no transitions away from a nonexistent state). + * + * @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 pointer to linked list with all possible next hops from the last + * matched token. If this is empty, the input did not match any command. + */ +struct list * +match_command_complete (struct graph_node *, const char *, enum filter_type); + +/** + * Builds an argument list given a cmd_element and a matching input line. + * + * @param[in] input line + * @param[in] cmd_element struct + * @return pointer to argument linked list + */ struct list * -match_command (struct graph_node *, enum filter_type, const char *); +match_build_argv (const char *, struct cmd_element *); #endif diff --git a/lib/command_new.h b/lib/command_new.h deleted file mode 100644 index 87f453d85a..0000000000 --- a/lib/command_new.h +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Zebra configuration command interface routine - * Copyright (C) 1997, 98 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef _ZEBRA_COMMAND_H -#define _ZEBRA_COMMAND_H - -#include "vector.h" -#include "vty.h" -#include "lib/route_types.h" - -/* Host configuration variable */ -struct host -{ - /* Host name of this router. */ - char *name; - - /* Password for vty interface. */ - char *password; - char *password_encrypt; - - /* Enable password */ - char *enable; - char *enable_encrypt; - - /* System wide terminal lines. */ - int lines; - - /* Log filename. */ - char *logfile; - - /* config file name of this host */ - char *config; - - /* Flags for services */ - int advanced; - int encrypt; - - /* Banner configuration. */ - const char *motd; - char *motdfile; -}; - -/* There are some command levels which called from command node. */ -enum node_type -{ - AUTH_NODE, /* Authentication mode of vty interface. */ - RESTRICTED_NODE, /* Restricted view mode */ - VIEW_NODE, /* View node. Default mode of vty interface. */ - AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ - ENABLE_NODE, /* Enable node. */ - CONFIG_NODE, /* Config node. Default mode of config file. */ - SERVICE_NODE, /* Service node. */ - DEBUG_NODE, /* Debug node. */ - VRF_DEBUG_NODE, /* Vrf Debug node. */ - AAA_NODE, /* AAA node. */ - KEYCHAIN_NODE, /* Key-chain node. */ - KEYCHAIN_KEY_NODE, /* Key-chain key node. */ - VRF_NODE, /* VRF mode node. */ - INTERFACE_NODE, /* Interface mode node. */ - ZEBRA_NODE, /* zebra connection node. */ - TABLE_NODE, /* rtm_table selection node. */ - RIP_NODE, /* RIP protocol mode node. */ - RIPNG_NODE, /* RIPng protocol mode node. */ - BGP_NODE, /* BGP protocol mode which includes BGP4+ */ - BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ - BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ - BGP_IPV6_NODE, /* BGP IPv6 address family */ - BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ - BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ - BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ - OSPF_NODE, /* OSPF protocol mode */ - OSPF6_NODE, /* OSPF protocol for IPv6 mode */ - ISIS_NODE, /* ISIS protocol mode */ - PIM_NODE, /* PIM protocol mode */ - MASC_NODE, /* MASC for multicast. */ - IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ - IP_NODE, /* Static ip route node. */ - ACCESS_NODE, /* Access list node. */ - PREFIX_NODE, /* Prefix list node. */ - ACCESS_IPV6_NODE, /* Access list node. */ - PREFIX_IPV6_NODE, /* Prefix list node. */ - AS_LIST_NODE, /* AS list node. */ - COMMUNITY_LIST_NODE, /* Community list node. */ - RMAP_NODE, /* Route map node. */ - SMUX_NODE, /* SNMP configuration node. */ - DUMP_NODE, /* Packet dump node. */ - FORWARDING_NODE, /* IP forwarding node. */ - PROTOCOL_NODE, /* protocol filtering node */ - VTY_NODE, /* Vty node. */ -}; - -/* Node which has some commands and prompt string and configuration - function pointer . */ -struct cmd_node -{ - /* Node index. */ - enum node_type node; - - /* Prompt character at vty interface. */ - const char *prompt; - - /* Is this node's configuration goes to vtysh ? */ - int vtysh; - - /* Node's configuration write function */ - int (*func) (struct vty *); - - /* Start of this node's command graph */ - struct graph_node * cmd_graph; -}; - -enum -{ - CMD_ATTR_DEPRECATED = 1, - CMD_ATTR_HIDDEN, -}; - -/* Structure of command element. */ -struct cmd_element -{ - const char *string; // command format string - const char *doc; // command doc string - int daemon; // daemon associated with command - u_char attr; // Command attributes - - // function for this command - int (*func) (struct cmd_element *, struct vty *, int, const char *[]); -}; - -/* argument to be recorded on argv[] if it's not a literal */ -#define TERMINAL_RECORD(t) ((t) >= TERMINAL_OPTION) - -/* Return value of the commands. */ -#define CMD_SUCCESS 0 -#define CMD_WARNING 1 -#define CMD_ERR_NO_MATCH 2 -#define CMD_ERR_AMBIGUOUS 3 -#define CMD_ERR_INCOMPLETE 4 -#define CMD_ERR_EXEED_ARGC_MAX 5 -#define CMD_ERR_NOTHING_TODO 6 -#define CMD_COMPLETE_FULL_MATCH 7 -#define CMD_COMPLETE_MATCH 8 -#define CMD_COMPLETE_LIST_MATCH 9 -#define CMD_SUCCESS_DAEMON 10 -#define CMD_ERR_NO_FILE 11 - -/* Argc max counts. */ -#define CMD_ARGC_MAX 25 - -/* Turn off these macros when uisng cpp with extract.pl */ -#ifndef VTYSH_EXTRACT_PL - -/* helper defines for end-user DEFUN* macros */ -#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ - struct cmd_element cmdname = \ - { \ - .string = cmdstr, \ - .doc = helpstr, \ - .daemon = dnum, \ - .attr = attrs, \ - .func = funcname, \ - }; - -#define DEFUN_CMD_FUNC_DECL(funcname) \ - static int funcname (struct cmd_element *, struct vty *, int, const char *[]); - -#define DEFUN_CMD_FUNC_TEXT(funcname) \ - static int funcname \ - (struct cmd_element *self __attribute__ ((unused)), \ - struct vty *vty __attribute__ ((unused)), \ - int argc __attribute__ ((unused)), \ - const char *argv[] __attribute__ ((unused)) ) - -/* DEFUN for vty command interface. - * - * DEFUN(funcname, cmdname, cmdstr, helpstr) - * - * funcname - * ======== - * Name of the function that will be defined. - * - * cmdname - * ======= - * Name of the struct that will be defined for the command. - * - * cmdstr - * ====== - * The cmdstr defines the command syntax. It is used by the vty subsystem - * and vtysh to perform matching and completion in the CLI. - * - * Syntax Summary - * ---------------- - * - * - * Abbreviated BNF - * ---------------- - * - * - * - * helpstr - * ======= - * - * The helpstr is used to show a short explantion for the commands that - * are available when the user presses '?' on the CLI. It is the concatenation - * of the helpstrings for all the tokens that make up the command. - * - * There should be one helpstring for each token in the cmdstr except those - * containing other tokens, like Multiple or Keyword Tokens. For those, there - * will only be the helpstrings of the contained tokens. - * - * The individual helpstrings are expected to be in the same order as their - * respective Tokens appear in the cmdstr. They should each be terminated with - * a linefeed. The last helpstring should be terminated with a linefeed as well. - * - * Care should also be taken to avoid having similar tokens with different - * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp". - * they both contain a helpstring for "show", but only one will be displayed - * when the user enters "sh?". If those two helpstrings differ, it is not - * defined which one will be shown and the behavior is therefore unpredictable. - */ -#define DEFUN(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_FUNC_DECL(funcname) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ - DEFUN_CMD_FUNC_TEXT(funcname) - -#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ - DEFUN_CMD_FUNC_DECL(funcname) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ - DEFUN_CMD_FUNC_TEXT(funcname) - -#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) - -#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \ - -/* DEFUN_NOSH for commands that vtysh should ignore */ -#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ - DEFUN(funcname, cmdname, cmdstr, helpstr) - -/* DEFSH for vtysh. */ -#define DEFSH(daemon, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \ - -#define DEFSH_HIDDEN(daemon, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon) \ - -/* DEFUN + DEFSH */ -#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_FUNC_DECL(funcname) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \ - DEFUN_CMD_FUNC_TEXT(funcname) - -/* DEFUN + DEFSH with attributes */ -#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \ - DEFUN_CMD_FUNC_DECL(funcname) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \ - DEFUN_CMD_FUNC_TEXT(funcname) - -#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) - -#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) - -/* ALIAS macro which define existing command's alias. */ -#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) - -#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) - -#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0) - -#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0) - -#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) - -#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon) - -#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon) - -#endif /* VTYSH_EXTRACT_PL */ - -/* Some macroes */ - -/* - * Sometimes #defines create maximum values that - * need to have strings created from them that - * allow the parser to match against them. - * These macros allow that. - */ -#define CMD_CREATE_STR(s) CMD_CREATE_STR_HELPER(s) -#define CMD_CREATE_STR_HELPER(s) #s -#define CMD_RANGE_STR(a,s) "<" CMD_CREATE_STR(a) "-" CMD_CREATE_STR(s) ">" - -/* Common descriptions. */ -#define SHOW_STR "Show running system information\n" -#define IP_STR "IP information\n" -#define IPV6_STR "IPv6 information\n" -#define NO_STR "Negate a command or set its defaults\n" -#define REDIST_STR "Redistribute information from another routing protocol\n" -#define CLEAR_STR "Reset functions\n" -#define RIP_STR "RIP information\n" -#define BGP_STR "BGP information\n" -#define BGP_SOFT_STR "Soft reconfig inbound and outbound updates\n" -#define BGP_SOFT_IN_STR "Send route-refresh unless using 'soft-reconfiguration inbound'\n" -#define BGP_SOFT_OUT_STR "Resend all outbound updates\n" -#define BGP_SOFT_RSCLIENT_RIB_STR "Soft reconfig for rsclient RIB\n" -#define OSPF_STR "OSPF information\n" -#define NEIGHBOR_STR "Specify neighbor router\n" -#define DEBUG_STR "Debugging functions (see also 'undebug')\n" -#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n" -#define ROUTER_STR "Enable a routing process\n" -#define AS_STR "AS number\n" -#define MBGP_STR "MBGP information\n" -#define MATCH_STR "Match values from routing table\n" -#define SET_STR "Set values in destination routing protocol\n" -#define OUT_STR "Filter outgoing routing updates\n" -#define IN_STR "Filter incoming routing updates\n" -#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n" -#define OSPF6_NUMBER_STR "Specify by number\n" -#define INTERFACE_STR "Interface infomation\n" -#define IFNAME_STR "Interface name(e.g. ep0)\n" -#define IP6_STR "IPv6 Information\n" -#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" -#define OSPF6_ROUTER_STR "Enable a routing process\n" -#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" -#define SECONDS_STR "<1-65535> Seconds\n" -#define ROUTE_STR "Routing Table\n" -#define PREFIX_LIST_STR "Build a prefix list\n" -#define OSPF6_DUMP_TYPE_LIST \ -"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" -#define ISIS_STR "IS-IS information\n" -#define AREA_TAG_STR "[area tag]\n" -#define COMMUNITY_AANN_STR "Community number where AA and NN are <0-65535>\n" -#define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are <0-65535>) or local-AS|no-advertise|no-export|internet or additive\n" - -#define CONF_BACKUP_EXT ".sav" - -/* IPv4 only machine should not accept IPv6 address for peer's IP - address. So we replace VTY command string like below. */ -#ifdef HAVE_IPV6 -#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) " -#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) " -#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" -#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) " -#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) " -#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nInterface name or neighbor tag\n" -#define NEIGHBOR_ADDR_STR3 "Neighbor address\nIPv6 address\nInterface name\n" -#else -#define NEIGHBOR_CMD "neighbor A.B.C.D " -#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D " -#define NEIGHBOR_ADDR_STR "Neighbor address\n" -#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) " -#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) " -#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" -#endif /* HAVE_IPV6 */ - -/* Dynamic neighbor (listen range) configuration */ -#ifdef HAVE_IPV6 -#define LISTEN_RANGE_CMD "bgp listen range (A.B.C.D/M|X:X::X:X/M) " -#define LISTEN_RANGE_ADDR_STR "Neighbor address\nNeighbor IPv6 address\n" -#else -#define LISTEN_RANGE_CMD "bgp listen range A.B.C.D/M " -#define LISTEN_RANGE_ADDR_STR "Neighbor address\n" -#endif /* HAVE_IPV6 */ - -/* Prototypes. */ -extern void install_node (struct cmd_node *, int (*) (struct vty *)); -extern void install_default (enum node_type); -extern void install_element (enum node_type, struct cmd_element *); - -/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated - string with a space between each element (allocated using - XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */ -extern char *argv_concat (const char **argv, int argc, int shift); - -extern vector cmd_make_strvec (const char *); -extern void cmd_free_strvec (vector); -extern vector cmd_describe_command (vector, struct vty *, int *status); -extern char **cmd_complete_command (vector, struct vty *, int *status); -extern char **cmd_complete_command_lib (vector, struct vty *, int *status, int islib); -extern const char *cmd_prompt (enum node_type); -extern int command_config_read_one_line (struct vty *vty, struct cmd_element **, int use_config_node); -extern int config_from_file (struct vty *, FILE *, unsigned int *line_num); -extern enum node_type node_parent (enum node_type); -extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int); -extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **); -extern void cmd_init (int); -extern void cmd_terminate (void); - -/* Export typical functions. */ -extern struct cmd_element config_end_cmd; -extern struct cmd_element config_exit_cmd; -extern struct cmd_element config_quit_cmd; -extern struct cmd_element config_help_cmd; -extern struct cmd_element config_list_cmd; -extern char *host_config_file (void); -extern void host_config_set (const char *); - -extern void print_version (const char *); - -extern int cmd_banner_motd_file (const char *); - -/* struct host global, ick */ -extern struct host host; - -/* "" global */ -extern char *command_cr; -#endif /* _ZEBRA_COMMAND_H */ diff --git a/lib/command_parse.y b/lib/command_parse.y index 1cef6766ae..7b56aec952 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -9,22 +9,24 @@ */ %{ -#include "command_graph.h" -#include "command_new.h" - extern int yylex(void); extern void yyerror(const char *); // compile with debugging facilities #define YYDEBUG 1 %} +%code requires { + #include "command.h" + #include "command_graph.h" +} %code provides { -extern struct -graph_node *cmd_parse_format(const char *, const char *, struct graph_node *); -extern void -set_buffer_string(const char *); + extern void + set_buffer_string(const char *); + struct graph_node * + parse_command_format(struct graph_node *, struct cmd_element *); } + /* valid types for tokens */ %union{ int integer; @@ -45,7 +47,7 @@ struct graph_node *optnode_start, // start node for option set struct graph_node *selnode_start, // start node for selector set *selnode_end; // end node for selector set -const struct cmd_element *command; // command we're parsing +struct cmd_element *command; // command we're parsing %} %token WORD @@ -86,12 +88,16 @@ start: sentence_root struct graph_node *end = new_node(END_GN); end->element = command; + // ensure there are no END_GN children + for (unsigned int i = 0; i < vector_active(currnode->children); i++) + { + struct graph_node *child = vector_slot(currnode->children, i); + if (child->type == END_GN) + yyerror("Duplicate command."); + } + // add node end = add_node(currnode, end); - - // check that we did not get back an existing node - if (!strcmp(command->string, end->element->string) - yyerror("Duplicate command."); } sentence_root: WORD @@ -166,12 +172,7 @@ placeholder_token: // get the numbers out strsep(&yylval.string, "(-)"); $$->min = atoi( strsep(&yylval.string, "(-)") ); - strsep(&yylval.string, "(-)"); $$->max = atoi( strsep(&yylval.string, "(-)") ); - - // we could do this a variety of ways with either - // the lexer or the parser, but this is the simplest - // and involves the least amount of free() } ; @@ -304,9 +305,9 @@ void yyerror(char const *message) { } struct graph_node * -cmd_parse_format(struct graph_node *start, struct cmd_element *cmd) +parse_command_format(struct graph_node *start, struct cmd_element *cmd) { - fprintf(stderr, "parsing: %s\n", string); + fprintf(stderr, "parsing: %s\n", cmd->string); /* clear state pointers */ startnode = currnode = seqhead = NULL; @@ -318,7 +319,7 @@ cmd_parse_format(struct graph_node *start, struct cmd_element *cmd) // command string command = cmd; // make flex read from a string - set_buffer_string(input); + set_buffer_string(command->string); // initialize the start node of this command dfa startnode = start; // parse command into DFA diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index 24f882c89f..6eee9c8b65 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -14,11 +14,11 @@ DEFUN (grammar_test, GRAMMAR_STR "command to pass to new parser\n") { - - const char* command = argv_concat(argv, argc, 0); - cmd_parse_format(command, "lol", nodegraph); + char* command = argv_concat(argv, argc, 0); + struct cmd_element *cmd = malloc(sizeof(struct cmd_element)); + cmd->string = command; + parse_command_format(nodegraph, cmd); walk_graph(nodegraph, 0); - return CMD_SUCCESS; } @@ -32,15 +32,15 @@ DEFUN (grammar_test_show, return CMD_SUCCESS; } -DEFUN (grammar_test_match, - grammar_test_match_cmd, - "grammar match .COMMAND", +DEFUN (grammar_test_complete, + grammar_test_complete_cmd, + "grammar complete .COMMAND", GRAMMAR_STR - "attempt to match input on DFA\n" - "command to match") + "attempt to complete input on DFA\n" + "command to complete") { const char* command = argv_concat(argv, argc, 0); - struct list *result = match_command(nodegraph, FILTER_STRICT, command); + struct list *result = match_command_complete (nodegraph, command, FILTER_STRICT); if (result->count == 0) // invalid command fprintf(stderr, "%% Unknown command\n"); @@ -53,7 +53,7 @@ DEFUN (grammar_test_match, // print possible next hops, if any for (ALL_LIST_ELEMENTS_RO(result,node,cnode)) { if (cnode->type == END_GN) - fprintf(stderr, ""); + fprintf(stderr, "\n"); else fprintf(stderr, "%s\n", describe_node(cnode, desc, 50)); } @@ -64,6 +64,39 @@ DEFUN (grammar_test_match, 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); + struct cmd_element *element = match_command (nodegraph, command, FILTER_STRICT); + + if (element) + fprintf(stderr, "Matched: %s\n", element->string); + else { + fprintf(stderr, "Returned NULL\n"); + return CMD_SUCCESS; + } + + struct list *argvv = match_build_argv (command, element); + fprintf(stderr, "num args: %d\n", argvv->count); + + struct listnode *ln; + struct graph_node *gn; + for (ALL_LIST_ELEMENTS_RO(argvv,ln,gn)) { + fprintf(stderr, "node text: %s\n", gn->text); + if (gn->arg) + fprintf(stderr, "node arg: %s\n", gn->arg); + else + fprintf(stderr, "No arg.\n"); + } + + return CMD_SUCCESS; +} + void grammar_sandbox_init(void); void grammar_sandbox_init() { @@ -72,4 +105,5 @@ void grammar_sandbox_init() { install_element (ENABLE_NODE, &grammar_test_cmd); install_element (ENABLE_NODE, &grammar_test_show_cmd); install_element (ENABLE_NODE, &grammar_test_match_cmd); + install_element (ENABLE_NODE, &grammar_test_complete_cmd); }