From 880e24a1e4cc78cb23ebdd72f2e5cea861cf8be2 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 22 Jul 2016 19:04:16 +0000 Subject: [PATCH] lib: Reorganize some matching stuff Introduce new node type, END_GN, and remove is_leaf flags. Reorganize command_match.c & remove internal functions from command_match.h. Start rewriting command.h in command_new.h with changes for new backend. Signed-off-by: Quentin Young --- lib/command_graph.c | 39 ++-- lib/command_graph.h | 20 +- lib/command_match.c | 262 ++++++++++++++----------- lib/command_match.h | 20 +- lib/command_new.h | 439 ++++++++++++++++++++++++++++++++++++++++++ lib/command_parse.y | 25 ++- lib/grammar_sandbox.c | 21 +- 7 files changed, 654 insertions(+), 172 deletions(-) create mode 100644 lib/command_new.h diff --git a/lib/command_graph.c b/lib/command_graph.c index 4e99884dc8..b81e35ac9a 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -53,6 +53,9 @@ cmp_node(struct graph_node *first, struct graph_node *second) case SELECTOR_GN: case OPTION_GN: return 0; + // end nodes are always considered equal, since each node may only + // have one at a time + case END_GN: default: break; } @@ -66,27 +69,23 @@ 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; + node->element = NULL; return node; } -const char * -describe_node(struct graph_node *node) +char * +describe_node(struct graph_node *node, char* buffer, unsigned int bufsize) { - const char *desc = NULL; - char num[21]; - if (node == NULL) { - desc = "(null node)"; - return desc; + snprintf(buffer, bufsize, "(null node)"); + return buffer; } // print this node @@ -98,32 +97,38 @@ describe_node(struct graph_node *node) case IPV6_PREFIX_GN: case VARIABLE_GN: case RANGE_GN: - desc = node->text; + snprintf(buffer, bufsize, node->text); break; case NUMBER_GN: - sprintf(num, "%d", node->value); + snprintf(buffer, bufsize, "%d", node->value); break; case SELECTOR_GN: - desc = "<>"; + snprintf(buffer, bufsize, "<>"); break; case OPTION_GN: - desc = "[]"; + snprintf(buffer, bufsize, "[]"); break; case NUL_GN: - desc = "NUL"; + snprintf(buffer, bufsize, "NUL"); + break; + case END_GN: + snprintf(buffer, bufsize, "END"); break; default: - desc = "ERROR"; + snprintf(buffer, bufsize, "ERROR"); } - return desc; + + return buffer; } void walk_graph(struct graph_node *start, int level) { + char* desc = malloc(50); // print this node - fprintf(stderr, "%s[%d] ", describe_node(start), vector_active(start->children)); + fprintf(stderr, "%s[%d] ", describe_node(start, desc, 50), vector_active(start->children)); + free(desc); if (vector_active(start->children)) { if (vector_active(start->children) == 1) diff --git a/lib/command_graph.h b/lib/command_graph.h index 081ecaac4c..ce458d0b29 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -16,7 +16,8 @@ enum graph_node_type NUMBER_GN, SELECTOR_GN, OPTION_GN, - NUL_GN + NUL_GN, + END_GN }; struct graph_node @@ -24,15 +25,15 @@ 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 + struct graph_node * end; // pointer to end for SELECTOR_GN & OPTION_GN - int (*func)(struct vty *, int, const char *[]); + // cmd_element struct pointer, only valid for END_GN + struct cmd_element *element; /* various data fields for nodes */ - char* text; // for words and variables - int value; // for numbers - int min, max; // for ranges + char* text; // for WORD_GN and VARIABLE_GN + int value; // for NUMBER_GN + int min, max; // for RANGE_GN }; /* @@ -85,8 +86,9 @@ walk_graph(struct graph_node *, int); /** * Returns a string representation of the given node. * @param[in] the node to describe + * @param[out] the buffer to write the description into * @return pointer to description string */ -extern const char * -describe_node(struct graph_node *); +extern char * +describe_node(struct graph_node *, char *, unsigned int); #endif diff --git a/lib/command_match.c b/lib/command_match.c index f7e2789253..dbf9984ca8 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -3,132 +3,61 @@ #include "vector.h" #include "command_match.h" -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; +/* prototypes */ +static int +add_nexthops(struct list *, struct graph_node *); - if (string == NULL) - return NULL; +static enum match_type +cmd_ipv4_match (const char *); - cp = string; +static enum match_type +cmd_ipv4_prefix_match (const char *); - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') - cp++; +static enum match_type +cmd_ipv6_match (const char *); - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; +static enum match_type +cmd_ipv6_prefix_match (const char *); - if (*cp == '!' || *cp == '#') - return NULL; +static enum match_type +cmd_range_match (struct graph_node *, const char *str); - /* Prepare return vector. */ - strvec = vector_init (VECTOR_MIN_SIZE); +static enum match_type +cmd_word_match (struct graph_node *, enum filter_type, const char *); - /* 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); +static vector +cmd_make_strvec (const char *); - while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; +static enum match_type +match_token (struct graph_node *, char *, enum filter_type); - if (*cp == '\0') - return strvec; - } -} +static vector +cmd_make_strvec (const char *); -/** - * 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; -} +/* matching functions */ /** * 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 + * against the DFA until the input is exhausted or a mismatch is encountered. * - * Any other states indicate a programming error. + * 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 an array with two lists. The first list is + * @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** +struct list * match_command (struct graph_node *start, enum filter_type filter, const char *input) { // break command @@ -176,19 +105,122 @@ match_command (struct graph_node *start, enum filter_type filter, const char *in * matched = set of all nodes reachable with current input * next = set of all nodes reachable from all nodes in `matched` */ + list_free (current); + list_free (matched); + + 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. + * + * @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; +} - struct list **result = malloc( 2 * sizeof(struct list *) ); - result[0] = matched; - result[1] = next; - return result; +/* matching utility functions */ + +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 NUL_GN: + return exact_match; + case VARIABLE_GN: + default: + return no_match; + } } +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; + } +} #define IPV4_ADDR_STR "0123456789." #define IPV4_PREFIX_STR "0123456789./" -enum match_type +static enum match_type cmd_ipv4_match (const char *str) { struct sockaddr_in sin_dummy; @@ -205,7 +237,7 @@ cmd_ipv4_match (const char *str) return exact_match; } -enum match_type +static enum match_type cmd_ipv4_prefix_match (const char *str) { struct sockaddr_in sin_dummy; @@ -246,7 +278,7 @@ cmd_ipv4_prefix_match (const char *str) #define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" #ifdef HAVE_IPV6 -enum match_type +static enum match_type cmd_ipv6_match (const char *str) { struct sockaddr_in6 sin6_dummy; @@ -266,7 +298,7 @@ cmd_ipv6_match (const char *str) return no_match; } -enum match_type +static enum match_type cmd_ipv6_prefix_match (const char *str) { struct sockaddr_in6 sin6_dummy; @@ -304,7 +336,7 @@ cmd_ipv6_prefix_match (const char *str) } #endif -enum match_type +static enum match_type cmd_range_match (struct graph_node *rangenode, const char *str) { char *endptr = NULL; @@ -324,7 +356,7 @@ cmd_range_match (struct graph_node *rangenode, const char *str) return exact_match; } -enum match_type +static enum match_type cmd_word_match(struct graph_node *wordnode, enum filter_type filter, const char *word) diff --git a/lib/command_match.h b/lib/command_match.h index 4fc0995940..695dda2827 100644 --- a/lib/command_match.h +++ b/lib/command_match.h @@ -46,25 +46,7 @@ enum match_type || (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 *); - -struct list** +struct list * match_command (struct graph_node *, enum filter_type, const char *); #endif diff --git a/lib/command_new.h b/lib/command_new.h new file mode 100644 index 0000000000..972b420ae3 --- /dev/null +++ b/lib/command_new.h @@ -0,0 +1,439 @@ +/* + * 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 * commands; + /* Vector of this node's command list. + vector cmd_vector;*/ +}; + +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 9c1b3fff24..9f28391748 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -43,6 +43,8 @@ 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 char *input; %} %token WORD @@ -73,7 +75,24 @@ struct graph_node *selnode_start, // start node for selector set %% start: sentence_root - cmd_token_seq; + cmd_token_seq +{ + // this should never happen... + if (currnode->type == END_GN) + yyerror("Unexpected leaf\n"); + + // create function pointer node + struct graph_node *end = new_node(END_GN); + + // set cmd_element + // + + // add node + add_node(currnode, end); + + // check that we did not get back an existing node + // +} sentence_root: WORD { @@ -279,6 +298,7 @@ option_token: %% void yyerror(char const *message) { + // fail on bad parse printf("Grammar error: %s\n", message); exit(EXIT_FAILURE); } @@ -296,7 +316,8 @@ cmd_parse_format(const char *string, const char *desc, struct graph_node *start) // trace parser yydebug = 1; // make flex read from a string - set_buffer_string(string); + input = string; + set_buffer_string(input); // 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 fab78f3e21..24f882c89f 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -40,25 +40,26 @@ DEFUN (grammar_test_match, "command to match") { const char* command = argv_concat(argv, argc, 0); - struct list **result = match_command(nodegraph, FILTER_STRICT, command); - struct list *matched = result[0]; - struct list *next = result[1]; + struct list *result = match_command(nodegraph, FILTER_STRICT, command); - if (matched->count == 0) // the last token tried yielded no matches + if (result->count == 0) // invalid command fprintf(stderr, "%% Unknown command\n"); else { fprintf(stderr, "%% Matched full input, possible completions:\n"); + char* desc = malloc(50); 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, "\n"); // print possible next hops, if any - for (ALL_LIST_ELEMENTS_RO(next,node,cnode)) - fprintf(stderr, "%s\n",describe_node(cnode)); + for (ALL_LIST_ELEMENTS_RO(result,node,cnode)) { + if (cnode->type == END_GN) + fprintf(stderr, ""); + else + fprintf(stderr, "%s\n", describe_node(cnode, desc, 50)); + } + free(desc); } + list_free(result); return CMD_SUCCESS; } -- 2.39.5