Initial revision
diff --git a/lib/routemap.c b/lib/routemap.c
new file mode 100644
index 0000000..b000f2f
--- /dev/null
+++ b/lib/routemap.c
@@ -0,0 +1,1077 @@
+/* Route map function.
+   Copyright (C) 1998, 1999 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU Zebra is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "memory.h"
+#include "vector.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "command.h"
+
+/* Vector for route match rules. */
+static vector route_match_vec;
+
+/* Vector for route set rules. */
+static vector route_set_vec;
+
+/* Route map rule. This rule has both `match' rule and `set' rule. */
+struct route_map_rule
+{
+  /* Rule type. */
+  struct route_map_rule_cmd *cmd;
+
+  /* For pretty printing. */
+  char *rule_str;
+
+  /* Pre-compiled match rule. */
+  void *value;
+
+  /* Linked list. */
+  struct route_map_rule *next;
+  struct route_map_rule *prev;
+};
+
+/* Making route map list. */
+struct route_map_list
+{
+  struct route_map *head;
+  struct route_map *tail;
+
+  void (*add_hook) (char *);
+  void (*delete_hook) (char *);
+  void (*event_hook) (route_map_event_t, char *); 
+};
+
+/* Master list of route map. */
+static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL };
+
+static void
+route_map_rule_delete (struct route_map_rule_list *,
+		       struct route_map_rule *);
+
+static void
+route_map_index_delete (struct route_map_index *, int);
+
+/* New route map allocation. Please note route map's name must be
+   specified. */
+static struct route_map *
+route_map_new (char *name)
+{
+  struct route_map *new;
+
+  new =  XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map));
+  new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name);
+  return new;
+}
+
+/* Add new name to route_map. */
+static struct route_map *
+route_map_add (char *name)
+{
+  struct route_map *map;
+  struct route_map_list *list;
+
+  map = route_map_new (name);
+  list = &route_map_master;
+    
+  map->next = NULL;
+  map->prev = list->tail;
+  if (list->tail)
+    list->tail->next = map;
+  else
+    list->head = map;
+  list->tail = map;
+
+  /* Execute hook. */
+  if (route_map_master.add_hook)
+    (*route_map_master.add_hook) (name);
+
+  return map;
+}
+
+/* Route map delete from list. */
+static void
+route_map_delete (struct route_map *map)
+{
+  struct route_map_list *list;
+  struct route_map_index *index;
+  char *name;
+  
+  while ((index = map->head) != NULL)
+    route_map_index_delete (index, 0);
+
+  name = map->name;
+
+  list = &route_map_master;
+
+  if (map->next)
+    map->next->prev = map->prev;
+  else
+    list->tail = map->prev;
+
+  if (map->prev)
+    map->prev->next = map->next;
+  else
+    list->head = map->next;
+
+  XFREE (MTYPE_ROUTE_MAP, map);
+
+  /* Execute deletion hook. */
+  if (route_map_master.delete_hook)
+    (*route_map_master.delete_hook) (name);
+
+  if (name)
+    XFREE (MTYPE_ROUTE_MAP_NAME, name);
+
+}
+
+/* Lookup route map by route map name string. */
+struct route_map *
+route_map_lookup_by_name (char *name)
+{
+  struct route_map *map;
+
+  for (map = route_map_master.head; map; map = map->next)
+    if (strcmp (map->name, name) == 0)
+      return map;
+  return NULL;
+}
+
+/* Lookup route map.  If there isn't route map create one and return
+   it. */
+struct route_map *
+route_map_get (char *name)
+{
+  struct route_map *map;
+
+  map = route_map_lookup_by_name (name);
+  if (map == NULL)
+    map = route_map_add (name);
+  return map;
+}
+
+/* Return route map's type string. */
+static char *
+route_map_type_str (enum route_map_type type)
+{
+  switch (type)
+    {
+    case RMAP_PERMIT:
+      return "permit";
+      break;
+    case RMAP_DENY:
+      return "deny";
+      break;
+    default:
+      return "";
+      break;
+    }
+}
+
+int
+route_map_empty (struct route_map *map)
+{
+  if (map->head == NULL && map->tail == NULL)
+    return 1;
+  else
+    return 0;
+}
+
+/* For debug. */
+void
+route_map_print ()
+{
+  struct route_map *map;
+  struct route_map_index *index;
+  struct route_map_rule *rule;
+
+  for (map = route_map_master.head; map; map = map->next)
+    for (index = map->head; index; index = index->next)
+      {
+	printf ("route-map %s %s %d\n", 
+		map->name,
+		route_map_type_str (index->type),
+		index->pref);
+	for (rule = index->match_list.head; rule; rule = rule->next)
+	  printf (" match %s %s\n", rule->cmd->str, rule->rule_str);
+	for (rule = index->set_list.head; rule; rule = rule->next)
+	  printf (" set %s %s\n", rule->cmd->str, rule->rule_str);
+	if (index->exitpolicy == RMAP_GOTO)
+	  printf (" on-match goto %d\n", index->nextpref);
+	if (index->exitpolicy == RMAP_NEXT)
+	  printf (" on-match next\n");
+      }
+}
+
+/* New route map allocation. Please note route map's name must be
+   specified. */
+struct route_map_index *
+route_map_index_new ()
+{
+  struct route_map_index *new;
+
+  new =  XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index));
+  new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
+  return new;
+}
+
+/* Free route map index. */
+static void
+route_map_index_delete (struct route_map_index *index, int notify)
+{
+  struct route_map_rule *rule;
+
+  /* Free route match. */
+  while ((rule = index->match_list.head) != NULL)
+    route_map_rule_delete (&index->match_list, rule);
+
+  /* Free route set. */
+  while ((rule = index->set_list.head) != NULL)
+    route_map_rule_delete (&index->set_list, rule);
+
+  /* Remove index from route map list. */
+  if (index->next)
+    index->next->prev = index->prev;
+  else
+    index->map->tail = index->prev;
+
+  if (index->prev)
+    index->prev->next = index->next;
+  else
+    index->map->head = index->next;
+
+    /* Execute event hook. */
+  if (route_map_master.event_hook && notify)
+    (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED,
+				    index->map->name);
+
+  XFREE (MTYPE_ROUTE_MAP_INDEX, index);
+}
+
+/* Lookup index from route map. */
+struct route_map_index *
+route_map_index_lookup (struct route_map *map, enum route_map_type type,
+			int pref)
+{
+  struct route_map_index *index;
+
+  for (index = map->head; index; index = index->next)
+    if ((index->type == type || type == RMAP_ANY)
+	&& index->pref == pref)
+      return index;
+  return NULL;
+}
+
+/* Add new index to route map. */
+struct route_map_index *
+route_map_index_add (struct route_map *map, enum route_map_type type,
+		     int pref)
+{
+  struct route_map_index *index;
+  struct route_map_index *point;
+
+  /* Allocate new route map inex. */
+  index = route_map_index_new ();
+  index->map = map;
+  index->type = type;
+  index->pref = pref;
+  
+  /* Compare preference. */
+  for (point = map->head; point; point = point->next)
+    if (point->pref >= pref)
+      break;
+
+  if (map->head == NULL)
+    {
+      map->head = map->tail = index;
+    }
+  else if (point == NULL)
+    {
+      index->prev = map->tail;
+      map->tail->next = index;
+      map->tail = index;
+    }
+  else if (point == map->head)
+    {
+      index->next = map->head;
+      map->head->prev = index;
+      map->head = index;
+    }
+  else
+    {
+      index->next = point;
+      index->prev = point->prev;
+      if (point->prev)
+	point->prev->next = index;
+      point->prev = index;
+    }
+
+  /* Execute event hook. */
+  if (route_map_master.event_hook)
+    (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED,
+				    map->name);
+
+  return index;
+}
+
+/* Get route map index. */
+struct route_map_index *
+route_map_index_get (struct route_map *map, enum route_map_type type, 
+		     int pref)
+{
+  struct route_map_index *index;
+
+  index = route_map_index_lookup (map, RMAP_ANY, pref);
+  if (index && index->type != type)
+    {
+      /* Delete index from route map. */
+      route_map_index_delete (index, 1);
+      index = NULL;
+    }
+  if (index == NULL)
+    index = route_map_index_add (map, type, pref);
+  return index;
+}
+
+/* New route map rule */
+struct route_map_rule *
+route_map_rule_new ()
+{
+  struct route_map_rule *new;
+
+  new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
+  return new;
+}
+
+/* Install rule command to the match list. */
+void
+route_map_install_match (struct route_map_rule_cmd *cmd)
+{
+  vector_set (route_match_vec, cmd);
+}
+
+/* Install rule command to the set list. */
+void
+route_map_install_set (struct route_map_rule_cmd *cmd)
+{
+  vector_set (route_set_vec, cmd);
+}
+
+/* Lookup rule command from match list. */
+struct route_map_rule_cmd *
+route_map_lookup_match (char *name)
+{
+  int i;
+  struct route_map_rule_cmd *rule;
+
+  for (i = 0; i < vector_max (route_match_vec); i++)
+    if ((rule = vector_slot (route_match_vec, i)) != NULL)
+      if (strcmp (rule->str, name) == 0)
+	return rule;
+  return NULL;
+}
+
+/* Lookup rule command from set list. */
+struct route_map_rule_cmd *
+route_map_lookup_set (char *name)
+{
+  int i;
+  struct route_map_rule_cmd *rule;
+
+  for (i = 0; i < vector_max (route_set_vec); i++)
+    if ((rule = vector_slot (route_set_vec, i)) != NULL)
+      if (strcmp (rule->str, name) == 0)
+	return rule;
+  return NULL;
+}
+
+/* Add match and set rule to rule list. */
+static void
+route_map_rule_add (struct route_map_rule_list *list,
+		    struct route_map_rule *rule)
+{
+  rule->next = NULL;
+  rule->prev = list->tail;
+  if (list->tail)
+    list->tail->next = rule;
+  else
+    list->head = rule;
+  list->tail = rule;
+}
+
+/* Delete rule from rule list. */
+static void
+route_map_rule_delete (struct route_map_rule_list *list,
+		       struct route_map_rule *rule)
+{
+  if (rule->cmd->func_free)
+    (*rule->cmd->func_free) (rule->value);
+
+  if (rule->rule_str)
+    XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);
+
+  if (rule->next)
+    rule->next->prev = rule->prev;
+  else
+    list->tail = rule->prev;
+  if (rule->prev)
+    rule->prev->next = rule->next;
+  else
+    list->head = rule->next;
+
+  XFREE (MTYPE_ROUTE_MAP_RULE, rule);
+}
+
+/* strcmp wrapper function which don't crush even argument is NULL. */
+int
+rulecmp (char *dst, char *src)
+{
+  if (dst == NULL)
+    {
+      if (src ==  NULL)
+	return 0;
+      else
+	return 1;
+    }
+  else
+    {
+      if (src == NULL)
+	return 1;
+      else
+	return strcmp (dst, src);
+    }
+  return 1;
+}
+
+/* Add match statement to route map. */
+int
+route_map_add_match (struct route_map_index *index, char *match_name,
+		     char *match_arg)
+{
+  struct route_map_rule *rule;
+  struct route_map_rule *next;
+  struct route_map_rule_cmd *cmd;
+  void *compile;
+  int replaced = 0;
+
+  /* First lookup rule for add match statement. */
+  cmd = route_map_lookup_match (match_name);
+  if (cmd == NULL)
+    return RMAP_RULE_MISSING;
+
+  /* Next call compile function for this match statement. */
+  if (cmd->func_compile)
+    {
+      compile= (*cmd->func_compile)(match_arg);
+      if (compile == NULL)
+	return RMAP_COMPILE_ERROR;
+    }
+  else
+    compile = NULL;
+
+  /* If argument is completely same ignore it. */
+  for (rule = index->match_list.head; rule; rule = next)
+    {
+      next = rule->next;
+      if (rule->cmd == cmd)
+	{	
+	  route_map_rule_delete (&index->match_list, rule);
+	  replaced = 1;
+	}
+    }
+
+  /* Add new route map match rule. */
+  rule = route_map_rule_new ();
+  rule->cmd = cmd;
+  rule->value = compile;
+  if (match_arg)
+    rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg);
+  else
+    rule->rule_str = NULL;
+
+  /* Add new route match rule to linked list. */
+  route_map_rule_add (&index->match_list, rule);
+
+  /* Execute event hook. */
+  if (route_map_master.event_hook)
+    (*route_map_master.event_hook) (replaced ?
+				    RMAP_EVENT_MATCH_REPLACED:
+				    RMAP_EVENT_MATCH_ADDED,
+				    index->map->name);
+
+  return 0;
+}
+
+/* Delete specified route match rule. */
+int
+route_map_delete_match (struct route_map_index *index, char *match_name,
+			char *match_arg)
+{
+  struct route_map_rule *rule;
+  struct route_map_rule_cmd *cmd;
+
+  cmd = route_map_lookup_match (match_name);
+  if (cmd == NULL)
+    return 1;
+  
+  for (rule = index->match_list.head; rule; rule = rule->next)
+    if (rule->cmd == cmd && 
+	(rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
+      {
+	route_map_rule_delete (&index->match_list, rule);
+	/* Execute event hook. */
+	if (route_map_master.event_hook)
+	  (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED,
+					  index->map->name);
+	return 0;
+      }
+  /* Can't find matched rule. */
+  return 1;
+}
+
+/* Add route-map set statement to the route map. */
+int
+route_map_add_set (struct route_map_index *index, char *set_name,
+		   char *set_arg)
+{
+  struct route_map_rule *rule;
+  struct route_map_rule *next;
+  struct route_map_rule_cmd *cmd;
+  void *compile;
+  int replaced = 0;
+
+  cmd = route_map_lookup_set (set_name);
+  if (cmd == NULL)
+    return RMAP_RULE_MISSING;
+
+  /* Next call compile function for this match statement. */
+  if (cmd->func_compile)
+    {
+      compile= (*cmd->func_compile)(set_arg);
+      if (compile == NULL)
+	return RMAP_COMPILE_ERROR;
+    }
+  else
+    compile = NULL;
+
+ /* Add by WJL. if old set command of same kind exist, delete it first
+    to ensure only one set command of same kind exist under a
+    route_map_index. */
+  for (rule = index->set_list.head; rule; rule = next)
+    {
+      next = rule->next;
+      if (rule->cmd == cmd)
+	{
+	  route_map_rule_delete (&index->set_list, rule);
+	  replaced = 1;
+	}
+    }
+
+  /* Add new route map match rule. */
+  rule = route_map_rule_new ();
+  rule->cmd = cmd;
+  rule->value = compile;
+  if (set_arg)
+    rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg);
+  else
+    rule->rule_str = NULL;
+
+  /* Add new route match rule to linked list. */
+  route_map_rule_add (&index->set_list, rule);
+
+  /* Execute event hook. */
+  if (route_map_master.event_hook)
+    (*route_map_master.event_hook) (replaced ?
+				    RMAP_EVENT_SET_REPLACED:
+				    RMAP_EVENT_SET_ADDED,
+				    index->map->name);
+  return 0;
+}
+
+/* Delete route map set rule. */
+int
+route_map_delete_set (struct route_map_index *index, char *set_name,
+			char *set_arg)
+{
+  struct route_map_rule *rule;
+  struct route_map_rule_cmd *cmd;
+
+  cmd = route_map_lookup_set (set_name);
+  if (cmd == NULL)
+    return 1;
+  
+  for (rule = index->set_list.head; rule; rule = rule->next)
+    if ((rule->cmd == cmd) &&
+         (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
+      {
+        route_map_rule_delete (&index->set_list, rule);
+	/* Execute event hook. */
+	if (route_map_master.event_hook)
+	  (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED,
+					  index->map->name);
+        return 0;
+      }
+  /* Can't find matched rule. */
+  return 1;
+}
+
+/* Apply route map's each index to the object. */
+/*
+** The matrix for a route-map looks like this:
+** (note, this includes the description for the "NEXT"
+** and "GOTO" frobs now
+**
+**            Match   |   No Match
+**                    |
+**  permit      a     |      c
+**                    |
+**  ------------------+---------------
+**                    |
+**  deny        b     |      d
+**                    |
+**
+** a)   Apply Set statements, accept route
+**      If NEXT is specified, goto NEXT statement
+**      If GOTO is specified, goto the first clause where pref > nextpref
+**      If nothing is specified, do as Cisco and finish
+** b)   Finish route-map processing, and deny route
+** c) & d)   Goto Next index
+**
+** If we get no matches after we've processed all updates, then the route
+** is dropped too.
+**
+** Some notes on the new "NEXT" and "GOTO"
+**   on-match next    - If this clause is matched, then the set statements
+**                      are executed and then we drop through to the next clause
+**   on-match goto n  - If this clause is matched, then the set statments
+**                      are executed and then we goto the nth clause, or the
+**                      first clause greater than this. In order to ensure
+**                      route-maps *always* exit, you cannot jump backwards.
+**                      Sorry ;)
+**
+** We need to make sure our route-map processing matches the above
+*/
+route_map_result_t
+route_map_apply_index (struct route_map_index *index, struct prefix *prefix,
+                       route_map_object_t type, void *object)
+{
+  int ret;
+  struct route_map_rule *match;
+  struct route_map_rule *set;
+  
+  /* Check all match rule and if there is no match rule return 0. */
+  for (match = index->match_list.head; match; match = match->next)
+    {
+      /* Try each match statement in turn. If any return something
+       other than RM_MATCH then we don't need to check anymore and can
+       return */
+      ret = (*match->cmd->func_apply)(match->value, prefix, type, object);
+      if (ret != RMAP_MATCH)
+	return ret;
+    }
+
+  /* We get here if all match statements matched From the matrix
+   above, if this is PERMIT we go on and apply the SET functions.  If
+   we're deny, we return indicating we matched a deny */
+
+  /* Apply set statement to the object. */
+  if (index->type == RMAP_PERMIT)
+    {
+      for (set = index->set_list.head; set; set = set->next)
+	{
+	  ret = (*set->cmd->func_apply)(set->value, prefix, type, object);
+	}
+      return RMAP_MATCH;
+    }
+  else 
+    {
+      return RMAP_DENYMATCH;
+    }
+  /* Should not get here! */
+  return RMAP_MATCH;
+}
+
+/* Apply route map to the object. */
+route_map_result_t
+route_map_apply (struct route_map *map, struct prefix *prefix, 
+		 route_map_object_t type, void *object)
+{
+  int ret = 0;
+  struct route_map_index *index;
+
+  if (map == NULL)
+    return RMAP_DENYMATCH;
+
+  for (index = map->head; index; index = index->next)
+    {
+      /* Apply this index. End here if we get a RM_NOMATCH */
+      ret = route_map_apply_index (index, prefix, type, object);
+
+      if (ret != RMAP_NOMATCH)
+	{
+	  /* We now have to handle the NEXT and GOTO clauses */
+	  if(index->exitpolicy == RMAP_EXIT)
+	    return ret;
+	  if(index->exitpolicy == RMAP_GOTO)
+	    {
+	      /* Find the next clause to jump to */
+	      struct route_map_index *next;
+
+	      next = index->next;
+	      while (next && next->pref < index->nextpref)
+		{
+		  index = next;
+		  next = next->next;
+		}
+	      if (next == NULL)
+		{
+		  /* No clauses match! */
+		  return ret;
+		}
+	    }
+	  /* Otherwise, we fall through as it was a NEXT */
+	}
+    }
+  /* Finally route-map does not match at all. */
+  return RMAP_DENYMATCH;
+}
+
+void
+route_map_add_hook (void (*func) (char *))
+{
+  route_map_master.add_hook = func;
+}
+
+void
+route_map_delete_hook (void (*func) (char *))
+{
+  route_map_master.delete_hook = func;
+}
+
+void
+route_map_event_hook (void (*func) (route_map_event_t, char *))
+{
+  route_map_master.event_hook = func;
+}
+
+void
+route_map_init ()
+{
+  /* Make vector for match and set. */
+  route_match_vec = vector_init (1);
+  route_set_vec = vector_init (1);
+}
+
+/* VTY related functions. */
+DEFUN (route_map,
+       route_map_cmd,
+       "route-map WORD (deny|permit) <1-65535>",
+       "Create route-map or enter route-map command mode\n"
+       "Route map tag\n"
+       "Route map denies set operations\n"
+       "Route map permits set operations\n"
+       "Sequence to insert to/delete from existing route-map entry\n")
+{
+  int permit;
+  unsigned long pref;
+  struct route_map *map;
+  struct route_map_index *index;
+  char *endptr = NULL;
+
+  /* Permit check. */
+  if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
+    permit = RMAP_PERMIT;
+  else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
+    permit = RMAP_DENY;
+  else
+    {
+      vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Preference check. */
+  pref = strtoul (argv[2], &endptr, 10);
+  if (pref == ULONG_MAX || *endptr != '\0')
+    {
+      vty_out (vty, "the fourth field must be positive integer%s",
+	       VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pref == 0 || pref > 65535)
+    {
+      vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get route map. */
+  map = route_map_get (argv[0]);
+  index = route_map_index_get (map, permit, pref);
+
+  vty->index = index;
+  vty->node = RMAP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_route_map_all,
+       no_route_map_all_cmd,
+       "no route-map WORD",
+       NO_STR
+       "Create route-map or enter route-map command mode\n"
+       "Route map tag\n")
+{
+  struct route_map *map;
+
+  map = route_map_lookup_by_name (argv[0]);
+  if (map == NULL)
+    {
+      vty_out (vty, "%% Could not find route-map %s%s",
+	       argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  route_map_delete (map);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_route_map,
+       no_route_map_cmd,
+       "no route-map WORD (deny|permit) <1-65535>",
+       NO_STR
+       "Create route-map or enter route-map command mode\n"
+       "Route map tag\n"
+       "Route map denies set operations\n"
+       "Route map permits set operations\n"
+       "Sequence to insert to/delete from existing route-map entry\n")
+{
+  int permit;
+  unsigned long pref;
+  struct route_map *map;
+  struct route_map_index *index;
+  char *endptr = NULL;
+
+  /* Permit check. */
+  if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
+    permit = RMAP_PERMIT;
+  else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
+    permit = RMAP_DENY;
+  else
+    {
+      vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Preference. */
+  pref = strtoul (argv[2], &endptr, 10);
+  if (pref == ULONG_MAX || *endptr != '\0')
+    {
+      vty_out (vty, "the fourth field must be positive integer%s",
+	       VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pref == 0 || pref > 65535)
+    {
+      vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Existence check. */
+  map = route_map_lookup_by_name (argv[0]);
+  if (map == NULL)
+    {
+      vty_out (vty, "%% Could not find route-map %s%s",
+	       argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Lookup route map index. */
+  index = route_map_index_lookup (map, permit, pref);
+  if (index == NULL)
+    {
+      vty_out (vty, "%% Could not find route-map entry %s %s%s", 
+	       argv[0], argv[2], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Delete index from route map. */
+  route_map_index_delete (index, 1);
+
+  /* If this route rule is the last one, delete route map itself. */
+  if (route_map_empty (map))
+    route_map_delete (map);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (rmap_onmatch_next,
+       rmap_onmatch_next_cmd,
+       "on-match next",
+       "Exit policy on matches\n"
+       "Next clause\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+
+  if (index)
+    index->exitpolicy = RMAP_NEXT;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rmap_onmatch_next,
+       no_rmap_onmatch_next_cmd,
+       "no on-match next",
+       NO_STR
+       "Exit policy on matches\n"
+       "Next clause\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+  
+  if (index)
+    index->exitpolicy = RMAP_EXIT;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (rmap_onmatch_goto,
+       rmap_onmatch_goto_cmd,
+       "on-match goto <1-65535>",
+       "Exit policy on matches\n"
+       "Goto Clause number\n"
+       "Number\n")
+{
+  struct route_map_index *index;
+  int d = 0;
+
+  if (argv[0])
+    d = atoi(argv[0]);
+
+  index = vty->index;
+  if (index)
+    {
+      if (d <= index->pref)
+	{
+	  /* Can't allow you to do that, Dave */
+	  vty_out (vty, "can't jump backwards in route-maps%s", 
+		   VTY_NEWLINE);
+	  return CMD_WARNING;
+	}
+      else
+	{
+	  index->exitpolicy = RMAP_GOTO;
+	  index->nextpref = d;
+	}
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rmap_onmatch_goto,
+       no_rmap_onmatch_goto_cmd,
+       "no on-match goto",
+       NO_STR
+       "Exit policy on matches\n"
+       "Next clause\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+
+  if (index)
+    index->exitpolicy = RMAP_EXIT;
+  
+  return CMD_SUCCESS;
+}
+
+/* Configuration write function. */
+int
+route_map_config_write (struct vty *vty)
+{
+  struct route_map *map;
+  struct route_map_index *index;
+  struct route_map_rule *rule;
+  int first = 1;
+  int write = 0;
+
+  for (map = route_map_master.head; map; map = map->next)
+    for (index = map->head; index; index = index->next)
+      {
+	if (!first)
+	  vty_out (vty, "!%s", VTY_NEWLINE);
+	else
+	  first = 0;
+
+	vty_out (vty, "route-map %s %s %d%s", 
+		 map->name,
+		 route_map_type_str (index->type),
+		 index->pref, VTY_NEWLINE);
+
+	for (rule = index->match_list.head; rule; rule = rule->next)
+	  vty_out (vty, " match %s %s%s", rule->cmd->str, 
+		   rule->rule_str ? rule->rule_str : "",
+		   VTY_NEWLINE);
+
+	for (rule = index->set_list.head; rule; rule = rule->next)
+	  vty_out (vty, " set %s %s%s", rule->cmd->str,
+		   rule->rule_str ? rule->rule_str : "",
+		   VTY_NEWLINE);
+	if (index->exitpolicy == RMAP_GOTO)
+	  vty_out (vty, " on-match goto %d%s", index->nextpref,
+		   VTY_NEWLINE);
+	if (index->exitpolicy == RMAP_NEXT)
+	  vty_out (vty," on-match next%s", VTY_NEWLINE);
+	
+	write++;
+      }
+  return write;
+}
+
+/* Route map node structure. */
+struct cmd_node rmap_node =
+{
+  RMAP_NODE,
+  "%s(config-route-map)# ",
+  1
+};
+
+/* Initialization of route map vector. */
+void
+route_map_init_vty ()
+{
+  /* Install route map top node. */
+  install_node (&rmap_node, route_map_config_write);
+
+  /* Install route map commands. */
+  install_default (RMAP_NODE);
+  install_element (CONFIG_NODE, &route_map_cmd);
+  install_element (CONFIG_NODE, &no_route_map_cmd);
+  install_element (CONFIG_NODE, &no_route_map_all_cmd);
+
+  /* Install the on-match stuff */
+  install_element (RMAP_NODE, &route_map_cmd);
+  install_element (RMAP_NODE, &rmap_onmatch_next_cmd);
+  install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
+  install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
+  install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
+}