]> git.puffer.fish Git - matthieu/frr.git/commitdiff
Merge remote-tracking branch 'origin/cmaster-next' into vtysh-grammar
authorDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 21 Sep 2016 01:17:34 +0000 (21:17 -0400)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 21 Sep 2016 01:17:34 +0000 (21:17 -0400)
1  2 
configure.ac
lib/.gitignore
lib/Makefile.am
lib/command.c
lib/command.h
lib/command_match.c
lib/command_parse.y
lib/graph.c
lib/vty.c
vtysh/vtysh.c

diff --cc configure.ac
Simple merge
diff --cc lib/.gitignore
index 8174bda7d87d977e51a8111d6980d26538f76f90,fe75137bca481e3bab2c24e57f93a1071332cd87..e45fec17862372e54cc70d0d8c929a9a430075ee
@@@ -14,9 -14,5 +14,8 @@@ gitversion.h.tm
  .arch-ids
  *~
  *.loT
- memtypes.h
  route_types.h
 +command_lex.c
 +command_parse.c
 +command_parse.h
  refix
diff --cc lib/Makefile.am
index 3dfd09e2e60b7f37fe840bf1225223b074461318,dbb80076c57ff1a21a5ed82079cc3c6655e74b00..acaf7e674415a2a9ed3bb89ae8c1ad3a7838b5a7
@@@ -10,16 -9,14 +10,16 @@@ libzebra_la_LDFLAGS = -version-info 0:0
  
  libzebra_la_SOURCES = \
        network.c pid_output.c getopt.c getopt1.c daemon.c \
 -      checksum.c vector.c linklist.c vty.c command.c \
 +      checksum.c vector.c linklist.c vty.c \
 +      graph.c command_parse.y command_lex.l command_match.c \
 +      command.c \
-       sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
+       sockunion.c prefix.c thread.c if.c buffer.c table.c hash.c \
        filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
        zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \
-       sigevent.c pqueue.c jhash.c memtypes.c workqueue.c nexthop.c json.c \
-       ptm_lib.c csv.c bfd.c vrf.c systemd.c ns.c
+       sigevent.c pqueue.c jhash.c workqueue.c nexthop.c json.c \
+       ptm_lib.c csv.c bfd.c vrf.c systemd.c ns.c memory.c memory_vty.c
  
- BUILT_SOURCES = memtypes.h route_types.h gitversion.h command_parse.h
 -BUILT_SOURCES = route_types.h gitversion.h
++BUILT_SOURCES = route_types.h gitversion.h command_parse.h
  
  libzebra_la_DEPENDENCIES = @LIB_REGEX@
  
diff --cc lib/command.c
index b4ef30a3009ab1ef7ba177d37c7070a4ee01908a,cfdb91a5b9ae34039cfb7183f63a0baad0ea05aa..b6f97c317ec876f0339f4d0b636a64c4ef55eaee
@@@ -34,10 -33,11 +34,13 @@@ Boston, MA 02111-1307, USA.  *
  #include "command.h"
  #include "workqueue.h"
  #include "vrf.h"
 +#include "command_match.h"
 +#include "command_parse.h"
  
+ DEFINE_MTYPE(       LIB, HOST,       "Host config")
+ DEFINE_MTYPE(       LIB, STRVEC,     "String vector")
+ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
  /* Command vector which includes some level of command lists. Normally
     each daemon maintains each own cmdvec. */
  vector cmdvec = NULL;
diff --cc lib/command.h
index 5e1b48c68fd5ba0804ff9afed3f45063afd73c9d,9ee4f2db3d83c6fd064af2fef960c3af323d2071..ddb61c686f8b2ebfb9bef49b7b4c6804e2776127
  #include "vector.h"
  #include "vty.h"
  #include "lib/route_types.h"
 +#include "graph.h"
+ #include "memory.h"
+ DECLARE_MTYPE(HOST)
+ /* for test-commands.c */
+ DECLARE_MTYPE(STRVEC)
  
  /* Host configuration variable */
  struct host
index d7c2222ce6a0c572aa0e2fd87f9a907aff6da8a9,0000000000000000000000000000000000000000..dd68ca477e41eb589847ceb79931f0b6141a7023
mode 100644,000000..100644
--- /dev/null
@@@ -1,827 -1,0 +1,830 @@@
 +/*
 + * 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 "memory.h"
 +
++DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
++
 +/* matcher helper prototypes */
 +static int
 +add_nexthops (struct list *, struct graph_node *);
 +
 +static struct list *
 +command_match_r (struct graph_node *, vector, unsigned int);
 +
 +static int
 +score_precedence (enum cmd_token_type);
 +
 +static enum match_type
 +min_match_level (enum cmd_token_type);
 +
 +static void
 +del_arglist (struct list *);
 +
 +static struct cmd_token *
 +disambiguate_tokens (struct cmd_token *, struct cmd_token *, char *);
 +
 +static struct list *
 +disambiguate (struct list *, struct list *, vector, unsigned int);
 +
 +int
 +compare_completions (const void *, const void *);
 +
 +/* token matcher prototypes */
 +static enum match_type
 +match_token (struct cmd_token *, char *);
 +
 +static enum match_type
 +match_ipv4 (const char *);
 +
 +static enum match_type
 +match_ipv4_prefix (const char *);
 +
 +static enum match_type
 +match_ipv6 (const char *);
 +
 +static enum match_type
 +match_ipv6_prefix (const char *);
 +
 +static enum match_type
 +match_range (struct cmd_token *, const char *);
 +
 +static enum match_type
 +match_word (struct cmd_token *, const char *);
 +
 +static enum match_type
 +match_number (struct cmd_token *, const char *);
 +
 +static enum match_type
 +match_variable (struct cmd_token *, const char *);
 +
 +/* matching functions */
 +static enum matcher_rv matcher_rv;
 +
 +enum matcher_rv
 +command_match (struct graph *cmdgraph,
 +               vector vline,
 +               struct list **argv,
 +               struct cmd_element **el)
 +{
 +  matcher_rv = MATCHER_NO_MATCH;
 +
 +  // prepend a dummy token to match that pesky start node
 +  vector vvline = vector_init (vline->alloced + 1);
 +  vector_set_index (vvline, 0, (void *) XSTRDUP (MTYPE_TMP, "dummy"));
 +  memcpy (vvline->index + 1, vline->index, sizeof (void *) * vline->alloced);
 +  vvline->active = vline->active + 1;
 +
 +  struct graph_node *start = vector_slot (cmdgraph->nodes, 0);
 +  if ((*argv = command_match_r (start, vvline, 0))) // successful match
 +    {
 +      struct listnode *head = listhead (*argv);
 +      struct listnode *tail = listtail (*argv);
 +
 +      // delete dummy start node
 +      del_cmd_token ((struct cmd_token *) head->data);
 +      list_delete_node (*argv, head);
 +
 +      // get cmd_element out of list tail
 +      *el = listgetdata (tail);
 +      list_delete_node (*argv, tail);
 +
 +      // now argv is an ordered list of cmd_token matching the user
 +      // input, with each cmd_token->arg holding the corresponding input
 +      assert (*el);
 +    }
 +
 +  // free the leader token we alloc'd
 +  XFREE (MTYPE_TMP, vector_slot (vvline, 0));
 +  // free vector
 +  vector_free (vvline);
 +
 +  return matcher_rv;
 +}
 +
 +/**
 + * Builds an argument list given a DFA and a matching input line.
 + *
 + * First the function determines if the node it is passed matches the first
 + * token of input. If it does not, it returns NULL (MATCHER_NO_MATCH). If it
 + * does match, then it saves the input token as the head of an argument list.
 + *
 + * The next step is to see if there is further input in the input line. If
 + * there is not, the current node's children are searched to see if any of them
 + * are leaves (type END_TKN). If this is the case, then the bottom of the
 + * recursion stack has been reached, the leaf is pushed onto the argument list,
 + * the current node is pushed, and the resulting argument list is
 + * returned (MATCHER_OK). If it is not the case, NULL is returned, indicating
 + * that there is no match for the input along this path (MATCHER_INCOMPLETE).
 + *
 + * If there is further input, then the function recurses on each of the current
 + * node's children, passing them the input line minus the token that was just
 + * matched. For each child, the return value of the recursive call is
 + * inspected. If it is null, then there is no match for the input along the
 + * subgraph headed by that child. If it is not null, then there is at least one
 + * input match in that subgraph (more on this in a moment).
 + *
 + * If a recursive call on a child returns a non-null value, then it has matched
 + * the input given it on the subgraph that starts with that child. However, due
 + * to the flexibility of the grammar, it is sometimes the case that two or more
 + * child graphs match the same input (two or more of the recursive calls have
 + * non-NULL return values). This is not a valid state, since only one true
 + * match is possible. In order to resolve this conflict, the function keeps a
 + * reference to the child node that most specifically matches the input. This
 + * is done by assigning each node type a precedence. If a child is found to
 + * match the remaining input, then the precedence values of the current
 + * best-matching child and this new match are compared. The node with higher
 + * precedence is kept, and the other match is discarded. Due to the recursive
 + * nature of this function, it is only necessary to compare the precedence of
 + * immediate children, since all subsequent children will already have been
 + * disambiguated in this way.
 + *
 + * In the event that two children are found to match with the same precedence,
 + * then the input is ambiguous for the passed cmd_element and NULL is returned.
 + *
 + * The ultimate return value is an ordered linked list of nodes that comprise
 + * the best match for the command, each with their `arg` fields pointing to the
 + * matching token string.
 + *
 + * @param[in] start the start node.
 + * @param[in] vline the vectorized input line.
 + * @param[in] n the index of the first input token.
 + */
 +static struct list *
 +command_match_r (struct graph_node *start, vector vline, unsigned int n)
 +{
 +  assert (n < vector_active (vline));
 +
 +  // get the minimum match level that can count as a full match
 +  struct cmd_token *token = start->data;
 +  enum match_type minmatch = min_match_level (token->type);
 +
 +  // get the current operating input token
 +  char *input_token = vector_slot (vline, n);
 +
 +  // if we don't match this node, die
 +  if (match_token (token, input_token) < minmatch)
 +    return NULL;
 +
 +  // pointers for iterating linklist
 +  struct listnode *ln;
 +  struct graph_node *gn;
 +
 +  // get all possible nexthops
 +  struct list *next = list_new();
 +  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_TKN
 +      if (n+1 == vector_active (vline))
 +        {
 +          struct cmd_token *tok = gn->data;
 +          if (tok->type == END_TKN)
 +            {
 +              currbest = list_new();
 +              // node should have one child node with the element
 +              struct graph_node *leaf = vector_slot (gn->to, 0);
 +              // last node in the list will hold the cmd_element;
 +              // this is important because list_delete() expects
 +              // that all nodes have the same data type, so when
 +              // deleting this list the last node must be
 +              // manually deleted
 +              struct cmd_element *el = leaf->data;
 +              listnode_add (currbest, copy_cmd_element (el));
 +              currbest->del = (void (*)(void *)) &del_cmd_token;
 +              break;
 +            }
 +          else continue;
 +        }
 +
 +      // else recurse on candidate child node
 +      struct list *result = command_match_r (gn, vline, n+1);
 +
 +      // save the best match
 +      if (result && currbest)
 +        {
 +          // pick the best of two matches
 +          struct list *newbest = disambiguate (currbest, result, vline, n+1);
 +          // set ambiguity flag
 +          ambiguous = !newbest || (ambiguous && newbest == currbest);
 +          // delete the unnecessary result
 +          struct list *todelete = ((newbest && newbest == result) ? currbest : result);
 +          del_arglist (todelete);
 +
 +          currbest = newbest ? newbest : currbest;
 +        }
 +      else if (result)
 +        currbest = result;
 +    }
 +
 +  if (currbest)
 +    {
 +      if (ambiguous)
 +        {
 +          del_arglist (currbest);
 +          currbest = NULL;
 +          matcher_rv = MATCHER_AMBIGUOUS;
 +        }
 +      else
 +        {
 +          // copy token, set arg and prepend to currbest
 +          struct cmd_token *token = start->data;
 +          struct cmd_token *copy = copy_cmd_token (token);
 +          copy->arg = XSTRDUP (MTYPE_CMD_TOKENS, input_token);
 +          list_add_node_prev (currbest, currbest->head, copy);
 +          matcher_rv = MATCHER_OK;
 +        }
 +    }
 +  else if (n+1 == vector_active (vline) && matcher_rv == MATCHER_NO_MATCH)
 +    matcher_rv = MATCHER_INCOMPLETE;
 +
 +  // cleanup
 +  list_delete (next);
 +
 +  return currbest;
 +}
 +
 +enum matcher_rv
 +command_complete (struct graph *graph,
 +                  vector vline,
 +                  struct list **completions)
 +{
 +  // pointer to next input token to match
 +  char *input_token;
 +
 +  struct list *current = list_new(), // current nodes to match input token against
 +                 *next = list_new(); // possible next hops after current input token
 +
 +  // pointers used for iterating lists
 +  struct graph_node *gn;
 +  struct listnode *node;
 +
 +  // add all children of start node to list
 +  struct graph_node *start = vector_slot (graph->nodes, 0);
 +  add_nexthops (next, start);
 +
 +  unsigned int idx;
 +  for (idx = 0; idx < vector_active (vline) && next->count > 0; idx++)
 +    {
 +      list_delete (current);
 +      current = next;
 +      next = list_new();
 +
 +      input_token = vector_slot (vline, idx);
 +
 +      for (ALL_LIST_ELEMENTS_RO (current,node,gn))
 +        {
 +          struct cmd_token *token = gn->data;
 +          switch (match_token (token, input_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
 +   * -----------------------------------------------------------------
 +   * token    = last input token processed
 +   * idx      = index in `command` of last token processed
 +   * current  = set of all transitions from the previous input token
 +   * next     = set of all nodes reachable from all nodes in `matched`
 +   */
 +
 +  matcher_rv =
 +     idx == vector_active(vline) && next->count ?
 +     MATCHER_OK :
 +     MATCHER_NO_MATCH;
 +
 +  // extract cmd_token into list
 +  *completions = list_new ();
 +  for (ALL_LIST_ELEMENTS_RO (next,node,gn))
 +    listnode_add (*completions, gn->data);
 +
 +  list_delete (current);
 +  list_delete (next);
 +
 +  return matcher_rv;
 +}
 +
 +/**
 + * Adds all children that are reachable by one parser hop to the given list.
 + * NUL_TKN, SELECTOR_TKN, and OPTION_TKN nodes are treated as transparent.
 + *
 + * @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 *list, struct graph_node *node)
 +{
 +  int added = 0;
 +  struct graph_node *child;
 +  for (unsigned int i = 0; i < vector_active (node->to); i++)
 +    {
 +      child = vector_slot (node->to, i);
 +      struct cmd_token *token = child->data;
 +      switch (token->type)
 +        {
 +          case OPTION_TKN:
 +          case SELECTOR_TKN:
 +          case NUL_TKN:
 +            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 cmd_token_type type)
 +{
 +  switch (type)
 +    {
 +      // anything matches a start node, for the sake of recursion
 +      case START_TKN:
 +        return no_match;
 +      // allowing words to partly match enables command abbreviation
 +      case WORD_TKN:
 +        return partly_match;
 +      default:
 +        return exact_match;
 +    }
 +}
 +
 +/**
 + * Assigns precedence scores to node types.
 + *
 + * @param[in] type node type to score
 + * @return precedence score
 + */
 +static int
 +score_precedence (enum cmd_token_type type)
 +{
 +  switch (type)
 +    {
 +      // some of these are mutually exclusive, so they share
 +      // the same precedence value
 +      case IPV4_TKN:
 +      case IPV4_PREFIX_TKN:
 +      case IPV6_TKN:
 +      case IPV6_PREFIX_TKN:
 +      case NUMBER_TKN:
 +        return 1;
 +      case RANGE_TKN:
 +        return 2;
 +      case WORD_TKN:
 +        return 3;
 +      case VARIABLE_TKN:
 +        return 4;
 +      default:
 +        return 10;
 +    }
 +}
 +
 +/**
 + * 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 cmd_token *
 +disambiguate_tokens (struct cmd_token *first,
 +                     struct cmd_token *second,
 +                     char *input_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 they're the same, return the more exact match
 +  enum match_type fmtype = match_token (first, input_token);
 +  enum match_type smtype = match_token (second, input_token);
 +  if (fmtype != smtype)
 +    return fmtype > smtype ? first : second;
 +
 +  return NULL;
 +}
 +
 +/**
 + * Picks the better of two possible matches for an input line.
 + *
 + * @param[in] first candidate list of cmd_token matching vline
 + * @param[in] second candidate list of cmd_token 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 (struct list *first,
 +              struct list *second,
 +              vector vline,
 +              unsigned int n)
 +{
 +  // doesn't make sense for these to be inequal length
 +  assert (first->count == second->count);
 +  assert (first->count == vector_active (vline) - n+1);
 +
 +  struct listnode *fnode = listhead (first),
 +                  *snode = listhead (second);
 +  struct cmd_token *ftok = listgetdata (fnode),
 +                     *stok = listgetdata (snode),
 +                     *best = NULL;
 +
 +  // compare each token, if one matches better use that one
 +  for (unsigned int i = n; i < vector_active (vline); i++)
 +    {
 +      char *token = vector_slot(vline, i);
 +      if ((best = disambiguate_tokens (ftok, stok, token)))
 +        return best == ftok ? first : second;
 +      fnode = listnextnode (fnode);
 +      snode = listnextnode (snode);
 +      ftok = listgetdata (fnode);
 +      stok = listgetdata (snode);
 +    }
 +
 +  return NULL;
 +}
 +
 +/*
 + * Deletion function for arglist.
 + *
 + * Since list->del for arglists expects all listnode->data to hold cmd_token,
 + * but arglists have cmd_element as the data for the tail, this function
 + * manually deletes the tail before deleting the rest of the list as usual.
 + *
 + * @param list the arglist to delete
 + */
 +static void
 +del_arglist (struct list *list)
 +{
 +  // manually delete last node
 +  struct listnode *tail = listtail (list);
 +  del_cmd_element (tail->data);
 +  tail->data = NULL;
 +  list_delete_node (list, tail);
 +
 +  // delete the rest of the list as usual
 +  list_delete (list);
 +}
 +
 +/*---------- token level matching functions ----------*/
 +
 +static enum match_type
 +match_token (struct cmd_token *token, char *input_token)
 +{
 +  switch (token->type) {
 +    case WORD_TKN:
 +      return match_word (token, input_token);
 +    case IPV4_TKN:
 +      return match_ipv4 (input_token);
 +    case IPV4_PREFIX_TKN:
 +      return match_ipv4_prefix (input_token);
 +    case IPV6_TKN:
 +      return match_ipv6 (input_token);
 +    case IPV6_PREFIX_TKN:
 +      return match_ipv6_prefix (input_token);
 +    case RANGE_TKN:
 +      return match_range (token, input_token);
 +    case NUMBER_TKN:
 +      return match_number (token, input_token);
 +    case VARIABLE_TKN:
 +      return match_variable (token, input_token);
 +    case END_TKN:
 +    default:
 +      return no_match;
 +  }
 +}
 +
 +#define IPV4_ADDR_STR   "0123456789."
 +#define IPV4_PREFIX_STR "0123456789./"
 +
 +static enum match_type
 +match_ipv4 (const char *str)
 +{
 +  const char *sp;
 +  int dots = 0, nums = 0;
 +  char buf[4];
 +
 +  if (str == NULL)
 +    return partly_match;
 +
 +  for (;;)
 +    {
 +      memset (buf, 0, sizeof (buf));
 +      sp = str;
 +      while (*str != '\0')
 +        {
 +          if (*str == '.')
 +            {
 +              if (dots >= 3)
 +                return no_match;
 +
 +              if (*(str + 1) == '.')
 +                return no_match;
 +
 +              if (*(str + 1) == '\0')
 +                return partly_match;
 +
 +              dots++;
 +              break;
 +            }
 +          if (!isdigit ((int) *str))
 +            return no_match;
 +
 +          str++;
 +        }
 +
 +      if (str - sp > 3)
 +        return no_match;
 +
 +      strncpy (buf, sp, str - sp);
 +      if (atoi (buf) > 255)
 +        return no_match;
 +
 +      nums++;
 +
 +      if (*str == '\0')
 +        break;
 +
 +      str++;
 +    }
 +
 +  if (nums < 4)
 +    return partly_match;
 +
 +  return exact_match;
 +}
 +
 +static enum match_type
 +match_ipv4_prefix (const char *str)
 +{
 +  const char *sp;
 +  int dots = 0;
 +  char buf[4];
 +
 +  if (str == NULL)
 +    return partly_match;
 +
 +  for (;;)
 +    {
 +      memset (buf, 0, sizeof (buf));
 +      sp = str;
 +      while (*str != '\0' && *str != '/')
 +        {
 +          if (*str == '.')
 +            {
 +              if (dots == 3)
 +                return no_match;
 +
 +              if (*(str + 1) == '.' || *(str + 1) == '/')
 +                return no_match;
 +
 +              if (*(str + 1) == '\0')
 +                return partly_match;
 +
 +              dots++;
 +              break;
 +            }
 +
 +          if (!isdigit ((int) *str))
 +            return no_match;
 +
 +          str++;
 +        }
 +
 +      if (str - sp > 3)
 +        return no_match;
 +
 +      strncpy (buf, sp, str - sp);
 +      if (atoi (buf) > 255)
 +        return no_match;
 +
 +      if (dots == 3)
 +        {
 +          if (*str == '/')
 +            {
 +              if (*(str + 1) == '\0')
 +                return partly_match;
 +
 +              str++;
 +              break;
 +            }
 +          else if (*str == '\0')
 +            return partly_match;
 +        }
 +
 +      if (*str == '\0')
 +        return partly_match;
 +
 +      str++;
 +    }
 +
 +  sp = str;
 +  while (*str != '\0')
 +    {
 +      if (!isdigit ((int) *str))
 +        return no_match;
 +
 +      str++;
 +    }
 +
 +  if (atoi (sp) > 32)
 +    return no_match;
 +
 +  return exact_match;
 +}
 +
 +#ifdef HAVE_IPV6
 +#define IPV6_ADDR_STR   "0123456789abcdefABCDEF:."
 +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./"
 +
 +static enum match_type
 +match_ipv6 (const char *str)
 +{
 +  struct sockaddr_in6 sin6_dummy;
 +  int ret;
 +
 +  if (str == NULL)
 +    return partly_match;
 +
 +  if (strspn (str, IPV6_ADDR_STR) != strlen (str))
 +    return no_match;
 +
 +  ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
 +
 +  if (ret == 1)
 +    return exact_match;
 +
 +  return no_match;
 +}
 +
 +static enum match_type
 +match_ipv6_prefix (const char *str)
 +{
 +  struct sockaddr_in6 sin6_dummy;
 +  const char *delim = "/\0";
 +  char *tofree, *dupe, *prefix, *mask, *endptr;
 +  int nmask = -1;
 +
 +  if (str == NULL)
 +    return partly_match;
 +
 +  if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
 +    return no_match;
 +
 +  /* tokenize to prefix + mask */
 +  tofree = dupe = XSTRDUP (MTYPE_TMP, str);
 +  prefix = strsep (&dupe, delim);
 +  mask   = dupe;
 +
 +  /* validate prefix */
 +  if (inet_pton (AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1)
 +  {
 +    XFREE (MTYPE_TMP, tofree);
 +    return no_match;
 +  }
 +
 +  /* validate mask */
 +  if (!mask)
 +  {
 +    XFREE (MTYPE_TMP, tofree);
 +    return partly_match;
 +  }
 +
 +  nmask = strtoimax (mask, &endptr, 10);
 +  if (*endptr != '\0' || nmask < 0 || nmask > 128)
 +  {
 +    XFREE (MTYPE_TMP, tofree);
 +    return no_match;
 +  }
 +
 +  XFREE (MTYPE_TMP, tofree);
 +  return exact_match;
 +}
 +#endif
 +
 +static enum match_type
 +match_range (struct cmd_token *token, const char *str)
 +{
 +  assert (token->type == RANGE_TKN);
 +
 +  char *endptr = NULL;
 +  long long val;
 +
 +  if (str == NULL)
 +    return 1;
 +
 +  val = strtoll (str, &endptr, 10);
 +  if (*endptr != '\0')
 +    return 0;
 +
 +  if (val < token->min || val > token->max)
 +    return no_match;
 +  else
 +    return exact_match;
 +}
 +
 +static enum match_type
 +match_word (struct cmd_token *token, const char *word)
 +{
 +  assert (token->type == WORD_TKN);
 +
 +  // 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 (token->text))
 +    return !strncmp (token->text, word, strlen (word)) ?
 +       partly_match :
 +       no_match;
 +
 +  // if they are the same length and exactly equal, exact match
 +  else if (strlen (word) == strlen (token->text))
 +    return !strncmp (token->text, word, strlen (word)) ? exact_match : no_match;
 +
 +  return no_match;
 +}
 +
 +static enum match_type
 +match_number (struct cmd_token *token, const char *word)
 +{
 +  assert (token->type == NUMBER_TKN);
 +
 +  if (!strcmp ("\0", word)) return no_match;
 +  char *endptr;
 +  long long num = strtoll (word, &endptr, 10);
 +  if (endptr != '\0') return no_match;
 +  return num == token->value ? exact_match : no_match;
 +}
 +
 +#define VARIABLE_ALPHABET \
 +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890:"
 +
 +static enum match_type
 +match_variable (struct cmd_token *token, const char *word)
 +{
 +  assert (token->type == VARIABLE_TKN);
 +
 +  return strlen (word) == strspn(word, VARIABLE_ALPHABET) ?
 +     exact_match : no_match;
 +}
index 089b506b0322e12f066d64616849c562e32cc689,0000000000000000000000000000000000000000..6a5d75aabf83a7918a4918368ff61d3c25e3ad83
mode 100644,000000..100644
--- /dev/null
@@@ -1,547 -1,0 +1,548 @@@
-   #include "memory.h"
 +/*
 + * Command format string parser 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.
 + */
 +
 +%{
 +// compile with debugging facilities
 +#define YYDEBUG 1
 +%}
 +
 +/* names for generated header and parser files */
 +%defines "command_parse.h"
 +%output  "command_parse.c"
 +
 +/* required external units */
 +%code requires {
++  #include "stdlib.h"
++  #include "string.h"
 +  #include "command.h"
 +  #include "graph.h"
-   docstr = element->doc ? XSTRDUP(MTYPE_TMP, element->doc) : NULL;
 +
 +  extern int
 +  yylex (void);
 +
 +  extern void
 +  set_lexer_string (const char *);
 +
 +  extern void
 +  cleanup_lexer (void);
 +}
 +
 +/* functionality this unit exports */
 +%code provides {
 +  void
 +  command_parse_format (struct graph *, struct cmd_element *);
 +
 +  /* maximum length of a number, lexer will not match anything longer */
 +  #define DECIMAL_STRLEN_MAX 20
 +}
 +
 +/* valid semantic types for tokens and rules */
 +%union {
 +  long long number;
 +  char *string;
 +  struct graph_node *node;
 +  struct subgraph *subgraph;
 +}
 +
 +/* union types for lexed tokens */
 +%token <string> WORD
 +%token <string> IPV4
 +%token <string> IPV4_PREFIX
 +%token <string> IPV6
 +%token <string> IPV6_PREFIX
 +%token <string> VARIABLE
 +%token <string> RANGE
 +
 +/* union types for parsed rules */
 +%type <node> start
 +%type <node> sentence_root
 +%type <node> literal_token
 +%type <node> placeholder_token
 +%type <node> simple_token
 +%type <subgraph> option
 +%type <subgraph> option_token
 +%type <subgraph> option_token_seq
 +%type <subgraph> selector
 +%type <subgraph> selector_token
 +%type <subgraph> selector_token_seq
 +%type <subgraph> selector_seq_seq
 +%type <subgraph> compound_token
 +
 +%code {
 +  /* bison declarations */
 +  void
 +  yyerror (struct graph *, struct cmd_element *el, char const *msg);
 +
 +  /* subgraph semantic value */
 +  struct subgraph {
 +    struct graph_node *start, *end;
 +  };
 +
 +  struct graph_node *currnode, *startnode;
 +
 +  /* pointers to copy of command docstring */
 +  char *docstr_start, *docstr;
 +
 +  /* helper functions for parser */
 +  static char *
 +  doc_next (void);
 +
 +  static struct graph_node *
 +  node_adjacent (struct graph_node *, struct graph_node *);
 +
 +  static struct graph_node *
 +  add_edge_dedup (struct graph_node *, struct graph_node *);
 +
 +  static int
 +  cmp_token (struct cmd_token *, struct cmd_token *);
 +
 +  static struct graph_node *
 +  new_token_node (struct graph *,
 +                  enum cmd_token_type type,
 +                  char *text, char *doc);
 +
 +  static void
 +  terminate_graph (struct graph *,
 +                   struct graph_node *,
 +                   struct cmd_element *);
 +
 +  static void
 +  cleanup (void);
 +}
 +
 +/* yyparse parameters */
 +%parse-param { struct graph *graph }
 +%parse-param { struct cmd_element *element }
 +
 +/* called automatically before yyparse */
 +%initial-action {
 +  /* clear state pointers */
 +  currnode = startnode = NULL;
 +
 +  startnode = vector_slot (graph->nodes, 0);
 +
 +  /* set string to parse */
 +  set_lexer_string (element->string);
 +
 +  /* copy docstring and keep a pointer to the copy */
-     new_token_node (graph, WORD_TKN, XSTRDUP (MTYPE_CMD_TOKENS, $1), doc_next());
++  docstr = element->doc ? strdup(element->doc) : NULL;
 +  docstr_start = docstr;
 +}
 +
 +%%
 +
 +start:
 +  sentence_root cmd_token_seq
 +{
 +  // tack on the command element
 +  terminate_graph (graph, currnode, element);
 +}
 +| sentence_root cmd_token_seq placeholder_token '.' '.' '.'
 +{
 +  if ((currnode = add_edge_dedup (currnode, $3)) != $3)
 +    graph_delete_node (graph, $3);
 +
 +  // adding a node as a child of itself accepts any number
 +  // of the same token, which is what we want for varags
 +  add_edge_dedup (currnode, currnode);
 +
 +  // tack on the command element
 +  terminate_graph (graph, currnode, element);
 +}
 +;
 +
 +sentence_root: WORD
 +{
 +  struct graph_node *root =
-   XFREE (MTYPE_TMP, $1);
++    new_token_node (graph, WORD_TKN, strdup ($1), doc_next());
 +
 +  if ((currnode = add_edge_dedup (startnode, root)) != root)
 +    graph_delete_node (graph, root);
 +
 +  free ($1);
 +  $$ = currnode;
 +}
 +;
 +
 +cmd_token_seq:
 +  %empty
 +| cmd_token_seq cmd_token
 +;
 +
 +cmd_token:
 +  simple_token
 +{
 +  if ((currnode = add_edge_dedup (currnode, $1)) != $1)
 +    graph_delete_node (graph, $1);
 +}
 +| compound_token
 +{
 +  graph_add_edge (currnode, $1->start);
 +  currnode = $1->end;
-   $$ = new_token_node (graph, WORD_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
++  free ($1);
 +}
 +;
 +
 +simple_token:
 +  literal_token
 +| placeholder_token
 +;
 +
 +compound_token:
 +  selector
 +| option
 +;
 +
 +literal_token: WORD
 +{
-   $$ = new_token_node (graph, IPV4_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
++  $$ = new_token_node (graph, WORD_TKN, strdup($1), doc_next());
 +  free ($1);
 +}
 +;
 +
 +placeholder_token:
 +  IPV4
 +{
-   $$ = new_token_node (graph, IPV4_PREFIX_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
++  $$ = new_token_node (graph, IPV4_TKN, strdup($1), doc_next());
 +  free ($1);
 +}
 +| IPV4_PREFIX
 +{
-   $$ = new_token_node (graph, IPV6_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
++  $$ = new_token_node (graph, IPV4_PREFIX_TKN, strdup($1), doc_next());
 +  free ($1);
 +}
 +| IPV6
 +{
-   $$ = new_token_node (graph, IPV6_PREFIX_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
++  $$ = new_token_node (graph, IPV6_TKN, strdup($1), doc_next());
 +  free ($1);
 +}
 +| IPV6_PREFIX
 +{
-   $$ = new_token_node (graph, VARIABLE_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
++  $$ = new_token_node (graph, IPV6_PREFIX_TKN, strdup($1), doc_next());
 +  free ($1);
 +}
 +| VARIABLE
 +{
-   $$ = new_token_node (graph, RANGE_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
++  $$ = new_token_node (graph, VARIABLE_TKN, strdup($1), doc_next());
 +  free ($1);
 +}
 +| RANGE
 +{
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  $$ = new_token_node (graph, RANGE_TKN, strdup($1), doc_next());
 +  struct cmd_token *token = $$->data;
 +
 +  // get the numbers out
 +  yylval.string++;
 +  token->min = strtoll (yylval.string, &yylval.string, 10);
 +  strsep (&yylval.string, "-");
 +  token->max = strtoll (yylval.string, &yylval.string, 10);
 +
 +  // validate range
 +  if (token->min > token->max) yyerror (graph, element, "Invalid range.");
 +
 +  free ($1);
 +}
 +
 +/* <selector|set> productions */
 +selector: '<' selector_seq_seq '>'
 +{
-   XFREE (MTYPE_TMP, $2);
++  $$ = malloc (sizeof (struct subgraph));
 +  $$->start = new_token_node (graph, SELECTOR_TKN, NULL, NULL);
 +  $$->end   = new_token_node (graph, NUL_TKN, NULL, NULL);
 +  for (unsigned int i = 0; i < vector_active ($2->start->to); i++)
 +  {
 +    struct graph_node *sn = vector_slot ($2->start->to, i),
 +                      *en = vector_slot ($2->end->from, i);
 +    graph_add_edge ($$->start, sn);
 +    graph_add_edge (en, $$->end);
 +  }
 +  graph_delete_node (graph, $2->start);
 +  graph_delete_node (graph, $2->end);
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  free ($2);
 +};
 +
 +selector_seq_seq:
 +  selector_seq_seq '|' selector_token_seq
 +{
-   XFREE (MTYPE_TMP, $3);
++  $$ = malloc (sizeof (struct subgraph));
 +  $$->start = graph_new_node (graph, NULL, NULL);
 +  $$->end   = graph_new_node (graph, NULL, NULL);
 +
 +  // link in last sequence
 +  graph_add_edge ($$->start, $3->start);
 +  graph_add_edge ($3->end, $$->end);
-   XFREE (MTYPE_TMP, $1);
-   XFREE (MTYPE_TMP, $3);
++  free ($3);
 +
 +  for (unsigned int i = 0; i < vector_active ($1->start->to); i++)
 +  {
 +    struct graph_node *sn = vector_slot ($1->start->to, i),
 +                      *en = vector_slot ($1->end->from, i);
 +    graph_add_edge ($$->start, sn);
 +    graph_add_edge (en, $$->end);
 +  }
 +  graph_delete_node (graph, $1->start);
 +  graph_delete_node (graph, $1->end);
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  free ($1);
++  free ($3);
 +}
 +| selector_token_seq '|' selector_token_seq
 +{
-   XFREE (MTYPE_TMP, $1);
-   XFREE (MTYPE_TMP, $3);
++  $$ = malloc (sizeof (struct subgraph));
 +  $$->start = graph_new_node (graph, NULL, NULL);
 +  $$->end   = graph_new_node (graph, NULL, NULL);
 +  graph_add_edge ($$->start, $1->start);
 +  graph_add_edge ($1->end, $$->end);
 +  graph_add_edge ($$->start, $3->start);
 +  graph_add_edge ($3->end, $$->end);
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  free ($1);
++  free ($3);
 +}
 +;
 +
 +selector_token_seq:
 +  simple_token
 +{
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  $$ = malloc (sizeof (struct subgraph));
 +  $$->start = $$->end = $1;
 +}
 +| selector_token_seq selector_token
 +{
-   XFREE (MTYPE_TMP, $1);
-   XFREE (MTYPE_TMP, $2);
++  $$ = malloc (sizeof (struct subgraph));
 +  graph_add_edge ($1->end, $2->start);
 +  $$->start = $1->start;
 +  $$->end   = $2->end;
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  free ($1);
++  free ($2);
 +}
 +;
 +
 +selector_token:
 +  simple_token
 +{
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  $$ = malloc (sizeof (struct subgraph));
 +  $$->start = $$->end = $1;
 +}
 +| option
 +;
 +
 +/* [option] productions */
 +option: '[' option_token_seq ']'
 +{
 +  // make a new option
-   XFREE (MTYPE_TMP, $2);
++  $$ = malloc (sizeof (struct subgraph));
 +  $$->start = new_token_node (graph, OPTION_TKN, NULL, NULL);
 +  $$->end   = new_token_node (graph, NUL_TKN, NULL, NULL);
 +  // add a path through the sequence to the end
 +  graph_add_edge ($$->start, $2->start);
 +  graph_add_edge ($2->end, $$->end);
 +  // add a path directly from the start to the end
 +  graph_add_edge ($$->start, $$->end);
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  free ($2);
 +}
 +;
 +
 +option_token_seq:
 +  option_token
 +| option_token_seq option_token
 +{
-   XFREE (MTYPE_TMP, $1);
++  $$ = malloc (sizeof (struct subgraph));
 +  graph_add_edge ($1->end, $2->start);
 +  $$->start = $1->start;
 +  $$->end   = $2->end;
-   $$ = XMALLOC (MTYPE_TMP, sizeof (struct subgraph));
++  free ($1);
 +}
 +;
 +
 +option_token:
 +  simple_token
 +{
-                     XSTRDUP (MTYPE_CMD_TOKENS, CMD_CR_TEXT),
-                     XSTRDUP (MTYPE_CMD_TOKENS, ""));
++  $$ = malloc (sizeof (struct subgraph));
 +  $$->start = $$->end = $1;
 +}
 +| compound_token
 +;
 +
 +%%
 +
 +void
 +command_parse_format (struct graph *graph, struct cmd_element *cmd)
 +{
 +  // set to 1 to enable parser traces
 +  yydebug = 0;
 +
 +  // parse command into DFA
 +  yyparse (graph, cmd);
 +
 +  // cleanup
 +  cleanup ();
 +}
 +
 +/* parser helper functions */
 +
 +void
 +yyerror (struct graph *graph, struct cmd_element *el, char const *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);
 +}
 +
 +static void
 +cleanup()
 +{
 +  /* free resources */
 +  free (docstr_start);
 +
 +  /* cleanup lexer */
 +  cleanup_lexer ();
 +
 +  /* clear state pointers */
 +  currnode = NULL;
 +  docstr_start = docstr = NULL;
 +}
 +
 +static void
 +terminate_graph (struct graph *graph, struct graph_node *finalnode, struct cmd_element *element)
 +{
 +  // end of graph should look like this
 +  // * -> finalnode -> END_TKN -> cmd_element
 +  struct graph_node *end_token_node =
 +    new_token_node (graph,
 +                    END_TKN,
-     return XSTRDUP (MTYPE_CMD_TOKENS, "");
-   return XSTRDUP (MTYPE_CMD_TOKENS, piece);
++                    strdup (CMD_CR_TEXT),
++                    strdup (""));
 +  struct graph_node *end_element_node =
 +    graph_new_node (graph, element, (void (*)(void *)) &del_cmd_element);
 +
 +  if (node_adjacent (finalnode, end_token_node))
 +    yyerror (graph, element, "Duplicate command.");
 +
 +  graph_add_edge (finalnode, end_token_node);
 +  graph_add_edge (end_token_node, end_element_node);
 +}
 +
 +static char *
 +doc_next()
 +{
 +  char *piece = NULL;
 +  if (!docstr || !(piece = strsep (&docstr, "\n")))
++    return strdup ("");
++  return strdup (piece);
 +}
 +
 +static struct graph_node *
 +new_token_node (struct graph *graph, enum cmd_token_type type, char *text, char *doc)
 +{
 +  struct cmd_token *token = new_cmd_token (type, text, doc);
 +  return graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token);
 +}
 +
 +/**
 + * Determines if there is an out edge from the first node to the second
 + */
 +static struct graph_node *
 +node_adjacent (struct graph_node *first, struct graph_node *second)
 +{
 +  struct graph_node *adj;
 +  for (unsigned int i = 0; i < vector_active (first->to); i++)
 +    {
 +      adj = vector_slot (first->to, i);
 +      struct cmd_token *ftok = adj->data,
 +                         *stok = second->data;
 +      if (cmp_token (ftok, stok))
 +        return adj;
 +    }
 +  return NULL;
 +}
 +
 +/**
 + * Creates an edge betwen two nodes, unless there is already an edge to an
 + * equivalent node.
 + *
 + * The first node's out edges are searched to see if any of them point to a
 + * node that is equivalent to the second node. If such a node exists, it is
 + * returned. Otherwise an edge is created from the first node to the second.
 + *
 + * @param from start node for edge
 + * @param to end node for edge
 + * @return the node which the new edge points to
 + */
 +static struct graph_node *
 +add_edge_dedup (struct graph_node *from, struct graph_node *to)
 +{
 +  struct graph_node *existing = node_adjacent (from, to);
 +  return existing ? existing : graph_add_edge (from, to);
 +}
 +
 +/**
 + * Compares two cmd_token's for equality,
 + *
 + * As such, this function is the working definition of token equality
 + * for parsing purposes and determines overall graph structure.
 + */
 +static int
 +cmp_token (struct cmd_token *first, struct cmd_token *second)
 +{
 +  // compare types
 +  if (first->type != second->type) return 0;
 +
 +  switch (first->type) {
 +    case WORD_TKN:
 +    case VARIABLE_TKN:
 +      if (first->text && second->text)
 +        {
 +          if (strcmp (first->text, second->text))
 +            return 0;
 +        }
 +      else if (first->text != second->text) return 0;
 +      break;
 +    case RANGE_TKN:
 +      if (first->min != second->min || first->max != second->max)
 +        return 0;
 +      break;
 +    case NUMBER_TKN:
 +      if (first->value != second->value) return 0;
 +      break;
 +
 +    /* 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_TKN:
 +    case OPTION_TKN:
 +      return 0;
 +
 +    /* end nodes are always considered equal, since each node may only
 +     * have one END_TKN child at a time
 +     */
 +    case START_TKN:
 +    case END_TKN:
 +    case NUL_TKN:
 +    default:
 +      break;
 +  }
 +  return 1;
 +}
diff --cc lib/graph.c
index 7c59fe95b524e53b29da15a23164185e5f5917a6,0000000000000000000000000000000000000000..891ecc33c0da8907a1245334cdf2432b8ab38026
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,138 @@@
 +/*
 + * Graph data structure.
 + *
 + * --
 + * 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 "graph.h"
 +#include "memory.h"
 +
++DEFINE_MTYPE_STATIC(LIB, GRAPH,      "Graph")
++DEFINE_MTYPE_STATIC(LIB, GRAPH_NODE, "Graph Node")
 +struct graph *
 +graph_new ()
 +{
 +  struct graph *graph = XCALLOC (MTYPE_GRAPH, sizeof(struct graph));
 +  graph->nodes = vector_init (VECTOR_MIN_SIZE);
 +
 +  return graph;
 +}
 +
 +struct graph_node *
 +graph_new_node (struct graph *graph, void *data, void (*del) (void*))
 +{
 +  struct graph_node *node =
 +     XCALLOC(MTYPE_GRAPH_NODE, sizeof(struct graph_node));
 +
 +  node->from = vector_init (VECTOR_MIN_SIZE);
 +  node->to   = vector_init (VECTOR_MIN_SIZE);
 +  node->data = data;
 +  node->del  = del;
 +
 +  vector_set (graph->nodes, node);
 +
 +  return node;
 +}
 +
 +static void
 +vector_remove (vector v, unsigned int ix)
 +{
 +  vector_unset (v, ix);
 +  if (ix == vector_active (v)) return;
 +  for (; ix < vector_active (v) - 1; ix++)
 +    v->index[ix] = v->index[ix+1];
 +  v->active--;
 +}
 +
 +void
 +graph_delete_node (struct graph *graph, struct graph_node *node)
 +{
 +  if (!node) return;
 +
 +  // an adjacent node
 +  struct graph_node *adj;
 +
 +  // remove all edges from other nodes to us
 +  vector edges = vector_copy (node->from);
 +  for (unsigned int i = 0; i < vector_active (edges); i++)
 +    {
 +      adj = vector_slot (edges, i);
 +      graph_remove_edge (adj, node);
 +    }
 +  vector_free (edges);
 +
 +  // remove all edges from us to other nodes
 +  edges = vector_copy (node->to);
 +  for (unsigned int i = 0; i < vector_active (edges); i++)
 +    {
 +      adj = vector_slot (edges, i);
 +      graph_remove_edge (node, adj);
 +    }
 +  vector_free (edges);
 +
 +  // if there is a deletion callback, call it
 +  if (node->del && node->data)
 +    (*node->del) (node->data);
 +
 +  // free adjacency lists
 +  vector_free (node->to);
 +  vector_free (node->from);
 +
 +  // remove node from graph->nodes
 +  for (unsigned int i = 0; i < vector_active (graph->nodes); i++)
 +    if (vector_slot (graph->nodes, i) == node)
 +      vector_remove (graph->nodes, i);
 +
 +  // free the node itself
 +  XFREE (MTYPE_GRAPH_NODE, node);
 +}
 +
 +struct graph_node *
 +graph_add_edge (struct graph_node *from, struct graph_node *to)
 +{
 +  vector_set (from->to, to);
 +  vector_set (to->from, from);
 +  return to;
 +}
 +
 +void
 +graph_remove_edge (struct graph_node *from, struct graph_node *to)
 +{
 +  // remove from from to->from
 +  for (unsigned int i = 0; i < vector_active (to->from); i++)
 +    if (vector_slot (to->from, i) == from)
 +      vector_remove (to->from, i);
 +  // remove to from from->to
 +  for (unsigned int i = 0; i < vector_active (from->to); i++)
 +    if (vector_slot (from->to, i) == to)
 +      vector_remove (from->to, i);
 +}
 +
 +void
 +graph_delete_graph (struct graph *graph)
 +{
 +  // delete each node in the graph
 +  for (unsigned int i = 0; i < vector_active (graph->nodes); i++)
 +    graph_delete_node (graph, vector_slot (graph->nodes, i));
 +
 +  vector_free (graph->nodes);
 +  XFREE (MTYPE_GRAPH, graph);
 +}
diff --cc lib/vty.c
index 61b0c3b4be6ec4c0da105c7e36c63f59b66ca417,48b64a95c4461c5e752b6ff71c591e93874b4a03..ff559d17051f6042789c979a7cb3d4e8adf5dab9
+++ b/lib/vty.c
  #include <arpa/telnet.h>
  #include <termios.h>
  
+ DEFINE_MTYPE_STATIC(LIB, VTY,         "VTY")
+ DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer")
+ DEFINE_MTYPE_STATIC(LIB, VTY_HIST,    "VTY history")
  /* Vty events */
 -enum event 
 +enum event
  {
    VTY_SERV,
    VTY_READ,
diff --cc vtysh/vtysh.c
index 18ca0dca292e04c411f17f2ab4f7f0ad2cd31ce5,10bdef8cc83c818a7832c62d40533e7aee512845..f767c64d35f3578a069627f2190251b07e007ef4
  #include "ns.h"
  #include "vrf.h"
  
 +#include "lib/grammar_sandbox.h"
 +
+ DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy")
  /* Struct VTY. */
  struct vty *vty;