]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: Reorganize some matching stuff
authorQuentin Young <qlyoung@cumulusnetworks.com>
Fri, 22 Jul 2016 19:04:16 +0000 (19:04 +0000)
committerQuentin Young <qlyoung@cumulusnetworks.com>
Fri, 22 Jul 2016 19:04:16 +0000 (19:04 +0000)
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>
lib/command_graph.c
lib/command_graph.h
lib/command_match.c
lib/command_match.h
lib/command_new.h [new file with mode: 0644]
lib/command_parse.y
lib/grammar_sandbox.c

index 4e99884dc84f7208195b5ce3ea57ea5e0d24c40a..b81e35ac9a053872ad5c3e8cadfe35920d6aa94e 100644 (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)
index 081ecaac4c55550c311d3906db9da34a1c18cd7c..ce458d0b293f8fdb2428652903a79a425793d84d 100644 (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
index f7e2789253d132a3fd3dc37e68c0f84231607129..dbf9984ca81971de5df9147d466697369f5c0a0b 100644 (file)
 #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)
index 4fc099594079248899cbd72230a9f229e53fc2c7..695dda2827d39e913ea266bc1b7473051e1884db 100644 (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
diff --git a/lib/command_new.h b/lib/command_new.h
new file mode 100644 (file)
index 0000000..972b420
--- /dev/null
@@ -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 */
index 9c1b3fff24b89ffbb947982d20eed1f1c4c158c4..9f28391748b261dc2ddcd13aca753cf31e9901b1 100644 (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
index fab78f3e2119b3fe3c832ba951a2264f6dd50fc4..24f882c89f57e27c7883fe780df9a4588539ea76 100644 (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;
 }