diff options
Diffstat (limited to 'lib/command.c')
| -rw-r--r-- | lib/command.c | 3464 |
1 files changed, 762 insertions, 2702 deletions
diff --git a/lib/command.c b/lib/command.c index 0b0614b806..f691cb599a 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1,25 +1,29 @@ /* - Command interpreter routine for virtual terminal [aka TeletYpe] - Copyright (C) 1997, 98, 99 Kunihiro Ishiguro - Copyright (C) 2013 by Open Source Routing. - 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 -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. */ + * CLI backend interface. + * + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + * Copyright (C) 2013 by Open Source Routing. + * 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 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> @@ -29,10 +33,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" #include "qobj.h" DEFINE_MTYPE( LIB, HOST, "Host config") @@ -43,44 +50,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; @@ -128,7 +97,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 }, @@ -180,7 +149,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; @@ -200,7 +169,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; @@ -209,14 +178,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++ = ' '; } @@ -224,6 +193,26 @@ argv_concat (const char **argv, int argc, int shift) return str; } +/** + * Convenience function for accessing argv data. + * + * @param argc + * @param argv + * @param text definition snippet of the desired token + * @param index the starting index, and where to store the + * index of the found token if it exists + * @return 1 if found, 0 otherwise + */ +int +argv_find (struct cmd_token **argv, int argc, const char *text, int *index) +{ + int found = 0; + for (int i = *index; i < argc && found == 0; i++) + if ((found = strmatch (text, argv[i]->text))) + *index = i; + return found; +} + static unsigned int cmd_hash_key (void *p) { @@ -238,65 +227,57 @@ cmd_hash_cmp (const void *a, const void *b) /* 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->cmdgraph = graph_new (); node->cmd_vector = vector_init (VECTOR_MIN_SIZE); + // add start node + struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + graph_new_node (node->cmdgraph, token, (void (*)(void *)) &del_cmd_token); node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp); } -/* Breaking up string into each command piece. I assume given - character is separated by a space character. Return value is a - vector which includes char ** data element. */ +/** + * Tokenizes a string, storing tokens in a vector. + * Whitespace is ignored. + * + * Delimiter string = " \n\r\t". + * + * @param string to tokenize + * @return tokenized string + */ vector cmd_make_strvec (const char *string) { - const char *cp, *start; - char *token; - int strlen; - vector strvec; - - if (string == NULL) - return NULL; - - cp = string; + if (!string) return NULL; - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') - cp++; + char *copy, *copystart; + copystart = copy = XSTRDUP (MTYPE_TMP, string); - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; + // skip leading whitespace + while (isspace ((int) *copy) && *copy != '\0') copy++; - if (*cp == '!' || *cp == '#') + // if the entire string was whitespace or a comment, return + if (*copy == '\0' || *copy == '!' || *copy == '#') + { + XFREE (MTYPE_TMP, copystart); return NULL; + } - /* Prepare return vector. */ - strvec = vector_init (VECTOR_MIN_SIZE); + vector strvec = vector_init (VECTOR_MIN_SIZE); + const char *delim = " \n\r\t", *tok = NULL; + while (copy) + { + tok = strsep (©, delim); + if (*tok != '\0') + vector_set (strvec, XSTRDUP (MTYPE_STRVEC, tok)); + } - /* Copy each command piece and set into vector. */ - while (1) - { - start = cp; - while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && - *cp != '\0') - cp++; - strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - vector_set (strvec, token); - - while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; - - if (*cp == '\0') - return strvec; - } + XFREE (MTYPE_TMP, copystart); + return strvec; } /* Free allocated string vector. */ @@ -316,387 +297,22 @@ 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) +char * +cmd_concat_strvec (vector v) { - 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; + size_t strsize = 0; + for (unsigned int i = 0; i < vector_active (v); i++) + if (vector_slot (v, i)) + strsize += strlen ((char *) vector_slot (v, i)) + 1; - while (1) - { - while (isspace((int)state.cp[0]) && state.cp[0] != '\0') - state.cp++; + char *concatenated = calloc (sizeof (char), strsize); + for (unsigned int i = 0; i < vector_active (v); i++) + { + strlcat (concatenated, (char *) vector_slot (v, i), strsize); + strlcat (concatenated, " ", strsize); + } - 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 concatenated; } /* Return prompt character of specified node. */ @@ -714,7 +330,7 @@ void install_element (enum node_type ntype, struct cmd_element *cmd) { struct cmd_node *cnode; - + /* cmd_init hasn't been called */ if (!cmdvec) { @@ -722,29 +338,28 @@ install_element (enum node_type ntype, struct cmd_element *cmd) __func__); 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); } - + if (hash_lookup (cnode->cmd_hash, cmd) != NULL) { - fprintf (stderr, + fprintf (stderr, "Multiple command installs to node %d of command:\n%s\n", ntype, cmd->string); return; } - + assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern)); - + + 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); if (ntype == VIEW_NODE) install_element (ENABLE_NODE, cmd); @@ -756,7 +371,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; @@ -771,7 +386,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'; @@ -789,9 +404,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 { @@ -804,17 +419,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); } @@ -822,8 +437,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); } @@ -831,27 +446,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); @@ -861,7 +476,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); @@ -871,1376 +486,128 @@ config_write_host (struct vty *vty) return 1; } -/* Utility function for getting command vector. */ -static vector -cmd_node_vector (vector v, enum node_type ntype) +/* Utility function for getting command graph. */ +static struct graph * +cmd_node_graph (vector v, enum node_type ntype) { struct cmd_node *cnode = vector_slot (v, 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) -{ - 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; + 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 && + 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. - */ -static int -cmd_vector_filter(vector commands, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *matches) -{ - 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; -} - /** - * 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. + * Compare function for cmd_token. + * Used with qsort to sort command completions. */ static int -cmd_is_complete(struct cmd_element *cmd_element, - vector vline) +compare_completions (const void *fst, const void *snd) { - enum matcher_rv rv; - - rv = cmd_element_match(cmd_element, - FILTER_RELAXED, - vline, -1, - NULL, NULL, - NULL, NULL); - return (rv == MATCHER_COMPLETE); + struct cmd_token *first = *(struct cmd_token **) fst, + *secnd = *(struct cmd_token **) snd; + return strcmp (first->text, secnd->text); } /** - * Parse a given commandline and construct a list of arguments for the - * given command_element. + * Takes a list of completions returned by command_complete, + * dedeuplicates them based on both text and description, + * sorts them, and returns them as a vector. * - * @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. + * @param completions linked list of cmd_token + * @return deduplicated and sorted vector with */ -static int -cmd_parse(struct cmd_element *cmd_element, - vector vline, - int *argc, const char **argv) +static vector +completions_to_vec (struct list *completions) { - enum matcher_rv rv = cmd_element_match(cmd_element, - FILTER_RELAXED, - vline, -1, - NULL, NULL, - argc, argv); - switch (rv) - { - case MATCHER_COMPLETE: - return CMD_SUCCESS; - - case MATCHER_NO_MATCH: - return CMD_ERR_NO_MATCH; - - case MATCHER_AMBIGUOUS: - return CMD_ERR_AMBIGUOUS; + vector comps = vector_init (VECTOR_MIN_SIZE); - case MATCHER_EXCEED_ARGC_MAX: - return CMD_ERR_EXEED_ARGC_MAX; + struct listnode *ln; + struct cmd_token *token, *cr = NULL; + unsigned int i, exists; + for (ALL_LIST_ELEMENTS_RO(completions,ln,token)) + { + if (token->type == END_TKN && (cr = token)) + continue; - default: - return CMD_ERR_INCOMPLETE; + // linear search for token in completions vector + exists = 0; + for (i = 0; i < vector_active (comps) && !exists; i++) + { + 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; - - 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; + if (!exists) + vector_set (comps, token); + } - default: - assert(0); - return NULL; - } -} + // sort completions + qsort (comps->index, + vector_active (comps), + sizeof (void *), + &compare_completions); -/** - * 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; + // make <cr> the first element, if it is present + if (cr) + { + vector_set_index (comps, vector_active (comps), NULL); + memmove (comps->index + 1, comps->index, (comps->alloced - 1) * sizeof (void *)); + vector_set_index (comps, 0, cr); + } - for (i = 0; i < vector_active (v); i++) - if ((match = vector_slot (v, i)) != NULL) - if (strcmp (match, str) == 0) - return 0; - return 1; + return comps; } - /** - * 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 + * @return vector of struct cmd_token * with possible completions */ -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 && - 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 = NULL; - 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)); - - /* Prepare match vector */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - - /* Filter commands and build a list how they could possibly continue. */ - for (i = 0; i <= index; i++) - { - 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; - } - } - - /* 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); - } + struct list *completions; + struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); - 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; + enum matcher_rv rv = command_complete (cmdgraph, vline, &completions); - 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); + if (MATCHER_ERROR(rv)) + { + *status = CMD_ERR_NO_MATCH; + 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 @@ -2260,291 +627,93 @@ 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); + return cmd_complete_command_real (vline, vty, status); } +/** + * Generate possible tab-completions for the given input. This function only + * returns results that would result in a valid command if used as Readline + * completions (as is the case in vtysh). For instance, if the passed vline ends + * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned. + * + * @param vline vectorized input line + * @param vty the vty + * @param status location to store matcher status code in + * @return set of valid strings for use with Readline as tab-completions. + */ -/* 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) +char ** +cmd_complete_command (vector vline, struct vty *vty, int *status) { - 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); -} + char **ret = NULL; + int original_node = vty->node; + vector input_line = vector_init (vector_count (vline)); -static void -cmd_complete_sort(vector matchvec) -{ - qsort(matchvec->index, vector_active(matchvec), - sizeof(void*), cmd_complete_cmp); -} + // if the first token is 'do' we'll want to execute the command in the enable node + int do_shortcut = cmd_try_do_shortcut (vty->node, vector_slot (vline, 0)); + vty->node = do_shortcut ? ENABLE_NODE : original_node; -/* 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; + // construct the input line we'll be matching on + unsigned int offset = (do_shortcut) ? 1 : 0; + for (unsigned index = 0; index + offset < vector_active (vline); index++) + vector_set_index (input_line, index + offset, vector_lookup (vline, index)); - if (vector_active (vline) == 0) - { - vector_free (cmd_vector); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - else - index = vector_active (vline) - 1; + // get token completions -- this is a copying operation + vector comps = NULL, initial_comps; + initial_comps = cmd_complete_command_real (input_line, vty, status); - /* First, filter by command string */ - for (i = 0; i <= index; i++) + if (!MATCHER_ERROR (*status)) + { + assert (initial_comps); + // filter out everything that is not suitable for a tab-completion + comps = vector_init (VECTOR_MIN_SIZE); + for (unsigned int i = 0; i < vector_active(initial_comps); 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; - } + struct cmd_token *token = vector_slot (initial_comps, i); + if (token->type == WORD_TKN) + vector_set (comps, token); } - - /* Prepare match vector. */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); + vector_free (initial_comps); - /* 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))) + // copy completions text into an array of char* + ret = XMALLOC (MTYPE_TMP, (vector_active (comps)+1) * sizeof (char *)); + unsigned int i; + for (i = 0; i < vector_active (comps); 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 */)); - } + struct cmd_token *token = vector_slot (comps, i); + ret[i] = XSTRDUP (MTYPE_TMP, token->text); + vector_unset (comps, i); } + // set the last element to NULL, because this array is used in + // a Readline completion_generator function which expects NULL + // as a sentinel value + ret[i] = NULL; + vector_free (comps); + comps = NULL; + } - /* 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; -} - -char ** -cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) -{ - char **ret; - - if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) - { - enum node_type onode; - vector shifted_vline; - unsigned int index; - - onode = vty->node; - vty->node = ENABLE_NODE; - /* We can try it on enable node, cos' the vty is authenticated */ - - 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)); - } - - ret = cmd_complete_command_real (shifted_vline, vty, status, islib); + // comps should always be null here + assert (!comps); - vector_free(shifted_vline); - vty->node = onode; - return ret; - } + // free the adjusted input line + vector_free (input_line); - return cmd_complete_command_real (vline, vty, status, islib); -} + // reset vty->node to its original value + vty->node = original_node; -char ** -cmd_complete_command (vector vline, struct vty *vty, int *status) -{ - return cmd_complete_command_lib (vline, vty, status, 0); + return ret; } /* return parent node */ @@ -2563,8 +732,8 @@ node_parent ( enum node_type node ) case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: case BGP_VNC_DEFAULTS_NODE: - case BGP_VNC_NVE_GROUP_NODE: - case BGP_VNC_L2_GROUP_NODE: + case BGP_VNC_NVE_GROUP_NODE: + case BGP_VNC_L2_GROUP_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -2601,109 +770,55 @@ 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) + enum filter_type filter, + struct vty *vty, + const 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; + struct list *argv_list; + enum matcher_rv status; + const struct cmd_element *matched_element = NULL; - 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++; - } - } + struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); + status = command_match (cmdgraph, vline, &argv_list, &matched_element); - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); + if (cmd) + *cmd = matched_element; - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) + // if matcher error, return corresponding CMD_ERR + if (MATCHER_ERROR(status)) + { + switch (status) { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; + 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; - - ret = cmd_parse(matched_element, vline, &argc, argv); - if (ret != CMD_SUCCESS) - return ret; + // 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; - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; + int argc = argv_list->count; + int ret; if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; + ret = CMD_SUCCESS_DAEMON; + else + ret = matched_element->func (matched_element, vty, argc, argv); - /* Execute matched command. */ - return (*matched_element->func) (matched_element, vty, argc, argv); + // delete list and cmd_token's in it + list_delete (argv_list); + XFREE (MTYPE_TMP, argv); + + return ret; } /** @@ -2722,14 +837,16 @@ cmd_execute_command_real (vector vline, * as to why no command could be executed. */ int -cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, - int vtysh) { - int ret, saved_ret, tried = 0; +cmd_execute_command (vector vline, struct vty *vty, + const struct cmd_element **cmd, + int vtysh) +{ + int ret, saved_ret = 0; enum node_type onode, try_node; onode = try_node = vty->node; - if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { vector shifted_vline; unsigned int index; @@ -2739,10 +856,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); @@ -2751,30 +866,27 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, return ret; } - saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, 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; } @@ -2793,7 +905,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) + const struct cmd_element **cmd) { return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); } @@ -2811,7 +923,7 @@ cmd_execute_command_strict (vector vline, struct vty *vty, * as to why no command could be executed. */ int -command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon) +command_config_read_one_line (struct vty *vty, const struct cmd_element **cmd, int use_daemon) { vector vline; int saved_node; @@ -2837,7 +949,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); @@ -2848,14 +960,16 @@ 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; } } + if (ret != CMD_SUCCESS && ret != CMD_WARNING) + memcpy (vty->error_buf, vty->buf, VTY_BUFSIZ); + cmd_free_strvec (vline); return ret; @@ -2871,13 +985,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) { @@ -2905,7 +1019,7 @@ DEFUN (config_terminal, } /* Enable command */ -DEFUN (enable, +DEFUN (enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") @@ -2921,7 +1035,7 @@ DEFUN (enable, } /* Disable command */ -DEFUN (disable, +DEFUN (disable, config_disable_cmd, "disable", "Turn off privileged mode command\n") @@ -2942,9 +1056,9 @@ DEFUN (config_exit, case VIEW_NODE: case ENABLE_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; @@ -3007,12 +1121,16 @@ DEFUN (config_exit, return CMD_SUCCESS; } -/* quit is alias of exit. */ -ALIAS (config_exit, +/* ALIAS_FIXME */ +DEFUN (config_quit, config_quit_cmd, "quit", "Exit current mode and down to previous mode\n") - +{ + return config_exit (self, vty, argc, argv); +} + + /* End of configuration. */ DEFUN (config_end, config_end_cmd, @@ -3078,7 +1196,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); @@ -3092,8 +1210,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\ @@ -3105,37 +1223,92 @@ 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; } +static void +permute (struct graph_node *start, struct vty *vty) +{ + static struct list *position = NULL; + if (!position) position = list_new (); + + // recursive dfs + listnode_add (position, start); + for (unsigned int i = 0; i < vector_active (start->to); i++) + { + struct graph_node *gn = vector_slot (start->to, i); + struct cmd_token *tok = gn->data; + if (tok->attr == CMD_ATTR_HIDDEN || + tok->attr == CMD_ATTR_DEPRECATED) + continue; + else if (tok->type == END_TKN || gn == start) + { + struct graph_node *gnn; + struct listnode *ln; + vty_out (vty, " "); + for (ALL_LIST_ELEMENTS_RO (position,ln,gnn)) + { + struct cmd_token *tt = gnn->data; + if (tt->type < SELECTOR_TKN) + vty_out (vty, " %s", tt->text); + } + if (gn == start) + vty_out (vty, "..."); + vty_out (vty, VTY_NEWLINE); + } + else + permute (gn, vty); + } + list_delete_node (position, listtail(position)); +} + /* Help display function for all node. */ DEFUN (config_list, config_list_cmd, - "list", - "Print command list\n") + "list [permutations]", + "Print command list\n" + "Print all possible command permutations\n") { - unsigned int i; - struct cmd_node *cnode = vector_slot (cmdvec, vty->node); - struct cmd_element *cmd; - - for (i = 0; i < vector_active (cnode->cmd_vector); i++) - if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL - && !(cmd->attr == CMD_ATTR_DEPRECATED - || cmd->attr == CMD_ATTR_HIDDEN)) - vty_out (vty, " %s%s", cmd->string, - VTY_NEWLINE); + struct cmd_node *node = vector_slot (cmdvec, vty->node); + + if ((strmatch (argv[0]->text, "list") && argc == 2) || + (strmatch (argv[0]->text, "show") && argc == 3)) + permute (vector_slot (node->cmdgraph->nodes, 0), vty); + else + { + /* loop over all commands at this node */ + struct cmd_element *element = NULL; + for (unsigned int i = 0; i < vector_active(node->cmd_vector); i++) + if ((element = vector_slot (node->cmd_vector, i)) && + element->attr != CMD_ATTR_DEPRECATED && + element->attr != CMD_ATTR_HIDDEN) + vty_out (vty, " %s%s", element->string, VTY_NEWLINE); + } return CMD_SUCCESS; } +DEFUN (show_commandtree, + show_commandtree_cmd, + "show commandtree [permutations]", + SHOW_STR + "Show command tree\n") +{ + return config_list (self, vty, argc, argv); +} + /* 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") { + int idx_type = 1; unsigned int i; int fd; struct cmd_node *node; @@ -3146,6 +1319,37 @@ DEFUN (config_write_file, struct vty *file_vty; struct stat conf_stat; + // if command was 'write terminal', 'write memory' or 'show running-config' + if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal") || + !strcmp(argv[idx_type]->text, "memory") || + !strcmp(argv[0]->text, "show"))) + { + 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; + } + if (host.noconfig) return CMD_SUCCESS; @@ -3153,13 +1357,13 @@ DEFUN (config_write_file, 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); @@ -3168,16 +1372,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; @@ -3191,51 +1395,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: @@ -3245,76 +1449,34 @@ 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_FIXME for 'write <terminal|memory>' */ +DEFUN (show_running_config, + show_running_config_cmd, + "show running-config", + SHOW_STR + "running configuration (same as write terminal/memory)\n") +{ + return config_write (self, vty, argc, argv); +} -ALIAS (config_write_file, - copy_runningconfig_startupconfig_cmd, - "copy running-config startup-config", +/* ALIAS_FIXME for 'write file' */ +DEFUN (copy_runningconf_startupconf, + copy_runningconf_startupconf_cmd, + "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 (host.noconfig) - return CMD_SUCCESS; - - 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; + return config_write (self, vty, argc, argv); } - -/* Write current configuration into the terminal. */ -ALIAS (config_write_terminal, - show_running_config_cmd, - "show running-config", - SHOW_STR - "running configuration\n") +/** -- **/ /* Write startup configuration into the terminal. */ 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; @@ -3328,7 +1490,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; } @@ -3337,7 +1499,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); @@ -3349,13 +1511,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; @@ -3363,12 +1527,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 @@ -3382,43 +1546,30 @@ DEFUN (config_no_hostname, } /* VTY interface password set. */ -DEFUN (config_password, password_cmd, - "password (8|) WORD", +DEFUN (config_password, + password_cmd, + "password [(8-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; - } - } + int idx_8 = 1; + int idx_word = 2; + 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[idx_word]->arg); + return CMD_SUCCESS; + } - if (!isalnum ((int) *argv[0])) + if (!isalnum (argv[idx_8]->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; } @@ -3429,62 +1580,53 @@ 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[idx_8]->arg)); } else - host.password = XSTRDUP (MTYPE_HOST, argv[0]); + host.password = XSTRDUP (MTYPE_HOST, argv[idx_8]->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", +DEFUN (config_enable_password, + enable_password_cmd, + "enable password [(8-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; - } + int idx_8 = 2; + int idx_word = 3; /* 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[idx_8]->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[idx_word]->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[idx_8]->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; } @@ -3496,24 +1638,18 @@ 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[idx_8]->arg)); } else - host.enable = XSTRDUP (MTYPE_HOST, argv[0]); + host.enable = XSTRDUP (MTYPE_HOST, argv[idx_8]->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, +DEFUN (no_config_enable_password, + no_enable_password_cmd, "no enable password", NO_STR "Modify enable password parameters\n" @@ -3529,7 +1665,7 @@ DEFUN (no_config_enable_password, no_enable_password_cmd, return CMD_SUCCESS; } - + DEFUN (service_password_encrypt, service_password_encrypt_cmd, "service password-encryption", @@ -3544,13 +1680,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)); } @@ -3580,16 +1716,18 @@ DEFUN (no_service_password_encrypt, return CMD_SUCCESS; } -DEFUN (config_terminal_length, config_terminal_length_cmd, - "terminal length <0-512>", +DEFUN (config_terminal_length, + config_terminal_length_cmd, + "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") { + int idx_number = 2; int lines; char *endptr = NULL; - lines = strtol (argv[0], &endptr, 10); + lines = strtol (argv[idx_number]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3600,7 +1738,8 @@ DEFUN (config_terminal_length, config_terminal_length_cmd, return CMD_SUCCESS; } -DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, +DEFUN (config_terminal_no_length, + config_terminal_no_length_cmd, "terminal no length", "Set terminal line parameters\n" NO_STR @@ -3610,16 +1749,18 @@ DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, return CMD_SUCCESS; } -DEFUN (service_terminal_length, service_terminal_length_cmd, - "service terminal-length <0-512>", +DEFUN (service_terminal_length, + service_terminal_length_cmd, + "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") { + int idx_number = 2; int lines; char *endptr = NULL; - lines = strtol (argv[0], &endptr, 10); + lines = strtol (argv[idx_number]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3630,8 +1771,9 @@ DEFUN (service_terminal_length, service_terminal_length_cmd, return CMD_SUCCESS; } -DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, - "no service terminal-length [<0-512>]", +DEFUN (no_service_terminal_length, + no_service_terminal_length_cmd, + "no service terminal-length [(0-512)]", NO_STR "Set up miscellaneous service\n" "System wide terminal length configuration\n" @@ -3642,15 +1784,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, 1)) ? message : ""), + VTY_NEWLINE); if (message) XFREE(MTYPE_TMP, message); return CMD_SUCCESS; @@ -3658,18 +1800,20 @@ DEFUN_HIDDEN (do_echo, DEFUN (config_logmsg, config_logmsg_cmd, - "logmsg "LOG_LEVELS" .MESSAGE", + "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...", "Send a message to enabled logging destinations\n" LOG_LEVEL_DESC "The message to send\n") { + int idx_log_level = 1; + int idx_message = 2; int level; char *message; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; - zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); + zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, idx_message)) ? message : "")); if (message) XFREE(MTYPE_TMP, message); @@ -3689,8 +1833,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: "); @@ -3698,7 +1842,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: "); @@ -3706,7 +1850,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: "); @@ -3715,40 +1859,37 @@ 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 [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC) { + int idx_log_level = 2; + + if (argc == idx_log_level) + { + 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[idx_log_level]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_set_level (NULL, ZLOG_DEST_STDOUT, level); return CMD_SUCCESS; @@ -3756,11 +1897,11 @@ DEFUN (config_log_stdout_level, DEFUN (no_config_log_stdout, no_config_log_stdout_cmd, - "no log stdout [LEVEL]", + "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", 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; @@ -3768,24 +1909,21 @@ 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 [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", "Logging control\n" "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) { + int idx_log_level = 2; + + if (argc == idx_log_level) + { + 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[idx_log_level]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_set_level (NULL, ZLOG_DEST_MONITOR, level); return CMD_SUCCESS; @@ -3793,11 +1931,11 @@ DEFUN (config_log_monitor_level, DEFUN (no_config_log_monitor, no_config_log_monitor_cmd, - "no log monitor [LEVEL]", + "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", 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; @@ -3809,19 +1947,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) { @@ -3859,36 +1997,34 @@ 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 [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", "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); + int idx_filename = 2; + int idx_log_levels = 3; + if (argc == 4) + { + int level; + if ((level = level_match(argv[idx_log_levels]->arg)) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[idx_filename]->arg, level); + } + else + return set_log_file(vty, argv[idx_filename]->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); @@ -3900,52 +2036,38 @@ 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 [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", "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; + int idx_log_levels = 2; + if (argc == 3) + { + int level; + if ((level = level_match (argv[idx_log_levels]->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 (kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7)", + "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; @@ -3954,63 +2076,54 @@ DEFUN_DEPRECATED (config_log_syslog_facility, DEFUN (no_config_log_syslog, no_config_log_syslog_cmd, - "no log syslog [LEVEL]", + "no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", 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, + "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>", "Logging control\n" "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) { - int facility; + int idx_target = 2; + int facility = facility_match(argv[idx_target]->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 [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]", 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 <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>", + "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; @@ -4021,12 +2134,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 [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]", + NO_STR + "Logging control\n" + "Permit all logging information\n" + LOG_LEVEL_DESC) { zlog_default->default_lvl = LOG_DEBUG; return CMD_SUCCESS; @@ -4055,20 +2168,15 @@ DEFUN (no_config_log_record_priority, DEFUN (config_log_timestamp_precision, config_log_timestamp_precision_cmd, - "log timestamp precision <0-6>", + "log timestamp precision (0-6)", "Logging control\n" "Timestamp configuration\n" "Set the timestamp precision\n" "Number of subsecond digits\n") { - if (argc != 1) - { - vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); - return CMD_WARNING; - } - + int idx_number = 3; VTY_GET_INTEGER_RANGE("Timestamp Precision", - zlog_default->timestamp_precision, argv[0], 0, 6); + zlog_default->timestamp_precision, argv[idx_number]->arg, 0, 6); return CMD_SUCCESS; } @@ -4099,7 +2207,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 @@ -4116,13 +2224,14 @@ DEFUN (banner_motd_file, "Banner from a file\n" "Filename\n") { - int cmd = cmd_banner_motd_file (argv[0]); + int idx_file = 3; + const char *filename = argv[idx_file]->arg; + int cmd = cmd_banner_motd_file (filename); if (cmd == CMD_ERR_NO_FILE) - vty_out (vty, "%s does not exist", argv[0]); + vty_out (vty, "%s does not exist", filename); else if (cmd == CMD_WARNING) - vty_out (vty, "%s must be in %s", - argv[0], SYSCONFDIR); + vty_out (vty, "%s must be in %s", filename, SYSCONFDIR); return cmd; } @@ -4146,43 +2255,12 @@ 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; } -DEFUN (show_commandtree, - show_commandtree_cmd, - "show commandtree", - NO_STR - "Show command tree\n") -{ - /* TBD */ - vector cmd_vector; - unsigned int i; - - vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE); - - /* vector of all commands installed at this node */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - - /* loop over all commands at this node */ - for (i = 0; i < vector_active(cmd_vector); ++i) - { - struct cmd_element *cmd_element; - - /* A cmd_element (seems to be) is an individual command */ - if ((cmd_element = vector_slot (cmd_vector, i)) == NULL) - continue; - - vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE); - } - - vector_free (cmd_vector); - return CMD_SUCCESS; -} - /* Set config filename. Called from vty.c */ void host_config_set (const char *filename) @@ -4207,9 +2285,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); } @@ -4224,12 +2299,6 @@ cmd_init (int terminal) { qobj_init (); - 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); @@ -4272,10 +2341,7 @@ cmd_init (int terminal) install_element (ENABLE_NODE, &config_end_cmd); install_element (ENABLE_NODE, &config_disable_cmd); install_element (ENABLE_NODE, &config_terminal_cmd); - install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); - install_element (ENABLE_NODE, &config_write_terminal_cmd); - install_element (ENABLE_NODE, &config_write_file_cmd); - install_element (ENABLE_NODE, &config_write_memory_cmd); + install_element (ENABLE_NODE, ©_runningconf_startupconf_cmd); install_element (ENABLE_NODE, &config_write_cmd); install_element (ENABLE_NODE, &show_running_config_cmd); } @@ -4291,33 +2357,24 @@ cmd_init (int terminal) install_element (VIEW_NODE, &show_work_queues_cmd); } - + install_element (CONFIG_NODE, &hostname_cmd); install_element (CONFIG_NODE, &no_hostname_cmd); if (terminal > 0) { 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); @@ -4339,87 +2396,90 @@ 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, u_char attr, 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->attr = attr; + 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, token->attr, NULL, NULL); + 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; + free ((char *) cmd->string); + free ((char *) cmd->doc); + free (cmd); +} - vector_free(cmd->tokens); - cmd->tokens = NULL; +struct cmd_element * +copy_cmd_element(const 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); - hash_clean (cmd_node->cmd_hash, NULL); - hash_free (cmd_node->cmd_hash); - cmd_node->cmd_hash = NULL; - } + { + // deleting the graph delets the cmd_element as well + graph_delete_graph (cmd_node->cmdgraph); + vector_free (cmd_node->cmd_vector); + hash_clean (cmd_node->cmd_hash, NULL); + hash_free (cmd_node->cmd_hash); + cmd_node->cmd_hash = NULL; + } 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) |
