diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/.gitignore | 3 | ||||
| -rw-r--r-- | lib/Makefile.am | 11 | ||||
| -rw-r--r-- | lib/command.c | 3068 | ||||
| -rw-r--r-- | lib/command.h | 415 | ||||
| -rw-r--r-- | lib/command_lex.l | 71 | ||||
| -rw-r--r-- | lib/command_match.c | 830 | ||||
| -rw-r--r-- | lib/command_match.h | 95 | ||||
| -rw-r--r-- | lib/command_parse.y | 548 | ||||
| -rw-r--r-- | lib/grammar_sandbox.c | 383 | ||||
| -rw-r--r-- | lib/grammar_sandbox.h | 65 | ||||
| -rw-r--r-- | lib/graph.c | 138 | ||||
| -rw-r--r-- | lib/graph.h | 101 | ||||
| -rw-r--r-- | lib/vty.c | 1106 | 
13 files changed, 3436 insertions, 3398 deletions
diff --git a/lib/.gitignore b/lib/.gitignore index fe75137bca..e45fec1786 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -15,4 +15,7 @@ gitversion.h.tmp  *~  *.loT  route_types.h +command_lex.c +command_parse.c +command_parse.h  refix diff --git a/lib/Makefile.am b/lib/Makefile.am index dbb80076c5..acaf7e6744 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -3,28 +3,33 @@  AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib  AM_CFLAGS = $(WERROR)  DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +AM_YFLAGS = -d  lib_LTLIBRARIES = libzebra.la  libzebra_la_LDFLAGS = -version-info 0: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 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 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 = route_types.h gitversion.h +BUILT_SOURCES = route_types.h gitversion.h command_parse.h  libzebra_la_DEPENDENCIES = @LIB_REGEX@  libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@  pkginclude_HEADERS = \ -	buffer.h checksum.h command.h filter.h getopt.h hash.h \ +	buffer.h checksum.h filter.h getopt.h hash.h \  	if.h linklist.h log.h \ +	graph.h command_match.h \ +	command.h \  	memory.h network.h prefix.h routemap.h distribute.h sockunion.h \  	str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \  	plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \ diff --git a/lib/command.c b/lib/command.c index cfdb91a5b9..b6f97c317e 100644 --- a/lib/command.c +++ b/lib/command.c @@ -5,7 +5,7 @@     Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")  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 @@ -29,10 +29,13 @@ Boston, MA 02111-1307, USA.  */  #include <lib/version.h>  #include "thread.h"  #include "vector.h" +#include "linklist.h"  #include "vty.h"  #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") @@ -42,44 +45,6 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")     each daemon maintains each own cmdvec. */  vector cmdvec = NULL; -struct cmd_token token_cr; -char *command_cr = NULL; - -/** - * Filter types. These tell the parser whether to allow - * partial matching on tokens. - */ -enum filter_type -{ -  FILTER_RELAXED, -  FILTER_STRICT -}; - -/** - * Command matcher result value. - */ -enum matcher_rv -{ -  MATCHER_OK, -  MATCHER_COMPLETE, -  MATCHER_INCOMPLETE, -  MATCHER_NO_MATCH, -  MATCHER_AMBIGUOUS, -  MATCHER_EXCEED_ARGC_MAX -}; - -/** - * Defines which matcher_rv values constitute - * an error. Should be used against matcher_rv - * return values to do basic error checking. - */ -#define MATCHER_ERROR(matcher_rv) \ -  (   (matcher_rv) == MATCHER_INCOMPLETE \ -   || (matcher_rv) == MATCHER_NO_MATCH \ -   || (matcher_rv) == MATCHER_AMBIGUOUS \ -   || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ -  ) -  /* Host information structure. */  struct host host; @@ -133,7 +98,7 @@ static const struct facility_map {    int facility;    const char *name;    size_t match; -} syslog_facilities[] =  +} syslog_facilities[] =    {      { LOG_KERN, "kern", 1 },      { LOG_USER, "user", 2 }, @@ -185,7 +150,7 @@ static int  level_match(const char *s)  {    int level ; -   +    for ( level = 0 ; zlog_priority [level] != NULL ; level ++ )      if (!strncmp (s, zlog_priority[level], 2))        return level; @@ -205,7 +170,7 @@ print_version (const char *progname)  /* Utility function to concatenate argv argument into a single string     with inserting ' ' character between each argument.  */  char * -argv_concat (const char **argv, int argc, int shift) +argv_concat (struct cmd_token **argv, int argc, int shift)  {    int i;    size_t len; @@ -214,14 +179,14 @@ argv_concat (const char **argv, int argc, int shift)    len = 0;    for (i = shift; i < argc; i++) -    len += strlen(argv[i])+1; +    len += strlen(argv[i]->arg)+1;    if (!len)      return NULL;    p = str = XMALLOC(MTYPE_TMP, len);    for (i = shift; i < argc; i++)      {        size_t arglen; -      memcpy(p, argv[i], (arglen = strlen(argv[i]))); +      memcpy(p, argv[i]->arg, (arglen = strlen(argv[i]->arg)));        p += arglen;        *p++ = ' ';      } @@ -231,12 +196,12 @@ argv_concat (const char **argv, int argc, int shift)  /* Install top node of command vector. */  void -install_node (struct cmd_node *node,  -	      int (*func) (struct vty *)) +install_node (struct cmd_node *node, +              int (*func) (struct vty *))  {    vector_set_index (cmdvec, node->node, node);    node->func = func; -  node->cmd_vector = vector_init (VECTOR_MIN_SIZE); +  node->cmdgraph = graph_new ();  }  /* Breaking up string into each command piece. I assume given @@ -249,10 +214,10 @@ cmd_make_strvec (const char *string)    char *token;    int strlen;    vector strvec; -   +    if (string == NULL)      return NULL; -   +    cp = string;    /* Skip white spaces. */ @@ -270,12 +235,12 @@ cmd_make_strvec (const char *string)    strvec = vector_init (VECTOR_MIN_SIZE);    /* Copy each command piece and set into vector. */ -  while (1)  +  while (1)      {        start = cp;        while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && -	     *cp != '\0') -	cp++; +             *cp != '\0') +        cp++;        strlen = cp - start;        token = XMALLOC (MTYPE_STRVEC, strlen + 1);        memcpy (token, start, strlen); @@ -283,11 +248,11 @@ cmd_make_strvec (const char *string)        vector_set (strvec, token);        while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && -	     *cp != '\0') -	cp++; +             *cp != '\0') +        cp++;        if (*cp == '\0') -	return strvec; +        return strvec;      }  } @@ -308,388 +273,6 @@ cmd_free_strvec (vector v)    vector_free (v);  } -/** - * State structure for command format parser. Tracks - * parse tree position and miscellaneous state variables. - * Used when building a command vector from format strings. - */ -struct format_parser_state -{ -  vector topvect;       /* Top level vector */ -  vector intvect;       /* Intermediate level vector, used when there's -                           a multiple in a keyword. */ -  vector curvect;       /* current vector where read tokens should be -                           appended. */ - -  const char *string;   /* pointer to command string, not modified */ -  const char *cp;       /* pointer in command string, moved along while -                           parsing */ -  const char *dp;       /* pointer in description string, moved along while -                           parsing */ - -  int in_keyword;       /* flag to remember if we are in a keyword group */ -  int in_multiple;      /* flag to remember if we are in a multiple group */ -  int just_read_word;   /* flag to remember if the last thing we read was a -                           real word and not some abstract token */ -}; - -static void -format_parser_error(struct format_parser_state *state, const char *message) -{ -  int offset = state->cp - state->string + 1; - -  fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string); -  fprintf(stderr, "                        %*c\n", offset, '^'); -  fprintf(stderr, "%s at offset %d.\n", message, offset); -  fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n"); -  exit(1); -} - -/** - * Reads out one section of a help string from state->dp. - * Leading whitespace is trimmed and the string is read until - * a newline is reached. - * - * @param[out] state format parser state - * @return the help string token read - */ -static char * -format_parser_desc_str(struct format_parser_state *state) -{ -  const char *cp, *start; -  char *token; -  int strlen; - -  cp = state->dp; - -  if (cp == NULL) -    return NULL; - -  /* Skip white spaces. */ -  while (isspace ((int) *cp) && *cp != '\0') -    cp++; - -  /* Return if there is only white spaces */ -  if (*cp == '\0') -    return NULL; - -  start = cp; - -  while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') -    cp++; - -  strlen = cp - start; -  token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); -  memcpy (token, start, strlen); -  *(token + strlen) = '\0'; - -  state->dp = cp; - -  return token; -} - -/** - * Transitions format parser state into keyword parsing mode. - * A cmd_token struct, `token`, representing this keyword token is initialized - * and appended to state->curvect. token->keyword is initialized as a vector of - * vector, a new vector is initialized and added to token->keyword, and - * state->curvect is set to point at this vector. When control returns to the - * caller newly parsed tokens will be added to this vector. - * - * In short: - *   state->curvect[HEAD]               = new cmd_token - *   state->curvect[HEAD]->keyword[0]   = new vector - *   state->curvect                     = state->curvect[HEAD]->keyword[0] - * - * @param[out] state state struct to transition - */ -static void -format_parser_begin_keyword(struct format_parser_state *state) -{ -  struct cmd_token *token; -  vector keyword_vect; - -  if (state->in_keyword -      || state->in_multiple) -    format_parser_error(state, "Unexpected '{'"); - -  state->cp++; -  state->in_keyword = 1; - -  token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); -  token->type = TOKEN_KEYWORD; -  token->keyword = vector_init(VECTOR_MIN_SIZE); - -  keyword_vect = vector_init(VECTOR_MIN_SIZE); -  vector_set(token->keyword, keyword_vect); - -  vector_set(state->curvect, token); -  state->curvect = keyword_vect; -} - -/** - * Transitions format parser state into multiple parsing mode. - * A cmd_token struct, `token`, representing this multiple token is initialized - * and appended to state->curvect. token->multiple is initialized as a vector - * of cmd_token and state->curvect is set to point at token->multiple. If - * state->curvect != state->topvect (i.e. this multiple token is nested inside - * another composite token) then a pointer to state->curvect is saved in - * state->intvect. - * - * In short: - *   state->curvect[HEAD]               = new cmd_token - *   state->curvect[HEAD]->multiple     = new vector - *   state->intvect                     = state->curvect IFF nested token - *   state->curvect                     = state->curvect[HEAD]->multiple - * - * @param[out] state state struct to transition - */ -static void -format_parser_begin_multiple(struct format_parser_state *state) -{ -  struct cmd_token *token; - -  if (state->in_keyword == 1) -    format_parser_error(state, "Keyword starting with '('"); - -  if (state->in_multiple) -    format_parser_error(state, "Nested group"); - -  state->cp++; -  state->in_multiple = 1; -  state->just_read_word = 0; - -  token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); -  token->type = TOKEN_MULTIPLE; -  token->multiple = vector_init(VECTOR_MIN_SIZE); - -  vector_set(state->curvect, token); -  if (state->curvect != state->topvect) -    state->intvect = state->curvect; -  state->curvect = token->multiple; -} - -/** - * Transition format parser state out of keyword parsing mode. - * This function is called upon encountering '}'. - * state->curvect is reassigned to the top level vector (as - * keywords cannot be nested) and state flags are set appropriately. - * - * @param[out] state state struct to transition - */ -static void -format_parser_end_keyword(struct format_parser_state *state) -{ -  if (state->in_multiple -      || !state->in_keyword) -    format_parser_error(state, "Unexpected '}'"); - -  if (state->in_keyword == 1) -    format_parser_error(state, "Empty keyword group"); - -  state->cp++; -  state->in_keyword = 0; -  state->curvect = state->topvect; -} - -/** - * Transition format parser state out of multiple parsing mode. - * This function is called upon encountering ')'. - * state->curvect is reassigned to its parent vector (state->intvect - * if the multiple token being exited was nested inside another token, - * state->topvect otherwise) and state flags are set appropriately. - * - * @param[out] state state struct to transition - */ -static void -format_parser_end_multiple(struct format_parser_state *state) -{ -  char *dummy; - -  if (!state->in_multiple) -    format_parser_error(state, "Unexpected ')'"); - -  if (vector_active(state->curvect) == 0) -    format_parser_error(state, "Empty multiple section"); - -  if (!state->just_read_word) -    { -      /* There are constructions like -       * 'show ip ospf database ... (self-originate|)' -       * in use. -       * The old parser reads a description string for the -       * word '' between |) which will never match. -       * Simulate this behvaior by dropping the next desc -       * string in such a case. */ - -      dummy = format_parser_desc_str(state); -      XFREE(MTYPE_CMD_TOKENS, dummy); -    } - -  state->cp++; -  state->in_multiple = 0; - -  if (state->intvect) -    state->curvect = state->intvect; -  else -    state->curvect = state->topvect; -} - -/** - * Format parser handler for pipe '|' character. - * This character separates subtokens in multiple and keyword type tokens. - * If the current token is a multiple keyword, the position pointer is - * simply moved past the pipe and state flags are set appropriately. - * If the current token is a keyword token, the position pointer is moved - * past the pipe. Then the cmd_token struct for the keyword is fetched and - * a new vector of cmd_token is appended to its vector of vector. Finally - * state->curvect is set to point at this new vector. - * - * In short: - *   state->curvect = state->topvect[HEAD]->keyword[HEAD] = new vector - * - * @param[out] state state struct to transition - */ -static void -format_parser_handle_pipe(struct format_parser_state *state) -{ -  struct cmd_token *keyword_token; -  vector keyword_vect; - -  if (state->in_multiple) -    { -      state->just_read_word = 0; -      state->cp++; -    } -  else if (state->in_keyword) -    { -      state->in_keyword = 1; -      state->cp++; - -      keyword_token = vector_slot(state->topvect, -                                  vector_active(state->topvect) - 1); -      keyword_vect = vector_init(VECTOR_MIN_SIZE); -      vector_set(keyword_token->keyword, keyword_vect); -      state->curvect = keyword_vect; -    } -  else -    { -      format_parser_error(state, "Unexpected '|'"); -    } -} - -/** - * Format parser handler for terminal tokens. - * Parses the token, appends it to state->curvect, and sets - * state flags appropriately. - * - * @param[out] state state struct for current format parser state - */ -static void -format_parser_read_word(struct format_parser_state *state) -{ -  const char *start; -  int len; -  char *cmd; -  struct cmd_token *token; - -  start = state->cp; - -  while (state->cp[0] != '\0' -         && !strchr("\r\n(){}|", state->cp[0]) -         && !isspace((int)state->cp[0])) -    state->cp++; - -  len = state->cp - start; -  cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1); -  memcpy(cmd, start, len); -  cmd[len] = '\0'; - -  token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); -  token->type = TOKEN_TERMINAL; -  if (strcmp (cmd, "A.B.C.D") == 0) -    token->terminal = TERMINAL_IPV4; -  else if (strcmp (cmd, "A.B.C.D/M") == 0) -    token->terminal = TERMINAL_IPV4_PREFIX; -  else if (strcmp (cmd, "X:X::X:X") == 0) -    token->terminal = TERMINAL_IPV6; -  else if (strcmp (cmd, "X:X::X:X/M") == 0) -    token->terminal = TERMINAL_IPV6_PREFIX; -  else if (cmd[0] == '[') -    token->terminal = TERMINAL_OPTION; -  else if (cmd[0] == '.') -    token->terminal = TERMINAL_VARARG; -  else if (cmd[0] == '<') -    token->terminal = TERMINAL_RANGE; -  else if (cmd[0] >= 'A' && cmd[0] <= 'Z') -    token->terminal = TERMINAL_VARIABLE; -  else -    token->terminal = TERMINAL_LITERAL; - -  token->cmd = cmd; -  token->desc = format_parser_desc_str(state); -  vector_set(state->curvect, token); - -  if (state->in_keyword == 1) -    state->in_keyword = 2; - -  state->just_read_word = 1; -} - -/** - * Parse a given command format string and build a tree of tokens from - * it that is suitable to be used by the command subsystem. - * - * @param string Command format string. - * @param descstr Description string. - * @return A vector of struct cmd_token representing the given command, - *         or NULL on error. - */ -static vector -cmd_parse_format(const char *string, const char *descstr) -{ -  struct format_parser_state state; - -  if (string == NULL) -    return NULL; - -  memset(&state, 0, sizeof(state)); -  state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE); -  state.cp = state.string = string; -  state.dp = descstr; - -  while (1) -    { -      while (isspace((int)state.cp[0]) && state.cp[0] != '\0') -        state.cp++; - -      switch (state.cp[0]) -        { -        case '\0': -          if (state.in_keyword -              || state.in_multiple) -            format_parser_error(&state, "Unclosed group/keyword"); -          return state.topvect; -        case '{': -          format_parser_begin_keyword(&state); -          break; -        case '(': -          format_parser_begin_multiple(&state); -          break; -        case '}': -          format_parser_end_keyword(&state); -          break; -        case ')': -          format_parser_end_multiple(&state); -          break; -        case '|': -          format_parser_handle_pipe(&state); -          break; -        default: -          format_parser_read_word(&state); -        } -    } -}  /* Return prompt character of specified node. */  const char * @@ -706,23 +289,23 @@ void  install_element (enum node_type ntype, struct cmd_element *cmd)  {    struct cmd_node *cnode; -   +    /* cmd_init hasn't been called */    if (!cmdvec)      return; -   +    cnode = vector_slot (cmdvec, ntype); -  if (cnode == NULL)  +  if (cnode == NULL)      {        fprintf (stderr, "Command node %d doesn't exist, please check it\n", -	       ntype); -      exit (1); +               ntype); +      exit (EXIT_FAILURE);      } +  // add node to command graph and command vector +  command_parse_format (cnode->cmdgraph, cmd);    vector_set (cnode->cmd_vector, cmd); -  if (cmd->tokens == NULL) -    cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);  }  static const unsigned char itoa64[] = @@ -731,7 +314,7 @@ static const unsigned char itoa64[] =  static void  to64(char *s, long v, int n)  { -  while (--n >= 0)  +  while (--n >= 0)      {        *s++ = itoa64[v&0x3f];        v >>= 6; @@ -746,7 +329,7 @@ zencrypt (const char *passwd)    char *crypt (const char *, const char *);    gettimeofday(&tv,0); -   +    to64(&salt[0], random(), 3);    to64(&salt[3], tv.tv_usec, 3);    salt[5] = '\0'; @@ -764,9 +347,9 @@ config_write_host (struct vty *vty)    if (host.encrypt)      {        if (host.password_encrypt) -        vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);  +        vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);        if (host.enable_encrypt) -        vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);  +        vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);      }    else      { @@ -779,17 +362,17 @@ config_write_host (struct vty *vty)    if (zlog_default->default_lvl != LOG_DEBUG)      {        vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s", -	       VTY_NEWLINE); +               VTY_NEWLINE);        vty_out (vty, "log trap %s%s", -	       zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); +               zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);      }    if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))      {        vty_out (vty, "log file %s", host.logfile);        if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl) -	vty_out (vty, " %s", -		 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); +        vty_out (vty, " %s", +                 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);        vty_out (vty, "%s", VTY_NEWLINE);      } @@ -797,8 +380,8 @@ config_write_host (struct vty *vty)      {        vty_out (vty, "log stdout");        if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl) -	vty_out (vty, " %s", -		 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); +        vty_out (vty, " %s", +                 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);        vty_out (vty, "%s", VTY_NEWLINE);      } @@ -806,27 +389,27 @@ config_write_host (struct vty *vty)      vty_out(vty,"no log monitor%s",VTY_NEWLINE);    else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)      vty_out(vty,"log monitor %s%s", -	    zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); +            zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);    if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)      {        vty_out (vty, "log syslog");        if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl) -	vty_out (vty, " %s", -		 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); +        vty_out (vty, " %s", +                 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);        vty_out (vty, "%s", VTY_NEWLINE);      }    if (zlog_default->facility != LOG_DAEMON)      vty_out (vty, "log facility %s%s", -	     facility_name(zlog_default->facility), VTY_NEWLINE); +             facility_name(zlog_default->facility), VTY_NEWLINE);    if (zlog_default->record_priority == 1)      vty_out (vty, "log record-priority%s", VTY_NEWLINE);    if (zlog_default->timestamp_precision > 0)      vty_out (vty, "log timestamp precision %d%s", -	     zlog_default->timestamp_precision, VTY_NEWLINE); +             zlog_default->timestamp_precision, VTY_NEWLINE);    if (host.advanced)      vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); @@ -836,7 +419,7 @@ config_write_host (struct vty *vty)    if (host.lines >= 0)      vty_out (vty, "service terminal-length %d%s", host.lines, -	     VTY_NEWLINE); +             VTY_NEWLINE);    if (host.motdfile)      vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE); @@ -854,1369 +437,120 @@ cmd_node_vector (vector v, enum node_type ntype)    return cnode->cmd_vector;  } -/* Completion match types. */ -enum match_type  -{ -  no_match, -  extend_match, -  ipv4_prefix_match, -  ipv4_match, -  ipv6_prefix_match, -  ipv6_match, -  range_match, -  vararg_match, -  partly_match, -  exact_match  -}; - -static enum match_type -cmd_ipv4_match (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 -cmd_ipv4_prefix_match (const char *str) +/* Utility function for getting command graph. */ +static struct graph * +cmd_node_graph (vector v, enum node_type ntype)  { -  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; -} - -#define IPV6_ADDR_STR       "0123456789abcdefABCDEF:." -#define IPV6_PREFIX_STR     "0123456789abcdefABCDEF:./" - -#ifdef HAVE_IPV6 - -static enum match_type -cmd_ipv6_match (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; - -  /* use inet_pton that has a better support, -   * for example inet_pton can support the automatic addresses: -   *  ::1.2.3.4 -   */ -  ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); -    -  if (ret == 1) -    return exact_match; - -  return no_match; -} - -static enum match_type -cmd_ipv6_prefix_match (const char *str) -{ -  struct sockaddr_in6 sin6_dummy; -  const char *delim = "/\0"; -  char *dupe, *prefix, *mask, *context, *endptr; -  int nmask = -1; - -  if (str == NULL) -    return partly_match; - -  if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) -    return no_match; - -  /* tokenize to address + mask */ -  dupe = XMALLOC(MTYPE_TMP, strlen(str)+1); -  strncpy(dupe, str, strlen(str)+1); -  prefix = strtok_r(dupe, delim, &context); -  mask   = strtok_r(NULL, delim, &context); - -  if (!mask) -    return partly_match; - -  /* validate prefix */ -  if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1) -    return no_match; - -  /* validate mask */ -  nmask = strtol (mask, &endptr, 10); -  if (*endptr != '\0' || nmask < 0 || nmask > 128) -    return no_match; - -  XFREE(MTYPE_TMP, dupe); - -  return exact_match; +  struct cmd_node *cnode = vector_slot (v, ntype); +  return cnode->cmdgraph;  } -#endif /* HAVE_IPV6  */ - -#define DECIMAL_STRLEN_MAX 20 -  static int -cmd_range_match (const char *range, const char *str) -{ -  char *p; -  char buf[DECIMAL_STRLEN_MAX + 1]; -  char *endptr = NULL; -  signed long long min, max, val; - -  if (str == NULL) +cmd_try_do_shortcut (enum node_type node, char* first_word) { +  if ( first_word != NULL && +       node != AUTH_NODE && +       node != VIEW_NODE && +       node != AUTH_ENABLE_NODE && +       node != ENABLE_NODE && +       node != RESTRICTED_NODE && +       0 == strcmp( "do", first_word ) )      return 1; - -  val = strtoll (str, &endptr, 10); -  if (*endptr != '\0') -    return 0; -  val = llabs(val); - -  range++; -  p = strchr (range, '-'); -  if (p == NULL) -    return 0; -  if (p - range > DECIMAL_STRLEN_MAX) -    return 0; -  strncpy (buf, range, p - range); -  buf[p - range] = '\0'; -  min = strtoll (buf, &endptr, 10); -  if (*endptr != '\0') -    return 0; - -  range = p + 1; -  p = strchr (range, '>'); -  if (p == NULL) -    return 0; -  if (p - range > DECIMAL_STRLEN_MAX) -    return 0; -  strncpy (buf, range, p - range); -  buf[p - range] = '\0'; -  max = strtoll (buf, &endptr, 10); -  if (*endptr != '\0') -    return 0; - -  if (val < min || val > max) -    return 0; - -  return 1; -} - -static enum match_type -cmd_word_match(struct cmd_token *token, -               enum filter_type filter, -               const char *word) -{ -  const char *str; -  enum match_type match_type; - -  str = token->cmd; - -  if (filter == FILTER_RELAXED) -    if (!word || !strlen(word)) -      return partly_match; - -  if (!word) -    return no_match; - -  switch (token->terminal) -    { -      case TERMINAL_VARARG: -        return vararg_match; - -      case TERMINAL_RANGE: -        if (cmd_range_match(str, word)) -          return range_match; -        break; - -      case TERMINAL_IPV6: -        match_type = cmd_ipv6_match(word); -        if ((filter == FILTER_RELAXED && match_type != no_match) -          || (filter == FILTER_STRICT && match_type == exact_match)) -          return ipv6_match; -        break; - -      case TERMINAL_IPV6_PREFIX: -        match_type = cmd_ipv6_prefix_match(word); -        if ((filter == FILTER_RELAXED && match_type != no_match) -            || (filter == FILTER_STRICT && match_type == exact_match)) -          return ipv6_prefix_match; -        break; - -      case TERMINAL_IPV4: -        match_type = cmd_ipv4_match(word); -        if ((filter == FILTER_RELAXED && match_type != no_match) -            || (filter == FILTER_STRICT && match_type == exact_match)) -          return ipv4_match; -        break; - -      case TERMINAL_IPV4_PREFIX: -        match_type = cmd_ipv4_prefix_match(word); -        if ((filter == FILTER_RELAXED && match_type != no_match) -            || (filter == FILTER_STRICT && match_type == exact_match)) -          return ipv4_prefix_match; -        break; - -      case TERMINAL_OPTION: -      case TERMINAL_VARIABLE: -        return extend_match; - -      case TERMINAL_LITERAL: -        if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) -          { -            if (!strcmp(str, word)) -              return exact_match; -            return partly_match; -          } -        if (filter == FILTER_STRICT && !strcmp(str, word)) -          return exact_match; -        break; - -      default: -        assert (0); -    } - -  return no_match; -} - -struct cmd_matcher -{ -  struct cmd_element *cmd; /* The command element the matcher is using */ -  enum filter_type filter; /* Whether to use strict or relaxed matching */ -  vector vline; /* The tokenized commandline which is to be matched */ -  unsigned int index; /* The index up to which matching should be done */ - -  /* If set, construct a list of matches at the position given by index */ -  enum match_type *match_type; -  vector *match; - -  unsigned int word_index; /* iterating over vline */ -}; - -static int -push_argument(int *argc, const char **argv, const char *arg) -{ -  if (!arg || !strlen(arg)) -    arg = NULL; - -  if (!argc || !argv) -    return 0; - -  if (*argc >= CMD_ARGC_MAX) -    return -1; - -  argv[(*argc)++] = arg;    return 0;  } -static void -cmd_matcher_record_match(struct cmd_matcher *matcher, -                         enum match_type match_type, -                         struct cmd_token *token) -{ -  if (matcher->word_index != matcher->index) -    return; - -  if (matcher->match) -    { -      if (!*matcher->match) -        *matcher->match = vector_init(VECTOR_MIN_SIZE); -      vector_set(*matcher->match, token); -    } - -  if (matcher->match_type) -    { -      if (match_type > *matcher->match_type) -        *matcher->match_type = match_type; -    } -} - -static int -cmd_matcher_words_left(struct cmd_matcher *matcher) -{ -  return matcher->word_index < vector_active(matcher->vline); -} - -static const char* -cmd_matcher_get_word(struct cmd_matcher *matcher) -{ -  assert(cmd_matcher_words_left(matcher)); - -  return vector_slot(matcher->vline, matcher->word_index); -} - -static enum matcher_rv -cmd_matcher_match_terminal(struct cmd_matcher *matcher, -                           struct cmd_token *token, -                           int *argc, const char **argv) -{ -  const char *word; -  enum match_type word_match; - -  assert(token->type == TOKEN_TERMINAL); - -  if (!cmd_matcher_words_left(matcher)) -    { -      if (token->terminal == TERMINAL_OPTION) -        return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ -      else -        return MATCHER_INCOMPLETE; -    } - -  word = cmd_matcher_get_word(matcher); -  word_match = cmd_word_match(token, matcher->filter, word); -  if (word_match == no_match) -    return MATCHER_NO_MATCH; - -  /* We have to record the input word as argument if it matched -   * against a variable. */ -  if (TERMINAL_RECORD (token->terminal)) -    { -      if (push_argument(argc, argv, word)) -        return MATCHER_EXCEED_ARGC_MAX; -    } - -  cmd_matcher_record_match(matcher, word_match, token); - -  matcher->word_index++; - -  /* A vararg token should consume all left over words as arguments */ -  if (token->terminal == TERMINAL_VARARG) -    while (cmd_matcher_words_left(matcher)) -      { -        word = cmd_matcher_get_word(matcher); -        if (word && strlen(word)) -          push_argument(argc, argv, word); -        matcher->word_index++; -      } - -  return MATCHER_OK; -} - -static enum matcher_rv -cmd_matcher_match_multiple(struct cmd_matcher *matcher, -                           struct cmd_token *token, -                           int *argc, const char **argv) -{ -  enum match_type multiple_match; -  unsigned int multiple_index; -  const char *word; -  const char *arg = NULL; -  struct cmd_token *word_token; -  enum match_type word_match; - -  assert(token->type == TOKEN_MULTIPLE); - -  multiple_match = no_match; - -  if (!cmd_matcher_words_left(matcher)) -    return MATCHER_INCOMPLETE; - -  word = cmd_matcher_get_word(matcher); -  for (multiple_index = 0; -       multiple_index < vector_active(token->multiple); -       multiple_index++) -    { -      word_token = vector_slot(token->multiple, multiple_index); - -      word_match = cmd_word_match(word_token, matcher->filter, word); -      if (word_match == no_match) -        continue; - -      cmd_matcher_record_match(matcher, word_match, word_token); - -      if (word_match > multiple_match) -        { -          multiple_match = word_match; -          arg = word; -        } -      /* To mimic the behavior of the old command implementation, we -       * tolerate any ambiguities here :/ */ -    } - -  matcher->word_index++; - -  if (multiple_match == no_match) -    return MATCHER_NO_MATCH; - -  if (push_argument(argc, argv, arg)) -    return MATCHER_EXCEED_ARGC_MAX; - -  return MATCHER_OK; -} - -static enum matcher_rv -cmd_matcher_read_keywords(struct cmd_matcher *matcher, -                          struct cmd_token *token, -                          vector args_vector) -{ -  unsigned int i; -  unsigned long keyword_mask; -  unsigned int keyword_found; -  enum match_type keyword_match; -  enum match_type word_match; -  vector keyword_vector; -  struct cmd_token *word_token; -  const char *word; -  int keyword_argc; -  const char **keyword_argv; -  enum matcher_rv rv = MATCHER_OK; - -  keyword_mask = 0; -  while (1) -    { -      if (!cmd_matcher_words_left(matcher)) -        return MATCHER_OK; - -      word = cmd_matcher_get_word(matcher); - -      keyword_found = -1; -      keyword_match = no_match; -      for (i = 0; i < vector_active(token->keyword); i++) -        { -          if (keyword_mask & (1 << i)) -            continue; - -          keyword_vector = vector_slot(token->keyword, i); -          word_token = vector_slot(keyword_vector, 0); - -          word_match = cmd_word_match(word_token, matcher->filter, word); -          if (word_match == no_match) -            continue; - -          cmd_matcher_record_match(matcher, word_match, word_token); - -          if (word_match > keyword_match) -            { -              keyword_match = word_match; -              keyword_found = i; -            } -          else if (word_match == keyword_match) -            { -              if (matcher->word_index != matcher->index || args_vector) -                return MATCHER_AMBIGUOUS; -            } -        } - -      if (keyword_found == (unsigned int)-1) -        return MATCHER_NO_MATCH; - -      matcher->word_index++; - -      if (matcher->word_index > matcher->index) -        return MATCHER_OK; - -      keyword_mask |= (1 << keyword_found); - -      if (args_vector) -        { -          keyword_argc = 0; -          keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*)); -          /* We use -1 as a marker for unused fields as NULL might be a valid value */ -          for (i = 0; i < CMD_ARGC_MAX + 1; i++) -            keyword_argv[i] = (void*)-1; -          vector_set_index(args_vector, keyword_found, keyword_argv); -        } -      else -        { -          keyword_argv = NULL; -        } - -      keyword_vector = vector_slot(token->keyword, keyword_found); -      /* the keyword itself is at 0. We are only interested in the arguments, -       * so start counting at 1. */ -      for (i = 1; i < vector_active(keyword_vector); i++) -        { -          word_token = vector_slot(keyword_vector, i); - -          switch (word_token->type) -            { -            case TOKEN_TERMINAL: -              rv = cmd_matcher_match_terminal(matcher, word_token, -                                              &keyword_argc, keyword_argv); -              break; -            case TOKEN_MULTIPLE: -              rv = cmd_matcher_match_multiple(matcher, word_token, -                                              &keyword_argc, keyword_argv); -              break; -            case TOKEN_KEYWORD: -              assert(!"Keywords should never be nested."); -              break; -            } - -          if (MATCHER_ERROR(rv)) -            return rv; - -          if (matcher->word_index > matcher->index) -            return MATCHER_OK; -        } -    } -  /* not reached */ -} - -static enum matcher_rv -cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, -                               struct cmd_token *token, -                               int *argc, const char **argv, -                               vector keyword_args_vector) -{ -  unsigned int i, j; -  const char **keyword_args; -  vector keyword_vector; -  struct cmd_token *word_token; -  const char *arg; -  enum matcher_rv rv; - -  rv = MATCHER_OK; - -  if (keyword_args_vector == NULL) -    return rv; - -  for (i = 0; i < vector_active(token->keyword); i++) -    { -      keyword_vector = vector_slot(token->keyword, i); -      keyword_args = vector_lookup(keyword_args_vector, i); - -      if (vector_active(keyword_vector) == 1) -        { -          /* this is a keyword without arguments */ -          if (keyword_args) -            { -              word_token = vector_slot(keyword_vector, 0); -              arg = word_token->cmd; -              XFREE (MTYPE_TMP, keyword_args); -            } -          else -            { -              arg = NULL; -            } - -          if (push_argument(argc, argv, arg)) -            rv = MATCHER_EXCEED_ARGC_MAX; -        } -      else -        { -          /* this is a keyword with arguments */ -          if (keyword_args) -            { -              /* the keyword was present, so just fill in the arguments */ -              for (j = 0; keyword_args[j] != (void*)-1; j++) -                if (push_argument(argc, argv, keyword_args[j])) -                  rv = MATCHER_EXCEED_ARGC_MAX; -              XFREE(MTYPE_TMP, keyword_args); -            } -          else -            { -              /* the keyword was not present, insert NULL for the arguments -               * the keyword would have taken. */ -              for (j = 1; j < vector_active(keyword_vector); j++) -                { -                  word_token = vector_slot(keyword_vector, j); -                  if ((word_token->type == TOKEN_TERMINAL -                       && TERMINAL_RECORD (word_token->terminal)) -                      || word_token->type == TOKEN_MULTIPLE) -                    { -                      if (push_argument(argc, argv, NULL)) -                        rv = MATCHER_EXCEED_ARGC_MAX; -                    } -                } -            } -        } -    } -  vector_free(keyword_args_vector); -  return rv; -} - -static enum matcher_rv -cmd_matcher_match_keyword(struct cmd_matcher *matcher, -                          struct cmd_token *token, -                          int *argc, const char **argv) -{ -  vector keyword_args_vector; -  enum matcher_rv reader_rv; -  enum matcher_rv builder_rv; - -  assert(token->type == TOKEN_KEYWORD); - -  if (argc && argv) -    keyword_args_vector = vector_init(VECTOR_MIN_SIZE); -  else -    keyword_args_vector = NULL; - -  reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector); -  builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc, -                                              argv, keyword_args_vector); -  /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */ - -  if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv)) -    return builder_rv; - -  return reader_rv; -} - -static void -cmd_matcher_init(struct cmd_matcher *matcher, -                 struct cmd_element *cmd, -                 enum filter_type filter, -                 vector vline, -                 unsigned int index, -                 enum match_type *match_type, -                 vector *match) -{ -  memset(matcher, 0, sizeof(*matcher)); - -  matcher->cmd = cmd; -  matcher->filter = filter; -  matcher->vline = vline; -  matcher->index = index; - -  matcher->match_type = match_type; -  if (matcher->match_type) -    *matcher->match_type = no_match; -  matcher->match = match; - -  matcher->word_index = 0; -} - -static enum matcher_rv -cmd_element_match(struct cmd_element *cmd_element, -                  enum filter_type filter, -                  vector vline, -                  unsigned int index, -                  enum match_type *match_type, -                  vector *match, -                  int *argc, -                  const char **argv) -{ -  struct cmd_matcher matcher; -  unsigned int token_index; -  enum matcher_rv rv = MATCHER_OK; - -  cmd_matcher_init(&matcher, cmd_element, filter, -                   vline, index, match_type, match); - -  if (argc != NULL) -    *argc = 0; - -  for (token_index = 0; -       token_index < vector_active(cmd_element->tokens); -       token_index++) -    { -      struct cmd_token *token = vector_slot(cmd_element->tokens, token_index); - -      switch (token->type) -        { -        case TOKEN_TERMINAL: -          rv = cmd_matcher_match_terminal(&matcher, token, argc, argv); -          break; -        case TOKEN_MULTIPLE: -          rv = cmd_matcher_match_multiple(&matcher, token, argc, argv); -          break; -        case TOKEN_KEYWORD: -          rv = cmd_matcher_match_keyword(&matcher, token, argc, argv); -        } - -      if (MATCHER_ERROR(rv)) -        return rv; - -      if (matcher.word_index > index) -        return MATCHER_OK; -    } - -  /* return MATCHER_COMPLETE if all words were consumed */ -  if (matcher.word_index >= vector_active(vline)) -    return MATCHER_COMPLETE; - -  /* return MATCHER_COMPLETE also if only an empty word is left. */ -  if (matcher.word_index == vector_active(vline) - 1 -      && (!vector_slot(vline, matcher.word_index) -          || !strlen((char*)vector_slot(vline, matcher.word_index)))) -    return MATCHER_COMPLETE; - -  return MATCHER_NO_MATCH; /* command is too long to match */ -} -  /** - * Filter a given vector of commands against a given commandline and - * calculate possible completions. - * - * @param commands A vector of struct cmd_element*. Commands that don't - *                 match against the given command line will be overwritten - *                 with NULL in that vector. - * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically - *               determines how incomplete commands are handled, compare with - *               cmd_word_match for details. - * @param vline A vector of char* containing the tokenized commandline. - * @param index Only match up to the given token of the commandline. - * @param match_type Record the type of the best match here. - * @param matches Record the matches here. For each cmd_element in the commands - *                vector, a match vector will be created in the matches vector. - *                That vector will contain all struct command_token* of the - *                cmd_element which matched against the given vline at the given - *                index. - * @return A code specifying if an error occured. If all went right, it's - *         CMD_SUCCESS. + * Compare function for cmd_token. + * Used with qsort to sort command completions.   */  static int -cmd_vector_filter(vector commands, -                  enum filter_type filter, -                  vector vline, -                  unsigned int index, -                  enum match_type *match_type, -                  vector *matches) +compare_completions (const void *fst, const void *snd)  { -  unsigned int i; -  struct cmd_element *cmd_element; -  enum match_type best_match; -  enum match_type element_match; -  enum matcher_rv matcher_rv; - -  best_match = no_match; -  *matches = vector_init(VECTOR_MIN_SIZE); - -  for (i = 0; i < vector_active (commands); i++) -    if ((cmd_element = vector_slot (commands, i)) != NULL) -      { -        vector_set_index(*matches, i, NULL); -        matcher_rv = cmd_element_match(cmd_element, filter, -                                       vline, index, -                                       &element_match, -                                       (vector*)&vector_slot(*matches, i), -                                       NULL, NULL); -        if (MATCHER_ERROR(matcher_rv)) -          { -            vector_slot(commands, i) = NULL; -            if (matcher_rv == MATCHER_AMBIGUOUS) -              return CMD_ERR_AMBIGUOUS; -            if (matcher_rv == MATCHER_EXCEED_ARGC_MAX) -              return CMD_ERR_EXEED_ARGC_MAX; -          } -        else if (element_match > best_match) -          { -            best_match = element_match; -          } -      } -  *match_type = best_match; -  return CMD_SUCCESS; +  struct cmd_token *first = *(struct cmd_token **) fst, +                     *secnd = *(struct cmd_token **) snd; +  return strcmp (first->text, secnd->text);  }  /** - * Check whether a given commandline is complete if used for a specific - * cmd_element. - * - * @param cmd_element A cmd_element against which the commandline should be - *                    checked. - * @param vline The tokenized commandline. - * @return 1 if the given commandline is complete, 0 otherwise. + * Takes a list of completions returned by command_complete, + * dedeuplicates them based on both text and description, + * and returns them as a vector.   */ -static int -cmd_is_complete(struct cmd_element *cmd_element, -                vector vline) +static vector +completions_to_vec (struct list *completions)  { -  enum matcher_rv rv; - -  rv = cmd_element_match(cmd_element, -                         FILTER_RELAXED, -                         vline, -1, -                         NULL, NULL, -                         NULL, NULL); -  return (rv == MATCHER_COMPLETE); -} +  vector comps = vector_init (VECTOR_MIN_SIZE); -/** - * Parse a given commandline and construct a list of arguments for the - * given command_element. - * - * @param cmd_element The cmd_element for which we want to construct arguments. - * @param vline The tokenized commandline. - * @param argc Where to store the argument count. - * @param argv Where to store the argument list. Should be at least - *             CMD_ARGC_MAX elements long. - * @return CMD_SUCCESS if everything went alright, an error otherwise. - */ -static int -cmd_parse(struct cmd_element *cmd_element, -          vector vline, -          int *argc, const char **argv) -{ -  enum matcher_rv rv = cmd_element_match(cmd_element, -                                         FILTER_RELAXED, -                                         vline, -1, -                                         NULL, NULL, -                                         argc, argv); -  switch (rv) +  struct listnode *ln; +  struct cmd_token *token; +  unsigned int i, exists; +  for (ALL_LIST_ELEMENTS_RO(completions,ln,token)) +  { +    // linear search for token in completions vector +    exists = 0; +    for (i = 0; i < vector_active (comps) && !exists; i++)      { -    case MATCHER_COMPLETE: -      return CMD_SUCCESS; - -    case MATCHER_NO_MATCH: -      return CMD_ERR_NO_MATCH; - -    case MATCHER_AMBIGUOUS: -      return CMD_ERR_AMBIGUOUS; - -    case MATCHER_EXCEED_ARGC_MAX: -      return CMD_ERR_EXEED_ARGC_MAX; - -    default: -      return CMD_ERR_INCOMPLETE; +      struct cmd_token *curr = vector_slot (comps, i); +      exists = !strcmp (curr->text, token->text) && +               !strcmp (curr->desc, token->desc);      } -} - -/* Check ambiguous match */ -static int -is_cmd_ambiguous (vector cmd_vector, -                  const char *command, -                  vector matches, -                  enum match_type type) -{ -  unsigned int i; -  unsigned int j; -  const char *str = NULL; -  const char *matched = NULL; -  vector match_vector; -  struct cmd_token *cmd_token; - -  if (command == NULL) -    command = ""; - -  for (i = 0; i < vector_active (matches); i++) -    if ((match_vector = vector_slot (matches, i)) != NULL) -      { -	int match = 0; - -	for (j = 0; j < vector_active (match_vector); j++) -	  if ((cmd_token = vector_slot (match_vector, j)) != NULL) -	    { -	      enum match_type ret; - -	      assert(cmd_token->type == TOKEN_TERMINAL); -	      if (cmd_token->type != TOKEN_TERMINAL) -		continue; - -	      str = cmd_token->cmd; - -	      switch (type) -		{ -		case exact_match: -		  if (!TERMINAL_RECORD (cmd_token->terminal) -		      && strcmp (command, str) == 0) -		    match++; -		  break; -		case partly_match: -		  if (!TERMINAL_RECORD (cmd_token->terminal) -		      && strncmp (command, str, strlen (command)) == 0) -		    { -		      if (matched && strcmp (matched, str) != 0) -			return 1;	/* There is ambiguous match. */ -		      else -			matched = str; -		      match++; -		    } -		  break; -		case range_match: -		  if (cmd_range_match (str, command)) -		    { -		      if (matched && strcmp (matched, str) != 0) -			return 1; -		      else -			matched = str; -		      match++; -		    } -		  break; -#ifdef HAVE_IPV6 -		case ipv6_match: -		  if (cmd_token->terminal == TERMINAL_IPV6) -		    match++; -		  break; -		case ipv6_prefix_match: -		  if ((ret = cmd_ipv6_prefix_match (command)) != no_match) -		    { -		      if (ret == partly_match) -			return 2;	/* There is incomplete match. */ - -		      match++; -		    } -		  break; -#endif /* HAVE_IPV6 */ -		case ipv4_match: -		  if (cmd_token->terminal == TERMINAL_IPV4) -		    match++; -		  break; -		case ipv4_prefix_match: -		  if ((ret = cmd_ipv4_prefix_match (command)) != no_match) -		    { -		      if (ret == partly_match) -			return 2;	/* There is incomplete match. */ - -		      match++; -		    } -		  break; -		case extend_match: -		  if (TERMINAL_RECORD (cmd_token->terminal)) -		    match++; -		  break; -		case no_match: -		default: -		  break; -		} -	    } -	if (!match) -	  vector_slot (cmd_vector, i) = NULL; -      } -  return 0; -} -/* If src matches dst return dst string, otherwise return NULL */ -static const char * -cmd_entry_function (const char *src, struct cmd_token *token) -{ -  const char *dst = token->cmd; - -  /* Skip variable arguments. */ -  if (TERMINAL_RECORD (token->terminal)) -    return NULL; - -  /* In case of 'command \t', given src is NULL string. */ -  if (src == NULL) -    return dst; - -  /* Matched with input string. */ -  if (strncmp (src, dst, strlen (src)) == 0) -    return dst; - -  return NULL; -} - -/* If src matches dst return dst string, otherwise return NULL */ -/* This version will return the dst string always if it is -   CMD_VARIABLE for '?' key processing */ -static const char * -cmd_entry_function_desc (const char *src, struct cmd_token *token) -{ -  const char *dst = token->cmd; +    if (!exists) +      vector_set (comps, copy_cmd_token (token)); +  } -  switch (token->terminal) -    { -      case TERMINAL_VARARG: -        return dst; - -      case TERMINAL_RANGE: -        if (cmd_range_match (dst, src)) -          return dst; -        else -          return NULL; - -      case TERMINAL_IPV6: -        if (cmd_ipv6_match (src)) -          return dst; -        else -          return NULL; - -      case TERMINAL_IPV6_PREFIX: -        if (cmd_ipv6_prefix_match (src)) -          return dst; -        else -          return NULL; - -      case TERMINAL_IPV4: -        if (cmd_ipv4_match (src)) -          return dst; -        else -          return NULL; - -      case TERMINAL_IPV4_PREFIX: -        if (cmd_ipv4_prefix_match (src)) -          return dst; -        else -          return NULL; - -      /* Optional or variable commands always match on '?' */ -      case TERMINAL_OPTION: -      case TERMINAL_VARIABLE: -        return dst; - -      case TERMINAL_LITERAL: -        /* In case of 'command \t', given src is NULL string. */ -        if (src == NULL) -          return dst; - -        if (strncmp (src, dst, strlen (src)) == 0) -          return dst; -        else -          return NULL; +  // sort completions +  qsort (comps->index, +         vector_active (comps), +         sizeof (void *), +         &compare_completions); -      default: -        assert(0); -        return NULL; -    } +  return comps;  } -  /** - * Check whether a string is already present in a vector of strings. - * @param v A vector of char*. - * @param str A char*. - * @return 0 if str is already present in the vector, 1 otherwise. - */ -static int -cmd_unique_string (vector v, const char *str) -{ -  unsigned int i; -  char *match; - -  for (i = 0; i < vector_active (v); i++) -    if ((match = vector_slot (v, i)) != NULL) -      if (strcmp (match, str) == 0) -	return 0; -  return 1; -} - -/** - * Check whether a struct cmd_token matching a given string is already - * present in a vector of struct cmd_token. - * @param v A vector of struct cmd_token*. - * @param str A char* which should be searched for. - * @return 0 if there is a struct cmd_token* with its cmd matching str, - *         1 otherwise. + * Generates a vector of cmd_token representing possible completions + * on the current input. + * + * @param vline the vectorized input line + * @param vty the vty with the node to match on + * @param status pointer to matcher status code   */ -static int -desc_unique_string (vector v, const char *str) -{ -  unsigned int i; -  struct cmd_token *token; - -  for (i = 0; i < vector_active (v); i++) -    if ((token = vector_slot (v, i)) != NULL) -      if (strcmp (token->cmd, str) == 0) -	return 0; -  return 1; -} - -static int  -cmd_try_do_shortcut (enum node_type node, char* first_word) { -  if ( first_word != NULL && -       node != AUTH_NODE && -       node != VIEW_NODE && -       node != AUTH_ENABLE_NODE && -       node != ENABLE_NODE && -       node != RESTRICTED_NODE && -       0 == strcmp( "do", first_word ) ) -    return 1; -  return 0; -} - -static void -cmd_matches_free(vector *matches) -{ -  unsigned int i; -  vector cmd_matches; - -  for (i = 0; i < vector_active(*matches); i++) -    if ((cmd_matches = vector_slot(*matches, i)) != NULL) -      vector_free(cmd_matches); -  vector_free(*matches); -  *matches = NULL; -} - -static int -cmd_describe_cmp(const void *a, const void *b) -{ -  const struct cmd_token *first = *(struct cmd_token * const *)a; -  const struct cmd_token *second = *(struct cmd_token * const *)b; - -  return strcmp(first->cmd, second->cmd); -} - -static void -cmd_describe_sort(vector matchvec) -{ -  qsort(matchvec->index, vector_active(matchvec), -        sizeof(void*), cmd_describe_cmp); -} - -/* '?' describe command support. */  static vector -cmd_describe_command_real (vector vline, struct vty *vty, int *status) +cmd_complete_command_real (vector vline, struct vty *vty, int *status)  { -  unsigned int i; -  vector cmd_vector; -#define INIT_MATCHVEC_SIZE 10 -  vector matchvec; -  struct cmd_element *cmd_element; -  unsigned int index; -  int ret; -  enum match_type match; -  char *command; -  vector matches = NULL; -  vector match_vector; -  uint32_t command_found = 0; -  const char *last_word; - -  /* Set index. */ -  if (vector_active (vline) == 0) -    { -      *status = CMD_ERR_NO_MATCH; -      return NULL; -    } - -  index = vector_active (vline) - 1; - -  /* Make copy vector of current node's command vector. */ -  cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); +  struct list *completions; +  struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); -  /* Prepare match vector */ -  matchvec = vector_init (INIT_MATCHVEC_SIZE); +  enum matcher_rv rv = command_complete (cmdgraph, vline, &completions); -  /* Filter commands and build a list how they could possibly continue. */ -  for (i = 0; i <= index; i++) +  if (MATCHER_ERROR(rv)) +  { +    switch (rv)      { -      command = vector_slot (vline, i); - -      if (matches) -	cmd_matches_free(&matches); - -      ret = cmd_vector_filter(cmd_vector, -	                      FILTER_RELAXED, -	                      vline, i, -	                      &match, -	                      &matches); - -      if (ret != CMD_SUCCESS) -	{ -	  vector_free (cmd_vector); -	  vector_free (matchvec); -	  cmd_matches_free(&matches); -	  *status = ret; -	  return NULL; -	} - -      /* The last match may well be ambigious, so break here */ -      if (i == index) -	break; - -      if (match == vararg_match) -	{ -	  /* We found a vararg match - so we can throw out the current matches here -	   * and don't need to continue checking the command input */ -	  unsigned int j, k; - -	  for (j = 0; j < vector_active (matches); j++) -	    if ((match_vector = vector_slot (matches, j)) != NULL) -	      for (k = 0; k < vector_active (match_vector); k++) -	        { -	          struct cmd_token *token = vector_slot (match_vector, k); -	          vector_set (matchvec, token); -	        } - -	  *status = CMD_SUCCESS; -	  vector_set(matchvec, &token_cr); -	  vector_free (cmd_vector); -	  cmd_matches_free(&matches); -	  cmd_describe_sort(matchvec); -	  return matchvec; -	} - -      ret = is_cmd_ambiguous(cmd_vector, command, matches, match); -      if (ret == 1) -	{ -	  vector_free (cmd_vector); -	  vector_free (matchvec); -	  cmd_matches_free(&matches); -	  *status = CMD_ERR_AMBIGUOUS; -	  return NULL; -	} -      else if (ret == 2) -	{ -	  vector_free (cmd_vector); -	  vector_free (matchvec); -	  cmd_matches_free(&matches); -	  *status = CMD_ERR_NO_MATCH; -	  return NULL; -	} +      case MATCHER_AMBIGUOUS: +        *status = CMD_ERR_AMBIGUOUS; +      default: +        *status = CMD_ERR_NO_MATCH;      } - -  /* Make description vector. */ -  for (i = 0; i < vector_active (matches); i++) { -    if ((cmd_element = vector_slot (cmd_vector, i)) != NULL && -        !(cmd_element->attr == CMD_ATTR_DEPRECATED || -          cmd_element->attr == CMD_ATTR_HIDDEN)) -      { -        unsigned int j; -        vector vline_trimmed; - -	command_found++; -        last_word = vector_slot(vline, vector_active(vline) - 1); -        if (last_word == NULL || !strlen(last_word)) -          { -            vline_trimmed = vector_copy(vline); -            vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); - -            if (cmd_is_complete(cmd_element, vline_trimmed) -                && desc_unique_string(matchvec, command_cr)) -              { -                if (match != vararg_match) -                  vector_set(matchvec, &token_cr); -              } - -            vector_free(vline_trimmed); -          } - -        match_vector = vector_slot (matches, i); -        if (match_vector) -          for (j = 0; j < vector_active(match_vector); j++) -            { -              struct cmd_token *token = vector_slot(match_vector, j); -              const char *string; - -              string = cmd_entry_function_desc(command, token); -              if (string && desc_unique_string(matchvec, string)) -                vector_set(matchvec, token); -            } -      } -  } - -  /* -   * We can get into this situation when the command is complete -   * but the last part of the command is an optional piece of -   * cli. -   */ -  last_word = vector_slot(vline, vector_active(vline) - 1); -  if (command_found == 0 && (last_word == NULL || !strlen(last_word))) { -    vector_set(matchvec, &token_cr); +    return NULL;    } -  vector_free (cmd_vector); -  cmd_matches_free(&matches); +  vector comps = completions_to_vec (completions); +  list_delete (completions); -  if (vector_slot (matchvec, 0) == NULL) -    { -      vector_free (matchvec); +  // set status code appropriately +  switch (vector_active (comps)) +  { +    case 0:        *status = CMD_ERR_NO_MATCH; -      return NULL; -    } +      break; +    case 1: +      *status = CMD_COMPLETE_FULL_MATCH; +      break; +    default: +      *status = CMD_COMPLETE_LIST_MATCH; +  } -  *status = CMD_SUCCESS; -  cmd_describe_sort(matchvec); -  return matchvec; +  return comps;  }  vector @@ -2236,253 +570,19 @@ cmd_describe_command (vector vline, struct vty *vty, int *status)        shifted_vline = vector_init (vector_count(vline));        /* use memcpy? */ -      for (index = 1; index < vector_active (vline); index++)  -	{ -	  vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); -	} +      for (index = 1; index < vector_active (vline); index++) +        { +          vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); +        } -      ret = cmd_describe_command_real (shifted_vline, vty, status); +      ret = cmd_complete_command_real (shifted_vline, vty, status);        vector_free(shifted_vline);        vty->node = onode;        return ret;    } - -  return cmd_describe_command_real (vline, vty, status); -} - - -/* Check LCD of matched command. */ -static int -cmd_lcd (char **matched) -{ -  int i; -  int j; -  int lcd = -1; -  char *s1, *s2; -  char c1, c2; - -  if (matched[0] == NULL || matched[1] == NULL) -    return 0; - -  for (i = 1; matched[i] != NULL; i++) -    { -      s1 = matched[i - 1]; -      s2 = matched[i]; - -      for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) -	if (c1 != c2) -	  break; - -      if (lcd < 0) -	lcd = j; -      else -	{ -	  if (lcd > j) -	    lcd = j; -	} -    } -  return lcd; -} - -static int -cmd_complete_cmp(const void *a, const void *b) -{ -  const char *first = *(char * const *)a; -  const char *second = *(char * const *)b; - -  if (!first) -    { -      if (!second) -        return 0; -      return 1; -    } -  if (!second) -    return -1; - -  return strcmp(first, second); -} - -static void -cmd_complete_sort(vector matchvec) -{ -  qsort(matchvec->index, vector_active(matchvec), -        sizeof(void*), cmd_complete_cmp); -} - -/* Command line completion support. */ -static char ** -cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib) -{ -  unsigned int i; -  vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); -#define INIT_MATCHVEC_SIZE 10 -  vector matchvec; -  unsigned int index; -  char **match_str; -  struct cmd_token *token; -  char *command; -  int lcd; -  vector matches = NULL; -  vector match_vector; - -  if (vector_active (vline) == 0) -    { -      vector_free (cmd_vector); -      *status = CMD_ERR_NO_MATCH; -      return NULL; -    } -  else -    index = vector_active (vline) - 1; - -  /* First, filter by command string */ -  for (i = 0; i <= index; i++) -    { -      command = vector_slot (vline, i); -      enum match_type match; -      int ret; - -      if (matches) -        cmd_matches_free(&matches); - -      /* First try completion match, if there is exactly match return 1 */ -      ret = cmd_vector_filter(cmd_vector, -	                      FILTER_RELAXED, -	                      vline, i, -	                      &match, -	                      &matches); - -      if (ret != CMD_SUCCESS) -	{ -	  vector_free(cmd_vector); -	  cmd_matches_free(&matches); -	  *status = ret; -	  return NULL; -	} - -      /* Break here - the completion mustn't be checked to be non-ambiguous */ -      if (i == index) -	break; - -      /* If there is exact match then filter ambiguous match else check -	 ambiguousness. */ -      ret = is_cmd_ambiguous (cmd_vector, command, matches, match); -      if (ret == 1) -	{ -	  vector_free (cmd_vector); -	  cmd_matches_free(&matches); -	  *status = CMD_ERR_AMBIGUOUS; -	  return NULL; -	} -    } -   -  /* Prepare match vector. */ -  matchvec = vector_init (INIT_MATCHVEC_SIZE); - -  /* Build the possible list of continuations into a list of completions */ -  for (i = 0; i < vector_active (matches); i++) -    if ((match_vector = vector_slot (matches, i))) -      { -	const char *string; -	unsigned int j; - -	for (j = 0; j < vector_active (match_vector); j++) -	  if ((token = vector_slot (match_vector, j))) -            { -              string = cmd_entry_function (vector_slot (vline, index), -                                           token); -              if (string && cmd_unique_string (matchvec, string)) -                vector_set (matchvec, (islib != 0 ? -                                      XSTRDUP (MTYPE_TMP, string) : -                                      strdup (string) /* rl freed */)); -            } -      } - -  /* We don't need cmd_vector any more. */ -  vector_free (cmd_vector); -  cmd_matches_free(&matches); - -  /* No matched command */ -  if (vector_slot (matchvec, 0) == NULL) -    { -      vector_free (matchvec); - -      /* In case of 'command \t' pattern.  Do you need '?' command at -         the end of the line. */ -      if (vector_slot (vline, index) == '\0') -	*status = CMD_ERR_NOTHING_TODO; -      else -	*status = CMD_ERR_NO_MATCH; -      return NULL; -    } - -  /* Only one matched */ -  if (vector_slot (matchvec, 1) == NULL) -    { -      size_t index_size = matchvec->alloced * sizeof (void *); -      match_str = XMALLOC (MTYPE_TMP, index_size); -      memcpy (match_str, matchvec->index, index_size); -      vector_free (matchvec); - -      *status = CMD_COMPLETE_FULL_MATCH; -      return match_str; -    } -  /* Make it sure last element is NULL. */ -  vector_set (matchvec, NULL); - -  /* Check LCD of matched strings. */ -  if (vector_slot (vline, index) != NULL) -    { -      lcd = cmd_lcd ((char **) matchvec->index); - -      if (lcd) -	{ -	  int len = strlen (vector_slot (vline, index)); - -	  if (len < lcd) -	    { -	      char *lcdstr; - -	      lcdstr = (islib != 0 ? -                        XMALLOC (MTYPE_TMP, lcd + 1) : -                        malloc(lcd + 1)); -	      memcpy (lcdstr, matchvec->index[0], lcd); -	      lcdstr[lcd] = '\0'; - -	      /* Free matchvec. */ -	      for (i = 0; i < vector_active (matchvec); i++) -                { -                  if (vector_slot (matchvec, i)) -                    { -                      if (islib != 0) -                        XFREE (MTYPE_TMP, vector_slot (matchvec, i)); -                      else -                        free (vector_slot (matchvec, i)); -                    } -                } -	      vector_free (matchvec); - -	      /* Make new matchvec. */ -	      matchvec = vector_init (INIT_MATCHVEC_SIZE); -	      vector_set (matchvec, lcdstr); - -              size_t index_size = matchvec->alloced * sizeof (void *); -              match_str = XMALLOC (MTYPE_TMP, index_size); -              memcpy (match_str, matchvec->index, index_size); -              vector_free (matchvec); - -	      *status = CMD_COMPLETE_MATCH; -	      return match_str; -	    } -	} -    } - -  match_str = (char **) matchvec->index; -  cmd_complete_sort(matchvec); -  vector_only_wrapper_free (matchvec); -  *status = CMD_COMPLETE_LIST_MATCH; -  return match_str; +  return cmd_complete_command_real (vline, vty, status);  }  char ** @@ -2502,19 +602,41 @@ cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib)        shifted_vline = vector_init (vector_count(vline));        /* use memcpy? */ -      for (index = 1; index < vector_active (vline); index++)  -	{ -	  vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); -	} +      for (index = 1; index < vector_active (vline); index++) +        { +          vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); +        } -      ret = cmd_complete_command_real (shifted_vline, vty, status, islib); +      // get token completions +      vector comps = cmd_complete_command_real (shifted_vline, vty, status); +      ret = XMALLOC (MTYPE_TMP, vector_active (comps) * sizeof (char *)); +      for (unsigned int i = 0; i < vector_active (comps); i++) +        { +          struct cmd_token *token = vector_slot (comps, i); +          ret[i] = XSTRDUP (MTYPE_TMP, token->text); +          vector_unset (comps, i); +          del_cmd_token (token); +        } +      vector_free (comps);        vector_free(shifted_vline);        vty->node = onode;        return ret;    } -  return cmd_complete_command_real (vline, vty, status, islib); +  // get token completions +  vector comps = cmd_complete_command_real (vline, vty, status); +  ret = XMALLOC (MTYPE_TMP, vector_active (comps) * sizeof (char *)); +  for (unsigned int i = 0; i < vector_active (comps); i++) +    { +      struct cmd_token *token = vector_slot (comps, i); +      ret[i] = XSTRDUP (MTYPE_TMP, token->text); +      vector_unset (comps, i); +      del_cmd_token (token); +    } +  vector_free (comps); + +  return ret;  }  char ** @@ -2561,109 +683,47 @@ node_parent ( enum node_type node )  /* Execute command by argument vline vector. */  static int  cmd_execute_command_real (vector vline, -			  enum filter_type filter, -			  struct vty *vty, -			  struct cmd_element **cmd) -{ -  unsigned int i; -  unsigned int index; -  vector cmd_vector; -  struct cmd_element *cmd_element; -  struct cmd_element *matched_element; -  unsigned int matched_count, incomplete_count; -  int argc; -  const char *argv[CMD_ARGC_MAX]; -  enum match_type match = 0; -  char *command; -  int ret; -  vector matches; - -  /* Make copy of command elements. */ -  cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - -  for (index = 0; index < vector_active (vline); index++) -    { -      command = vector_slot (vline, index); -      ret = cmd_vector_filter(cmd_vector, -			      filter, -			      vline, index, -			      &match, -			      &matches); - -      if (ret != CMD_SUCCESS) -	{ -	  cmd_matches_free(&matches); -	  return ret; -	} - -      if (match == vararg_match) -	{ -	  cmd_matches_free(&matches); -	  break; -	} - -      ret = is_cmd_ambiguous (cmd_vector, command, matches, match); -      cmd_matches_free(&matches); - -      if (ret == 1) -	{ -	  vector_free(cmd_vector); -	  return CMD_ERR_AMBIGUOUS; -	} -      else if (ret == 2) -	{ -	  vector_free(cmd_vector); -	  return CMD_ERR_NO_MATCH; -	} -    } - -  /* Check matched count. */ -  matched_element = NULL; -  matched_count = 0; -  incomplete_count = 0; - -  for (i = 0; i < vector_active (cmd_vector); i++) -    if ((cmd_element = vector_slot (cmd_vector, i))) -      { -	if (cmd_is_complete(cmd_element, vline)) -	  { -	    matched_element = cmd_element; -	    matched_count++; -	  } -	else -	  { -	    incomplete_count++; -	  } -      } - -  /* Finish of using cmd_vector. */ -  vector_free (cmd_vector); - -  /* To execute command, matched_count must be 1. */ -  if (matched_count == 0) -    { -      if (incomplete_count) -	return CMD_ERR_INCOMPLETE; -      else -	return CMD_ERR_NO_MATCH; +                          enum filter_type filter, +                          struct vty *vty, +                          struct cmd_element **cmd) +{ +  struct list *argv_list; +  enum matcher_rv status; +  struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); +  status = command_match (cmdgraph, vline, &argv_list, cmd); + +  // if matcher error, return corresponding CMD_ERR +  if (MATCHER_ERROR(status)) +    switch (status) +    { +      case MATCHER_INCOMPLETE: +        return CMD_ERR_INCOMPLETE; +      case MATCHER_AMBIGUOUS: +        return CMD_ERR_AMBIGUOUS; +      default: +        return CMD_ERR_NO_MATCH;      } -  if (matched_count > 1) -    return CMD_ERR_AMBIGUOUS; +  // build argv array from argv list +  struct cmd_token **argv = XMALLOC (MTYPE_TMP, argv_list->count * sizeof (struct cmd_token *)); +  struct listnode *ln; +  struct cmd_token *token; +  unsigned int i = 0; +  for (ALL_LIST_ELEMENTS_RO(argv_list,ln,token)) +    argv[i++] = token; -  ret = cmd_parse(matched_element, vline, &argc, argv); -  if (ret != CMD_SUCCESS) -    return ret; +  int argc = argv_list->count; -  /* For vtysh execution. */ -  if (cmd) -    *cmd = matched_element; +  int ret; +  if ((*cmd)->daemon) +    ret = CMD_SUCCESS_DAEMON; +  else +    ret = (*cmd)->func (*cmd, vty, argc, argv); -  if (matched_element->daemon) -    return CMD_SUCCESS_DAEMON; +  // delete list and cmd_token's in it +  list_delete (argv_list); -  /* Execute matched command. */ -  return (*matched_element->func) (matched_element, vty, argc, argv); +  return ret;  }  /** @@ -2683,8 +743,8 @@ cmd_execute_command_real (vector vline,   */  int  cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, -		     int vtysh) { -  int ret, saved_ret, tried = 0; +                     int vtysh) { +  int ret, saved_ret = 0;    enum node_type onode, try_node;    onode = try_node = vty->node; @@ -2699,10 +759,8 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,        shifted_vline = vector_init (vector_count(vline));        /* use memcpy? */ -      for (index = 1; index < vector_active (vline); index++)  -	{ -	  vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); -	} +      for (index = 1; index < vector_active (vline); index++) +        vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));        ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); @@ -2717,24 +775,22 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,    if (vtysh)      return saved_ret; -  /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ -  while ( ret != CMD_SUCCESS && ret != CMD_WARNING  -	  && vty->node > CONFIG_NODE ) +  if (ret != CMD_SUCCESS && ret != CMD_WARNING)      { -      try_node = node_parent(try_node); -      vty->node = try_node; -      ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); -      tried = 1; -      if (ret == CMD_SUCCESS || ret == CMD_WARNING) -	{ -	  /* succesfull command, leave the node as is */ -	  return ret; -	} +      /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ +      while (vty->node > CONFIG_NODE) +        { +          try_node = node_parent(try_node); +          vty->node = try_node; +          ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); +          if (ret == CMD_SUCCESS || ret == CMD_WARNING) +            return ret; +        } +      /* no command succeeded, reset the vty to the original node */ +      vty->node = onode;      } -  /* no command succeeded, reset the vty to the original node and -     return the error for this node */ -  if ( tried ) -    vty->node = onode; + +  /* return command status for original node */    return saved_ret;  } @@ -2753,7 +809,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,   */  int  cmd_execute_command_strict (vector vline, struct vty *vty, -			    struct cmd_element **cmd) +                            struct cmd_element **cmd)  {    return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);  } @@ -2797,7 +853,7 @@ command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use      while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&             !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) && -	   ret != CMD_SUCCESS && +           ret != CMD_SUCCESS &&             ret != CMD_WARNING &&             vty->node > CONFIG_NODE) {        vty->node = node_parent(vty->node); @@ -2808,11 +864,11 @@ command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use      // stay at the same node      if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&          !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) && -	ret != CMD_SUCCESS && +        ret != CMD_SUCCESS &&          ret != CMD_WARNING)        { -	vty->node = saved_node; -	memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ); +        vty->node = saved_node; +        memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ);        }    } @@ -2831,13 +887,13 @@ config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)    while (fgets (vty->buf, VTY_BUFSIZ, fp))      {        if (!error_ret) -	++(*line_num); +        ++(*line_num);        ret = command_config_read_one_line (vty, NULL, 0);        if (ret != CMD_SUCCESS && ret != CMD_WARNING && -	  ret != CMD_ERR_NOTHING_TODO) -	error_ret = ret; +          ret != CMD_ERR_NOTHING_TODO) +        error_ret = ret;      }    if (error_ret) { @@ -2865,7 +921,7 @@ DEFUN (config_terminal,  }  /* Enable command */ -DEFUN (enable,  +DEFUN (enable,         config_enable_cmd,         "enable",         "Turn on privileged mode command\n") @@ -2881,7 +937,7 @@ DEFUN (enable,  }  /* Disable command */ -DEFUN (disable,  +DEFUN (disable,         config_disable_cmd,         "disable",         "Turn off privileged mode command\n") @@ -2903,9 +959,9 @@ DEFUN (config_exit,      case ENABLE_NODE:      case RESTRICTED_NODE:        if (vty_shell (vty)) -	exit (0); +        exit (0);        else -	vty->status = VTY_CLOSE; +        vty->status = VTY_CLOSE;        break;      case CONFIG_NODE:        vty->node = ENABLE_NODE; @@ -2955,7 +1011,7 @@ ALIAS (config_exit,         config_quit_cmd,         "quit",         "Exit current mode and down to previous mode\n") -        +  /* End of configuration. */  DEFUN (config_end,         config_end_cmd, @@ -3012,7 +1068,7 @@ DEFUN (show_version,         "Displays zebra version\n")  {    vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", -	   VTY_NEWLINE); +           VTY_NEWLINE);    vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);    vty_out (vty, "configured with:%s    %s%s", VTY_NEWLINE,             QUAGGA_CONFIG_ARGS, VTY_NEWLINE); @@ -3026,8 +1082,8 @@ DEFUN (config_help,         "help",         "Description of the interactive help system\n")  { -  vty_out (vty,  -	   "Quagga VTY provides advanced help feature.  When you need help,%s\ +  vty_out (vty, +           "Quagga VTY provides advanced help feature.  When you need help,%s\  anytime at the command line please press '?'.%s\  %s\  If nothing matches, the help list will be empty and you must backup%s\ @@ -3039,8 +1095,8 @@ argument.%s\  2. Partial help is provided when an abbreviated argument is entered%s\     and you want to know what arguments match the input%s\     (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, -	   VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, -	   VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); +           VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, +           VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);    return CMD_SUCCESS;  } @@ -3059,16 +1115,18 @@ DEFUN (config_list,          && !(cmd->attr == CMD_ATTR_DEPRECATED               || cmd->attr == CMD_ATTR_HIDDEN))        vty_out (vty, "  %s%s", cmd->string, -	       VTY_NEWLINE); +               VTY_NEWLINE);    return CMD_SUCCESS;  }  /* Write current configuration into file. */ -DEFUN (config_write_file,  -       config_write_file_cmd, -       "write file",   +DEFUN (config_write, +       config_write_cmd, +       "write [<file|memory|terminal>]",         "Write running configuration to memory, network, or terminal\n" -       "Write to configuration file\n") +       "Write to configuration file\n" +       "Write configuration currently in memory\n" +       "Write configuration to terminal\n")  {    unsigned int i;    int fd; @@ -3080,17 +1138,47 @@ DEFUN (config_write_file,    struct vty *file_vty;    struct stat conf_stat; +  // if command was 'write terminal' or 'show running-config' +  if (argc == 2 && (!strcmp(argv[1]->arg, "terminal") || +                    !strcmp(argv[1]->arg, "running-config"))) +  { +    if (vty->type == VTY_SHELL_SERV) +      { +        for (i = 0; i < vector_active (cmdvec); i++) +          if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) +            { +              if ((*node->func) (vty)) +                vty_out (vty, "!%s", VTY_NEWLINE); +            } +      } +    else +      { +        vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, +                 VTY_NEWLINE); +        vty_out (vty, "!%s", VTY_NEWLINE); + +        for (i = 0; i < vector_active (cmdvec); i++) +          if ((node = vector_slot (cmdvec, i)) && node->func) +            { +              if ((*node->func) (vty)) +                vty_out (vty, "!%s", VTY_NEWLINE); +            } +        vty_out (vty, "end%s",VTY_NEWLINE); +      } +    return CMD_SUCCESS; +  } +    /* Check and see if we are operating under vtysh configuration */    if (host.config == NULL)      {        vty_out (vty, "Can't save to configuration file, using vtysh.%s", -	       VTY_NEWLINE); +               VTY_NEWLINE);        return CMD_WARNING;      }    /* Get filename. */    config_file = host.config; -   +    config_file_sav =      XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);    strcpy (config_file_sav, config_file); @@ -3099,16 +1187,16 @@ DEFUN (config_write_file,    config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);    sprintf (config_file_tmp, "%s.XXXXXX", config_file); -   +    /* Open file to configuration write. */    fd = mkstemp (config_file_tmp);    if (fd < 0)      {        vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp, -	       VTY_NEWLINE); +               VTY_NEWLINE);        goto finished;      } -   +    /* Make vty for configuration file. */    file_vty = vty_new ();    file_vty->wfd = fd; @@ -3122,51 +1210,51 @@ DEFUN (config_write_file,    for (i = 0; i < vector_active (cmdvec); i++)      if ((node = vector_slot (cmdvec, i)) && node->func)        { -	if ((*node->func) (file_vty)) -	  vty_out (file_vty, "!\n"); +        if ((*node->func) (file_vty)) +          vty_out (file_vty, "!\n");        }    vty_close (file_vty);    if (stat(config_file, &conf_stat) >= 0)      {        if (unlink (config_file_sav) != 0) -	if (errno != ENOENT) -	  { -	    vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, -		     VTY_NEWLINE); -	    goto finished; -	  } +        if (errno != ENOENT) +          { +            vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, +                     VTY_NEWLINE); +            goto finished; +          }        if (link (config_file, config_file_sav) != 0) -	{ -	  vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, -		   VTY_NEWLINE); -	  goto finished; -	} +        { +          vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, +                   VTY_NEWLINE); +          goto finished; +        }        sync ();        if (unlink (config_file) != 0) -	{ -	  vty_out (vty, "Can't unlink configuration file %s.%s", config_file, -		   VTY_NEWLINE); -	  goto finished; -	} +        { +          vty_out (vty, "Can't unlink configuration file %s.%s", config_file, +                   VTY_NEWLINE); +          goto finished; +        }      }    if (link (config_file_tmp, config_file) != 0)      {        vty_out (vty, "Can't save configuration file %s.%s", config_file, -	       VTY_NEWLINE); +               VTY_NEWLINE);        goto finished;      }    sync (); -   +    if (chmod (config_file, CONFIGFILE_MASK) != 0)      { -      vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",  -	config_file, safe_strerror(errno), errno, VTY_NEWLINE); +      vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s", +        config_file, safe_strerror(errno), errno, VTY_NEWLINE);        goto finished;      }    vty_out (vty, "Configuration saved to %s%s", config_file, -	   VTY_NEWLINE); +           VTY_NEWLINE);    ret = CMD_SUCCESS;  finished: @@ -3176,62 +1264,15 @@ finished:    return ret;  } -ALIAS (config_write_file,  -       config_write_cmd, -       "write",   -       "Write running configuration to memory, network, or terminal\n") - -ALIAS (config_write_file,  -       config_write_memory_cmd, -       "write memory",   -       "Write running configuration to memory, network, or terminal\n" -       "Write configuration to the file (same as write file)\n") - -ALIAS (config_write_file,  +ALIAS (config_write,         copy_runningconfig_startupconfig_cmd, -       "copy running-config startup-config",   +       "copy running-config startup-config",         "Copy configuration\n"         "Copy running config to... \n"         "Copy running config to startup config (same as write file)\n")  /* Write current configuration into the terminal. */ -DEFUN (config_write_terminal, -       config_write_terminal_cmd, -       "write terminal", -       "Write running configuration to memory, network, or terminal\n" -       "Write to terminal\n") -{ -  unsigned int i; -  struct cmd_node *node; - -  if (vty->type == VTY_SHELL_SERV) -    { -      for (i = 0; i < vector_active (cmdvec); i++) -	if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) -	  { -	    if ((*node->func) (vty)) -	      vty_out (vty, "!%s", VTY_NEWLINE); -	  } -    } -  else -    { -      vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, -	       VTY_NEWLINE); -      vty_out (vty, "!%s", VTY_NEWLINE); - -      for (i = 0; i < vector_active (cmdvec); i++) -	if ((node = vector_slot (cmdvec, i)) && node->func) -	  { -	    if ((*node->func) (vty)) -	      vty_out (vty, "!%s", VTY_NEWLINE); -	  } -      vty_out (vty, "end%s",VTY_NEWLINE); -    } -  return CMD_SUCCESS; -} - -/* Write current configuration into the terminal. */ -ALIAS (config_write_terminal, +ALIAS (config_write,         show_running_config_cmd,         "show running-config",         SHOW_STR @@ -3242,7 +1283,7 @@ DEFUN (show_startup_config,         show_startup_config_cmd,         "show startup-config",         SHOW_STR -       "Contentes of startup configuration\n") +       "Contents of startup configuration\n")  {    char buf[BUFSIZ];    FILE *confp; @@ -3251,7 +1292,7 @@ DEFUN (show_startup_config,    if (confp == NULL)      {        vty_out (vty, "Can't open configuration file [%s] due to '%s'%s", -	       host.config, safe_strerror(errno), VTY_NEWLINE); +               host.config, safe_strerror(errno), VTY_NEWLINE);        return CMD_WARNING;      } @@ -3260,7 +1301,7 @@ DEFUN (show_startup_config,        char *cp = buf;        while (*cp != '\r' && *cp != '\n' && *cp != '\0') -	cp++; +        cp++;        *cp = '\0';        vty_out (vty, "%s%s", buf, VTY_NEWLINE); @@ -3272,13 +1313,15 @@ DEFUN (show_startup_config,  }  /* Hostname configuration */ -DEFUN (config_hostname,  +DEFUN (config_hostname,         hostname_cmd,         "hostname WORD",         "Set system's network name\n"         "This system's network name\n")  { -  if (!isalpha((int) *argv[0])) +  struct cmd_token *word = argv[1]; + +  if (!isalpha((int) word->arg[0]))      {        vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);        return CMD_WARNING; @@ -3286,12 +1329,12 @@ DEFUN (config_hostname,    if (host.name)      XFREE (MTYPE_HOST, host.name); -     -  host.name = XSTRDUP (MTYPE_HOST, argv[0]); + +  host.name = XSTRDUP (MTYPE_HOST, word->arg);    return CMD_SUCCESS;  } -DEFUN (config_no_hostname,  +DEFUN (config_no_hostname,         no_hostname_cmd,         "no hostname [HOSTNAME]",         NO_STR @@ -3306,42 +1349,26 @@ DEFUN (config_no_hostname,  /* VTY interface password set. */  DEFUN (config_password, password_cmd, -       "password (8|) WORD", +       "password [8] WORD",         "Assign the terminal connection password\n"         "Specifies a HIDDEN password will follow\n" -       "dummy string \n" -       "The HIDDEN line password string\n") +       "The password string\n")  { -  /* Argument check. */ -  if (argc == 0) -    { -      vty_out (vty, "Please specify password.%s", VTY_NEWLINE); -      return CMD_WARNING; -    } - -  if (argc == 2) -    { -      if (*argv[0] == '8') -	{ -	  if (host.password) -	    XFREE (MTYPE_HOST, host.password); -	  host.password = NULL; -	  if (host.password_encrypt) -	    XFREE (MTYPE_HOST, host.password_encrypt); -	  host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); -	  return CMD_SUCCESS; -	} -      else -	{ -	  vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); -	  return CMD_WARNING; -	} -    } +  if (argc == 3) // '8' was specified +  { +    if (host.password) +      XFREE (MTYPE_HOST, host.password); +    host.password = NULL; +    if (host.password_encrypt) +      XFREE (MTYPE_HOST, host.password_encrypt); +    host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[2]->arg); +    return CMD_SUCCESS; +  } -  if (!isalnum ((int) *argv[0])) +  if (!isalnum (argv[1]->arg[0]))      { -      vty_out (vty,  -	       "Please specify string starting with alphanumeric%s", VTY_NEWLINE); +      vty_out (vty, +               "Please specify string starting with alphanumeric%s", VTY_NEWLINE);        return CMD_WARNING;      } @@ -3352,62 +1379,50 @@ DEFUN (config_password, password_cmd,    if (host.encrypt)      {        if (host.password_encrypt) -	XFREE (MTYPE_HOST, host.password_encrypt); -      host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); +        XFREE (MTYPE_HOST, host.password_encrypt); +      host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[1]->arg));      }    else -    host.password = XSTRDUP (MTYPE_HOST, argv[0]); +    host.password = XSTRDUP (MTYPE_HOST, argv[1]->arg);    return CMD_SUCCESS;  } -ALIAS (config_password, password_text_cmd, -       "password LINE", -       "Assign the terminal connection password\n" -       "The UNENCRYPTED (cleartext) line password\n") -  /* VTY enable password set. */  DEFUN (config_enable_password, enable_password_cmd, -       "enable password (8|) WORD", +       "enable password [8] WORD",         "Modify enable password parameters\n"         "Assign the privileged level password\n"         "Specifies a HIDDEN password will follow\n"         "dummy string \n"         "The HIDDEN 'enable' password string\n")  { -  /* Argument check. */ -  if (argc == 0) -    { -      vty_out (vty, "Please specify password.%s", VTY_NEWLINE); -      return CMD_WARNING; -    } -    /* Crypt type is specified. */ -  if (argc == 2) +  if (argc == 4)      { -      if (*argv[0] == '8') -	{ -	  if (host.enable) -	    XFREE (MTYPE_HOST, host.enable); -	  host.enable = NULL; - -	  if (host.enable_encrypt) -	    XFREE (MTYPE_HOST, host.enable_encrypt); -	  host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - -	  return CMD_SUCCESS; -	} +      if (argv[2]->arg[0] == '8') +        { +          if (host.enable) +            XFREE (MTYPE_HOST, host.enable); +          host.enable = NULL; + +          if (host.enable_encrypt) +            XFREE (MTYPE_HOST, host.enable_encrypt); +          host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[3]->arg); + +          return CMD_SUCCESS; +        }        else -	{ -	  vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); -	  return CMD_WARNING; -	} +        { +          vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); +          return CMD_WARNING; +        }      } -  if (!isalnum ((int) *argv[0])) +  if (!isalnum (argv[2]->arg[0]))      { -      vty_out (vty,  -	       "Please specify string starting with alphanumeric%s", VTY_NEWLINE); +      vty_out (vty, +               "Please specify string starting with alphanumeric%s", VTY_NEWLINE);        return CMD_WARNING;      } @@ -3419,22 +1434,15 @@ DEFUN (config_enable_password, enable_password_cmd,    if (host.encrypt)      {        if (host.enable_encrypt) -	XFREE (MTYPE_HOST, host.enable_encrypt); -      host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); +        XFREE (MTYPE_HOST, host.enable_encrypt); +      host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[2]->arg));      }    else -    host.enable = XSTRDUP (MTYPE_HOST, argv[0]); +    host.enable = XSTRDUP (MTYPE_HOST, argv[2]->arg);    return CMD_SUCCESS;  } -ALIAS (config_enable_password, -       enable_password_text_cmd, -       "enable password LINE", -       "Modify enable password parameters\n" -       "Assign the privileged level password\n" -       "The UNENCRYPTED (cleartext) 'enable' password\n") -  /* VTY enable password delete. */  DEFUN (no_config_enable_password, no_enable_password_cmd,         "no enable password", @@ -3452,7 +1460,7 @@ DEFUN (no_config_enable_password, no_enable_password_cmd,    return CMD_SUCCESS;  } -	 +  DEFUN (service_password_encrypt,         service_password_encrypt_cmd,         "service password-encryption", @@ -3467,13 +1475,13 @@ DEFUN (service_password_encrypt,    if (host.password)      {        if (host.password_encrypt) -	XFREE (MTYPE_HOST, host.password_encrypt); +        XFREE (MTYPE_HOST, host.password_encrypt);        host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));      }    if (host.enable)      {        if (host.enable_encrypt) -	XFREE (MTYPE_HOST, host.enable_encrypt); +        XFREE (MTYPE_HOST, host.enable_encrypt);        host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));      } @@ -3504,7 +1512,7 @@ DEFUN (no_service_password_encrypt,  }  DEFUN (config_terminal_length, config_terminal_length_cmd, -       "terminal length <0-512>", +       "terminal length (0-512)",         "Set terminal line parameters\n"         "Set number of lines on a screen\n"         "Number of lines on screen (0 for no pausing)\n") @@ -3512,7 +1520,7 @@ DEFUN (config_terminal_length, config_terminal_length_cmd,    int lines;    char *endptr = NULL; -  lines = strtol (argv[0], &endptr, 10); +  lines = strtol (argv[2]->arg, &endptr, 10);    if (lines < 0 || lines > 512 || *endptr != '\0')      {        vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3534,7 +1542,7 @@ DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,  }  DEFUN (service_terminal_length, service_terminal_length_cmd, -       "service terminal-length <0-512>", +       "service terminal-length (0-512)",         "Set up miscellaneous service\n"         "System wide terminal length configuration\n"         "Number of lines of VTY (0 means no line control)\n") @@ -3542,7 +1550,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd,    int lines;    char *endptr = NULL; -  lines = strtol (argv[0], &endptr, 10); +  lines = strtol (argv[2]->arg, &endptr, 10);    if (lines < 0 || lines > 512 || *endptr != '\0')      {        vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3554,7 +1562,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd,  }  DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, -       "no service terminal-length [<0-512>]", +       "no service terminal-length [(0-512)]",         NO_STR         "Set up miscellaneous service\n"         "System wide terminal length configuration\n" @@ -3565,15 +1573,15 @@ DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,  }  DEFUN_HIDDEN (do_echo, -	      echo_cmd, -	      "echo .MESSAGE", -	      "Echo a message back to the vty\n" -	      "The message to echo\n") +              echo_cmd, +              "echo MESSAGE...", +              "Echo a message back to the vty\n" +              "The message to echo\n")  {    char *message; -  vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""), -	   VTY_NEWLINE); +  vty_out (vty, "%s%s", ((message = argv_concat (argv, argc, 0)) ? message : ""), +           VTY_NEWLINE);    if (message)      XFREE(MTYPE_TMP, message);    return CMD_SUCCESS; @@ -3581,7 +1589,7 @@ DEFUN_HIDDEN (do_echo,  DEFUN (config_logmsg,         config_logmsg_cmd, -       "logmsg "LOG_LEVELS" .MESSAGE", +       "logmsg "LOG_LEVELS" MESSAGE...",         "Send a message to enabled logging destinations\n"         LOG_LEVEL_DESC         "The message to send\n") @@ -3589,7 +1597,7 @@ DEFUN (config_logmsg,    int level;    char *message; -  if ((level = level_match(argv[0])) == ZLOG_DISABLED) +  if ((level = level_match(argv[1]->arg)) == ZLOG_DISABLED)      return CMD_ERR_NO_MATCH;    zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); @@ -3611,8 +1619,8 @@ DEFUN (show_logging,      vty_out (vty, "disabled");    else      vty_out (vty, "level %s, facility %s, ident %s", -	     zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], -	     facility_name(zl->facility), zl->ident); +             zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], +             facility_name(zl->facility), zl->ident);    vty_out (vty, "%s", VTY_NEWLINE);    vty_out (vty, "Stdout logging: "); @@ -3620,7 +1628,7 @@ DEFUN (show_logging,      vty_out (vty, "disabled");    else      vty_out (vty, "level %s", -	     zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); +             zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);    vty_out (vty, "%s", VTY_NEWLINE);    vty_out (vty, "Monitor logging: "); @@ -3628,7 +1636,7 @@ DEFUN (show_logging,      vty_out (vty, "disabled");    else      vty_out (vty, "level %s", -	     zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); +             zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);    vty_out (vty, "%s", VTY_NEWLINE);    vty_out (vty, "File logging: "); @@ -3637,40 +1645,35 @@ DEFUN (show_logging,      vty_out (vty, "disabled");    else      vty_out (vty, "level %s, filename %s", -	     zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], -	     zl->filename); +             zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], +             zl->filename);    vty_out (vty, "%s", VTY_NEWLINE);    vty_out (vty, "Protocol name: %s%s", -  	   zlog_proto_names[zl->protocol], VTY_NEWLINE); +           zlog_proto_names[zl->protocol], VTY_NEWLINE);    vty_out (vty, "Record priority: %s%s", -  	   (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); +           (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);    vty_out (vty, "Timestamp precision: %d%s", -	   zl->timestamp_precision, VTY_NEWLINE); +           zl->timestamp_precision, VTY_NEWLINE);    return CMD_SUCCESS;  }  DEFUN (config_log_stdout,         config_log_stdout_cmd, -       "log stdout", -       "Logging control\n" -       "Set stdout logging level\n") -{ -  zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); -  return CMD_SUCCESS; -} - -DEFUN (config_log_stdout_level, -       config_log_stdout_level_cmd, -       "log stdout "LOG_LEVELS, +       "log stdout ["LOG_LEVELS"]",         "Logging control\n"         "Set stdout logging level\n"         LOG_LEVEL_DESC)  { +  if (argc == 2) +  { +    zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); +    return CMD_SUCCESS; +  }    int level; -  if ((level = level_match(argv[0])) == ZLOG_DISABLED) +  if ((level = level_match(argv[2]->arg)) == ZLOG_DISABLED)      return CMD_ERR_NO_MATCH;    zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);    return CMD_SUCCESS; @@ -3678,11 +1681,11 @@ DEFUN (config_log_stdout_level,  DEFUN (no_config_log_stdout,         no_config_log_stdout_cmd, -       "no log stdout [LEVEL]", +       "no log stdout ["LOG_LEVELS"]",         NO_STR         "Logging control\n"         "Cancel logging to stdout\n" -       "Logging level\n") +       LOG_LEVEL_DESC)  {    zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);    return CMD_SUCCESS; @@ -3690,24 +1693,19 @@ DEFUN (no_config_log_stdout,  DEFUN (config_log_monitor,         config_log_monitor_cmd, -       "log monitor", -       "Logging control\n" -       "Set terminal line (monitor) logging level\n") -{ -  zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); -  return CMD_SUCCESS; -} - -DEFUN (config_log_monitor_level, -       config_log_monitor_level_cmd, -       "log monitor "LOG_LEVELS, +       "log monitor ["LOG_LEVELS"]",         "Logging control\n"         "Set terminal line (monitor) logging level\n"         LOG_LEVEL_DESC)  { +  if (argc == 2) +  { +    zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); +    return CMD_SUCCESS; +  }    int level; -  if ((level = level_match(argv[0])) == ZLOG_DISABLED) +  if ((level = level_match(argv[2]->arg)) == ZLOG_DISABLED)      return CMD_ERR_NO_MATCH;    zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);    return CMD_SUCCESS; @@ -3715,11 +1713,11 @@ DEFUN (config_log_monitor_level,  DEFUN (no_config_log_monitor,         no_config_log_monitor_cmd, -       "no log monitor [LEVEL]", +       "no log monitor ["LOG_LEVELS"]",         NO_STR         "Logging control\n"         "Disable terminal line (monitor) logging\n" -       "Logging level\n") +       LOG_LEVEL_DESC)  {    zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);    return CMD_SUCCESS; @@ -3731,19 +1729,19 @@ set_log_file(struct vty *vty, const char *fname, int loglevel)    int ret;    char *p = NULL;    const char *fullpath; -   +    /* Path detection. */    if (! IS_DIRECTORY_SEP (*fname))      {        char cwd[MAXPATHLEN+1];        cwd[MAXPATHLEN] = '\0'; -       +        if (getcwd (cwd, MAXPATHLEN) == NULL)          {            zlog_err ("config_log_file: Unable to alloc mem!");            return CMD_WARNING;          } -       +        if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))            == NULL)          { @@ -3781,36 +1779,32 @@ set_log_file(struct vty *vty, const char *fname, int loglevel)  DEFUN (config_log_file,         config_log_file_cmd, -       "log file FILENAME", -       "Logging control\n" -       "Logging to file\n" -       "Logging filename\n") -{ -  return set_log_file(vty, argv[0], zlog_default->default_lvl); -} - -DEFUN (config_log_file_level, -       config_log_file_level_cmd, -       "log file FILENAME "LOG_LEVELS, +       "log file FILENAME [" LOG_LEVELS "]",         "Logging control\n"         "Logging to file\n"         "Logging filename\n"         LOG_LEVEL_DESC)  { -  int level; - -  if ((level = level_match(argv[1])) == ZLOG_DISABLED) -    return CMD_ERR_NO_MATCH; -  return set_log_file(vty, argv[0], level); +  if (argc == 4) +  { +    int level; +    if ((level = level_match(argv[3]->arg)) == ZLOG_DISABLED) +      return CMD_ERR_NO_MATCH; +    return set_log_file(vty, argv[2]->arg, level); +  } +  else +    return set_log_file(vty, argv[2]->arg, zlog_default->default_lvl);  }  DEFUN (no_config_log_file,         no_config_log_file_cmd, -       "no log file [FILENAME]", +       "no log file [FILENAME [LEVEL]]",         NO_STR         "Logging control\n"         "Cancel logging to file\n" -       "Logging file name\n") +       "Logging file name\n" +       "Logging file name\n" +       "Logging level\n")  {    zlog_reset_file (NULL); @@ -3822,52 +1816,37 @@ DEFUN (no_config_log_file,    return CMD_SUCCESS;  } -ALIAS (no_config_log_file, -       no_config_log_file_level_cmd, -       "no log file FILENAME LEVEL", -       NO_STR -       "Logging control\n" -       "Cancel logging to file\n" -       "Logging file name\n" -       "Logging level\n") -  DEFUN (config_log_syslog,         config_log_syslog_cmd, -       "log syslog", -       "Logging control\n" -       "Set syslog logging level\n") -{ -  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); -  return CMD_SUCCESS; -} - -DEFUN (config_log_syslog_level, -       config_log_syslog_level_cmd, -       "log syslog "LOG_LEVELS, +       "log syslog [" LOG_LEVELS "]",         "Logging control\n"         "Set syslog logging level\n"         LOG_LEVEL_DESC)  { -  int level; - -  if ((level = level_match(argv[0])) == ZLOG_DISABLED) -    return CMD_ERR_NO_MATCH; -  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level); -  return CMD_SUCCESS; +  if (argc == 3) +  { +    int level; +    if ((level = level_match (argv[2]->arg)) == ZLOG_DISABLED) +      return CMD_ERR_NO_MATCH; +    zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level); +    return CMD_SUCCESS; +  } +  else +  { +    zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); +    return CMD_SUCCESS; +  }  }  DEFUN_DEPRECATED (config_log_syslog_facility, -		  config_log_syslog_facility_cmd, -		  "log syslog facility "LOG_FACILITIES, -		  "Logging control\n" -		  "Logging goes to syslog\n" -		  "(Deprecated) Facility parameter for syslog messages\n" -		  LOG_FACILITY_DESC) +                  config_log_syslog_facility_cmd, +                  "log syslog facility "LOG_FACILITIES, +                  "Logging control\n" +                  "Logging goes to syslog\n" +                  "(Deprecated) Facility parameter for syslog messages\n" +                  LOG_FACILITY_DESC)  { -  int facility; - -  if ((facility = facility_match(argv[0])) < 0) -    return CMD_ERR_NO_MATCH; +  int facility = facility_match(argv[3]->arg);    zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);    zlog_default->facility = facility; @@ -3876,25 +1855,17 @@ DEFUN_DEPRECATED (config_log_syslog_facility,  DEFUN (no_config_log_syslog,         no_config_log_syslog_cmd, -       "no log syslog [LEVEL]", +       "no log syslog [" LOG_FACILITIES "] ["LOG_LEVELS"]",         NO_STR         "Logging control\n"         "Cancel logging to syslog\n" -       "Logging level\n") +       LOG_FACILITY_DESC +       LOG_LEVEL_DESC)  {    zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);    return CMD_SUCCESS;  } -ALIAS (no_config_log_syslog, -       no_config_log_syslog_facility_cmd, -       "no log syslog facility "LOG_FACILITIES, -       NO_STR -       "Logging control\n" -       "Logging goes to syslog\n" -       "Facility parameter for syslog messages\n" -       LOG_FACILITY_DESC) -  DEFUN (config_log_facility,         config_log_facility_cmd,         "log facility "LOG_FACILITIES, @@ -3902,37 +1873,35 @@ DEFUN (config_log_facility,         "Facility parameter for syslog messages\n"         LOG_FACILITY_DESC)  { -  int facility; +  int facility = facility_match(argv[2]->arg); -  if ((facility = facility_match(argv[0])) < 0) -    return CMD_ERR_NO_MATCH;    zlog_default->facility = facility;    return CMD_SUCCESS;  }  DEFUN (no_config_log_facility,         no_config_log_facility_cmd, -       "no log facility [FACILITY]", +       "no log facility ["LOG_FACILITIES"]",         NO_STR         "Logging control\n"         "Reset syslog facility to default (daemon)\n" -       "Syslog facility\n") +       LOG_FACILITY_DESC)  {    zlog_default->facility = LOG_DAEMON;    return CMD_SUCCESS;  }  DEFUN_DEPRECATED (config_log_trap, -		  config_log_trap_cmd, -		  "log trap "LOG_LEVELS, -		  "Logging control\n" -		  "(Deprecated) Set logging level and default for all destinations\n" -		  LOG_LEVEL_DESC) +                  config_log_trap_cmd, +                  "log trap "LOG_LEVELS, +                  "Logging control\n" +                  "(Deprecated) Set logging level and default for all destinations\n" +                  LOG_LEVEL_DESC)  {    int new_level ;    int i; -   -  if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) + +  if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED)      return CMD_ERR_NO_MATCH;    zlog_default->default_lvl = new_level; @@ -3943,12 +1912,12 @@ DEFUN_DEPRECATED (config_log_trap,  }  DEFUN_DEPRECATED (no_config_log_trap, -		  no_config_log_trap_cmd, -		  "no log trap [LEVEL]", -		  NO_STR -		  "Logging control\n" -		  "Permit all logging information\n" -		  "Logging level\n") +                  no_config_log_trap_cmd, +                  "no log trap ["LOG_LEVELS"]", +                  NO_STR +                  "Logging control\n" +                  "Permit all logging information\n" +                  LOG_LEVEL_DESC)  {    zlog_default->default_lvl = LOG_DEBUG;    return CMD_SUCCESS; @@ -3983,14 +1952,8 @@ DEFUN (config_log_timestamp_precision,         "Set the timestamp precision\n"         "Number of subsecond digits\n")  { -  if (argc != 1) -    { -      vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); -      return CMD_WARNING; -    } -    VTY_GET_INTEGER_RANGE("Timestamp Precision", -  			zlog_default->timestamp_precision, argv[0], 0, 6); +                        zlog_default->timestamp_precision, argv[3]->arg, 0, 6);    return CMD_SUCCESS;  } @@ -4021,7 +1984,7 @@ cmd_banner_motd_file (const char *file)    if (in == rpath)      {        if (host.motdfile) -	XFREE (MTYPE_HOST, host.motdfile); +        XFREE (MTYPE_HOST, host.motdfile);        host.motdfile = XSTRDUP (MTYPE_HOST, file);      }    else @@ -4038,13 +2001,13 @@ DEFUN (banner_motd_file,         "Banner from a file\n"         "Filename\n")  { -  int cmd = cmd_banner_motd_file (argv[0]); +  int cmd = cmd_banner_motd_file (argv[3]->arg);    if (cmd == CMD_ERR_NO_FILE) -    vty_out (vty, "%s does not exist", argv[0]); +    vty_out (vty, "%s does not exist", argv[3]->arg);    else if (cmd == CMD_WARNING)      vty_out (vty, "%s must be in %s", -	     argv[0], SYSCONFDIR); +             argv[0], SYSCONFDIR);    return cmd;  } @@ -4068,7 +2031,7 @@ DEFUN (no_banner_motd,         "Strings for motd\n")  {    host.motd = NULL; -  if (host.motdfile)  +  if (host.motdfile)      XFREE (MTYPE_HOST, host.motdfile);    host.motdfile = NULL;    return CMD_SUCCESS; @@ -4123,9 +2086,6 @@ install_default (enum node_type node)    install_element (node, &config_help_cmd);    install_element (node, &config_list_cmd); -  install_element (node, &config_write_terminal_cmd); -  install_element (node, &config_write_file_cmd); -  install_element (node, &config_write_memory_cmd);    install_element (node, &config_write_cmd);    install_element (node, &show_running_config_cmd);  } @@ -4134,12 +2094,6 @@ install_default (enum node_type node)  void  cmd_init (int terminal)  { -  command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>"); -  token_cr.type = TOKEN_TERMINAL; -  token_cr.terminal = TERMINAL_LITERAL; -  token_cr.cmd = command_cr; -  token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); -    /* Allocate initial top vector of commands. */    cmdvec = vector_init (VECTOR_MIN_SIZE); @@ -4207,33 +2161,24 @@ cmd_init (int terminal)        install_default (CONFIG_NODE);      } -   +    install_element (CONFIG_NODE, &hostname_cmd);    install_element (CONFIG_NODE, &no_hostname_cmd);    if (terminal)      {        install_element (CONFIG_NODE, &password_cmd); -      install_element (CONFIG_NODE, &password_text_cmd);        install_element (CONFIG_NODE, &enable_password_cmd); -      install_element (CONFIG_NODE, &enable_password_text_cmd);        install_element (CONFIG_NODE, &no_enable_password_cmd);        install_element (CONFIG_NODE, &config_log_stdout_cmd); -      install_element (CONFIG_NODE, &config_log_stdout_level_cmd);        install_element (CONFIG_NODE, &no_config_log_stdout_cmd);        install_element (CONFIG_NODE, &config_log_monitor_cmd); -      install_element (CONFIG_NODE, &config_log_monitor_level_cmd);        install_element (CONFIG_NODE, &no_config_log_monitor_cmd);        install_element (CONFIG_NODE, &config_log_file_cmd); -      install_element (CONFIG_NODE, &config_log_file_level_cmd);        install_element (CONFIG_NODE, &no_config_log_file_cmd); -      install_element (CONFIG_NODE, &no_config_log_file_level_cmd);        install_element (CONFIG_NODE, &config_log_syslog_cmd); -      install_element (CONFIG_NODE, &config_log_syslog_level_cmd); -      install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);        install_element (CONFIG_NODE, &no_config_log_syslog_cmd); -      install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);        install_element (CONFIG_NODE, &config_log_facility_cmd);        install_element (CONFIG_NODE, &no_config_log_facility_cmd);        install_element (CONFIG_NODE, &config_log_trap_cmd); @@ -4253,7 +2198,7 @@ cmd_init (int terminal)        install_element (VIEW_NODE, &show_thread_cpu_cmd);        install_element (ENABLE_NODE, &show_thread_cpu_cmd);        install_element (RESTRICTED_NODE, &show_thread_cpu_cmd); -       +        install_element (ENABLE_NODE, &clear_thread_cpu_cmd);        install_element (VIEW_NODE, &show_work_queues_cmd);        install_element (ENABLE_NODE, &show_work_queues_cmd); @@ -4263,84 +2208,87 @@ cmd_init (int terminal)    srandom(time(NULL));  } -static void -cmd_terminate_token(struct cmd_token *token) +struct cmd_token * +new_cmd_token (enum cmd_token_type type, char *text, char *desc)  { -  unsigned int i, j; -  vector keyword_vect; +  struct cmd_token *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token)); +  token->type = type; +  token->text = text; +  token->desc = desc; +  token->arg  = NULL; -  if (token->multiple) -    { -      for (i = 0; i < vector_active(token->multiple); i++) -        cmd_terminate_token(vector_slot(token->multiple, i)); -      vector_free(token->multiple); -      token->multiple = NULL; -    } +  return token; +} -  if (token->keyword) -    { -      for (i = 0; i < vector_active(token->keyword); i++) -        { -          keyword_vect = vector_slot(token->keyword, i); -          for (j = 0; j < vector_active(keyword_vect); j++) -            cmd_terminate_token(vector_slot(keyword_vect, j)); -          vector_free(keyword_vect); -        } -      vector_free(token->keyword); -      token->keyword = NULL; -    } +void +del_cmd_token (struct cmd_token *token) +{ +  if (!token) return; -  XFREE(MTYPE_CMD_TOKENS, token->cmd); -  XFREE(MTYPE_CMD_TOKENS, token->desc); +  if (token->text) +    XFREE (MTYPE_CMD_TOKENS, token->text); +  if (token->desc) +    XFREE (MTYPE_CMD_TOKENS, token->desc); +  if (token->arg) +    XFREE (MTYPE_CMD_TOKENS, token->arg); -  XFREE(MTYPE_CMD_TOKENS, token); +  XFREE (MTYPE_CMD_TOKENS, token);  } -static void -cmd_terminate_element(struct cmd_element *cmd) +struct cmd_token * +copy_cmd_token (struct cmd_token *token)  { -  unsigned int i; +  struct cmd_token *copy = new_cmd_token (token->type, NULL, NULL); +  copy->value = token->value; +  copy->max   = token->max; +  copy->min   = token->min; +  copy->text  = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL; +  copy->desc  = token->desc ? XSTRDUP (MTYPE_CMD_TOKENS, token->desc) : NULL; +  copy->arg   = token->arg  ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL; -  if (cmd->tokens == NULL) -    return; +  return copy; +} -  for (i = 0; i < vector_active(cmd->tokens); i++) -    cmd_terminate_token(vector_slot(cmd->tokens, i)); +void +del_cmd_element(struct cmd_element *cmd) +{ +  if (!cmd) return; +  XFREE (MTYPE_CMD_TOKENS, cmd->string); +  XFREE (MTYPE_CMD_TOKENS, cmd->doc); +  XFREE (MTYPE_CMD_TOKENS, cmd); +} -  vector_free(cmd->tokens); -  cmd->tokens = NULL; +struct cmd_element * +copy_cmd_element(struct cmd_element *cmd) +{ +  struct cmd_element *el = XMALLOC(MTYPE_CMD_TOKENS, sizeof (struct cmd_element)); +  el->string = cmd->string ? XSTRDUP(MTYPE_CMD_TOKENS, cmd->string) : NULL; +  el->func = cmd->func; +  el->doc = cmd->doc ? XSTRDUP(MTYPE_CMD_TOKENS, cmd->doc) : NULL; +  el->daemon = cmd->daemon; +  el->attr = cmd->attr; +  return el;  }  void  cmd_terminate ()  { -  unsigned int i, j;    struct cmd_node *cmd_node; -  struct cmd_element *cmd_element; -  vector cmd_node_v;    if (cmdvec)      { -      for (i = 0; i < vector_active (cmdvec); i++)  +      for (unsigned int i = 0; i < vector_active (cmdvec); i++)          if ((cmd_node = vector_slot (cmdvec, i)) != NULL) -          { -            cmd_node_v = cmd_node->cmd_vector; - -            for (j = 0; j < vector_active (cmd_node_v); j++) -              if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) -                cmd_terminate_element(cmd_element); - -            vector_free (cmd_node_v); -          } +        { +          // deleting the graph delets the cmd_element as well +          graph_delete_graph (cmd_node->cmdgraph); +          vector_free (cmd_node->cmd_vector); +        }        vector_free (cmdvec);        cmdvec = NULL;      } -  if (command_cr) -    XFREE(MTYPE_CMD_TOKENS, command_cr); -  if (token_cr.desc) -    XFREE(MTYPE_CMD_TOKENS, token_cr.desc);    if (host.name)      XFREE (MTYPE_HOST, host.name);    if (host.password) diff --git a/lib/command.h b/lib/command.h index 9ee4f2db3d..ddb61c686f 100644 --- a/lib/command.h +++ b/lib/command.h @@ -8,7 +8,7 @@   * 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 @@ -26,6 +26,7 @@  #include "vector.h"  #include "vty.h"  #include "lib/route_types.h" +#include "graph.h"  #include "memory.h"  DECLARE_MTYPE(HOST) @@ -66,135 +67,138 @@ struct host  };  /* There are some command levels which called from command node. */ -enum node_type  +enum node_type  { -  AUTH_NODE,			/* Authentication mode of vty interface. */ -  RESTRICTED_NODE,		/* Restricted view mode */  -  VIEW_NODE,			/* View node. Default mode of vty interface. */ -  AUTH_ENABLE_NODE,		/* Authentication mode for change enable. */ -  ENABLE_NODE,			/* Enable node. */ -  CONFIG_NODE,			/* Config node. Default mode of config file. */ -  SERVICE_NODE, 		/* Service node. */ -  DEBUG_NODE,			/* Debug node. */ +  AUTH_NODE,                    /* Authentication mode of vty interface. */ +  RESTRICTED_NODE,              /* Restricted view mode */ +  VIEW_NODE,                    /* View node. Default mode of vty interface. */ +  AUTH_ENABLE_NODE,             /* Authentication mode for change enable. */ +  ENABLE_NODE,                  /* Enable node. */ +  CONFIG_NODE,                  /* Config node. Default mode of config file. */ +  SERVICE_NODE,                 /* Service node. */ +  DEBUG_NODE,                   /* Debug node. */    VRF_DEBUG_NODE,               /* Vrf Debug node. */ -  AAA_NODE,			/* AAA node. */ -  KEYCHAIN_NODE,		/* Key-chain node. */ -  KEYCHAIN_KEY_NODE,		/* Key-chain key node. */ -  NS_NODE,			/* Logical-Router node. */ -  VRF_NODE,		        /* VRF mode node. */ -  INTERFACE_NODE,		/* Interface mode node. */ -  ZEBRA_NODE,			/* zebra connection node. */ -  TABLE_NODE,			/* rtm_table selection node. */ -  RIP_NODE,			/* RIP protocol mode node. */  -  RIPNG_NODE,			/* RIPng protocol mode node. */ -  BGP_NODE,			/* BGP protocol mode which includes BGP4+ */ -  BGP_VPNV4_NODE,		/* BGP MPLS-VPN PE exchange. */ -  BGP_VPNV6_NODE,		/* BGP MPLS-VPN PE exchange. */ -  BGP_IPV4_NODE,		/* BGP IPv4 unicast address family.  */ -  BGP_IPV4M_NODE,		/* BGP IPv4 multicast address family.  */ -  BGP_IPV6_NODE,		/* BGP IPv6 address family */ -  BGP_IPV6M_NODE,		/* BGP IPv6 multicast address family. */ -  BGP_ENCAP_NODE,		/* BGP ENCAP SAFI */ -  BGP_ENCAPV6_NODE,		/* BGP ENCAP SAFI */ -  OSPF_NODE,			/* OSPF protocol mode */ -  OSPF6_NODE,			/* OSPF protocol for IPv6 mode */ -  ISIS_NODE,			/* ISIS protocol mode */ -  PIM_NODE,			/* PIM protocol mode */ -  MASC_NODE,			/* MASC for multicast.  */ -  IRDP_NODE,			/* ICMP Router Discovery Protocol mode. */  -  IP_NODE,			/* Static ip route node. */ -  ACCESS_NODE,			/* Access list node. */ -  PREFIX_NODE,			/* Prefix list node. */ -  ACCESS_IPV6_NODE,		/* Access list node. */ -  PREFIX_IPV6_NODE,		/* Prefix list node. */ -  AS_LIST_NODE,			/* AS list node. */ -  COMMUNITY_LIST_NODE,		/* Community list node. */ -  RMAP_NODE,			/* Route map node. */ -  SMUX_NODE,			/* SNMP configuration node. */ -  DUMP_NODE,			/* Packet dump node. */ -  FORWARDING_NODE,		/* IP forwarding node. */ +  AAA_NODE,                     /* AAA node. */ +  KEYCHAIN_NODE,                /* Key-chain node. */ +  KEYCHAIN_KEY_NODE,            /* Key-chain key node. */ +  NS_NODE,                      /* Logical-Router node. */ +  VRF_NODE,                     /* VRF mode node. */ +  INTERFACE_NODE,               /* Interface mode node. */ +  ZEBRA_NODE,                   /* zebra connection node. */ +  TABLE_NODE,                   /* rtm_table selection node. */ +  RIP_NODE,                     /* RIP protocol mode node. */ +  RIPNG_NODE,                   /* RIPng protocol mode node. */ +  BGP_NODE,                     /* BGP protocol mode which includes BGP4+ */ +  BGP_VPNV4_NODE,               /* BGP MPLS-VPN PE exchange. */ +  BGP_VPNV6_NODE,               /* BGP MPLS-VPN PE exchange. */ +  BGP_IPV4_NODE,                /* BGP IPv4 unicast address family.  */ +  BGP_IPV4M_NODE,               /* BGP IPv4 multicast address family.  */ +  BGP_IPV6_NODE,                /* BGP IPv6 address family */ +  BGP_IPV6M_NODE,               /* BGP IPv6 multicast address family. */ +  BGP_ENCAP_NODE,               /* BGP ENCAP SAFI */ +  BGP_ENCAPV6_NODE,             /* BGP ENCAP SAFI */ +  OSPF_NODE,                    /* OSPF protocol mode */ +  OSPF6_NODE,                   /* OSPF protocol for IPv6 mode */ +  ISIS_NODE,                    /* ISIS protocol mode */ +  PIM_NODE,                     /* PIM protocol mode */ +  MASC_NODE,                    /* MASC for multicast.  */ +  IRDP_NODE,                    /* ICMP Router Discovery Protocol mode. */ +  IP_NODE,                      /* Static ip route node. */ +  ACCESS_NODE,                  /* Access list node. */ +  PREFIX_NODE,                  /* Prefix list node. */ +  ACCESS_IPV6_NODE,             /* Access list node. */ +  PREFIX_IPV6_NODE,             /* Prefix list node. */ +  AS_LIST_NODE,                 /* AS list node. */ +  COMMUNITY_LIST_NODE,          /* Community list node. */ +  RMAP_NODE,                    /* Route map node. */ +  SMUX_NODE,                    /* SNMP configuration node. */ +  DUMP_NODE,                    /* Packet dump node. */ +  FORWARDING_NODE,              /* IP forwarding node. */    PROTOCOL_NODE,                /* protocol filtering node */ -  VTY_NODE,			/* Vty node. */ -  LINK_PARAMS_NODE,		/* Link-parameters node */ +  VTY_NODE,                     /* Vty node. */ +  LINK_PARAMS_NODE,             /* Link-parameters node */  };  /* Node which has some commands and prompt string and configuration     function pointer . */ -struct cmd_node  +struct cmd_node  {    /* Node index. */ -  enum node_type node;		 +  enum node_type node;    /* Prompt character at vty interface. */ -  const char *prompt;			 +  const char *prompt;    /* Is this node's configuration goes to vtysh ? */    int vtysh; -   +    /* Node's configuration write function */    int (*func) (struct vty *); +  /* Node's command graph */ +  struct graph *cmdgraph; +    /* Vector of this node's command list. */ -  vector cmd_vector;	 +  vector cmd_vector;  }; -enum +/** + * Types for tokens. + * + * The type determines what kind of data the token can match (in the + * matching use case) or hold (in the argv use case). + */ +enum cmd_token_type  { -  CMD_ATTR_DEPRECATED = 1, -  CMD_ATTR_HIDDEN, +  WORD_TKN,         // words +  NUMBER_TKN,       // integral numbers +  VARIABLE_TKN,     // almost anything +  RANGE_TKN,        // integer range +  IPV4_TKN,         // IPV4 addresses +  IPV4_PREFIX_TKN,  // IPV4 network prefixes +  IPV6_TKN,         // IPV6 prefixes +  IPV6_PREFIX_TKN,  // IPV6 network prefixes + +  /* plumbing types */ +  SELECTOR_TKN,     // marks beginning of selector +  OPTION_TKN,       // marks beginning of option +  NUL_TKN,          // dummy token +  START_TKN,        // first token in line +  END_TKN,          // last token in line  }; -/* Structure of command element. */ -struct cmd_element  +/** + * Token struct. + */ +struct cmd_token  { -  const char *string;			/* Command specification by string. */ -  int (*func) (struct cmd_element *, struct vty *, int, const char *[]); -  const char *doc;			/* Documentation of this command. */ -  int daemon;                   /* Daemon to which this command belong. */ -  vector tokens;		/* Vector of cmd_tokens */ -  u_char attr;			/* Command attributes */ -}; +  enum cmd_token_type type;   // token type +  char *text;                   // token text +  char *desc;                   // token description -enum cmd_token_type -{ -  TOKEN_TERMINAL = 0, -  TOKEN_MULTIPLE, -  TOKEN_KEYWORD, +  long long value;              // for numeric types +  long long min, max;           // for ranges + +  char *arg;                    // user input that matches this token  }; -enum cmd_terminal_type +enum  { -  _TERMINAL_BUG = 0, -  TERMINAL_LITERAL, -  TERMINAL_OPTION, -  TERMINAL_VARIABLE, -  TERMINAL_VARARG, -  TERMINAL_RANGE, -  TERMINAL_IPV4, -  TERMINAL_IPV4_PREFIX, -  TERMINAL_IPV6, -  TERMINAL_IPV6_PREFIX, +  CMD_ATTR_DEPRECATED = 1, +  CMD_ATTR_HIDDEN,  }; -/* argument to be recorded on argv[] if it's not a literal */ -#define TERMINAL_RECORD(t) ((t) >= TERMINAL_OPTION) - -/* Command description structure. */ -struct cmd_token +/* Structure of command element. */ +struct cmd_element  { -  enum cmd_token_type type; -  enum cmd_terminal_type terminal; - -  /* Used for type == MULTIPLE */ -  vector multiple; /* vector of cmd_token, type == FINAL */ - -  /* Used for type == KEYWORD */ -  vector keyword; /* vector of vector of cmd_tokens */ +  const char *string;           /* Command specification by string. */ +  const char *doc;              /* Documentation of this command. */ +  int daemon;                   /* Daemon to which this command belong. */ +  u_char attr;                  /* Command attributes */ -  /* Used for type == TERMINAL */ -  char *cmd;                    /* Command string. */ -  char *desc;                    /* Command's description. */ +  /* handler function for command */ +  int (*func) (struct cmd_element *, struct vty *, int, struct cmd_token *[]);  };  /* Return value of the commands. */ @@ -215,7 +219,7 @@ struct cmd_token  #define CMD_ARGC_MAX   25  /* Turn off these macros when uisng cpp with extract.pl */ -#ifndef VTYSH_EXTRACT_PL   +#ifndef VTYSH_EXTRACT_PL  /* helper defines for end-user DEFUN* macros */  #define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ @@ -229,179 +233,15 @@ struct cmd_token    };  #define DEFUN_CMD_FUNC_DECL(funcname) \ -  static int funcname (struct cmd_element *, struct vty *, int, const char *[]); +  static int funcname (struct cmd_element *, struct vty *, int, struct cmd_token *[]);  #define DEFUN_CMD_FUNC_TEXT(funcname) \    static int funcname \      (struct cmd_element *self __attribute__ ((unused)), \       struct vty *vty __attribute__ ((unused)), \       int argc __attribute__ ((unused)), \ -     const char *argv[] __attribute__ ((unused)) ) +     struct cmd_token *argv[] __attribute__ ((unused)) ) -/* DEFUN for vty command interafce. Little bit hacky ;-). - * - * DEFUN(funcname, cmdname, cmdstr, helpstr) - * - * funcname - * ======== - * - * Name of the function that will be defined. - * - * cmdname - * ======= - * - * Name of the struct that will be defined for the command. - * - * cmdstr - * ====== - * - * The cmdstr defines the command syntax. It is used by the vty subsystem - * and vtysh to perform matching and completion in the cli. So you have to take - * care to construct it adhering to the following grammar. The names used - * for the production rules losely represent the names used in lib/command.c - * - * cmdstr = cmd_token , { " " , cmd_token } ; - * - * cmd_token = cmd_terminal - *           | cmd_multiple - *           | cmd_keyword ; - * - * cmd_terminal_fixed = fixed_string - *                    | variable - *                    | range - *                    | ipv4 - *                    | ipv4_prefix - *                    | ipv6 - *                    | ipv6_prefix ; - * - * cmd_terminal = cmd_terminal_fixed - *              | option - *              | vararg ; - * - * multiple_part = cmd_terminal_fixed ; - * cmd_multiple = "(" , multiple_part , ( "|" | { "|" , multiple_part } ) , ")" ; - * - * keyword_part = fixed_string , { " " , ( cmd_terminal_fixed | cmd_multiple ) } ; - * cmd_keyword = "{" , keyword_part , { "|" , keyword_part } , "}" ; - * - * lowercase = "a" | ... | "z" ; - * uppercase = "A" | ... | "Z" ; - * digit = "0" | ... | "9" ; - * number = digit , { digit } ; - * - * fixed_string = (lowercase | digit) , { lowercase | digit | uppercase | "-" | "_" } ; - * variable = uppercase , { uppercase | "_" } ; - * range = "<" , number , "-" , number , ">" ; - * ipv4 = "A.B.C.D" ; - * ipv4_prefix = "A.B.C.D/M" ; - * ipv6 = "X:X::X:X" ; - * ipv6_prefix = "X:X::X:X/M" ; - * option = "[" , variable , "]" ; - * vararg = "." , variable ; - * - * To put that all in a textual description: A cmdstr is a sequence of tokens, - * separated by spaces. - * - * Terminal Tokens: - * - * A very simple cmdstring would be something like: "show ip bgp". It consists - * of three Terminal Tokens, each containing a fixed string. When this command - * is called, no arguments will be passed down to the function implementing it, - * as it only consists of fixed strings. - * - * Apart from fixed strings, Terminal Tokens can also contain variables: - * An example would be "show ip bgp A.B.C.D". This command expects an IPv4 - * as argument. As this is a variable, the IP address entered by the user will - * be passed down as an argument. Apart from two exceptions, the other options - * for Terminal Tokens behave exactly as we just discussed and only make a - * difference for the CLI. The two exceptions will be discussed in the next - * paragraphs. - * - * A Terminal Token can contain a so called option match. This is a simple - * string variable that the user may omit. An example would be: - * "show interface [IFNAME]". If the user calls this without an interface as - * argument, no arguments will be passed down to the function implementing - * this command. Otherwise, the interface name will be provided to the function - * as a regular argument. - - * Also, a Terminal Token can contain a so called vararg. This is used e.g. in - * "show ip bgp regexp .LINE". The last token is a vararg match and will - * consume all the arguments the user inputs on the command line and append - * those to the list of arguments passed down to the function implementing this - * command. (Therefore, it doesn't make much sense to have any tokens after a - * vararg because the vararg will already consume all the words the user entered - * in the CLI) - * - * Multiple Tokens: - * - * The Multiple Token type can be used if there are multiple possibilities what - * arguments may be used for a command, but it should map to the same function - * nonetheless. An example would be "ip route A.B.C.D/M (reject|blackhole)" - * In that case both "reject" and "blackhole" would be acceptable as last - * arguments. The words matched by Multiple Tokens are always added to the - * argument list, even if they are matched by fixed strings. Such a Multiple - * Token can contain almost any type of token that would also be acceptable - * for a Terminal Token, the exception are optional variables and varag. - * - * There is one special case that is used in some places of Quagga that should be - * pointed out here shortly. An example would be "password (8|) WORD". This - * construct is used to have fixed strings communicated as arguments. (The "8" - * will be passed down as an argument in this case) It does not mean that - * the "8" is optional. Another historic and possibly surprising property of - * this construct is that it consumes two parts of helpstr. (Help - * strings will be explained later) - * - * Keyword Tokens: - * - * There are commands that take a lot of different and possibly optional arguments. - * An example from ospf would be the "default-information originate" command. This - * command takes a lot of optional arguments that may be provided in any order. - * To accomodate such commands, the Keyword Token has been implemented. - * Using the keyword token, the "default-information originate" command and all - * its possible options can be represented using this single cmdstr: - * "default-information originate \ - *  {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}" - * - * Keywords always start with a fixed string and may be followed by arguments. - * Except optional variables and vararg, everything is permitted here. - * - * For the special case of a keyword without arguments, either NULL or the - * keyword itself will be pushed as an argument, depending on whether the - * keyword is present. - * For the other keywords, arguments will be only pushed for - * variables/Multiple Tokens. If the keyword is not present, the arguments that - * would have been pushed will be substituted by NULL. - * - * A few examples: - *   "default information originate metric-type 1 metric 1000" - * would yield the following arguments: - *   { NULL, "1000", "1", NULL } - * - *   "default information originate always route-map RMAP-DEFAULT" - * would yield the following arguments: - *   { "always", NULL, NULL, "RMAP-DEFAULT" } - * - * helpstr - * ======= - * - * The helpstr is used to show a short explantion for the commands that - * are available when the user presses '?' on the CLI. It is the concatenation - * of the helpstrings for all the tokens that make up the command. - * - * There should be one helpstring for each token in the cmdstr except those - * containing other tokens, like Multiple or Keyword Tokens. For those, there - * will only be the helpstrings of the contained tokens. - * - * The individual helpstrings are expected to be in the same order as their - * respective Tokens appear in the cmdstr. They should each be terminated with - * a linefeed. The last helpstring should be terminated with a linefeed as well. - * - * Care should also be taken to avoid having similar tokens with different - * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp". - * they both contain a helpstring for "show", but only one will be displayed - * when the user enters "sh?". If those two helpstrings differ, it is not - * defined which one will be shown and the behavior is therefore unpredictable. - */  #define DEFUN(funcname, cmdname, cmdstr, helpstr) \    DEFUN_CMD_FUNC_DECL(funcname) \    DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ @@ -514,16 +354,16 @@ struct cmd_token  #define IP6_STR "IPv6 Information\n"  #define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n"  #define OSPF6_ROUTER_STR "Enable a routing process\n" -#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" -#define SECONDS_STR "<1-65535> Seconds\n" +#define OSPF6_INSTANCE_STR "(1-65535) Instance ID\n" +#define SECONDS_STR "(1-65535) Seconds\n"  #define ROUTE_STR "Routing Table\n"  #define PREFIX_LIST_STR "Build a prefix list\n"  #define OSPF6_DUMP_TYPE_LIST \ -"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" +"<neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr>"  #define ISIS_STR "IS-IS information\n"  #define AREA_TAG_STR "[area tag]\n" -#define COMMUNITY_AANN_STR "Community number where AA and NN are <0-65535>\n" -#define COMMUNITY_VAL_STR  "Community number in AA:NN format (where AA and NN are <0-65535>) or local-AS|no-advertise|no-export|internet or additive\n" +#define COMMUNITY_AANN_STR "Community number where AA and NN are (0-65535)\n" +#define COMMUNITY_VAL_STR  "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet or additive\n"  #define MPLS_TE_STR "MPLS-TE specific commands\n"  #define LINK_PARAMS_STR "Configure interface link parameters\n"  #define OSPF_RI_STR "OSPF Router Information specific commands\n" @@ -534,25 +374,25 @@ struct cmd_token  /* IPv4 only machine should not accept IPv6 address for peer's IP     address.  So we replace VTY command string like below. */  #ifdef HAVE_IPV6 -#define NEIGHBOR_CMD       "neighbor (A.B.C.D|X:X::X:X) " -#define NO_NEIGHBOR_CMD    "no neighbor (A.B.C.D|X:X::X:X) " +#define NEIGHBOR_CMD       "neighbor <A.B.C.D|X:X::X:X> " +#define NO_NEIGHBOR_CMD    "no neighbor <A.B.C.D|X:X::X:X> "  #define NEIGHBOR_ADDR_STR  "Neighbor address\nIPv6 address\n" -#define NEIGHBOR_CMD2      "neighbor (A.B.C.D|X:X::X:X|WORD) " -#define NO_NEIGHBOR_CMD2   "no neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NEIGHBOR_CMD2      "neighbor <A.B.C.D|X:X::X:X|WORD> " +#define NO_NEIGHBOR_CMD2   "no neighbor <A.B.C.D|X:X::X:X|WORD> "  #define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nInterface name or neighbor tag\n"  #define NEIGHBOR_ADDR_STR3 "Neighbor address\nIPv6 address\nInterface name\n"  #else  #define NEIGHBOR_CMD       "neighbor A.B.C.D "  #define NO_NEIGHBOR_CMD    "no neighbor A.B.C.D "  #define NEIGHBOR_ADDR_STR  "Neighbor address\n" -#define NEIGHBOR_CMD2      "neighbor (A.B.C.D|WORD) " -#define NO_NEIGHBOR_CMD2   "no neighbor (A.B.C.D|WORD) " +#define NEIGHBOR_CMD2      "neighbor <A.B.C.D|WORD> " +#define NO_NEIGHBOR_CMD2   "no neighbor <A.B.C.D|WORD> "  #define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n"  #endif /* HAVE_IPV6 */  /* Dynamic neighbor (listen range) configuration */  #ifdef HAVE_IPV6 -#define LISTEN_RANGE_CMD      "bgp listen range (A.B.C.D/M|X:X::X:X/M) " +#define LISTEN_RANGE_CMD      "bgp listen range <A.B.C.D/M|X:X::X:X/M> "  #define LISTEN_RANGE_ADDR_STR "Neighbor address\nNeighbor IPv6 address\n"  #else  #define LISTEN_RANGE_CMD      "bgp listen range A.B.C.D/M " @@ -567,7 +407,7 @@ extern void install_element (enum node_type, struct cmd_element *);  /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated     string with a space between each element (allocated using     XMALLOC(MTYPE_TMP)).  Returns NULL if shift >= argc. */ -extern char *argv_concat (const char **argv, int argc, int shift); +extern char *argv_concat (struct cmd_token **argv, int argc, int shift);  extern vector cmd_make_strvec (const char *);  extern void cmd_free_strvec (vector); @@ -583,6 +423,20 @@ extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element  extern void cmd_init (int);  extern void cmd_terminate (void); +/* memory management for cmd_element */ +void +del_cmd_element(struct cmd_element *); +struct cmd_element * +copy_cmd_element(struct cmd_element *cmd); + +/* memory management for cmd_token */ +struct cmd_token * +new_cmd_token (enum cmd_token_type, char *, char *); +void +del_cmd_token (struct cmd_token *); +struct cmd_token * +copy_cmd_token (struct cmd_token *); +  /* Export typical functions. */  extern struct cmd_element config_end_cmd;  extern struct cmd_element config_exit_cmd; @@ -597,8 +451,9 @@ extern void print_version (const char *);  extern int cmd_banner_motd_file (const char *);  /* struct host global, ick */ -extern struct host host;  +extern struct host host; + +/* text for <cr> command */ +#define CMD_CR_TEXT "<cr>" -/* "<cr>" global */ -extern char *command_cr;  #endif /* _ZEBRA_COMMAND_H */ diff --git a/lib/command_lex.l b/lib/command_lex.l new file mode 100644 index 0000000000..5c709dce22 --- /dev/null +++ b/lib/command_lex.l @@ -0,0 +1,71 @@ +/* + * 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" + +extern void set_lexer_string (const char *); +extern void cleanup_lexer (void); +YY_BUFFER_STATE buffer; +%} + +WORD            (\-|\+)?[a-z\*][-+_a-zA-Z0-9\*]* +IPV4            A\.B\.C\.D +IPV4_PREFIX     A\.B\.C\.D\/M +IPV6            X:X::X:X +IPV6_PREFIX     X:X::X:X\/M +VARIABLE        [A-Z][-_a-zA-Z:0-9]+ +NUMBER          (\-|\+)?[0-9]{1,20} +RANGE           \({NUMBER}[ ]?\-[ ]?{NUMBER}\) + +/* yytext shall be a pointer */ +%pointer +%option noyywrap +%option nounput +%option noinput +%option outfile="command_lex.c" + +%% +[ /t]           /* ignore whitespace */; +{WORD}          {yylval.string = XSTRDUP(MTYPE_TMP, yytext); return WORD;} +{IPV4}          {yylval.string = XSTRDUP(MTYPE_TMP, yytext); return IPV4;} +{IPV4_PREFIX}   {yylval.string = XSTRDUP(MTYPE_TMP, yytext); return IPV4_PREFIX;} +{IPV6}          {yylval.string = XSTRDUP(MTYPE_TMP, yytext); return IPV6;} +{IPV6_PREFIX}   {yylval.string = XSTRDUP(MTYPE_TMP, yytext); return IPV6_PREFIX;} +{VARIABLE}      {yylval.string = XSTRDUP(MTYPE_TMP, yytext); return VARIABLE;} +{RANGE}         {yylval.string = XSTRDUP(MTYPE_TMP, yytext); return RANGE;} +.               {return yytext[0];} +%% + +void +set_lexer_string (const char *string) +{ +  buffer = yy_scan_string (string); +} + +void +cleanup_lexer () +{ +  yy_delete_buffer (buffer); +} diff --git a/lib/command_match.c b/lib/command_match.c new file mode 100644 index 0000000000..dd68ca477e --- /dev/null +++ b/lib/command_match.c @@ -0,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; +} diff --git a/lib/command_match.h b/lib/command_match.h new file mode 100644 index 0000000000..728d9c1d95 --- /dev/null +++ b/lib/command_match.h @@ -0,0 +1,95 @@ +/* + * 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 "graph.h" +#include "linklist.h" +#include "command.h" + +/* 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 */ +enum matcher_rv +{ +  MATCHER_NO_MATCH, +  MATCHER_INCOMPLETE, +  MATCHER_AMBIGUOUS, +  MATCHER_OK, +}; + +/* completion match types */ +enum match_type +{ +  no_match, +  partly_match, +  exact_match +}; + +/* 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 \ +   || (matcher_rv) == MATCHER_NO_MATCH \ +   || (matcher_rv) == MATCHER_AMBIGUOUS \ +  ) + +/** + * Attempt to find an exact command match for a line of user input. + * + * @param[in] cmdgraph 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 +command_match (struct graph *cmdgraph, +               vector vline, +               struct list **argv, +               struct cmd_element **element); + +/** + * Compiles possible completions for a given line of user input. + * + * @param[in] start the start node of the DFA to match against + * @param[in] vline vectorized input string + * @param[in] completions pointer to list of cmd_token representing + *            acceptable next inputs + */ +enum matcher_rv +command_complete (struct graph *cmdgraph, +                  vector vline, +                  struct list **completions); + +#endif /* _ZEBRA_COMMAND_MATCH_H */ diff --git a/lib/command_parse.y b/lib/command_parse.y new file mode 100644 index 0000000000..6a5d75aabf --- /dev/null +++ b/lib/command_parse.y @@ -0,0 +1,548 @@ +/* + * 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" + +  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 */ +  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 = +    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; +  free ($1); +} +; + +simple_token: +  literal_token +| placeholder_token +; + +compound_token: +  selector +| option +; + +literal_token: WORD +{ +  $$ = new_token_node (graph, WORD_TKN, strdup($1), doc_next()); +  free ($1); +} +; + +placeholder_token: +  IPV4 +{ +  $$ = new_token_node (graph, IPV4_TKN, strdup($1), doc_next()); +  free ($1); +} +| IPV4_PREFIX +{ +  $$ = new_token_node (graph, IPV4_PREFIX_TKN, strdup($1), doc_next()); +  free ($1); +} +| IPV6 +{ +  $$ = new_token_node (graph, IPV6_TKN, strdup($1), doc_next()); +  free ($1); +} +| IPV6_PREFIX +{ +  $$ = new_token_node (graph, IPV6_PREFIX_TKN, strdup($1), doc_next()); +  free ($1); +} +| VARIABLE +{ +  $$ = new_token_node (graph, VARIABLE_TKN, strdup($1), doc_next()); +  free ($1); +} +| RANGE +{ +  $$ = 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 '>' +{ +  $$ = 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); +  free ($2); +}; + +selector_seq_seq: +  selector_seq_seq '|' selector_token_seq +{ +  $$ = 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); +  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); +  free ($1); +  free ($3); +} +| selector_token_seq '|' selector_token_seq +{ +  $$ = 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); +  free ($1); +  free ($3); +} +; + +selector_token_seq: +  simple_token +{ +  $$ = malloc (sizeof (struct subgraph)); +  $$->start = $$->end = $1; +} +| selector_token_seq selector_token +{ +  $$ = malloc (sizeof (struct subgraph)); +  graph_add_edge ($1->end, $2->start); +  $$->start = $1->start; +  $$->end   = $2->end; +  free ($1); +  free ($2); +} +; + +selector_token: +  simple_token +{ +  $$ = malloc (sizeof (struct subgraph)); +  $$->start = $$->end = $1; +} +| option +; + +/* [option] productions */ +option: '[' option_token_seq ']' +{ +  // make a new option +  $$ = 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); +  free ($2); +} +; + +option_token_seq: +  option_token +| option_token_seq option_token +{ +  $$ = malloc (sizeof (struct subgraph)); +  graph_add_edge ($1->end, $2->start); +  $$->start = $1->start; +  $$->end   = $2->end; +  free ($1); +} +; + +option_token: +  simple_token +{ +  $$ = 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, +                    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 --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c new file mode 100644 index 0000000000..41fee1c1cb --- /dev/null +++ b/lib/grammar_sandbox.c @@ -0,0 +1,383 @@ +/* + * 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 "graph.h" +#include "command_parse.h" +#include "command_match.h" + +#define GRAMMAR_STR "CLI grammar sandbox\n" + +/** headers **/ +void +grammar_sandbox_init (void); +void +pretty_print_graph (struct graph_node *, int); +void +init_cmdgraph (struct graph **); +vector +completions_to_vec (struct list *); +int +compare_completions (const void *, const void *); + +/** shim interface commands **/ +struct graph *nodegraph; + +DEFUN (grammar_test, +       grammar_test_cmd, +       "grammar parse .COMMAND", +       GRAMMAR_STR +       "command to pass to new parser\n") +{ +  // make a string from tokenized command line +  char *command = argv_concat (argv, argc, 0); + +  // create cmd_element for parser +  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 the command and install it into the command graph +  command_parse_format (nodegraph, cmd); + +  return CMD_SUCCESS; +} + +DEFUN (grammar_test_complete, +       grammar_test_complete_cmd, +       "grammar complete .COMMAND", +       GRAMMAR_STR +       "attempt to complete input on DFA\n" +       "command to complete") +{ +  char *cmdstr = argv_concat (argv, argc, 0); +  vector command = cmd_make_strvec (cmdstr); + +  // generate completions of user input +  struct list *completions; +  enum matcher_rv result = command_complete (nodegraph, command, &completions); + +  // print completions or relevant error message +  if (!MATCHER_ERROR(result)) +    { +      vector comps = completions_to_vec (completions); +      struct cmd_token_t *tkn; + +      // calculate length of longest tkn->text in completions +      unsigned int width = 0, i = 0; +      for (i = 0; i < vector_active (comps); i++) { +        tkn = vector_slot (comps, i); +        unsigned int len = strlen (tkn->text); +        width = len > width ? len : width; +      } + +      // print completions +      for (i = 0; i < vector_active (comps); i++) { +        tkn = vector_slot (comps, i); +        fprintf (stdout, "  %-*s  %s%s", width, tkn->text, tkn->desc, "\n"); +      } + +      for (i = 0; i < vector_active (comps); i++) +        del_cmd_token ((struct cmd_token_t *) vector_slot (comps, i)); +      vector_free (comps); +    } +  else +    fprintf (stdout, "%% No match%s", "\n"); + +  // free resources +  list_delete (completions); +  cmd_free_strvec (command); +  free (cmdstr); + +  return CMD_SUCCESS; +} + +DEFUN (grammar_test_match, +       grammar_test_match_cmd, +       "grammar match .COMMAND", +       GRAMMAR_STR +       "attempt to match input on DFA\n" +       "command to match") +{ +  if (argv[0][0] == '#') +    return CMD_SUCCESS; + +  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 = command_match (nodegraph, command, &argvv, &element); + +  // print completions or relevant error message +  if (element) +    { +      fprintf (stdout, "Matched: %s%s", element->string, "\n"); +      struct listnode *ln; +      struct cmd_token_t *token; +      for (ALL_LIST_ELEMENTS_RO(argvv,ln,token)) +        fprintf (stdout, "%s -- %s%s", token->text, token->arg, "\n"); + +      fprintf (stdout, "func: %p%s", element->func, "\n"); + +      list_delete (argvv); +      del_cmd_element (element); +    } +  else { +     assert(MATCHER_ERROR(result)); +     switch (result) { +       case MATCHER_NO_MATCH: +          fprintf (stdout, "%% Unknown command%s", "\n"); +          break; +       case MATCHER_INCOMPLETE: +          fprintf (stdout, "%% Incomplete command%s", "\n"); +          break; +       case MATCHER_AMBIGUOUS: +          fprintf (stdout, "%% Ambiguous command%s", "\n"); +          break; +       default: +          fprintf (stdout, "%% Unknown error%s", "\n"); +          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 = XSTRDUP (MTYPE_CMD_TOKENS, "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG"); +  cmd->doc = XSTRDUP (MTYPE_CMD_TOKENS, +             "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 +  command_parse_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 (vector_slot (nodegraph->nodes, 0), 0); +  return CMD_SUCCESS; +} + +DEFUN (grammar_init_graph, +       grammar_init_graph_cmd, +       "grammar init graph", +       GRAMMAR_STR +       "(re)initialize graph\n") +{ +  graph_delete_graph (nodegraph); +  init_cmdgraph (&nodegraph); +  return CMD_SUCCESS; +} + +/* this is called in vtysh.c to set up the testing shim */ +void grammar_sandbox_init() { +  init_cmdgraph (&nodegraph); + +  // install all enable elements +  install_element (ENABLE_NODE, &grammar_test_cmd); +  install_element (ENABLE_NODE, &grammar_test_show_cmd); +  install_element (ENABLE_NODE, &grammar_test_match_cmd); +  install_element (ENABLE_NODE, &grammar_test_complete_cmd); +  install_element (ENABLE_NODE, &grammar_test_doc_cmd); +  install_element (ENABLE_NODE, &grammar_init_graph_cmd); +} + + +/** + * Pretty-prints a graph, assuming it is a tree. + * + * @param start the node to take as the root + * @param level indent level for recursive calls, always pass 0 + */ +void +pretty_print_graph (struct graph_node *start, int level) +{ +  // print this node +  struct cmd_token_t *tok = start->data; +  fprintf (stdout, "%s[%d] ", tok->text, tok->type); + +  int numto = vector_active (start->to); +  if (numto) +    { +      if (numto > 1) +        fprintf (stdout, "\n"); +      for (unsigned int i = 0; i < vector_active (start->to); i++) +        { +          struct graph_node *adj = vector_slot (start->to, i); +          // if we're listing multiple children, indent! +          if (numto > 1) +            for (int j = 0; j < level+1; j++) +              fprintf (stdout, "    "); +          // if this node is a vararg, just print * +          if (adj == start) +            fprintf (stdout, "*"); +          else +            pretty_print_graph (adj, numto > 1 ? level+1 : level); +        } +    } +  else +    fprintf(stdout, "\n"); +} + +/** stuff that should go in command.c + command.h */ +void +init_cmdgraph (struct graph **graph) +{ +  // initialize graph, add start noe +  *graph = graph_new (); +  struct cmd_token_t *token = new_cmd_token (START_TKN, NULL, NULL); +  graph_new_node (*graph, token, (void (*)(void *)) &del_cmd_token); +  fprintf (stdout, "initialized graph\n"); +} + +int +compare_completions (const void *fst, const void *snd) +{ +  struct cmd_token_t *first = *(struct cmd_token_t **) fst, +                     *secnd = *(struct cmd_token_t **) snd; +  return strcmp (first->text, secnd->text); +} + +vector +completions_to_vec (struct list *completions) +{ +  vector comps = vector_init (VECTOR_MIN_SIZE); + +  struct listnode *ln; +  struct cmd_token_t *token; +  unsigned int i, exists; +  for (ALL_LIST_ELEMENTS_RO(completions,ln,token)) +  { +    // linear search for token in completions vector +    exists = 0; +    for (i = 0; i < vector_active (comps) && !exists; i++) +    { +      struct cmd_token_t *curr = vector_slot (comps, i); +      exists = !strcmp (curr->text, token->text) && +               !strcmp (curr->desc, token->desc); +    } + +    if (!exists) +      vector_set (comps, copy_cmd_token (token)); +  } + +  // sort completions +  qsort (comps->index, +         vector_active (comps), +         sizeof (void *), +         &compare_completions); + +  return comps; +} + +struct cmd_token_t * +new_cmd_token (enum cmd_token_type_t type, char *text, char *desc) +{ +  struct cmd_token_t *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token_t)); +  token->type = type; +  token->text = text; +  token->desc = desc; +  token->arg  = NULL; + +  return token; +} + +void +del_cmd_token (struct cmd_token_t *token) +{ +  if (!token) return; + +  if (token->text) +    XFREE (MTYPE_CMD_TOKENS, token->text); +  if (token->desc) +    XFREE (MTYPE_CMD_TOKENS, token->desc); +  if (token->arg) +    XFREE (MTYPE_CMD_TOKENS, token->arg); + +  XFREE (MTYPE_CMD_TOKENS, token); +} + +struct cmd_token_t * +copy_cmd_token (struct cmd_token_t *token) +{ +  struct cmd_token_t *copy = new_cmd_token (token->type, NULL, NULL); +  copy->value = token->value; +  copy->max   = token->max; +  copy->min   = token->min; +  copy->text  = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL; +  copy->desc  = token->desc ? XSTRDUP (MTYPE_CMD_TOKENS, token->desc) : NULL; +  copy->arg   = token->arg  ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL; + +  return copy; +} diff --git a/lib/grammar_sandbox.h b/lib/grammar_sandbox.h new file mode 100644 index 0000000000..6e61ce1b46 --- /dev/null +++ b/lib/grammar_sandbox.h @@ -0,0 +1,65 @@ +#ifndef _GRAMMAR_SANDBOX_H +#define _GRAMMAR_SANDBOX_H + +/** + * Houses functionality for testing shim as well as code that should go into + * command.h and command.c during integration. + */ +#include "memory.h" + +#define CMD_CR_TEXT "<cr>" + +void +grammar_sandbox_init (void); + +/** + * Types for tokens. + * + * The type determines what kind of data the token can match (in the + * matching use case) or hold (in the argv use case). + */ +enum cmd_token_type_t +{ +  WORD_TKN,         // words +  NUMBER_TKN,       // integral numbers +  VARIABLE_TKN,     // almost anything +  RANGE_TKN,        // integer range +  IPV4_TKN,         // IPV4 addresses +  IPV4_PREFIX_TKN,  // IPV4 network prefixes +  IPV6_TKN,         // IPV6 prefixes +  IPV6_PREFIX_TKN,  // IPV6 network prefixes + +  /* plumbing types */ +  SELECTOR_TKN,     // marks beginning of selector +  OPTION_TKN,       // marks beginning of option +  NUL_TKN,          // dummy token +  START_TKN,        // first token in line +  END_TKN,          // last token in line +}; + +/** + * Token struct. + */ +struct cmd_token_t +{ +  enum cmd_token_type_t type;   // token type + +  char *text;                   // token text +  char *desc;                   // token description + +  long long value;              // for numeric types +  long long min, max;           // for ranges + +  char *arg;                    // user input that matches this token +}; + +struct cmd_token_t * +new_cmd_token (enum cmd_token_type_t, char *, char *); + +void +del_cmd_token (struct cmd_token_t *); + +struct cmd_token_t * +copy_cmd_token (struct cmd_token_t *); + +#endif /* _GRAMMAR_SANDBOX_H */ diff --git a/lib/graph.c b/lib/graph.c new file mode 100644 index 0000000000..891ecc33c0 --- /dev/null +++ b/lib/graph.c @@ -0,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 --git a/lib/graph.h b/lib/graph.h new file mode 100644 index 0000000000..8d8aa3823b --- /dev/null +++ b/lib/graph.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef _ZEBRA_COMMAND_GRAPH_H +#define _ZEBRA_COMMAND_GRAPH_H + +#include "vector.h" + +struct graph +{ +  vector nodes; +}; + +struct graph_node +{ +  vector from;                  // nodes which have edges to this node +  vector to;                    // nodes which this node has edges to + +  void *data;                   // node data +  void (*del) (void *data);     // deletion callback +}; + +struct graph * +graph_new (void); + +/** + * Creates a new node. + * + * @struct graph the graph this node exists in + * @param[in] data this node's data + * @param[in] del data deletion callback + * @return the new node + */ +struct graph_node * +graph_new_node (struct graph *graph, void *data, void (*del) (void*)); + +/** + * Deletes a node. + * + * Before deletion, this function removes all edges to and from this node from + * any neighbor nodes. + * + * If *data and *del are non-null, the following call is made: + *   (*node->del) (node->data); + * + * @param[in] graph the graph this node belongs to + * @param[out] node pointer to node to delete + */ +void +graph_delete_node (struct graph *graph, struct graph_node *node); + +/** + * Makes a directed edge between two nodes. + * + * @param[in] from + * @param[in] to + * @return to + */ +struct graph_node * +graph_add_edge (struct graph_node *from, struct graph_node *to); + +/** + * Removes a directed edge between two nodes. + * + * @param[in] from + * @param[in] to + */ +void +graph_remove_edge (struct graph_node *from, struct graph_node *to); + +/** + * Deletes a graph. + * Calls graph_delete_node on each node before freeing the graph struct itself. + * + * @param graph the graph to delete + */ +void +graph_delete_graph (struct graph *graph); + +#endif /* _ZEBRA_COMMAND_GRAPH_H */ @@ -17,7 +17,7 @@   * 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.   + * 02111-1307, USA.   */  #include <zebra.h> @@ -45,7 +45,7 @@ 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, @@ -121,37 +121,37 @@ vty_out (struct vty *vty, const char *format, ...)        /* Initial buffer is not enough.  */        if (len < 0 || len >= size) -	{ -	  while (1) -	    { -	      if (len > -1) -		size = len + 1; -	      else -		size = size * 2; - -	      p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); -	      if (! p) -		return -1; - -	      va_start (args, format); -	      len = vsnprintf (p, size, format, args); -	      va_end (args); - -	      if (len > -1 && len < size) -		break; -	    } -	} +        { +          while (1) +            { +              if (len > -1) +                size = len + 1; +              else +                size = size * 2; + +              p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); +              if (! p) +                return -1; + +              va_start (args, format); +              len = vsnprintf (p, size, format, args); +              va_end (args); + +              if (len > -1 && len < size) +                break; +            } +        }        /* When initial buffer is enough to store all output.  */        if (! p) -	p = buf; +        p = buf;        /* Pointer p must point out buffer. */        buffer_put (vty->obuf, (u_char *) p, len);        /* If p is not different with buf, it is allocated buffer.  */        if (p != buf) -	XFREE (MTYPE_VTY_OUT_BUF, p); +        XFREE (MTYPE_VTY_OUT_BUF, p);      }    return len; @@ -159,7 +159,7 @@ vty_out (struct vty *vty, const char *format, ...)  static int  vty_log_out (struct vty *vty, const char *level, const char *proto_str, -	     const char *format, struct timestamp_control *ctl, va_list va) +             const char *format, struct timestamp_control *ctl, va_list va)  {    int ret;    int len; @@ -193,13 +193,13 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str,    if (write(vty->wfd, buf, len) < 0)      {        if (ERRNO_IO_RETRY(errno)) -	/* Kernel buffer is full, probably too much debugging output, so just -	   drop the data and ignore. */ -	return -1; +        /* Kernel buffer is full, probably too much debugging output, so just +           drop the data and ignore. */ +        return -1;        /* Fatal I/O error. */        vty->monitor = 0; /* disable monitoring to avoid infinite recursion */        zlog_warn("%s: write failed to vty client fd %d, closing: %s", -		__func__, vty->fd, safe_strerror(errno)); +                __func__, vty->fd, safe_strerror(errno));        buffer_reset(vty->obuf);        /* cannot call vty_close, because a parent routine may still try           to access the vty struct */ @@ -215,7 +215,7 @@ void  vty_time_print (struct vty *vty, int cr)  {    char buf[QUAGGA_TIMESTAMP_LEN]; -   +    if (quagga_timestamp(0, buf, sizeof(buf)) == 0)      {        zlog (NULL, LOG_INFO, "quagga_timestamp error"); @@ -240,20 +240,20 @@ vty_hello (struct vty *vty)        f = fopen (host.motdfile, "r");        if (f) -	{ -	  while (fgets (buf, sizeof (buf), f)) -	    { -	      char *s; -	      /* work backwards to ignore trailling isspace() */ -	      for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); -		   s--); -	      *s = '\0'; -	      vty_out (vty, "%s%s", buf, VTY_NEWLINE); -	    } -	  fclose (f); -	} +        { +          while (fgets (buf, sizeof (buf), f)) +            { +              char *s; +              /* work backwards to ignore trailling isspace() */ +              for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); +                   s--); +              *s = '\0'; +              vty_out (vty, "%s%s", buf, VTY_NEWLINE); +            } +          fclose (f); +        }        else -	vty_out (vty, "MOTD file not found%s", VTY_NEWLINE); +        vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);      }    else if (host.motd)      vty_out (vty, "%s", host.motd); @@ -270,10 +270,10 @@ vty_prompt (struct vty *vty)      {        hostname = host.name;        if (!hostname) -	{ -	  uname (&names); -	  hostname = names.nodename; -	} +        { +          uname (&names); +          hostname = names.nodename; +        }        vty_out (vty, cmd_prompt (vty->node), hostname);      }  } @@ -326,7 +326,7 @@ vty_new ()  {    struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty)); -  new->obuf = buffer_new(0);	/* Use default buffer size. */ +  new->obuf = buffer_new(0);    /* Use default buffer size. */    new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);    new->error_buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);    new->max = VTY_BUFSIZ; @@ -347,19 +347,19 @@ vty_auth (struct vty *vty, char *buf)      {      case AUTH_NODE:        if (host.encrypt) -	passwd = host.password_encrypt; +        passwd = host.password_encrypt;        else -	passwd = host.password; +        passwd = host.password;        if (host.advanced) -	next_node = host.enable ? VIEW_NODE : ENABLE_NODE; +        next_node = host.enable ? VIEW_NODE : ENABLE_NODE;        else -	next_node = VIEW_NODE; +        next_node = VIEW_NODE;        break;      case AUTH_ENABLE_NODE:        if (host.encrypt) -	passwd = host.enable_encrypt; +        passwd = host.enable_encrypt;        else -	passwd = host.enable; +        passwd = host.enable;        next_node = ENABLE_NODE;        break;      } @@ -367,9 +367,9 @@ vty_auth (struct vty *vty, char *buf)    if (passwd)      {        if (host.encrypt) -	fail = strcmp (crypt(buf, passwd), passwd); +        fail = strcmp (crypt(buf, passwd), passwd);        else -	fail = strcmp (buf, passwd); +        fail = strcmp (buf, passwd);      }    else      fail = 1; @@ -377,26 +377,26 @@ vty_auth (struct vty *vty, char *buf)    if (! fail)      {        vty->fail = 0; -      vty->node = next_node;	/* Success ! */ +      vty->node = next_node;    /* Success ! */      }    else      {        vty->fail++;        if (vty->fail >= 3) -	{ -	  if (vty->node == AUTH_NODE) -	    { -	      vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); -	      vty->status = VTY_CLOSE; -	    } -	  else			 -	    { -	      /* AUTH_ENABLE_NODE */ -	      vty->fail = 0; -	      vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); -	      vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; -	    } -	} +        { +          if (vty->node == AUTH_NODE) +            { +              vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); +              vty->status = VTY_CLOSE; +            } +          else +            { +              /* AUTH_ENABLE_NODE */ +              vty->fail = 0; +              vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); +              vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; +            } +        }      }  } @@ -423,7 +423,7 @@ vty_command (struct vty *vty, char *buf)    if (cp != NULL && *cp != '\0')      {        unsigned i; -      char	vty_str[VTY_BUFSIZ]; +      char      vty_str[VTY_BUFSIZ];        char        prompt_str[VTY_BUFSIZ];        /* format the base vty info */ @@ -465,14 +465,14 @@ vty_command (struct vty *vty, char *buf)        protocolname = zlog_proto_names[zlog_default->protocol];    else        protocolname = zlog_proto_names[ZLOG_NONE]; -                                                                            +  #ifdef CONSUMED_TIME_CHECK      GETRUSAGE(&after);      if ((realtime = thread_consumed_time(&after, &before, &cputime)) > -    	CONSUMED_TIME_CHECK) +        CONSUMED_TIME_CHECK)        /* Warn about CPU hog that must be fixed. */        zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s", -      		realtime/1000, cputime/1000, buf); +                realtime/1000, cputime/1000, buf);    }  #endif /* CONSUMED_TIME_CHECK */ @@ -480,18 +480,18 @@ vty_command (struct vty *vty, char *buf)      switch (ret)        {        case CMD_WARNING: -	if (vty->type == VTY_FILE) -	  vty_out (vty, "Warning...%s", VTY_NEWLINE); -	break; +        if (vty->type == VTY_FILE) +          vty_out (vty, "Warning...%s", VTY_NEWLINE); +        break;        case CMD_ERR_AMBIGUOUS: -	vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); -	break; +        vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); +        break;        case CMD_ERR_NO_MATCH: -	vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); -	break; +        vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); +        break;        case CMD_ERR_INCOMPLETE: -	vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); -	break; +        vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); +        break;        }    cmd_free_strvec (vline); @@ -689,7 +689,7 @@ vty_forward_word (struct vty *vty)  {    while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')      vty_forward_char (vty); -   +    while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')      vty_forward_char (vty);  } @@ -786,14 +786,14 @@ vty_delete_char (struct vty *vty)      }    if (vty->cp == vty->length) -    return;			/* completion need here? */ +    return;                     /* completion need here? */    size = vty->length - vty->cp;    vty->length--;    memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);    vty->buf[vty->length] = '\0'; -   +    if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)      return; @@ -823,7 +823,7 @@ vty_kill_line (struct vty *vty)    int size;    size = vty->length - vty->cp; -   +    if (size == 0)      return; @@ -918,7 +918,7 @@ vty_complete_command (struct vty *vty)      vector_set (vline, NULL);    matched = cmd_complete_command_lib (vline, vty, &ret, 1); -   +    cmd_free_strvec (vline);    vty_out (vty, "%s", VTY_NEWLINE); @@ -953,12 +953,12 @@ vty_complete_command (struct vty *vty)        break;      case CMD_COMPLETE_LIST_MATCH:        for (i = 0; matched[i] != NULL; i++) -	{ -	  if (i != 0 && ((i % 6) == 0)) -	    vty_out (vty, "%s", VTY_NEWLINE); -	  vty_out (vty, "%-10s ", matched[i]); -	  XFREE (MTYPE_TMP, matched[i]); -	} +        { +          if (i != 0 && ((i % 6) == 0)) +            vty_out (vty, "%s", VTY_NEWLINE); +          vty_out (vty, "%-10s ", matched[i]); +          XFREE (MTYPE_TMP, matched[i]); +        }        vty_out (vty, "%s", VTY_NEWLINE);        vty_prompt (vty); @@ -977,14 +977,12 @@ vty_complete_command (struct vty *vty)  static void  vty_describe_fold (struct vty *vty, int cmd_width, -		   unsigned int desc_width, struct cmd_token *token) +                   unsigned int desc_width, struct cmd_token *token)  {    char *buf;    const char *cmd, *p;    int pos; -  cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd; -    if (desc_width <= 0)      {        vty_out (vty, "  %-*s  %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE); @@ -1032,7 +1030,7 @@ vty_describe_command (struct vty *vty)        vline = vector_init (1);        vector_set (vline, NULL);      } -  else  +  else      if (isspace ((int) vty->buf[vty->length - 1]))        vector_set (vline, NULL); @@ -1051,24 +1049,22 @@ vty_describe_command (struct vty *vty)        vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);        goto out;        break; -    }   +    }    /* Get width of command string. */    width = 0;    for (i = 0; i < vector_active (describe); i++)      if ((token = vector_slot (describe, i)) != NULL)        { -	unsigned int len; +        unsigned int len; -	if (token->cmd[0] == '\0') -	  continue; +        if (token->text[0] == '\0') +          continue; -	len = strlen (token->cmd); -	if (token->cmd[0] == '.') -	  len--; +        len = strlen (token->text); -	if (width < len) -	  width = len; +        if (width < len) +          width = len;        }    /* Get width of description string. */ @@ -1078,45 +1074,45 @@ vty_describe_command (struct vty *vty)    for (i = 0; i < vector_active (describe); i++)      if ((token = vector_slot (describe, i)) != NULL)        { -	if (token->cmd[0] == '\0') -	  continue; -	 -	if (strcmp (token->cmd, command_cr) == 0) -	  { -	    token_cr = token; -	    continue; -	  } - -	if (!token->desc) -	  vty_out (vty, "  %-s%s", -		   token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, -		   VTY_NEWLINE); -	else if (desc_width >= strlen (token->desc)) -	  vty_out (vty, "  %-*s  %s%s", width, -		   token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, -		   token->desc, VTY_NEWLINE); -	else -	  vty_describe_fold (vty, width, desc_width, token); +        if (token->text[0] == '\0') +          continue; + +        if (strcmp (token->text, CMD_CR_TEXT) == 0) +          { +            token_cr = token; +            continue; +          } + +        if (!token->desc) +          vty_out (vty, "  %-s%s", +                   token->text, +                   VTY_NEWLINE); +        else if (desc_width >= strlen (token->desc)) +          vty_out (vty, "  %-*s  %s%s", width, +                   token->text, +                   token->desc, VTY_NEWLINE); +        else +          vty_describe_fold (vty, width, desc_width, token);  #if 0 -	vty_out (vty, "  %-*s %s%s", width -		 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, -		 desc->str ? desc->str : "", VTY_NEWLINE); +        vty_out (vty, "  %-*s %s%s", width +                 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, +                 desc->str ? desc->str : "", VTY_NEWLINE);  #endif /* 0 */        }    if ((token = token_cr))      {        if (!token->desc) -	vty_out (vty, "  %-s%s", -		 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, -		 VTY_NEWLINE); +        vty_out (vty, "  %-s%s", +                 token->text, +                 VTY_NEWLINE);        else if (desc_width >= strlen (token->desc)) -	vty_out (vty, "  %-*s  %s%s", width, -		 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, -		 token->desc, VTY_NEWLINE); +        vty_out (vty, "  %-*s  %s%s", width, +                 token->text, +                 token->desc, VTY_NEWLINE);        else -	vty_describe_fold (vty, width, desc_width, token); +        vty_describe_fold (vty, width, desc_width, token);      }  out: @@ -1221,41 +1217,41 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)    for (i = 0; i < nbytes; i++)      {        switch (buf[i]) -	{ -	case IAC: -	  vty_out (vty, "IAC "); -	  break; -	case WILL: -	  vty_out (vty, "WILL "); -	  break; -	case WONT: -	  vty_out (vty, "WONT "); -	  break; -	case DO: -	  vty_out (vty, "DO "); -	  break; -	case DONT: -	  vty_out (vty, "DONT "); -	  break; -	case SB: -	  vty_out (vty, "SB "); -	  break; -	case SE: -	  vty_out (vty, "SE "); -	  break; -	case TELOPT_ECHO: -	  vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); -	  break; -	case TELOPT_SGA: -	  vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); -	  break; -	case TELOPT_NAWS: -	  vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); -	  break; -	default: -	  vty_out (vty, "%x ", buf[i]); -	  break; -	} +        { +        case IAC: +          vty_out (vty, "IAC "); +          break; +        case WILL: +          vty_out (vty, "WILL "); +          break; +        case WONT: +          vty_out (vty, "WONT "); +          break; +        case DO: +          vty_out (vty, "DO "); +          break; +        case DONT: +          vty_out (vty, "DONT "); +          break; +        case SB: +          vty_out (vty, "SB "); +          break; +        case SE: +          vty_out (vty, "SE "); +          break; +        case TELOPT_ECHO: +          vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); +          break; +        case TELOPT_SGA: +          vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); +          break; +        case TELOPT_NAWS: +          vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); +          break; +        default: +          vty_out (vty, "%x ", buf[i]); +          break; +        }      }    vty_out (vty, "%s", VTY_NEWLINE); @@ -1268,42 +1264,42 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)        vty->iac_sb_in_progress = 1;        return 0;        break; -    case SE:  +    case SE:        { -	if (!vty->iac_sb_in_progress) -	  return 0; - -	if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) -	  { -	    vty->iac_sb_in_progress = 0; -	    return 0; -	  } -	switch (vty->sb_buf[0]) -	  { -	  case TELOPT_NAWS: -	    if (vty->sb_len != TELNET_NAWS_SB_LEN) -	      zlog_warn("RFC 1073 violation detected: telnet NAWS option " -			"should send %d characters, but we received %lu", -			TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); -	    else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) -	      zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, " -		       "too small to handle the telnet NAWS option", -		       (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); -	    else -	      { -		vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); -		vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); +        if (!vty->iac_sb_in_progress) +          return 0; + +        if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) +          { +            vty->iac_sb_in_progress = 0; +            return 0; +          } +        switch (vty->sb_buf[0]) +          { +          case TELOPT_NAWS: +            if (vty->sb_len != TELNET_NAWS_SB_LEN) +              zlog_warn("RFC 1073 violation detected: telnet NAWS option " +                        "should send %d characters, but we received %lu", +                        TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); +            else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) +              zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, " +                       "too small to handle the telnet NAWS option", +                       (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); +            else +              { +                vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); +                vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);  #ifdef TELNET_OPTION_DEBUG -		vty_out(vty, "TELNET NAWS window size negotiation completed: " -			      "width %d, height %d%s", -			vty->width, vty->height, VTY_NEWLINE); +                vty_out(vty, "TELNET NAWS window size negotiation completed: " +                              "width %d, height %d%s", +                        vty->width, vty->height, VTY_NEWLINE);  #endif -	      } -	    break; -	  } -	vty->iac_sb_in_progress = 0; -	return 0; -	break; +              } +            break; +          } +        vty->iac_sb_in_progress = 0; +        return 0; +        break;        }      default:        break; @@ -1328,7 +1324,7 @@ vty_execute (struct vty *vty)      default:        ret = vty_command (vty, vty->buf);        if (vty->type == VTY_TERM) -	vty_hist_add (vty); +        vty_hist_add (vty);        break;      } @@ -1398,187 +1394,187 @@ vty_read (struct thread *thread)    if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)      {        if (nbytes < 0) -	{ -	  if (ERRNO_IO_RETRY(errno)) -	    { -	      vty_event (VTY_READ, vty_sock, vty); -	      return 0; -	    } -	  vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ -	  zlog_warn("%s: read error on vty client fd %d, closing: %s", -		    __func__, vty->fd, safe_strerror(errno)); +        { +          if (ERRNO_IO_RETRY(errno)) +            { +              vty_event (VTY_READ, vty_sock, vty); +              return 0; +            } +          vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ +          zlog_warn("%s: read error on vty client fd %d, closing: %s", +                    __func__, vty->fd, safe_strerror(errno));            buffer_reset(vty->obuf); -	} +        }        vty->status = VTY_CLOSE;      } -  for (i = 0; i < nbytes; i++)  +  for (i = 0; i < nbytes; i++)      {        if (buf[i] == IAC) -	{ -	  if (!vty->iac) -	    { -	      vty->iac = 1; -	      continue; -	    } -	  else -	    { -	      vty->iac = 0; -	    } -	} -       +        { +          if (!vty->iac) +            { +              vty->iac = 1; +              continue; +            } +          else +            { +              vty->iac = 0; +            } +        } +        if (vty->iac_sb_in_progress && !vty->iac) -	{ -	    if (vty->sb_len < sizeof(vty->sb_buf)) -	      vty->sb_buf[vty->sb_len] = buf[i]; -	    vty->sb_len++; -	    continue; -	} +        { +            if (vty->sb_len < sizeof(vty->sb_buf)) +              vty->sb_buf[vty->sb_len] = buf[i]; +            vty->sb_len++; +            continue; +        }        if (vty->iac) -	{ -	  /* In case of telnet command */ -	  int ret = 0; -	  ret = vty_telnet_option (vty, buf + i, nbytes - i); -	  vty->iac = 0; -	  i += ret; -	  continue; -	} -	         +        { +          /* In case of telnet command */ +          int ret = 0; +          ret = vty_telnet_option (vty, buf + i, nbytes - i); +          vty->iac = 0; +          i += ret; +          continue; +        } +        if (vty->status == VTY_MORE) -	{ -	  switch (buf[i]) -	    { -	    case CONTROL('C'): -	    case 'q': -	    case 'Q': -	      vty_buffer_reset (vty); -	      break; +        { +          switch (buf[i]) +            { +            case CONTROL('C'): +            case 'q': +            case 'Q': +              vty_buffer_reset (vty); +              break;  #if 0 /* More line does not work for "show ip bgp".  */ -	    case '\n': -	    case '\r': -	      vty->status = VTY_MORELINE; -	      break; +            case '\n': +            case '\r': +              vty->status = VTY_MORELINE; +              break;  #endif -	    default: -	      break; -	    } -	  continue; -	} +            default: +              break; +            } +          continue; +        }        /* Escape character. */        if (vty->escape == VTY_ESCAPE) -	{ -	  vty_escape_map (buf[i], vty); -	  continue; -	} +        { +          vty_escape_map (buf[i], vty); +          continue; +        }        /* Pre-escape status. */        if (vty->escape == VTY_PRE_ESCAPE) -	{ -	  switch (buf[i]) -	    { -	    case '[': -	      vty->escape = VTY_ESCAPE; -	      break; -	    case 'b': -	      vty_backward_word (vty); -	      vty->escape = VTY_NORMAL; -	      break; -	    case 'f': -	      vty_forward_word (vty); -	      vty->escape = VTY_NORMAL; -	      break; -	    case 'd': -	      vty_forward_kill_word (vty); -	      vty->escape = VTY_NORMAL; -	      break; -	    case CONTROL('H'): -	    case 0x7f: -	      vty_backward_kill_word (vty); -	      vty->escape = VTY_NORMAL; -	      break; -	    default: -	      vty->escape = VTY_NORMAL; -	      break; -	    } -	  continue; -	} +        { +          switch (buf[i]) +            { +            case '[': +              vty->escape = VTY_ESCAPE; +              break; +            case 'b': +              vty_backward_word (vty); +              vty->escape = VTY_NORMAL; +              break; +            case 'f': +              vty_forward_word (vty); +              vty->escape = VTY_NORMAL; +              break; +            case 'd': +              vty_forward_kill_word (vty); +              vty->escape = VTY_NORMAL; +              break; +            case CONTROL('H'): +            case 0x7f: +              vty_backward_kill_word (vty); +              vty->escape = VTY_NORMAL; +              break; +            default: +              vty->escape = VTY_NORMAL; +              break; +            } +          continue; +        }        switch (buf[i]) -	{ -	case CONTROL('A'): -	  vty_beginning_of_line (vty); -	  break; -	case CONTROL('B'): -	  vty_backward_char (vty); -	  break; -	case CONTROL('C'): -	  vty_stop_input (vty); -	  break; -	case CONTROL('D'): -	  vty_delete_char (vty); -	  break; -	case CONTROL('E'): -	  vty_end_of_line (vty); -	  break; -	case CONTROL('F'): -	  vty_forward_char (vty); -	  break; -	case CONTROL('H'): -	case 0x7f: -	  vty_delete_backward_char (vty); -	  break; -	case CONTROL('K'): -	  vty_kill_line (vty); -	  break; -	case CONTROL('N'): -	  vty_next_line (vty); -	  break; -	case CONTROL('P'): -	  vty_previous_line (vty); -	  break; -	case CONTROL('T'): -	  vty_transpose_chars (vty); -	  break; -	case CONTROL('U'): -	  vty_kill_line_from_beginning (vty); -	  break; -	case CONTROL('W'): -	  vty_backward_kill_word (vty); -	  break; -	case CONTROL('Z'): -	  vty_end_config (vty); -	  break; -	case '\n': -	case '\r': -	  vty_out (vty, "%s", VTY_NEWLINE); -	  vty_execute (vty); -	  break; -	case '\t': -	  vty_complete_command (vty); -	  break; -	case '?': -	  if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) -	    vty_self_insert (vty, buf[i]); -	  else -	    vty_describe_command (vty); -	  break; -	case '\033': -	  if (i + 1 < nbytes && buf[i + 1] == '[') -	    { -	      vty->escape = VTY_ESCAPE; -	      i++; -	    } -	  else -	    vty->escape = VTY_PRE_ESCAPE; -	  break; -	default: -	  if (buf[i] > 31 && buf[i] < 127) -	    vty_self_insert (vty, buf[i]); -	  break; -	} +        { +        case CONTROL('A'): +          vty_beginning_of_line (vty); +          break; +        case CONTROL('B'): +          vty_backward_char (vty); +          break; +        case CONTROL('C'): +          vty_stop_input (vty); +          break; +        case CONTROL('D'): +          vty_delete_char (vty); +          break; +        case CONTROL('E'): +          vty_end_of_line (vty); +          break; +        case CONTROL('F'): +          vty_forward_char (vty); +          break; +        case CONTROL('H'): +        case 0x7f: +          vty_delete_backward_char (vty); +          break; +        case CONTROL('K'): +          vty_kill_line (vty); +          break; +        case CONTROL('N'): +          vty_next_line (vty); +          break; +        case CONTROL('P'): +          vty_previous_line (vty); +          break; +        case CONTROL('T'): +          vty_transpose_chars (vty); +          break; +        case CONTROL('U'): +          vty_kill_line_from_beginning (vty); +          break; +        case CONTROL('W'): +          vty_backward_kill_word (vty); +          break; +        case CONTROL('Z'): +          vty_end_config (vty); +          break; +        case '\n': +        case '\r': +          vty_out (vty, "%s", VTY_NEWLINE); +          vty_execute (vty); +          break; +        case '\t': +          vty_complete_command (vty); +          break; +        case '?': +          if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) +            vty_self_insert (vty, buf[i]); +          else +            vty_describe_command (vty); +          break; +        case '\033': +          if (i + 1 < nbytes && buf[i + 1] == '[') +            { +              vty->escape = VTY_ESCAPE; +              i++; +            } +          else +            vty->escape = VTY_PRE_ESCAPE; +          break; +        default: +          if (buf[i] > 31 && buf[i] < 127) +            vty_self_insert (vty, buf[i]); +          break; +        }      }    /* Check status. */ @@ -1618,36 +1614,36 @@ vty_flush (struct thread *thread)      flushrc = buffer_flush_available(vty->obuf, vty_sock);    else if (vty->status == VTY_MORELINE)      flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, -				  1, erase, 0); +                                  1, erase, 0);    else      flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, -				  vty->lines >= 0 ? vty->lines : -						    vty->height, -				  erase, 0); +                                  vty->lines >= 0 ? vty->lines : +                                                    vty->height, +                                  erase, 0);    switch (flushrc)      {      case BUFFER_ERROR:        vty->monitor = 0; /* disable monitoring to avoid infinite recursion */        zlog_warn("buffer_flush failed on vty client fd %d, closing", -		vty->fd); +                vty->fd);        buffer_reset(vty->obuf);        vty_close(vty);        return 0;      case BUFFER_EMPTY:        if (vty->status == VTY_CLOSE) -	vty_close (vty); +        vty_close (vty);        else -	{ -	  vty->status = VTY_NORMAL; -	  if (vty->lines == 0) -	    vty_event (VTY_READ, vty_sock, vty); -	} +        { +          vty->status = VTY_NORMAL; +          if (vty->lines == 0) +            vty_event (VTY_READ, vty_sock, vty); +        }        break;      case BUFFER_PENDING:        /* There is more data waiting to be written. */        vty->status = VTY_MORE;        if (vty->lines == 0) -	vty_event (VTY_WRITE, vty_sock, vty); +        vty_event (VTY_WRITE, vty_sock, vty);        break;      } @@ -1702,9 +1698,9 @@ vty_create (int vty_sock, union sockunion *su)        if (restricted_mode)          vty->node = RESTRICTED_NODE;        else if (host.advanced) -	vty->node = ENABLE_NODE; +        vty->node = ENABLE_NODE;        else -	vty->node = VIEW_NODE; +        vty->node = VIEW_NODE;      }    if (host.lines >= 0)      vty->lines = host.lines; @@ -1713,12 +1709,12 @@ vty_create (int vty_sock, union sockunion *su)      {        /* Vty is not available if password isn't set. */        if (host.password == NULL && host.password_encrypt == NULL) -	{ -	  vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); -	  vty->status = VTY_CLOSE; -	  vty_close (vty); -	  return NULL; -	} +        { +          vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); +          vty->status = VTY_CLOSE; +          vty_close (vty); +          return NULL; +        }      }    /* Say hello to the world. */ @@ -1838,17 +1834,17 @@ vty_accept (struct thread *thread)    if (p.family == AF_INET && vty_accesslist_name)      {        if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) && -	  (access_list_apply (acl, &p) == FILTER_DENY)) -	{ -	  zlog (NULL, LOG_INFO, "Vty connection refused from %s", -		sockunion2str (&su, buf, SU_ADDRSTRLEN)); -	  close (vty_sock); -	   -	  /* continue accepting connections */ -	  vty_event (VTY_SERV, accept_sock, NULL); -	   -	  return 0; -	} +          (access_list_apply (acl, &p) == FILTER_DENY)) +        { +          zlog (NULL, LOG_INFO, "Vty connection refused from %s", +                sockunion2str (&su, buf, SU_ADDRSTRLEN)); +          close (vty_sock); + +          /* continue accepting connections */ +          vty_event (VTY_SERV, accept_sock, NULL); + +          return 0; +        }      }  #ifdef HAVE_IPV6 @@ -1856,29 +1852,29 @@ vty_accept (struct thread *thread)    if (p.family == AF_INET6 && vty_ipv6_accesslist_name)      {        if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) && -	  (access_list_apply (acl, &p) == FILTER_DENY)) -	{ -	  zlog (NULL, LOG_INFO, "Vty connection refused from %s", -		sockunion2str (&su, buf, SU_ADDRSTRLEN)); -	  close (vty_sock); -	   -	  /* continue accepting connections */ -	  vty_event (VTY_SERV, accept_sock, NULL); -	   -	  return 0; -	} +          (access_list_apply (acl, &p) == FILTER_DENY)) +        { +          zlog (NULL, LOG_INFO, "Vty connection refused from %s", +                sockunion2str (&su, buf, SU_ADDRSTRLEN)); +          close (vty_sock); + +          /* continue accepting connections */ +          vty_event (VTY_SERV, accept_sock, NULL); + +          return 0; +        }      }  #endif /* HAVE_IPV6 */ -   +    on = 1; -  ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,  -		    (char *) &on, sizeof (on)); +  ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, +                    (char *) &on, sizeof (on));    if (ret < 0) -    zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",  -	  safe_strerror (errno)); +    zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", +          safe_strerror (errno));    zlog (NULL, LOG_INFO, "Vty connection from %s", -	sockunion2str (&su, buf, SU_ADDRSTRLEN)); +        sockunion2str (&su, buf, SU_ADDRSTRLEN));    vty_create (vty_sock, &su); @@ -1917,14 +1913,14 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)      {        if (ainfo->ai_family != AF_INET  #ifdef HAVE_IPV6 -	  && ainfo->ai_family != AF_INET6 +          && ainfo->ai_family != AF_INET6  #endif /* HAVE_IPV6 */ -	  ) -	continue; +          ) +        continue;        sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);        if (sock < 0) -	continue; +        continue;        sockopt_v6only (ainfo->ai_family, sock);        sockopt_reuseaddr (sock); @@ -1932,17 +1928,17 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)        ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);        if (ret < 0) -	{ -	  close (sock);	/* Avoid sd leak. */ -	continue; -	} +        { +          close (sock); /* Avoid sd leak. */ +        continue; +        }        ret = listen (sock, 3); -      if (ret < 0)  -	{ -	  close (sock);	/* Avoid sd leak. */ -	continue; -	} +      if (ret < 0) +        { +          close (sock); /* Avoid sd leak. */ +        continue; +        }        vty_event (VTY_SERV, sock, NULL);      } @@ -1973,7 +1969,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)        case AF_INET6:          naddr=&su.sin6.sin6_addr;          break; -#endif	 +#endif      }    if(naddr) @@ -1981,11 +1977,11 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)      {        case -1:          zlog_err("bad address %s",addr); -	naddr=NULL; -	break; +        naddr=NULL; +        break;        case 0:          zlog_err("error translating address %s: %s",addr,safe_strerror(errno)); -	naddr=NULL; +        naddr=NULL;      }    /* Make new socket. */ @@ -2002,16 +1998,16 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)    if (ret < 0)      {        zlog_warn("can't bind socket"); -      close (accept_sock);	/* Avoid sd leak. */ +      close (accept_sock);      /* Avoid sd leak. */        return;      }    /* Listen socket under queue 3. */    ret = listen (accept_sock, 3); -  if (ret < 0)  +  if (ret < 0)      {        zlog (NULL, LOG_WARNING, "can't listen socket"); -      close (accept_sock);	/* Avoid sd leak. */ +      close (accept_sock);      /* Avoid sd leak. */        return;      } @@ -2033,7 +2029,7 @@ vty_serv_un (const char *path)    struct sockaddr_un serv;    mode_t old_mask;    struct zprivs_ids_t ids; -   +    /* First of all, unlink existing socket */    unlink (path); @@ -2062,7 +2058,7 @@ vty_serv_un (const char *path)    if (ret < 0)      {        zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno)); -      close (sock);	/* Avoid sd leak. */ +      close (sock);     /* Avoid sd leak. */        return;      } @@ -2070,14 +2066,14 @@ vty_serv_un (const char *path)    if (ret < 0)      {        zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno)); -      close (sock);	/* Avoid sd leak. */ +      close (sock);     /* Avoid sd leak. */        return;      }    umask (old_mask);    zprivs_get_ids(&ids); -   +    if (ids.gid_vty > 0)      {        /* set group of socket */ @@ -2101,7 +2097,7 @@ vtysh_accept (struct thread *thread)    int client_len;    struct sockaddr_un client;    struct vty *vty; -   +    accept_sock = THREAD_FD (thread);    vty_event (VTYSH_SERV, accept_sock, NULL); @@ -2110,7 +2106,7 @@ vtysh_accept (struct thread *thread)    client_len = sizeof (struct sockaddr_un);    sock = accept (accept_sock, (struct sockaddr *) &client, -		 (socklen_t *) &client_len); +                 (socklen_t *) &client_len);    if (sock < 0)      { @@ -2125,7 +2121,7 @@ vtysh_accept (struct thread *thread)        close (sock);        return -1;      } -   +  #ifdef VTYSH_DEBUG    printf ("VTY shell accept\n");  #endif /* VTYSH_DEBUG */ @@ -2180,16 +2176,16 @@ vtysh_read (struct thread *thread)    if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)      {        if (nbytes < 0) -	{ -	  if (ERRNO_IO_RETRY(errno)) -	    { -	      vty_event (VTYSH_READ, sock, vty); -	      return 0; -	    } -	  vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ -	  zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", -		    __func__, sock, safe_strerror(errno)); -	} +        { +          if (ERRNO_IO_RETRY(errno)) +            { +              vty_event (VTYSH_READ, sock, vty); +              return 0; +            } +          vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ +          zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", +                    __func__, sock, safe_strerror(errno)); +        }        buffer_reset(vty->obuf);        vty_close (vty);  #ifdef VTYSH_DEBUG @@ -2207,25 +2203,25 @@ vtysh_read (struct thread *thread)        vty_ensure(vty, vty->length+1);        vty->buf[vty->length++] = *p;        if (*p == '\0') -	{ -	  /* Pass this line to parser. */ -	  ret = vty_execute (vty); -	  /* Note that vty_execute clears the command buffer and resets -	     vty->length to 0. */ +        { +          /* Pass this line to parser. */ +          ret = vty_execute (vty); +          /* Note that vty_execute clears the command buffer and resets +             vty->length to 0. */ -	  /* Return result. */ +          /* Return result. */  #ifdef VTYSH_DEBUG -	  printf ("result: %d\n", ret); -	  printf ("vtysh node: %d\n", vty->node); +          printf ("result: %d\n", ret); +          printf ("vtysh node: %d\n", vty->node);  #endif /* VTYSH_DEBUG */ -	  header[3] = ret; -	  buffer_put(vty->obuf, header, 4); +          header[3] = ret; +          buffer_put(vty->obuf, header, 4); -	  if (!vty->t_write && (vtysh_flush(vty) < 0)) -	    /* Try to flush results; exit if a write error occurs. */ -	    return 0; -	} +          if (!vty->t_write && (vtysh_flush(vty) < 0)) +            /* Try to flush results; exit if a write error occurs. */ +            return 0; +        }      }    vty_event (VTYSH_READ, sock, vty); @@ -2354,14 +2350,14 @@ vty_read_file (FILE *confp)    vty->fd = STDIN_FILENO;    vty->type = VTY_FILE;    vty->node = CONFIG_NODE; -   +    /* Execute configuration file */    ret = config_from_file (vty, confp, &line_num);    /* Flush any previous errors before printing messages below */    buffer_flush_all (vty->obuf, vty->fd); -  if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )  +  if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )      {        switch (ret)         { @@ -2373,7 +2369,7 @@ vty_read_file (FILE *confp)             break;         }        fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n", -		       line_num, vty->error_buf); +                       line_num, vty->error_buf);      }    vty_close (vty); @@ -2388,7 +2384,7 @@ vty_use_backup_config (char *fullpath)    int tmp, sav;    int c;    char buffer[512]; -   +    fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);    strcpy (fullpath_sav, fullpath);    strcat (fullpath_sav, CONF_BACKUP_EXT); @@ -2400,7 +2396,7 @@ vty_use_backup_config (char *fullpath)    fullpath_tmp = malloc (strlen (fullpath) + 8);    sprintf (fullpath_tmp, "%s.XXXXXX", fullpath); -   +    /* Open file to configuration write. */    tmp = mkstemp (fullpath_tmp);    if (tmp < 0) @@ -2418,21 +2414,21 @@ vty_use_backup_config (char *fullpath)        free (fullpath_tmp);        return NULL;      } -   +    while((c = read (sav, buffer, 512)) > 0)      {        if (write (tmp, buffer, c) <= 0) -	{ -	  free (fullpath_sav); -	  free (fullpath_tmp); -	  close (sav); -	  close (tmp); -	  return NULL; -	} +        { +          free (fullpath_sav); +          free (fullpath_tmp); +          close (sav); +          close (tmp); +          return NULL; +        }      }    close (sav);    close (tmp); -   +    if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)      {        unlink (fullpath_tmp); @@ -2440,12 +2436,12 @@ vty_use_backup_config (char *fullpath)        free (fullpath_tmp);        return NULL;      } -   +    if (link (fullpath_tmp, fullpath) == 0)      ret = fopen (fullpath, "r");    unlink (fullpath_tmp); -   +    free (fullpath_sav);    free (fullpath_tmp);    return ret; @@ -2467,12 +2463,12 @@ vty_read_config (char *config_file,        if (! IS_DIRECTORY_SEP (config_file[0]))          {            if (getcwd (cwd, MAXPATHLEN) == NULL) -	    { -	      fprintf (stderr, "Failure to determine Current Working Directory %d!\n", errno); -	      exit (1); -	    } -          tmp = XMALLOC (MTYPE_TMP,  - 			      strlen (cwd) + strlen (config_file) + 2); +            { +              fprintf (stderr, "Failure to determine Current Working Directory %d!\n", errno); +              exit (1); +            } +          tmp = XMALLOC (MTYPE_TMP, +                              strlen (cwd) + strlen (config_file) + 2);            sprintf (tmp, "%s/%s", cwd, config_file);            fullpath = tmp;          } @@ -2485,14 +2481,14 @@ vty_read_config (char *config_file,          {            fprintf (stderr, "%s: failed to open configuration file %s: %s\n",                     __func__, fullpath, safe_strerror (errno)); -           +            confp = vty_use_backup_config (fullpath);            if (confp)              fprintf (stderr, "WARNING: using backup configuration file!\n");            else              { -              fprintf (stderr, "can't open configuration file [%s]\n",  -  	               config_file); +              fprintf (stderr, "can't open configuration file [%s]\n", +                       config_file);                exit(1);              }          } @@ -2524,7 +2520,7 @@ vty_read_config (char *config_file,          {            ret = stat (integrate_default, &conf_stat);            if (ret >= 0) -	    goto tmp_free_and_out; +            goto tmp_free_and_out;          }  #endif /* VTYSH */        confp = fopen (config_default_dir, "r"); @@ -2532,7 +2528,7 @@ vty_read_config (char *config_file,          {            fprintf (stderr, "%s: failed to open configuration file %s: %s\n",                     __func__, config_default_dir, safe_strerror (errno)); -           +            confp = vty_use_backup_config (config_default_dir);            if (confp)              { @@ -2542,10 +2538,10 @@ vty_read_config (char *config_file,            else              {                fprintf (stderr, "can't open configuration file [%s]\n", -		       config_default_dir); -	      goto tmp_free_and_out; +                       config_default_dir); +              goto tmp_free_and_out;              } -        }       +        }        else          fullpath = config_default_dir;      } @@ -2564,23 +2560,23 @@ tmp_free_and_out:  /* Small utility function which output log to the VTY. */  void  vty_log (const char *level, const char *proto_str, -	 const char *format, struct timestamp_control *ctl, va_list va) +         const char *format, struct timestamp_control *ctl, va_list va)  {    unsigned int i;    struct vty *vty; -   +    if (!vtyvec)      return;    for (i = 0; i < vector_active (vtyvec); i++)      if ((vty = vector_slot (vtyvec, i)) != NULL)        if (vty->monitor) -	{ -	  va_list ac; -	  va_copy(ac, va); -	  vty_log_out (vty, level, proto_str, format, ctl, ac); -	  va_end(ac); -	} +        { +          va_list ac; +          va_copy(ac, va); +          vty_log_out (vty, level, proto_str, format, ctl, ac); +          va_end(ac); +        }  }  /* Async-signal-safe version of vty_log for fixed strings. */ @@ -2594,7 +2590,7 @@ vty_log_fixed (char *buf, size_t len)    /* vty may not have been initialised */    if (!vtyvec)      return; -   +    iov[0].iov_base = buf;    iov[0].iov_len = len;    iov[1].iov_base = crlf; @@ -2604,13 +2600,13 @@ vty_log_fixed (char *buf, size_t len)      {        struct vty *vty;        if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor) -	/* N.B. We don't care about the return code, since process is -	   most likely just about to die anyway. */ -	if (writev(vty->wfd, iov, 2) == -1) -	  { -	    fprintf(stderr, "Failure to writev: %d\n", errno); -	    exit(-1); -	  } +        /* N.B. We don't care about the return code, since process is +           most likely just about to die anyway. */ +        if (writev(vty->wfd, iov, 2) == -1) +          { +            fprintf(stderr, "Failure to writev: %d\n", errno); +            exit(-1); +          }      }  } @@ -2667,28 +2663,28 @@ vty_event (enum event event, int sock, struct vty *vty)        /* Time out treatment. */        if (vty->v_timeout) -	{ -	  if (vty->t_timeout) -	    thread_cancel (vty->t_timeout); -	  vty->t_timeout =  -	    thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); -	} +        { +          if (vty->t_timeout) +            thread_cancel (vty->t_timeout); +          vty->t_timeout = +            thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); +        }        break;      case VTY_WRITE:        if (! vty->t_write) -	vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock); +        vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock);        break;      case VTY_TIMEOUT_RESET:        if (vty->t_timeout) -	{ -	  thread_cancel (vty->t_timeout); -	  vty->t_timeout = NULL; -	} +        { +          thread_cancel (vty->t_timeout); +          vty->t_timeout = NULL; +        }        if (vty->v_timeout) -	{ -	  vty->t_timeout =  -	    thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); -	} +        { +          vty->t_timeout = +            thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); +        }        break;      }  } @@ -2704,8 +2700,8 @@ DEFUN (config_who,    for (i = 0; i < vector_active (vtyvec); i++)      if ((v = vector_slot (vtyvec, i)) != NULL)        vty_out (vty, "%svty[%d] connected from %s.%s", -	       v->config ? "*" : " ", -	       i, v->address, VTY_NEWLINE); +               v->config ? "*" : " ", +               i, v->address, VTY_NEWLINE);    return CMD_SUCCESS;  } @@ -2798,7 +2794,7 @@ DEFUN (no_vty_access_class,    if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))      {        vty_out (vty, "Access-class is not currently applied to vty%s", -	       VTY_NEWLINE); +               VTY_NEWLINE);        return CMD_WARNING;      } @@ -2839,7 +2835,7 @@ DEFUN (no_vty_ipv6_access_class,        (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))      {        vty_out (vty, "IPv6 access-class is not currently applied to vty%s", -	       VTY_NEWLINE); +               VTY_NEWLINE);        return CMD_WARNING;      } @@ -2951,13 +2947,13 @@ DEFUN (show_history,    for (index = vty->hindex + 1; index != vty->hindex;)      {        if (index == VTY_MAXHIST) -	{ -	  index = 0; -	  continue; -	} +        { +          index = 0; +          continue; +        }        if (vty->hist[index] != NULL) -	vty_out (vty, "  %s%s", vty->hist[index], VTY_NEWLINE); +        vty_out (vty, "  %s%s", vty->hist[index], VTY_NEWLINE);        index++;      } @@ -2984,22 +2980,22 @@ vty_config_write (struct vty *vty)    if (vty_accesslist_name)      vty_out (vty, " access-class %s%s", -	     vty_accesslist_name, VTY_NEWLINE); +             vty_accesslist_name, VTY_NEWLINE);    if (vty_ipv6_accesslist_name)      vty_out (vty, " ipv6 access-class %s%s", -	     vty_ipv6_accesslist_name, VTY_NEWLINE); +             vty_ipv6_accesslist_name, VTY_NEWLINE);    /* exec-timeout */    if (vty_timeout_val != VTY_TIMEOUT_DEFAULT) -    vty_out (vty, " exec-timeout %ld %ld%s",  -	     vty_timeout_val / 60, -	     vty_timeout_val % 60, VTY_NEWLINE); +    vty_out (vty, " exec-timeout %ld %ld%s", +             vty_timeout_val / 60, +             vty_timeout_val % 60, VTY_NEWLINE);    /* login */    if (no_password_check)      vty_out (vty, " no login%s", VTY_NEWLINE); -     +    if (restricted_mode != restricted_mode_default)      {        if (restricted_mode_default) @@ -3010,7 +3006,7 @@ vty_config_write (struct vty *vty)    if (do_log_commands)      vty_out (vty, "log commands%s", VTY_NEWLINE); -     +    vty_out (vty, "!%s", VTY_NEWLINE);    return CMD_SUCCESS; @@ -3034,16 +3030,16 @@ vty_reset ()    for (i = 0; i < vector_active (vtyvec); i++)      if ((vty = vector_slot (vtyvec, i)) != NULL)        { -	buffer_reset (vty->obuf); -	vty->status = VTY_CLOSE; -	vty_close (vty); +        buffer_reset (vty->obuf); +        vty->status = VTY_CLOSE; +        vty_close (vty);        }    for (i = 0; i < vector_active (Vvty_serv_thread); i++)      if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)        { -	thread_cancel (vty_serv_thread); -	vector_slot (Vvty_serv_thread, i) = NULL; +        thread_cancel (vty_serv_thread); +        vector_slot (Vvty_serv_thread, i) = NULL;          close (i);        } @@ -3078,15 +3074,15 @@ vty_save_cwd (void)         * Hence not worrying about it too much.         */        if (!chdir (SYSCONFDIR)) -	{ -	  fprintf(stderr, "Failure to chdir to %s, errno: %d\n", SYSCONFDIR, errno); -	  exit(-1); -	} +        { +          fprintf(stderr, "Failure to chdir to %s, errno: %d\n", SYSCONFDIR, errno); +          exit(-1); +        }        if (getcwd (cwd, MAXPATHLEN) == NULL) -	{ -	  fprintf(stderr, "Failure to getcwd, errno: %d\n", errno); -	  exit(-1); -	} +        { +          fprintf(stderr, "Failure to getcwd, errno: %d\n", errno); +          exit(-1); +        }      }    vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);  | 
