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 <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2016-07-22 19:04:16 +00:00
parent 18be0e599d
commit 880e24a1e4
7 changed files with 654 additions and 172 deletions

View File

@ -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)

View File

@ -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

View File

@ -3,6 +3,145 @@
#include "vector.h"
#include "command_match.h"
/* prototypes */
static int
add_nexthops(struct list *, struct graph_node *);
static enum match_type
cmd_ipv4_match (const char *);
static enum match_type
cmd_ipv4_prefix_match (const char *);
static enum match_type
cmd_ipv6_match (const char *);
static enum match_type
cmd_ipv6_prefix_match (const char *);
static enum match_type
cmd_range_match (struct graph_node *, const char *str);
static enum match_type
cmd_word_match (struct graph_node *, enum filter_type, const char *);
static vector
cmd_make_strvec (const char *);
static enum match_type
match_token (struct graph_node *, char *, enum filter_type);
static vector
cmd_make_strvec (const char *);
/* 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 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 (struct graph_node *start, enum filter_type filter, const char *input)
{
// 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`
*/
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;
}
/* matching utility functions */
static enum match_type
match_token (struct graph_node *node, char *token, enum filter_type filter)
{
@ -21,15 +160,14 @@ match_token (struct graph_node *node, char *token, enum filter_type filter)
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;
}
}
/* 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)
{
@ -79,116 +217,10 @@ cmd_make_strvec (const char *string)
}
}
/**
* 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)
{
// 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;
}
#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)

View File

@ -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

439
lib/command_new.h Normal file
View File

@ -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
* ----------------
* <summary>
*
* Abbreviated BNF
* ----------------
* <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;
/* "<cr>" global */
extern char *command_cr;
#endif /* _ZEBRA_COMMAND_H */

View File

@ -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 <node> 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
// <set>
// add node
add_node(currnode, end);
// check that we did not get back an existing node
// <check>
}
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

View File

@ -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, "<cr>\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, "<cr>");
else
fprintf(stderr, "%s\n", describe_node(cnode, desc, 50));
}
free(desc);
}
list_free(result);
return CMD_SUCCESS;
}