]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: Code cleanup, formatting, & headers
authorQuentin Young <qlyoung@cumulusnetworks.com>
Mon, 8 Aug 2016 21:11:14 +0000 (21:11 +0000)
committerQuentin Young <qlyoung@cumulusnetworks.com>
Mon, 8 Aug 2016 21:11:14 +0000 (21:11 +0000)
Gnu-style code, add copyright headers, cleanup some
random style issues, shuffle around code into
relevant units, add docs.

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
lib/command_graph.c
lib/command_graph.h
lib/command_lex.l
lib/command_match.c
lib/command_match.h
lib/command_parse.y
lib/grammar_sandbox.c

index 0a866de330bf72f37ac4ac30a526c7f011bead16..f249935771d619c02b233f68a42ee66fda5a2f4f 100644 (file)
@@ -1,46 +1,52 @@
 /*
- * Command DFA module.
- * Provides a DFA data structure and associated functions for manipulating it.
- * Used to match user command line input.
+ * Graph data structure and companion routines for CLI backend.
  *
- * @author Quentin Young <qlyoung@cumulusnetworks.com>
+ * --
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * 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.
  */
-
-#include "command_graph.h"
 #include <zebra.h>
+#include "command_graph.h"
 #include "memory.h"
 
 struct graph_node *
-add_node(struct graph_node *parent, struct graph_node *child)
+add_node (struct graph_node *parent, struct graph_node *child)
 {
-  vector_set(parent->children, child);
+  vector_set (parent->children, child);
   child->refs++;
   return child;
 }
 
 struct graph_node *
-new_node(enum graph_node_type type)
+new_node (enum graph_node_type type)
 {
   struct graph_node *node =
-     XMALLOC(MTYPE_CMD_TOKENS, sizeof(struct graph_node));
+     XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct graph_node));
 
   node->type = type;
   node->children = vector_init(VECTOR_MIN_SIZE);
-  node->end      = NULL;
-  node->text     = NULL;
-  node->element  = NULL;
-  node->arg      = NULL;
-  node->is_start = 0;
-  node->value    = 0;
-  node->min      = 0;
-  node->max      = 0;
-  node->refs     = 0;
 
   return node;
 }
 
 void
-free_node (struct graph_node *node)
+delete_node (struct graph_node *node)
 {
   if (!node) return;
   if (node->children) vector_free (node->children);
@@ -51,100 +57,17 @@ free_node (struct graph_node *node)
 }
 
 void
-free_graph (struct graph_node *start)
+delete_graph (struct graph_node *start)
 {
-  if (start && start->children && vector_active(start->children) > 0) {
-    for (unsigned int i = 0; i < vector_active(start->children); i++) {
-      free_graph (vector_slot(start->children, i));
-      vector_unset(start->children, i);
+  if (start && start->children && vector_active (start->children) > 0)
+    {
+      for (unsigned int i = 0; i < vector_active (start->children); i++)
+        {
+          delete_graph (vector_slot(start->children, i));
+          vector_unset (start->children, i);
+        }
     }
-  }
 
   if (--(start->refs) == 0)
-    free_node (start);
-}
-
-char *
-describe_node(struct graph_node *node, char* buffer, unsigned int bufsize)
-{
-  if (node == NULL) {
-    snprintf(buffer, bufsize, "(null node)");
-    return buffer;
-  }
-
-  // print this node
-  switch (node->type) {
-    case WORD_GN:
-    case IPV4_GN:
-    case IPV4_PREFIX_GN:
-    case IPV6_GN:
-    case IPV6_PREFIX_GN:
-    case VARIABLE_GN:
-    case RANGE_GN:
-      snprintf(buffer, bufsize, node->text);
-      break;
-    case NUMBER_GN:
-      snprintf(buffer, bufsize, "%lld", node->value);
-      break;
-    case SELECTOR_GN:
-      snprintf(buffer, bufsize, "<>");
-      break;
-    case OPTION_GN:
-      snprintf(buffer, bufsize, "[]");
-      break;
-    case NUL_GN:
-      snprintf(buffer, bufsize, "NUL");
-      break;
-    case END_GN:
-      snprintf(buffer, bufsize, "END");
-      break;
-    case START_GN:
-      snprintf(buffer, bufsize, "START");
-      break;
-    default:
-      snprintf(buffer, bufsize, "ERROR");
-  }
-
-  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, desc, 50), vector_active(start->children));
-  free(desc);
-
-  if (vector_active(start->children)) {
-    if (vector_active(start->children) == 1)
-      walk_graph(vector_slot(start->children, 0), level);
-    else {
-      fprintf(stderr, "\n");
-      for (unsigned int i = 0; i < vector_active(start->children); i++) {
-        struct graph_node *r = vector_slot(start->children, i);
-        for (int j = 0; j < level+1; j++)
-          fprintf(stderr, "    ");
-        walk_graph(r, level+1);
-      }
-    }
-  }
-  else
-    fprintf(stderr, "\n");
-}
-
-void
-dump_node (struct graph_node *node)
-{
-  char buf[50];
-  describe_node(node, buf, 50);
-  fprintf(stderr, "%s[%d]\n", buf, node->type);
-  fprintf(stderr, "\t->text: %s\n", node->text);
-  fprintf(stderr, "\t->value: %lld\n", node->value);
-  fprintf(stderr, "\t->is_start: %d\n", node->is_start);
-  fprintf(stderr, "\t->element: %p\n", node->element);
-  fprintf(stderr, "\t->min: %lld\n->max: %lld\n", node->min, node->max);
-  fprintf(stderr, "\t->arg: %s\n", node->arg);
-  fprintf(stderr, "\t->refs: %d\n", node->refs);
-  fprintf(stderr, "\tnum children: %d\n", vector_active(node->children));
+    delete_node (start);
 }
index 718ce665533139a1e5779aac08b508eb0b0f703f..48c3d9cd0a98deff1f8d59c63c9ad88373f80e4a 100644 (file)
@@ -1,39 +1,74 @@
-#ifndef COMMAND_GRAPH_H
-#define COMMAND_GRAPH_H
+/*
+ * Graph data structure and companion routines for CLI backend.
+ *
+ * --
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * 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_GRAPH_H
+#define _ZEBRA_COMMAND_GRAPH_H
 
 #include "command.h"
 
+/**
+ * Types for graph nodes.
+ *
+ * The node type determines what kind of data the node can match (in the
+ * matching use case) or hold (in the argv use case).
+ */
 enum graph_node_type
 {
-  IPV4_GN,
-  IPV4_PREFIX_GN,
-  IPV6_GN,
-  IPV6_PREFIX_GN,
-  WORD_GN,
-  RANGE_GN,
-  NUMBER_GN,
-  VARIABLE_GN,
-  SELECTOR_GN,
-  OPTION_GN,
-  NUL_GN,
-  START_GN,
-  END_GN
+  IPV4_GN,          // IPV4 addresses
+  IPV4_PREFIX_GN,   // IPV4 network prefixes
+  IPV6_GN,          // IPV6 prefixes
+  IPV6_PREFIX_GN,   // IPV6 network prefixes
+  WORD_GN,          // words
+  RANGE_GN,         // integer ranges
+  NUMBER_GN,        // numbers
+  VARIABLE_GN,      // almost anything
+  /* plumbing types */
+  SELECTOR_GN,      // marks beginning of selector subgraph
+  OPTION_GN,        // marks beginning of option subgraph
+  NUL_GN,           // transparent node with various uses
+  START_GN,         // first node in the graph (has no parents)
+  END_GN            // leaf node in the graph, has pointer to cmd_element
 };
 
+/**
+ * Command graph node.
+ * Used for matching and passing arguments to vtysh commands.
+ */
 struct graph_node
 {
-  enum graph_node_type type;// data type this node matches or holds
-  unsigned int is_start;    // whether this node is a start node
-  vector children;          // this node's children
-  struct graph_node * end;  // pointer to end for SELECTOR_GN & OPTION_GN
+  enum graph_node_type type;    // data type this node matches or holds
+  vector children;              // this node's children
+  struct graph_node *end;       // pointer to end for SELECTOR_GN & OPTION_GN
 
-  char *text;               // original format text
-  char *doc;                // docstring for this node
-  long long value;          // for NUMBER_GN
-  long long min, max;       // for RANGE_GN
+  char *text;                   // original format text
+  char *doc;                    // docstring for this node
+  long long value;              // for NUMBER_GN
+  long long min, max;           // for RANGE_GN
 
   /* cmd_element struct pointer, only valid for END_GN */
   struct cmd_element *element;
+
   /* used for passing arguments to command functions */
   char *arg;
 
@@ -49,52 +84,33 @@ struct graph_node
  * @return child node
  */
 struct graph_node *
-add_node(struct graph_node *, struct graph_node *);
+add_node (struct graph_node *parent, struct graph_node *child);
 
-/*
- * Create a new node.
- * Initializes all fields to default values and sets the node type.
+/**
+ * Creates a new node, initializes all fields to default values and sets the
+ * node type.
  *
- * @param[in] node type
- * @return pointer to the newly allocated node
+ * @param[in] type node type
+ * @return pointer to the created node
  */
 struct graph_node *
-new_node(enum graph_node_type);
-
-/**
- * Frees the data associated with a graph_node.
- * @param[out] pointer to graph_node to free
- */
-void
-free_node(struct graph_node *);
+new_node (enum graph_node_type type);
 
 /**
- * Recursively calls free_node on a graph node
- * and all its children.
- * @param[out] graph to free
+ * Deletes a graph node without deleting its children.
+ *
+ * @param[out] node pointer to node to delete
  */
 void
-free_graph(struct graph_node *);
+delete_node (struct graph_node *node);
 
 /**
- * Walks a command DFA, printing structure to stdout.
- * For debugging.
+ * Deletes a graph node and recursively deletes all its direct and indirect
+ * children.
  *
- * @param[in] start node of graph to walk
- * @param[in] graph depth for recursion, caller passes 0
+ * @param[out] node start node of graph to free
  */
 void
-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
- */
-char *
-describe_node(struct graph_node *, char *, unsigned int);
+delete_graph (struct graph_node *node);
 
-void
-dump_node (struct graph_node *);
-#endif
+#endif /* _ZEBRA_COMMAND_GRAPH_H */
index 600c92d23fdd01c6d75ae5e37a10f7f113320c55..920c03db8ebedc827e5b1c0bb75a73b16a319e75 100644 (file)
@@ -1,3 +1,27 @@
+/*
+ * Command format string lexer for CLI backend.
+ *
+ * --
+ * Copyright (C) 2015 Cumulus Networks, Inc.
+ *
+ * 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.
+ */
+
 %{
 #include "command_parse.h"
 
index e4b95d5970753b5b3e1ea8b87c37b17ee61f7058..011ac698cbb8c0cafb5b026d9124f0748de2fc53 100644 (file)
@@ -1,11 +1,35 @@
+/*
+ * Input matching routines for CLI backend.
+ *
+ * --
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * 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.
+ */
+
+#include <zebra.h>
 #include "command_match.h"
 #include "command_parse.h"
-#include <zebra.h>
 #include "memory.h"
 
 /* matcher helper prototypes */
 static int
-add_nexthops(struct list *, struct graph_node *);
+add_nexthops (struct list *, struct graph_node *);
 
 static struct list *
 match_command_r (struct graph_node *, vector, unsigned int);
@@ -14,7 +38,7 @@ static int
 score_precedence (enum graph_node_type);
 
 static enum match_type
-min_match_level(enum node_type);
+min_match_level (enum node_type);
 
 static struct graph_node *
 copy_node (struct graph_node *);
@@ -23,10 +47,10 @@ static void
 delete_nodelist (void *);
 
 static struct graph_node *
-disambiguate (struct graph_node *, struct graph_node *, char *);
+disambiguate_nodes (struct graph_node *, struct graph_node *, char *);
 
 static struct list *
-disambiguate_nodelist (struct list *, struct list *, vector, unsigned int);
+disambiguate (struct list *, struct list *, vector, unsigned int);
 
 /* token matcher prototypes */
 static enum match_type
@@ -54,40 +78,33 @@ static enum match_type
 match_number (struct graph_node *, const char *);
 
 static enum match_type
-match_variable (struct graph_node *, const char *);
+match_variable (struct graph_node *node, const char *word);
 
 /* matching functions */
-static enum matcher_rv matcher_result_value;
+static enum matcher_rv matcher_rv;
 
 enum matcher_rv
 match_command (struct graph_node *start,
-               const char *line,
-               struct list **argvv,
+               vector vline,
+               struct list **argv,
                struct cmd_element **el)
 {
-  matcher_result_value = MATCHER_NO_MATCH;
-  // parse command
-  vector vline = cmd_make_strvec (line);
-
-  for (unsigned int i = 0; i < vector_active(start->children); i++)
-  {
-    // call recursive matcher on each starting child
-    *argvv = match_command_r(vector_slot(start->children, i), vline, 0);
-    if (*argvv) break;
-  }
+  matcher_rv = MATCHER_NO_MATCH;
 
-  if (*argvv) {
-    struct listnode *ln;
-    struct graph_node *gn;
-    for (ALL_LIST_ELEMENTS_RO(*argvv,ln,gn))
-      if (gn->type == END_GN) {
-        *el = gn->element;
-        break;
-      }
-    assert(*el);
-  }
+  // call recursive matcher on each starting child
+  for (unsigned int i = 0; i < vector_active (start->children); i++)
+    {
+      *argv = match_command_r (vector_slot (start->children, i), vline, 0);
+      if (*argv) // successful match
+        {
+          struct graph_node *end = listgetdata (listtail (*argv));
+          *el = end->element;
+          assert (*el);
+          break;
+        }
+    }
 
-  return matcher_result_value;
+  return matcher_rv;
 }
 
 /**
@@ -134,80 +151,83 @@ match_command (struct graph_node *start,
  * the best match for the command, each with their `arg` fields pointing to the
  * matching token string.
  *
- * @param[out] start the start node.
+ * @param[in] start the start node.
  * @param[in] vline the vectorized input line.
- * @param[in] n the index of the first input token. Should be 0 for external
- * callers.
+ * @param[in] n the index of the first input token.
  */
 static struct list *
 match_command_r (struct graph_node *start, vector vline, unsigned int n)
 {
   // get the minimum match level that can count as a full match
-  enum match_type minmatch = min_match_level(start->type);
+  enum match_type minmatch = min_match_level (start->type);
 
   // get the current operating token
-  char *token = vector_slot(vline, n);
+  char *token = vector_slot (vline, n);
 
   // if we don't match this node, die
-  if (match_token(start, token) < minmatch)
+  if (match_token (start, token) < minmatch)
     return NULL;
 
   // pointers for iterating linklist
+  struct listnode *ln;
   struct graph_node *gn;
-  struct listnode   *ln;
 
   // get all possible nexthops
   struct list *next = list_new();
-  add_nexthops(next, start);
+  add_nexthops (next, start);
 
   // determine the best match
   int ambiguous = 0;
   struct list *currbest = NULL;
-  for (ALL_LIST_ELEMENTS_RO(next,ln,gn))
-  {
-    // if we've matched all input we're looking for END_GN
-    if (n+1 == vector_active (vline)) {
-      if (gn->type == END_GN) {
-        currbest = list_new();
-        listnode_add(currbest, copy_node(gn));
-        currbest->del = &delete_nodelist;
-        break;
-      }
-      else continue;
-    }
+  for (ALL_LIST_ELEMENTS_RO (next,ln,gn))
+    {
+      // if we've matched all input we're looking for END_GN
+      if (n+1 == vector_active (vline))
+        {
+          if (gn->type == END_GN)
+            {
+              currbest = list_new();
+              listnode_add (currbest, copy_node(gn));
+              currbest->del = &delete_nodelist;
+              break;
+            }
+          else continue;
+        }
 
-    // else recurse on candidate child node
-    struct list *result = match_command_r (gn, vline, n+1);
+      // else recurse on candidate child node
+      struct list *result = match_command_r (gn, vline, n+1);
 
-    // save the best match, subtle logic at play here
-    if (result && currbest) {
-      struct list *newbest = disambiguate_nodelist(currbest, result, vline, n+1);
-      ambiguous = !newbest || (ambiguous && newbest == currbest);
-      list_delete ((newbest && newbest == result) ? currbest : result);
-      currbest = newbest ? newbest : currbest;
+      // save the best match
+      if (result && currbest)
+        {
+          struct list *newbest = disambiguate (currbest, result, vline, n+1);
+          ambiguous = !newbest || (ambiguous && newbest == currbest);
+          list_delete ((newbest && newbest == result) ? currbest : result);
+          currbest = newbest ? newbest : currbest;
+        }
+      else if (result)
+        currbest = result;
     }
-    else if (result)
-      currbest = result;
-  }
 
-  if (currbest) {
-    if (ambiguous) {
-      list_delete(currbest);
-      currbest = NULL;
-      matcher_result_value = MATCHER_AMBIGUOUS;
-    }
-    else {
-      // copy current node, set arg and prepend to currbest
-      struct graph_node *curr = copy_node(start);
-      curr->arg = XSTRDUP(MTYPE_CMD_TOKENS, token);
-      list_add_node_prev (currbest, currbest->head, curr);
-      matcher_result_value = MATCHER_OK;
+  if (currbest)
+    {
+      if (ambiguous)
+        {
+          list_delete (currbest);
+          currbest = NULL;
+          matcher_rv = MATCHER_AMBIGUOUS;
+        }
+      else
+        {
+          // copy current node, set arg and prepend to currbest
+          struct graph_node *curr = copy_node (start);
+          curr->arg = XSTRDUP(MTYPE_CMD_TOKENS, token);
+          list_add_node_prev (currbest, currbest->head, curr);
+          matcher_rv = MATCHER_OK;
+        }
     }
-  }
-  else {
-    if (n+1 == vector_active(vline) && matcher_result_value == MATCHER_NO_MATCH)
-      matcher_result_value = MATCHER_INCOMPLETE;
-  }
+  else if (n+1 == vector_active (vline) && matcher_rv == MATCHER_NO_MATCH)
+    matcher_rv = MATCHER_INCOMPLETE;
 
   // cleanup
   list_delete (next);
@@ -215,12 +235,9 @@ match_command_r (struct graph_node *start, vector vline, unsigned int n)
   return currbest;
 }
 
-struct list *
-match_command_complete (struct graph_node *start, const char *line)
+enum matcher_rv
+match_command_complete (struct graph_node *start, vector vline, struct list **completions)
 {
-  // vectorize command line
-  vector vline = cmd_make_strvec (line);
-
   // pointer to next input token to match
   char *token;
 
@@ -232,33 +249,35 @@ match_command_complete (struct graph_node *start, const char *line)
   struct listnode *node;
 
   // add all children of start node to list
-  add_nexthops(next, start);
+  add_nexthops (next, start);
 
   unsigned int idx;
-  for (idx = 0; idx < vector_active(vline) && next->count > 0; idx++)
-  {
-    list_free (current);
-    current = next;
-    next = list_new();
+  for (idx = 0; idx < vector_active (vline) && next->count > 0; idx++)
+    {
+      list_free (current);
+      current = next;
+      next = list_new();
 
-    token = vector_slot(vline, idx);
+      token = vector_slot (vline, idx);
 
-    for (ALL_LIST_ELEMENTS_RO(current,node,gn))
-    {
-      switch (match_token(gn, token)) {
-        case partly_match:
-          if (idx == vector_active(vline) - 1) {
-            listnode_add(next, gn);
-            break;
-          }
-        case exact_match:
-          add_nexthops(next, gn);
-          break;
-        default:
-          break;
-      }
+      for (ALL_LIST_ELEMENTS_RO (current,node,gn))
+        {
+          switch (match_token (gn, token))
+            {
+              case partly_match:
+                if (idx == vector_active (vline) - 1)
+                  {
+                    listnode_add (next, gn);
+                    break;
+                  }
+              case exact_match:
+                add_nexthops (next, gn);
+                break;
+              default:
+                break;
+            }
+        }
     }
-  }
 
   /* Variable summary
    * -----------------------------------------------------------------
@@ -268,101 +287,122 @@ match_command_complete (struct graph_node *start, const char *line)
    * next     = set of all nodes reachable from all nodes in `matched`
    */
 
-  matcher_result_value =
+  matcher_rv =
      idx + 1 == vector_active(vline) && next->count ?
      MATCHER_OK :
      MATCHER_NO_MATCH;
 
   list_free (current);
-  cmd_free_strvec(vline);
+  *completions = next;
 
-  return next;
+  return matcher_rv;
 }
 
 /**
- * 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 transparent.
+ * 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 transparent.
  *
- * @param[out] l the list to add the children to
- * @param[in] node the node to get the children of
+ * @param[in] list to add the nexthops to
+ * @param[in] node to start calculating nexthops from
  * @return the number of children added to the list
  */
 static int
-add_nexthops(struct list *l, struct graph_node *node)
+add_nexthops (struct list *list, 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);
-    switch (child->type) {
-      case OPTION_GN:
-      case SELECTOR_GN:
-      case NUL_GN:
-        added += add_nexthops(l, child);
-        break;
-      default:
-        listnode_add(l, child);
-        added++;
+  for (unsigned int i = 0; i < vector_active (node->children); i++)
+    {
+      child = vector_slot (node->children, i);
+      switch (child->type)
+        {
+          case OPTION_GN:
+          case SELECTOR_GN:
+          case NUL_GN:
+            added += add_nexthops (list, child);
+            break;
+          default:
+            listnode_add (list, child);
+            added++;
+        }
     }
-  }
+
   return added;
 }
 
 /**
  * Determines the node types for which a partial match may count as a full
  * match. Enables command abbrevations.
+ *
+ * @param[in] type node type
+ * @return minimum match level needed to for a token to fully match
  */
 static enum match_type
-min_match_level(enum node_type type)
+min_match_level (enum node_type type)
 {
-  switch (type) {
-    case WORD_GN:
-      return partly_match;
-    default:
-      return exact_match;
-  }
+  switch (type)
+    {
+      // allowing words to partly match enables command abbreviation
+      case WORD_GN:
+        return partly_match;
+      default:
+        return exact_match;
+    }
 }
 
-/* Precedence score used to disambiguate matches. */
+/**
+ * Assigns precedence scores to node types.
+ *
+ * @param[in] type node type to score
+ * @return precedence score
+ */
 static int
 score_precedence (enum graph_node_type type)
 {
   switch (type)
-  {
-    // some of these are mutually exclusive, so they share
-    // the same precedence value
-    case IPV4_GN:
-    case IPV4_PREFIX_GN:
-    case IPV6_GN:
-    case IPV6_PREFIX_GN:
-    case NUMBER_GN:
-      return 1;
-    case RANGE_GN:
-      return 2;
-    case WORD_GN:
-      return 3;
-    case VARIABLE_GN:
-      return 4;
-    default:
-      return 10;
-  }
+    {
+      // some of these are mutually exclusive, so they share
+      // the same precedence value
+      case IPV4_GN:
+      case IPV4_PREFIX_GN:
+      case IPV6_GN:
+      case IPV6_PREFIX_GN:
+      case NUMBER_GN:
+        return 1;
+      case RANGE_GN:
+        return 2;
+      case WORD_GN:
+        return 3;
+      case VARIABLE_GN:
+        return 4;
+      default:
+        return 10;
+    }
 }
 
-/* Disambiguation logic to pick the best of two possible matches */
+/**
+ * Picks the better of two possible matches for a token.
+ *
+ * @param[in] first candidate node matching token
+ * @param[in] second candidate node matching token
+ * @param[in] token the token being matched
+ * @return the best-matching node, or NULL if the two are entirely ambiguous
+ */
 static struct graph_node *
-disambiguate (struct graph_node *first, struct graph_node *second, char *token)
+disambiguate_nodes (struct graph_node *first,
+                    struct graph_node *second,
+                    char *token)
 {
   // if the types are different, simply go off of type precedence
-  if (first->type != second->type) {
-    int firstprec = score_precedence(first->type);
-    int secndprec = score_precedence(second->type);
-    if (firstprec != secndprec)
-      return firstprec < secndprec ? first : second;
-    else
-      return NULL;
-  }
+  if (first->type != second->type)
+    {
+      int firstprec = score_precedence (first->type);
+      int secndprec = score_precedence (second->type);
+      if (firstprec != secndprec)
+        return firstprec < secndprec ? first : second;
+      else
+        return NULL;
+    }
 
   // if they're the same, return the more exact match
   enum match_type fmtype = match_token (first, token);
@@ -373,40 +413,60 @@ disambiguate (struct graph_node *first, struct graph_node *second, char *token)
   return NULL;
 }
 
+/**
+ * Picks the better of two possible matches for an input line.
+ *
+ * @param[in] first candidate list of graph_node matching vline
+ * @param[in] second candidate list of graph_node matching vline
+ * @param[in] vline the input line being matched
+ * @param[in] n index into vline to start comparing at
+ * @return the best-matching list, or NULL if the two are entirely ambiguous
+ */
 static struct list *
-disambiguate_nodelist (struct list *first, struct list *second, vector vline, unsigned int n)
+disambiguate (struct list *first,
+              struct list *second,
+              vector vline,
+              unsigned int n)
 {
-  fprintf(stderr, "%d --- %d\n", first->count, n);
-
   // doesn't make sense for these to be inequal length
-  assert(first->count == second->count);
-  assert(first->count == vector_active(vline) - n+1);
+  assert (first->count == second->count);
+  assert (first->count == vector_active (vline) - n+1);
 
-  struct listnode *fnode = listhead(first),
-                  *snode = listhead(second);
-  struct graph_node *fgn = listgetdata(fnode),
-                    *sgn = listgetdata(snode),
+  struct listnode *fnode = listhead (first),
+                  *snode = listhead (second);
+  struct graph_node *fgn = listgetdata (fnode),
+                    *sgn = listgetdata (snode),
                     *best = NULL;
 
   // compare each node, if one matches better use that one
-  for (unsigned int i = n; i < vector_active(vline); i++) {
-    if ((best = disambiguate (fgn, sgn, (char*) vector_slot(vline, i))))
-      return best == fgn ? first : second;
-    fnode = listnextnode(fnode);
-    fgn = (struct graph_node *) listgetdata (fnode);
-    snode = listnextnode(snode);
-    sgn = (struct graph_node *) listgetdata (snode);
-  }
+  for (unsigned int i = n; i < vector_active (vline); i++)
+    {
+      char *token = vector_slot(vline, i);
+      if ((best = disambiguate_nodes (fgn, sgn, token)))
+        return best == fgn ? first : second;
+      fnode = listnextnode (fnode);
+      snode = listnextnode (snode);
+      fgn = (struct graph_node *) listgetdata (fnode);
+      sgn = (struct graph_node *) listgetdata (snode);
+    }
 
   return NULL;
 }
 
+/**
+ * Performs a deep copy on a node.
+ * Used to build argv node lists that can be safely deleted or modified by
+ * endpoint functions. Everything is copied except the children vector,
+ * subgraph end pointer and reference count.
+ *
+ * @param[in] node to copy
+ * @return the copy
+ */
 static struct graph_node *
 copy_node (struct graph_node *node)
 {
   struct graph_node *new = new_node(node->type);
   new->children = NULL;
-  new->is_start = node->is_start;
   new->end      = NULL;
   new->text     = node->text ? XSTRDUP(MTYPE_CMD_TOKENS, node->text) : NULL;
   new->value    = node->value;
@@ -418,11 +478,13 @@ copy_node (struct graph_node *node)
   return new;
 }
 
-/* Linked list data deletion callback */
+/**
+ * List deletion callback for argv lists.
+ */
 static void
 delete_nodelist (void *node)
 {
-  free_node ((struct graph_node *) node);
+  delete_node ((struct graph_node *) node);
 }
 
 
@@ -632,7 +694,7 @@ match_ipv6_prefix (const char *str)
     return no_match;
 
   /* tokenize to address + mask */
-  dupe = XMALLOC(MTYPE_TMP, strlen(str)+1);
+  dupe = XCALLOC(MTYPE_TMP, strlen(str)+1);
   strncpy(dupe, str, strlen(str)+1);
   prefix = strtok_r(dupe, delim, &context);
   mask   = strtok_r(NULL, delim, &context);
@@ -656,8 +718,10 @@ match_ipv6_prefix (const char *str)
 #endif
 
 static enum match_type
-match_range (struct graph_node *rangenode, const char *str)
+match_range (struct graph_node *node, const char *str)
 {
+  assert (node->type == RANGE_GN);
+
   char *endptr = NULL;
   long long val;
 
@@ -668,45 +732,54 @@ match_range (struct graph_node *rangenode, const char *str)
   if (*endptr != '\0')
     return 0;
 
-  if (val < rangenode->min || val > rangenode->max)
+  if (val < node->min || val > node->max)
     return no_match;
   else
     return exact_match;
 }
 
 static enum match_type
-match_word(struct graph_node *wordnode, const char *word)
+match_word (struct graph_node *node, const char *word)
 {
+  assert (node->type == WORD_GN);
+
   // if the passed token is null or 0 length, partly match
   if (!word || !strlen(word))
     return partly_match;
 
   // if the passed token is strictly a prefix of the full word, partly match
-  if (strlen(word) < strlen(wordnode->text))
-    return !strncmp(wordnode->text, word, strlen(word)) ? partly_match : no_match;
+  if (strlen (word) < strlen (node->text))
+    return !strncmp (node->text, word, strlen (word)) ?
+       partly_match :
+       no_match;
 
   // if they are the same length and exactly equal, exact match
-  else if (strlen(word) == strlen(wordnode->text))
-    return !strncmp(wordnode->text, word, strlen(word)) ? exact_match : no_match;
+  else if (strlen (word) == strlen (node->text))
+    return !strncmp (node->text, word, strlen (word)) ? exact_match : no_match;
 
   return no_match;
 }
 
 static enum match_type
-match_number(struct graph_node *numnode, const char *word)
+match_number (struct graph_node *node, const char *word)
 {
-  if (!strcmp("\0", word)) return no_match;
+  assert (node->type == NUMBER_GN);
+
+  if (!strcmp ("\0", word)) return no_match;
   char *endptr;
   long long num = strtoll (word, &endptr, 10);
   if (endptr != '\0') return no_match;
-  return num == numnode->value ? exact_match : no_match;
+  return num == node->value ? exact_match : no_match;
 }
 
-#define VARIABLE_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890:"
+#define VARIABLE_ALPHABET \
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890:"
 
 static enum match_type
-match_variable(struct graph_node *varnode, const char *word)
+match_variable (struct graph_node *node, const char *word)
 {
-  return strlen(word) == strspn(word, VARIABLE_ALPHABET) ?
+  assert (node->type == VARIABLE_GN);
+
+  return strlen (word) == strspn(word, VARIABLE_ALPHABET) ?
      exact_match : no_match;
 }
index 895a678dcefa925bbaebe9dabac4d8f87e99c669..6804a0777a9c189727ece870cdbf350e59d1c03f 100644 (file)
@@ -1,21 +1,45 @@
-#ifndef COMMAND_MATCH_H
-#define COMMAND_MATCH_H
+/*
+ * Input matching routines for CLI backend.
+ *
+ * --
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * 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_MATCH_H
+#define _ZEBRA_COMMAND_MATCH_H
 
 #include "command.h"
 #include "command_graph.h"
 #include "linklist.h"
 
 
-/** These definitions exist in command.c in
- * the current engine but will be relocated
- * here in the new engine*/
+/* These definitions exist in command.c in the current engine but should be
+ * relocated here in the new engine
+ */
 enum filter_type
 {
   FILTER_RELAXED,
   FILTER_STRICT
 };
 
-/* matcher result value. */
+/* matcher result value */
 enum matcher_rv
 {
   MATCHER_NO_MATCH,
@@ -24,7 +48,7 @@ enum matcher_rv
   MATCHER_OK,
 };
 
-/* Completion match types. */
+/* completion match types */
 enum match_type
 {
   no_match,
@@ -32,9 +56,8 @@ enum match_type
   exact_match
 };
 
-/* Defines which matcher_rv values constitute
- * an error. Should be used against matcher_rv
- * return values to do basic error checking.
+/* Defines which matcher_rv values constitute an error. Should be used with
+ * matcher_rv return values to do basic error checking.
  */
 #define MATCHER_ERROR(matcher_rv) \
   (   (matcher_rv) == MATCHER_INCOMPLETE \
@@ -45,37 +68,29 @@ enum match_type
 /**
  * Attempt to find an exact command match for a line of user input.
  *
- * @param DFA to match against
- * @param input string
- * @param pointer which will be pointed at argv upon match
- * @param pointer which will be pointed at matching cmd_element upon match
- * @return result of matcher run
+ * @param[in] start start node of command graph to match against
+ * @param[in] vline vectorized input string
+ * @param[out] argv pointer to argument list if successful match
+ * @param[out] element pointer to matched cmd_element if successful match
+ * @return matcher status
  */
 enum matcher_rv
-match_command (struct graph_node *, const char *, struct list **, struct cmd_element **);
+match_command (struct graph_node *start,
+               vector vline,
+               struct list **argv,
+               struct cmd_element **element);
 
 /**
- * Compiles next-hops for a given line of user input.
- *
- * Given a string of input and a start node for a matching DFA, runs the input
- * against the DFA until the input is exhausted or a mismatch is encountered.
- *
- * This function returns all valid next hops away from the current node.
- *  - If the input is a valid prefix to a longer command(s), the set of next
- *    hops determines what tokens are valid to follow the prefix. In other words,
- *    the returned list is a list of possible completions.
- *  - If the input matched a full command, exactly one of the next hops will be
- *    a node of type END_GN and its function pointer will be set.
- *  - If the input did not match any valid token sequence, the returned list
- *    will be empty (there are no transitions away from a nonexistent state).
+ * Compiles possible completions for a given line of user input.
  *
  * @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.
+ * @param[in] vline vectorized input string
+ * @param[in] completions pointer to possible completions
+ * @return matcher status
  */
-struct list *
-match_command_complete (struct graph_node *, const char *);
+enum matcher_rv
+match_command_complete (struct graph_node *start,
+                        vector vline,
+                        struct list **completions);
 
-#endif
+#endif /* _ZEBRA_COMMAND_MATCH_H */
index 5a8bb6930859306d7b792074b01eb87dc342d3f7..6b972afe9049151dbcf3fa205c75f3816a5d696a 100644 (file)
@@ -1,15 +1,30 @@
 /*
- * Command format string parser.
+ * Command format string parser for CLI backend.
  *
- * Turns a command definition into a DFA that together with the functions
- * provided in command_match.c may be used to map command line input to a
- * function.
+ * --
+ * Copyright (C) 2015 Cumulus Networks, Inc.
  *
- * @author Quentin Young <qlyoung@cumulusnetworks.com>
+ * 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.
  */
 
 %{
-#define YYDEBUG 1   // compile with debugging facilities
+// compile with debugging facilities
+#define YYDEBUG 1
 %}
 
 /* names for generated header and parser files */
   #include "memory.h"
 
   extern int
-  yylex(void);
+  yylex (void);
 
   extern void
-  set_buffer_string(const char *);
+  set_buffer_string (const char *);
 }
 
 /* functionality this unit exports */
 %code provides {
   struct graph_node *
-  parse_command_format(struct graph_node *, struct cmd_element *);
+  parse_command_format (struct graph_node *, struct cmd_element *);
 
   /* maximum length of a number, lexer will not match anything longer */
   #define DECIMAL_STRLEN_MAX 20
@@ -71,7 +86,7 @@
 %code {
   /* bison declarations */
   void
-  yyerror(struct cmd_element *el, struct graph_node *sn, char const *msg);
+  yyerror (struct cmd_element *el, struct graph_node *sn, char const *msg);
 
   /* state variables for a single parser run */
   struct graph_node *currnode,        // current position in DFA
   doc_next(void);
 
   static struct graph_node *
-  node_exists(struct graph_node *, struct graph_node *);
+  node_exists (struct graph_node *, struct graph_node *);
 
   static struct graph_node *
-  node_replace(struct graph_node *, struct graph_node *);
+  node_replace (struct graph_node *, struct graph_node *);
 
   static int
-  cmp_node(struct graph_node *, struct graph_node *);
+  cmp_node (struct graph_node *, struct graph_node *);
 
   static void
   terminate_graph (struct graph_node *,
                    struct cmd_element *);
 
   static void
-  cleanup(void);
+  cleanup (void);
 }
 
 /* yyparse parameters */
   optnode_start = optnode_end = NULL;
 
   /* set string to parse */
-  set_buffer_string(element->string);
+  set_buffer_string (element->string);
 
   /* copy docstring and keep a pointer to the copy */
   docstr = element->doc ? XSTRDUP(MTYPE_TMP, element->doc) : NULL;
@@ -138,13 +153,12 @@ start:
 }
 | sentence_root cmd_token_seq '.' placeholder_token
 {
-  if ((currnode = node_replace(currnode, $4)) != $4)
-    free_node ($4);
+  if ((currnode = node_replace (currnode, $4)) != $4)
+    delete_node ($4);
 
-  // since varargs may match any number of the last token,
-  // simply add this node as a child of itself and proceed
-  // wth normal command termination procedure
-  node_replace(currnode, currnode);
+  // adding a node as a child of itself accepts any number
+  // of the same token, which is what we want for varags
+  node_replace (currnode, currnode);
 
   // tack on the command element
   terminate_graph (startnode, currnode, element);
@@ -153,11 +167,11 @@ start:
 
 sentence_root: WORD
 {
-  struct graph_node *root = new_node(WORD_GN);
+  struct graph_node *root = new_node (WORD_GN);
   root->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
   root->doc = doc_next();
 
-  if ((currnode = node_replace(startnode, root)) != root)
+  if ((currnode = node_replace (startnode, root)) != root)
     free (root);
 
   free ($1);
@@ -167,24 +181,24 @@ sentence_root: WORD
 cmd_token:
   placeholder_token
 {
-  if ((currnode = node_replace(currnode, $1)) != $1)
-    free_node ($1);
+  if ((currnode = node_replace (currnode, $1)) != $1)
+    delete_node ($1);
 }
 | literal_token
 {
-  if ((currnode = node_replace(currnode, $1)) != $1)
-    free_node ($1);
+  if ((currnode = node_replace (currnode, $1)) != $1)
+    delete_node ($1);
 }
 /* selectors and options are subgraphs with start and end nodes */
 | selector
 {
-  add_node(currnode, $1);
+  add_node (currnode, $1);
   currnode = selnode_end;
   selnode_start = selnode_end = NULL;
 }
 | option
 {
-  add_node(currnode, $1);
+  add_node (currnode, $1);
   currnode = optnode_end;
   optnode_start = optnode_end = NULL;
 }
@@ -198,53 +212,53 @@ cmd_token_seq:
 placeholder_token:
   IPV4
 {
-  $$ = new_node(IPV4_GN);
+  $$ = new_node (IPV4_GN);
   $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
   $$->doc = doc_next();
   free ($1);
 }
 | IPV4_PREFIX
 {
-  $$ = new_node(IPV4_PREFIX_GN);
+  $$ = new_node (IPV4_PREFIX_GN);
   $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
   $$->doc = doc_next();
   free ($1);
 }
 | IPV6
 {
-  $$ = new_node(IPV6_GN);
+  $$ = new_node (IPV6_GN);
   $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
   $$->doc = doc_next();
   free ($1);
 }
 | IPV6_PREFIX
 {
-  $$ = new_node(IPV6_PREFIX_GN);
+  $$ = new_node (IPV6_PREFIX_GN);
   $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
   $$->doc = doc_next();
   free ($1);
 }
 | VARIABLE
 {
-  $$ = new_node(VARIABLE_GN);
+  $$ = new_node (VARIABLE_GN);
   $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
   $$->doc = doc_next();
   free ($1);
 }
 | RANGE
 {
-  $$ = new_node(RANGE_GN);
+  $$ = new_node (RANGE_GN);
   $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
   $$->doc = doc_next();
 
   // get the numbers out
   yylval.string++;
-  $$->min = strtoll( yylval.string, &yylval.string, 10 );
+  $$->min = strtoll (yylval.string, &yylval.string, 10);
   strsep (&yylval.string, "-");
-  $$->max = strtoll( yylval.string, &yylval.string, 10 );
+  $$->max = strtoll (yylval.string, &yylval.string, 10);
 
   // validate range
-  if ($$->min >= $$->max) yyerror(element, startnode, "Invalid range.");
+  if ($$->min >= $$->max) yyerror (element, startnode, "Invalid range.");
 
   free ($1);
 }
@@ -253,14 +267,14 @@ placeholder_token:
 literal_token:
   WORD
 {
-  $$ = new_node(WORD_GN);
+  $$ = new_node (WORD_GN);
   $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
   $$->doc = doc_next();
   free ($1);
 }
 | NUMBER
 {
-  $$ = new_node(NUMBER_GN);
+  $$ = new_node (NUMBER_GN);
   $$->value = yylval.number;
   $$->text = XCALLOC(MTYPE_CMD_TOKENS, DECIMAL_STRLEN_MAX+1);
   snprintf($$->text, DECIMAL_STRLEN_MAX, "%lld", $$->value);
@@ -286,26 +300,26 @@ selector_element: selector_element_root selector_token_seq
   // if the selector start and end do not exist, create them
   if (!selnode_start || !selnode_end) {     // if one is null
     assert(!selnode_start && !selnode_end); // both should be null
-    selnode_start = new_node(SELECTOR_GN);  // diverging node
-    selnode_end = new_node(NUL_GN);         // converging node
+    selnode_start = new_node (SELECTOR_GN);  // diverging node
+    selnode_end = new_node (NUL_GN);         // converging node
     selnode_start->end = selnode_end;       // duh
   }
 
   // add element head as a child of the selector
-  add_node(selnode_start, $1);
+  add_node (selnode_start, $1);
 
   if ($2->type != NUL_GN) {
-    add_node($1, seqhead);
-    add_node($2, selnode_end);
+    add_node ($1, seqhead);
+    add_node ($2, selnode_end);
   }
   else
-    add_node($1, selnode_end);
+    add_node ($1, selnode_end);
 
   seqhead = NULL;
 }
 
 selector_token_seq:
-  %empty { $$ = new_node(NUL_GN); }
+  %empty { $$ = new_node (NUL_GN); }
 | selector_token_seq selector_token
 {
   // if the sequence component is NUL_GN, this is a sequence start
@@ -314,7 +328,7 @@ selector_token_seq:
     seqhead = $2;
   }
   else // chain on new node
-    add_node($1, $2);
+    add_node ($1, $2);
 
   $$ = $2;
 }
@@ -334,7 +348,7 @@ selector_token:
 option: '[' option_part ']'
 {
   // add null path
-  add_node(optnode_start, optnode_end);
+  add_node (optnode_start, optnode_end);
   $$ = optnode_start;
 };
 
@@ -348,19 +362,19 @@ option_element:
 {
   if (!optnode_start || !optnode_end) {
     assert(!optnode_start && !optnode_end);
-    optnode_start = new_node(OPTION_GN);
-    optnode_end = new_node(NUL_GN);
+    optnode_start = new_node (OPTION_GN);
+    optnode_end = new_node (NUL_GN);
   }
 
-  add_node(optnode_start, seqhead);
-  add_node($1, optnode_end);
+  add_node (optnode_start, seqhead);
+  add_node ($1, optnode_end);
 }
 
 option_token_seq:
   option_token
 { $$ = seqhead = $1; }
 | option_token_seq option_token
-{ $$ = add_node($1, $2); }
+{ $$ = add_node ($1, $2); }
 ;
 
 option_token:
@@ -373,6 +387,7 @@ option_token:
 struct graph_node *
 parse_command_format(struct graph_node *start, struct cmd_element *cmd)
 {
+  // set to 1 to enable parser traces
   yydebug = 0;
 
   // parse command into DFA
@@ -384,9 +399,10 @@ parse_command_format(struct graph_node *start, struct cmd_element *cmd)
 /* parser helper functions */
 
 void
-yyerror(struct cmd_element *el, struct graph_node *sn, char const *msg)
+yyerror (struct cmd_element *el, struct graph_node *sn, char const *msg)
 {
-  fprintf(stderr, "Grammar error: %s\n", msg);
+  zlog_err ("%s: FATAL parse error: %s", __func__, msg);
+  zlog_err ("while parsing this command definition: \n\t%s\n", el->string);
   exit(EXIT_FAILURE);
 }
 
@@ -409,45 +425,45 @@ terminate_graph (struct graph_node *startnode,
                  struct graph_node *finalnode,
                  struct cmd_element *element)
 {
-  struct graph_node *end = new_node(END_GN);
+  struct graph_node *end = new_node (END_GN);
   end->element = element;
-  if (node_exists(finalnode, end))
-    yyerror(element, startnode, "Duplicate command.");
+  if (node_exists (finalnode, end))
+    yyerror (element, startnode, "Duplicate command.");
   else
-    add_node(finalnode, end);
+    add_node (finalnode, end);
 }
 
 static char *
 doc_next()
 {
   char *piece = NULL;
-  if (!docstr || !(piece = strsep(&docstr, "\n")))
+  if (!docstr || !(piece = strsep (&docstr, "\n")))
     return NULL;
   return XSTRDUP(MTYPE_CMD_TOKENS, piece);
 }
 
 static struct graph_node *
-node_exists(struct graph_node *parent, struct graph_node *child)
+node_exists (struct graph_node *parent, struct graph_node *child)
 {
   struct graph_node *p_child;
-  for (unsigned int i = 0; i < vector_active(parent->children); i++)
-  {
-    p_child = vector_slot(parent->children, i);
-    if (cmp_node(child, p_child))
-      return p_child;
-  }
+  for (unsigned int i = 0; i < vector_active (parent->children); i++)
+    {
+      p_child = vector_slot (parent->children, i);
+      if (cmp_node (child, p_child))
+        return p_child;
+    }
   return NULL;
 }
 
 static struct graph_node *
-node_replace(struct graph_node *parent, struct graph_node *child)
+node_replace (struct graph_node *parent, struct graph_node *child)
 {
   struct graph_node *existing = node_exists (parent, child);
-  return existing ? existing : add_node(parent, child);
+  return existing ? existing : add_node (parent, child);
 }
 
 static int
-cmp_node(struct graph_node *first, struct graph_node *second)
+cmp_node (struct graph_node *first, struct graph_node *second)
 {
   // compare types
   if (first->type != second->type) return 0;
@@ -455,9 +471,8 @@ cmp_node(struct graph_node *first, struct graph_node *second)
   switch (first->type) {
     case WORD_GN:
     case VARIABLE_GN:
-      if (first->text && second->text) {
-        if (strcmp(first->text, second->text)) return 0;
-      }
+      if (first->text && second->text && strcmp (first->text, second->text))
+        return 0;
       else if (first->text != second->text) return 0;
       break;
     case RANGE_GN:
@@ -467,16 +482,16 @@ cmp_node(struct graph_node *first, struct graph_node *second)
     case NUMBER_GN:
       if (first->value != second->value) return 0;
       break;
-    /* selectors and options should be equal if all paths are equal,
-     * but the graph isomorphism problem is not solvable in polynomial
-     * time so we consider selectors and options inequal in all cases;
-     * ultimately this forks the graph
+    /* selectors and options should be equal if their subgraphs are equal, but
+     * the graph isomorphism problem is not known to be solvable in polynomial time
+     * so we consider selectors and options inequal in all cases; ultimately this
+     * forks the graph, but the matcher can handle this regardless
      */
     case SELECTOR_GN:
     case OPTION_GN:
       return 0;
     /* end nodes are always considered equal, since each node may only
-     * have one at a time
+     * have one END_GN child at a time
      */
     case START_GN:
     case END_GN:
index d1779a9b6e805b4b9f4adf8cdafe21c2c2d1de97..39d4d6b7cd6cc99640383ed57aaddfaed2d7f6c5 100644 (file)
@@ -1,69 +1,86 @@
+/*
+ * Testing shim and API examples for the new CLI backend.
+ *
+ * This unit defines a number of commands in the old engine that can
+ * be used to test and interact with the new engine.
+ *
+ * This shim should be removed upon integration. It is currently hooked in
+ * vtysh/vtysh.c. It has no header, vtysh.c merely includes this entire unit
+ * since it clutters up the makefiles less and this is only a temporary shim.
+ *
+ * --
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * 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.
+ */
+
 #include "command.h"
 #include "command_graph.h"
 #include "command_parse.h"
 #include "command_match.h"
-#include "linklist.h"
 
 #define GRAMMAR_STR "CLI grammar sandbox\n"
 
+void
+grammar_sandbox_init(void);
+void
+pretty_print_graph (struct graph_node *start, int level);
+
+/*
+ * Start node for testing command graph.
+ *
+ * Each cmd_node will have one of these that replaces the `cmdvector` member.
+ * The examples below show how to install a command to the graph, calculate
+ * completions for a given input line, and match input against the graph.
+ */
 struct graph_node * nodegraph;
 
+/**
+ * Reference use of parsing / command installation API
+ */
 DEFUN (grammar_test,
        grammar_test_cmd,
        "grammar parse .COMMAND",
        GRAMMAR_STR
        "command to pass to new parser\n")
 {
-  char* command = argv_concat(argv, argc, 0);
-  struct cmd_element *cmd = malloc(sizeof(struct cmd_element));
+  char *command = argv_concat(argv, argc, 0);
+
+  // initialize a pretend cmd_element
+  struct cmd_element *cmd = XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
   cmd->string = command;
   cmd->doc = NULL;
   cmd->func = NULL;
   cmd->tokens = vector_init(VECTOR_MIN_SIZE);
-  parse_command_format(nodegraph, cmd);
-  return CMD_SUCCESS;
-}
 
-DEFUN (grammar_test_doc,
-       grammar_test_doc_cmd,
-       "grammar test docstring",
-       GRAMMAR_STR
-       "Test function for docstring\n"
-       "Command end\n")
-{
-  struct cmd_element *cmd = malloc(sizeof(struct cmd_element));
-  cmd->string = "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG";
-  cmd->doc = "Test stuff\n"
-             "docstring thing\n"
-             "first example\n"
-             "second example\n"
-             "follow\n"
-             "random range\n"
-             "end thingy\n"
-             "variable\n"
-             "optional variable\n"
-             "optional set\n"
-             "optional lol\n"
-             "vararg!\n";
-  cmd->func = NULL;
-  cmd->tokens = vector_init(VECTOR_MIN_SIZE);
-  parse_command_format(nodegraph, cmd);
-  return CMD_SUCCESS;
-}
+  // parse the command and install it into the command graph
+  parse_command_format (nodegraph, cmd);
+
+  // free resources
+  free (command);
 
-DEFUN (grammar_test_show,
-       grammar_test_show_cmd,
-       "grammar tree",
-       GRAMMAR_STR
-       "print current accumulated DFA\n")
-{
-  if (!nodegraph)
-    fprintf(stderr, "!nodegraph\n");
-  else
-    walk_graph(nodegraph, 0);
   return CMD_SUCCESS;
 }
 
+
+/**
+ * Reference use of completions API
+ */
 DEFUN (grammar_test_complete,
        grammar_test_complete_cmd,
        "grammar complete .COMMAND",
@@ -71,31 +88,42 @@ DEFUN (grammar_test_complete,
        "attempt to complete input on DFA\n"
        "command to complete")
 {
-  const char* command = argv_concat(argv, argc, 0);
-  struct list *result = match_command_complete (nodegraph, command);
+  char *cmdstr = argv_concat (argv, argc, 0);
+  vector command = cmd_make_strvec (cmdstr);
+
+  struct list *completions;
+  enum matcher_rv result = match_command_complete (nodegraph, command, &completions);
 
-  if (result->count == 0) // invalid command
-    fprintf(stderr, "%% Unknown command\n");
+  // print completions or relevant error message
+  if (completions)
+    {
+      struct listnode *ln;
+      struct graph_node *gn;
+      for (ALL_LIST_ELEMENTS_RO(completions,ln,gn))
+        {
+          if (gn->type == END_GN)
+            zlog_info ("<cr> (%p)", gn->element->func);
+          else
+            zlog_info ("%-30s%s", gn->text, gn->doc);
+        }
+      list_delete (completions);
+    }
   else
-  {
-    fprintf(stderr, "%% Matched full input, possible completions:\n");
-    char* desc = malloc(30);
-    struct listnode *node;
-    struct graph_node *cnode;
-    // print possible next hops, if any
-    for (ALL_LIST_ELEMENTS_RO(result,node,cnode)) {
-      if (cnode->type == END_GN)
-        fprintf(stderr, "<cr> %p\n", cnode->element->func);
-      else
-        fprintf(stderr, "%-30s%s\n", describe_node(cnode, desc, 30), cnode->doc);
+    {
+      assert(MATCHER_ERROR(result));
+      zlog_info ("%% No match for \"%s\"", cmdstr);
     }
-    free(desc);
-  }
-  list_delete(result);
+
+  // free resources
+  cmd_free_strvec (command);
+  free (cmdstr);
 
   return CMD_SUCCESS;
 }
 
+/**
+ * Reference use of matching API
+ */
 DEFUN (grammar_test_match,
        grammar_test_match_cmd,
        "grammar match .COMMAND",
@@ -103,43 +131,105 @@ DEFUN (grammar_test_match,
        "attempt to match input on DFA\n"
        "command to match")
 {
-  const char *line = argv_concat(argv, argc, 0);
+  char *cmdstr = argv_concat(argv, argc, 0);
+  vector command = cmd_make_strvec (cmdstr);
 
   struct list *argvv = NULL;
   struct cmd_element *element = NULL;
-  enum matcher_rv result = match_command (nodegraph, line, &argvv, &element);
-
-  if (element) {
-    fprintf(stderr, "Matched: %s\n", element->string);
-    struct listnode *ln;
-    struct graph_node *gn;
-    for (ALL_LIST_ELEMENTS_RO(argvv,ln,gn))
-      fprintf(stderr, "%s -- %s\n", gn->text, gn->arg);
-  }
+  enum matcher_rv result = match_command (nodegraph, command, &argvv, &element);
+
+  // print completions or relevant error message
+  if (element)
+    {
+      zlog_info ("Matched: %s", element->string);
+      struct listnode *ln;
+      struct graph_node *gn;
+      for (ALL_LIST_ELEMENTS_RO(argvv,ln,gn))
+        if (gn->type != END_GN)
+          zlog_info ("func: %p", gn->element->func);
+        else
+          zlog_info ("%s -- %s", gn->text, gn->arg);
+
+      list_delete (argvv);
+    }
   else {
+     assert(MATCHER_ERROR(result));
      switch (result) {
        case MATCHER_NO_MATCH:
-          fprintf(stderr, "%% Unknown command\n");
+          zlog_info ("%% Unknown command");
           break;
        case MATCHER_INCOMPLETE:
-          fprintf(stderr, "%% Incomplete command\n");
+          zlog_info ("%% Incomplete command");
           break;
        case MATCHER_AMBIGUOUS:
-          fprintf(stderr, "%% Ambiguous command\n");
+          zlog_info ("%% Ambiguous command");
           break;
        default:
-          fprintf(stderr, "%% Unknown error\n");
+          zlog_info ("%% Unknown error");
           break;
      }
   }
 
+  // free resources
+  cmd_free_strvec(command);
+  free(cmdstr);
+
   return CMD_SUCCESS;
 }
 
+/**
+ * Testing shim to test docstrings
+ */
+DEFUN (grammar_test_doc,
+       grammar_test_doc_cmd,
+       "grammar test docstring",
+       GRAMMAR_STR
+       "Test function for docstring\n"
+       "Command end\n")
+{
+  // create cmd_element with docstring
+  struct cmd_element *cmd = XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
+  cmd->string = "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG";
+  cmd->doc = "Test stuff\n"
+             "docstring thing\n"
+             "first example\n"
+             "second example\n"
+             "follow\n"
+             "random range\n"
+             "end thingy\n"
+             "variable\n"
+             "optional variable\n"
+             "optional set\n"
+             "optional lol\n"
+             "vararg!\n";
+  cmd->func = NULL;
+  cmd->tokens = vector_init (VECTOR_MIN_SIZE);
+
+  // parse element
+  parse_command_format (nodegraph, cmd);
+
+  return CMD_SUCCESS;
+}
+
+/**
+ * Debugging command to print command graph
+ */
+DEFUN (grammar_test_show,
+       grammar_test_show_cmd,
+       "grammar show graph",
+       GRAMMAR_STR
+       "print current accumulated DFA\n")
+{
+  if (!nodegraph)
+    zlog_info("nodegraph uninitialized");
+  else
+    pretty_print_graph (nodegraph, 0);
+  return CMD_SUCCESS;
+}
 
-void grammar_sandbox_init(void);
+/* this is called in vtysh.c to set up the testing shim */
 void grammar_sandbox_init() {
-  fprintf(stderr, "reinitializing graph\n");
+  zlog_info ("Initializing grammar testing shim");
   nodegraph = new_node(START_GN);
   install_element (ENABLE_NODE, &grammar_test_cmd);
   install_element (ENABLE_NODE, &grammar_test_show_cmd);
@@ -147,3 +237,30 @@ void grammar_sandbox_init() {
   install_element (ENABLE_NODE, &grammar_test_complete_cmd);
   install_element (ENABLE_NODE, &grammar_test_doc_cmd);
 }
+
+/* recursive pretty-print for command graph */
+void
+pretty_print_graph (struct graph_node *start, int level)
+{
+  // print this node
+  fprintf (stdout, "%s[%d] ", start->text, vector_active (start->children));
+
+  if (vector_active (start->children))
+    {
+      if (vector_active (start->children) == 1)
+        pretty_print_graph (vector_slot (start->children, 0), level);
+      else
+        {
+          fprintf(stdout, "\n");
+          for (unsigned int i = 0; i < vector_active (start->children); i++)
+            {
+              struct graph_node *r = vector_slot (start->children, i);
+              for (int j = 0; j < level+1; j++)
+                fprintf (stdout, "    ");
+              pretty_print_graph (r, level+1);
+            }
+        }
+    }
+  else
+    fprintf(stdout, "\n");
+}