diff options
Diffstat (limited to 'lib/command.c')
| -rw-r--r-- | lib/command.c | 3068 |
1 files changed, 508 insertions, 2560 deletions
diff --git a/lib/command.c b/lib/command.c index cfdb91a5b9..b6f97c317e 100644 --- a/lib/command.c +++ b/lib/command.c @@ -5,7 +5,7 @@ Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") This file is part of GNU Zebra. - + GNU Zebra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your @@ -29,10 +29,13 @@ Boston, MA 02111-1307, USA. */ #include <lib/version.h> #include "thread.h" #include "vector.h" +#include "linklist.h" #include "vty.h" #include "command.h" #include "workqueue.h" #include "vrf.h" +#include "command_match.h" +#include "command_parse.h" DEFINE_MTYPE( LIB, HOST, "Host config") DEFINE_MTYPE( LIB, STRVEC, "String vector") @@ -42,44 +45,6 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") each daemon maintains each own cmdvec. */ vector cmdvec = NULL; -struct cmd_token token_cr; -char *command_cr = NULL; - -/** - * Filter types. These tell the parser whether to allow - * partial matching on tokens. - */ -enum filter_type -{ - FILTER_RELAXED, - FILTER_STRICT -}; - -/** - * Command matcher result value. - */ -enum matcher_rv -{ - MATCHER_OK, - MATCHER_COMPLETE, - MATCHER_INCOMPLETE, - MATCHER_NO_MATCH, - MATCHER_AMBIGUOUS, - MATCHER_EXCEED_ARGC_MAX -}; - -/** - * Defines which matcher_rv values constitute - * an error. Should be used against matcher_rv - * return values to do basic error checking. - */ -#define MATCHER_ERROR(matcher_rv) \ - ( (matcher_rv) == MATCHER_INCOMPLETE \ - || (matcher_rv) == MATCHER_NO_MATCH \ - || (matcher_rv) == MATCHER_AMBIGUOUS \ - || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ - ) - /* Host information structure. */ struct host host; @@ -133,7 +98,7 @@ static const struct facility_map { int facility; const char *name; size_t match; -} syslog_facilities[] = +} syslog_facilities[] = { { LOG_KERN, "kern", 1 }, { LOG_USER, "user", 2 }, @@ -185,7 +150,7 @@ static int level_match(const char *s) { int level ; - + for ( level = 0 ; zlog_priority [level] != NULL ; level ++ ) if (!strncmp (s, zlog_priority[level], 2)) return level; @@ -205,7 +170,7 @@ print_version (const char *progname) /* Utility function to concatenate argv argument into a single string with inserting ' ' character between each argument. */ char * -argv_concat (const char **argv, int argc, int shift) +argv_concat (struct cmd_token **argv, int argc, int shift) { int i; size_t len; @@ -214,14 +179,14 @@ argv_concat (const char **argv, int argc, int shift) len = 0; for (i = shift; i < argc; i++) - len += strlen(argv[i])+1; + len += strlen(argv[i]->arg)+1; if (!len) return NULL; p = str = XMALLOC(MTYPE_TMP, len); for (i = shift; i < argc; i++) { size_t arglen; - memcpy(p, argv[i], (arglen = strlen(argv[i]))); + memcpy(p, argv[i]->arg, (arglen = strlen(argv[i]->arg))); p += arglen; *p++ = ' '; } @@ -231,12 +196,12 @@ argv_concat (const char **argv, int argc, int shift) /* Install top node of command vector. */ void -install_node (struct cmd_node *node, - int (*func) (struct vty *)) +install_node (struct cmd_node *node, + int (*func) (struct vty *)) { vector_set_index (cmdvec, node->node, node); node->func = func; - node->cmd_vector = vector_init (VECTOR_MIN_SIZE); + node->cmdgraph = graph_new (); } /* Breaking up string into each command piece. I assume given @@ -249,10 +214,10 @@ cmd_make_strvec (const char *string) char *token; int strlen; vector strvec; - + if (string == NULL) return NULL; - + cp = string; /* Skip white spaces. */ @@ -270,12 +235,12 @@ cmd_make_strvec (const char *string) strvec = vector_init (VECTOR_MIN_SIZE); /* Copy each command piece and set into vector. */ - while (1) + while (1) { start = cp; while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && - *cp != '\0') - cp++; + *cp != '\0') + cp++; strlen = cp - start; token = XMALLOC (MTYPE_STRVEC, strlen + 1); memcpy (token, start, strlen); @@ -283,11 +248,11 @@ cmd_make_strvec (const char *string) vector_set (strvec, token); while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; + *cp != '\0') + cp++; if (*cp == '\0') - return strvec; + return strvec; } } @@ -308,388 +273,6 @@ cmd_free_strvec (vector v) vector_free (v); } -/** - * State structure for command format parser. Tracks - * parse tree position and miscellaneous state variables. - * Used when building a command vector from format strings. - */ -struct format_parser_state -{ - vector topvect; /* Top level vector */ - vector intvect; /* Intermediate level vector, used when there's - a multiple in a keyword. */ - vector curvect; /* current vector where read tokens should be - appended. */ - - const char *string; /* pointer to command string, not modified */ - const char *cp; /* pointer in command string, moved along while - parsing */ - const char *dp; /* pointer in description string, moved along while - parsing */ - - int in_keyword; /* flag to remember if we are in a keyword group */ - int in_multiple; /* flag to remember if we are in a multiple group */ - int just_read_word; /* flag to remember if the last thing we read was a - real word and not some abstract token */ -}; - -static void -format_parser_error(struct format_parser_state *state, const char *message) -{ - int offset = state->cp - state->string + 1; - - fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string); - fprintf(stderr, " %*c\n", offset, '^'); - fprintf(stderr, "%s at offset %d.\n", message, offset); - fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n"); - exit(1); -} - -/** - * Reads out one section of a help string from state->dp. - * Leading whitespace is trimmed and the string is read until - * a newline is reached. - * - * @param[out] state format parser state - * @return the help string token read - */ -static char * -format_parser_desc_str(struct format_parser_state *state) -{ - const char *cp, *start; - char *token; - int strlen; - - cp = state->dp; - - if (cp == NULL) - return NULL; - - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') - cp++; - - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; - - start = cp; - - while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') - cp++; - - strlen = cp - start; - token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - - state->dp = cp; - - return token; -} - -/** - * Transitions format parser state into keyword parsing mode. - * A cmd_token struct, `token`, representing this keyword token is initialized - * and appended to state->curvect. token->keyword is initialized as a vector of - * vector, a new vector is initialized and added to token->keyword, and - * state->curvect is set to point at this vector. When control returns to the - * caller newly parsed tokens will be added to this vector. - * - * In short: - * state->curvect[HEAD] = new cmd_token - * state->curvect[HEAD]->keyword[0] = new vector - * state->curvect = state->curvect[HEAD]->keyword[0] - * - * @param[out] state state struct to transition - */ -static void -format_parser_begin_keyword(struct format_parser_state *state) -{ - struct cmd_token *token; - vector keyword_vect; - - if (state->in_keyword - || state->in_multiple) - format_parser_error(state, "Unexpected '{'"); - - state->cp++; - state->in_keyword = 1; - - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_KEYWORD; - token->keyword = vector_init(VECTOR_MIN_SIZE); - - keyword_vect = vector_init(VECTOR_MIN_SIZE); - vector_set(token->keyword, keyword_vect); - - vector_set(state->curvect, token); - state->curvect = keyword_vect; -} - -/** - * Transitions format parser state into multiple parsing mode. - * A cmd_token struct, `token`, representing this multiple token is initialized - * and appended to state->curvect. token->multiple is initialized as a vector - * of cmd_token and state->curvect is set to point at token->multiple. If - * state->curvect != state->topvect (i.e. this multiple token is nested inside - * another composite token) then a pointer to state->curvect is saved in - * state->intvect. - * - * In short: - * state->curvect[HEAD] = new cmd_token - * state->curvect[HEAD]->multiple = new vector - * state->intvect = state->curvect IFF nested token - * state->curvect = state->curvect[HEAD]->multiple - * - * @param[out] state state struct to transition - */ -static void -format_parser_begin_multiple(struct format_parser_state *state) -{ - struct cmd_token *token; - - if (state->in_keyword == 1) - format_parser_error(state, "Keyword starting with '('"); - - if (state->in_multiple) - format_parser_error(state, "Nested group"); - - state->cp++; - state->in_multiple = 1; - state->just_read_word = 0; - - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_MULTIPLE; - token->multiple = vector_init(VECTOR_MIN_SIZE); - - vector_set(state->curvect, token); - if (state->curvect != state->topvect) - state->intvect = state->curvect; - state->curvect = token->multiple; -} - -/** - * Transition format parser state out of keyword parsing mode. - * This function is called upon encountering '}'. - * state->curvect is reassigned to the top level vector (as - * keywords cannot be nested) and state flags are set appropriately. - * - * @param[out] state state struct to transition - */ -static void -format_parser_end_keyword(struct format_parser_state *state) -{ - if (state->in_multiple - || !state->in_keyword) - format_parser_error(state, "Unexpected '}'"); - - if (state->in_keyword == 1) - format_parser_error(state, "Empty keyword group"); - - state->cp++; - state->in_keyword = 0; - state->curvect = state->topvect; -} - -/** - * Transition format parser state out of multiple parsing mode. - * This function is called upon encountering ')'. - * state->curvect is reassigned to its parent vector (state->intvect - * if the multiple token being exited was nested inside another token, - * state->topvect otherwise) and state flags are set appropriately. - * - * @param[out] state state struct to transition - */ -static void -format_parser_end_multiple(struct format_parser_state *state) -{ - char *dummy; - - if (!state->in_multiple) - format_parser_error(state, "Unexpected ')'"); - - if (vector_active(state->curvect) == 0) - format_parser_error(state, "Empty multiple section"); - - if (!state->just_read_word) - { - /* There are constructions like - * 'show ip ospf database ... (self-originate|)' - * in use. - * The old parser reads a description string for the - * word '' between |) which will never match. - * Simulate this behvaior by dropping the next desc - * string in such a case. */ - - dummy = format_parser_desc_str(state); - XFREE(MTYPE_CMD_TOKENS, dummy); - } - - state->cp++; - state->in_multiple = 0; - - if (state->intvect) - state->curvect = state->intvect; - else - state->curvect = state->topvect; -} - -/** - * Format parser handler for pipe '|' character. - * This character separates subtokens in multiple and keyword type tokens. - * If the current token is a multiple keyword, the position pointer is - * simply moved past the pipe and state flags are set appropriately. - * If the current token is a keyword token, the position pointer is moved - * past the pipe. Then the cmd_token struct for the keyword is fetched and - * a new vector of cmd_token is appended to its vector of vector. Finally - * state->curvect is set to point at this new vector. - * - * In short: - * state->curvect = state->topvect[HEAD]->keyword[HEAD] = new vector - * - * @param[out] state state struct to transition - */ -static void -format_parser_handle_pipe(struct format_parser_state *state) -{ - struct cmd_token *keyword_token; - vector keyword_vect; - - if (state->in_multiple) - { - state->just_read_word = 0; - state->cp++; - } - else if (state->in_keyword) - { - state->in_keyword = 1; - state->cp++; - - keyword_token = vector_slot(state->topvect, - vector_active(state->topvect) - 1); - keyword_vect = vector_init(VECTOR_MIN_SIZE); - vector_set(keyword_token->keyword, keyword_vect); - state->curvect = keyword_vect; - } - else - { - format_parser_error(state, "Unexpected '|'"); - } -} - -/** - * Format parser handler for terminal tokens. - * Parses the token, appends it to state->curvect, and sets - * state flags appropriately. - * - * @param[out] state state struct for current format parser state - */ -static void -format_parser_read_word(struct format_parser_state *state) -{ - const char *start; - int len; - char *cmd; - struct cmd_token *token; - - start = state->cp; - - while (state->cp[0] != '\0' - && !strchr("\r\n(){}|", state->cp[0]) - && !isspace((int)state->cp[0])) - state->cp++; - - len = state->cp - start; - cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1); - memcpy(cmd, start, len); - cmd[len] = '\0'; - - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_TERMINAL; - if (strcmp (cmd, "A.B.C.D") == 0) - token->terminal = TERMINAL_IPV4; - else if (strcmp (cmd, "A.B.C.D/M") == 0) - token->terminal = TERMINAL_IPV4_PREFIX; - else if (strcmp (cmd, "X:X::X:X") == 0) - token->terminal = TERMINAL_IPV6; - else if (strcmp (cmd, "X:X::X:X/M") == 0) - token->terminal = TERMINAL_IPV6_PREFIX; - else if (cmd[0] == '[') - token->terminal = TERMINAL_OPTION; - else if (cmd[0] == '.') - token->terminal = TERMINAL_VARARG; - else if (cmd[0] == '<') - token->terminal = TERMINAL_RANGE; - else if (cmd[0] >= 'A' && cmd[0] <= 'Z') - token->terminal = TERMINAL_VARIABLE; - else - token->terminal = TERMINAL_LITERAL; - - token->cmd = cmd; - token->desc = format_parser_desc_str(state); - vector_set(state->curvect, token); - - if (state->in_keyword == 1) - state->in_keyword = 2; - - state->just_read_word = 1; -} - -/** - * Parse a given command format string and build a tree of tokens from - * it that is suitable to be used by the command subsystem. - * - * @param string Command format string. - * @param descstr Description string. - * @return A vector of struct cmd_token representing the given command, - * or NULL on error. - */ -static vector -cmd_parse_format(const char *string, const char *descstr) -{ - struct format_parser_state state; - - if (string == NULL) - return NULL; - - memset(&state, 0, sizeof(state)); - state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE); - state.cp = state.string = string; - state.dp = descstr; - - while (1) - { - while (isspace((int)state.cp[0]) && state.cp[0] != '\0') - state.cp++; - - switch (state.cp[0]) - { - case '\0': - if (state.in_keyword - || state.in_multiple) - format_parser_error(&state, "Unclosed group/keyword"); - return state.topvect; - case '{': - format_parser_begin_keyword(&state); - break; - case '(': - format_parser_begin_multiple(&state); - break; - case '}': - format_parser_end_keyword(&state); - break; - case ')': - format_parser_end_multiple(&state); - break; - case '|': - format_parser_handle_pipe(&state); - break; - default: - format_parser_read_word(&state); - } - } -} /* Return prompt character of specified node. */ const char * @@ -706,23 +289,23 @@ void install_element (enum node_type ntype, struct cmd_element *cmd) { struct cmd_node *cnode; - + /* cmd_init hasn't been called */ if (!cmdvec) return; - + cnode = vector_slot (cmdvec, ntype); - if (cnode == NULL) + if (cnode == NULL) { fprintf (stderr, "Command node %d doesn't exist, please check it\n", - ntype); - exit (1); + ntype); + exit (EXIT_FAILURE); } + // add node to command graph and command vector + command_parse_format (cnode->cmdgraph, cmd); vector_set (cnode->cmd_vector, cmd); - if (cmd->tokens == NULL) - cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); } static const unsigned char itoa64[] = @@ -731,7 +314,7 @@ static const unsigned char itoa64[] = static void to64(char *s, long v, int n) { - while (--n >= 0) + while (--n >= 0) { *s++ = itoa64[v&0x3f]; v >>= 6; @@ -746,7 +329,7 @@ zencrypt (const char *passwd) char *crypt (const char *, const char *); gettimeofday(&tv,0); - + to64(&salt[0], random(), 3); to64(&salt[3], tv.tv_usec, 3); salt[5] = '\0'; @@ -764,9 +347,9 @@ config_write_host (struct vty *vty) if (host.encrypt) { if (host.password_encrypt) - vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); + vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); if (host.enable_encrypt) - vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); + vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); } else { @@ -779,17 +362,17 @@ config_write_host (struct vty *vty) if (zlog_default->default_lvl != LOG_DEBUG) { vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s", - VTY_NEWLINE); + VTY_NEWLINE); vty_out (vty, "log trap %s%s", - zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); + zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); } if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) { vty_out (vty, "log file %s", host.logfile); if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); vty_out (vty, "%s", VTY_NEWLINE); } @@ -797,8 +380,8 @@ config_write_host (struct vty *vty) { vty_out (vty, "log stdout"); if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); vty_out (vty, "%s", VTY_NEWLINE); } @@ -806,27 +389,27 @@ config_write_host (struct vty *vty) vty_out(vty,"no log monitor%s",VTY_NEWLINE); else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl) vty_out(vty,"log monitor %s%s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { vty_out (vty, "log syslog"); if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); vty_out (vty, "%s", VTY_NEWLINE); } if (zlog_default->facility != LOG_DAEMON) vty_out (vty, "log facility %s%s", - facility_name(zlog_default->facility), VTY_NEWLINE); + facility_name(zlog_default->facility), VTY_NEWLINE); if (zlog_default->record_priority == 1) vty_out (vty, "log record-priority%s", VTY_NEWLINE); if (zlog_default->timestamp_precision > 0) vty_out (vty, "log timestamp precision %d%s", - zlog_default->timestamp_precision, VTY_NEWLINE); + zlog_default->timestamp_precision, VTY_NEWLINE); if (host.advanced) vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); @@ -836,7 +419,7 @@ config_write_host (struct vty *vty) if (host.lines >= 0) vty_out (vty, "service terminal-length %d%s", host.lines, - VTY_NEWLINE); + VTY_NEWLINE); if (host.motdfile) vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE); @@ -854,1369 +437,120 @@ cmd_node_vector (vector v, enum node_type ntype) return cnode->cmd_vector; } -/* Completion match types. */ -enum match_type -{ - no_match, - extend_match, - ipv4_prefix_match, - ipv4_match, - ipv6_prefix_match, - ipv6_match, - range_match, - vararg_match, - partly_match, - exact_match -}; - -static enum match_type -cmd_ipv4_match (const char *str) -{ - const char *sp; - int dots = 0, nums = 0; - char buf[4]; - - if (str == NULL) - return partly_match; - - for (;;) - { - memset (buf, 0, sizeof (buf)); - sp = str; - while (*str != '\0') - { - if (*str == '.') - { - if (dots >= 3) - return no_match; - - if (*(str + 1) == '.') - return no_match; - - if (*(str + 1) == '\0') - return partly_match; - - dots++; - break; - } - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (str - sp > 3) - return no_match; - - strncpy (buf, sp, str - sp); - if (atoi (buf) > 255) - return no_match; - - nums++; - - if (*str == '\0') - break; - - str++; - } - - if (nums < 4) - return partly_match; - - return exact_match; -} - -static enum match_type -cmd_ipv4_prefix_match (const char *str) +/* Utility function for getting command graph. */ +static struct graph * +cmd_node_graph (vector v, enum node_type ntype) { - const char *sp; - int dots = 0; - char buf[4]; - - if (str == NULL) - return partly_match; - - for (;;) - { - memset (buf, 0, sizeof (buf)); - sp = str; - while (*str != '\0' && *str != '/') - { - if (*str == '.') - { - if (dots == 3) - return no_match; - - if (*(str + 1) == '.' || *(str + 1) == '/') - return no_match; - - if (*(str + 1) == '\0') - return partly_match; - - dots++; - break; - } - - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (str - sp > 3) - return no_match; - - strncpy (buf, sp, str - sp); - if (atoi (buf) > 255) - return no_match; - - if (dots == 3) - { - if (*str == '/') - { - if (*(str + 1) == '\0') - return partly_match; - - str++; - break; - } - else if (*str == '\0') - return partly_match; - } - - if (*str == '\0') - return partly_match; - - str++; - } - - sp = str; - while (*str != '\0') - { - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (atoi (sp) > 32) - return no_match; - - return exact_match; -} - -#define IPV6_ADDR_STR "0123456789abcdefABCDEF:." -#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" - -#ifdef HAVE_IPV6 - -static enum match_type -cmd_ipv6_match (const char *str) -{ - struct sockaddr_in6 sin6_dummy; - int ret; - - if (str == NULL) - return partly_match; - - if (strspn (str, IPV6_ADDR_STR) != strlen (str)) - return no_match; - - /* use inet_pton that has a better support, - * for example inet_pton can support the automatic addresses: - * ::1.2.3.4 - */ - ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); - - if (ret == 1) - return exact_match; - - return no_match; -} - -static enum match_type -cmd_ipv6_prefix_match (const char *str) -{ - struct sockaddr_in6 sin6_dummy; - const char *delim = "/\0"; - char *dupe, *prefix, *mask, *context, *endptr; - int nmask = -1; - - if (str == NULL) - return partly_match; - - if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) - return no_match; - - /* tokenize to address + mask */ - dupe = XMALLOC(MTYPE_TMP, strlen(str)+1); - strncpy(dupe, str, strlen(str)+1); - prefix = strtok_r(dupe, delim, &context); - mask = strtok_r(NULL, delim, &context); - - if (!mask) - return partly_match; - - /* validate prefix */ - if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1) - return no_match; - - /* validate mask */ - nmask = strtol (mask, &endptr, 10); - if (*endptr != '\0' || nmask < 0 || nmask > 128) - return no_match; - - XFREE(MTYPE_TMP, dupe); - - return exact_match; + struct cmd_node *cnode = vector_slot (v, ntype); + return cnode->cmdgraph; } -#endif /* HAVE_IPV6 */ - -#define DECIMAL_STRLEN_MAX 20 - static int -cmd_range_match (const char *range, const char *str) -{ - char *p; - char buf[DECIMAL_STRLEN_MAX + 1]; - char *endptr = NULL; - signed long long min, max, val; - - if (str == NULL) +cmd_try_do_shortcut (enum node_type node, char* first_word) { + if ( first_word != NULL && + node != AUTH_NODE && + node != VIEW_NODE && + node != AUTH_ENABLE_NODE && + node != ENABLE_NODE && + node != RESTRICTED_NODE && + 0 == strcmp( "do", first_word ) ) return 1; - - val = strtoll (str, &endptr, 10); - if (*endptr != '\0') - return 0; - val = llabs(val); - - range++; - p = strchr (range, '-'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy (buf, range, p - range); - buf[p - range] = '\0'; - min = strtoll (buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - range = p + 1; - p = strchr (range, '>'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy (buf, range, p - range); - buf[p - range] = '\0'; - max = strtoll (buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - if (val < min || val > max) - return 0; - - return 1; -} - -static enum match_type -cmd_word_match(struct cmd_token *token, - enum filter_type filter, - const char *word) -{ - const char *str; - enum match_type match_type; - - str = token->cmd; - - if (filter == FILTER_RELAXED) - if (!word || !strlen(word)) - return partly_match; - - if (!word) - return no_match; - - switch (token->terminal) - { - case TERMINAL_VARARG: - return vararg_match; - - case TERMINAL_RANGE: - if (cmd_range_match(str, word)) - return range_match; - break; - - case TERMINAL_IPV6: - match_type = cmd_ipv6_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_match; - break; - - case TERMINAL_IPV6_PREFIX: - match_type = cmd_ipv6_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_prefix_match; - break; - - case TERMINAL_IPV4: - match_type = cmd_ipv4_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_match; - break; - - case TERMINAL_IPV4_PREFIX: - match_type = cmd_ipv4_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_prefix_match; - break; - - case TERMINAL_OPTION: - case TERMINAL_VARIABLE: - return extend_match; - - case TERMINAL_LITERAL: - if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) - { - if (!strcmp(str, word)) - return exact_match; - return partly_match; - } - if (filter == FILTER_STRICT && !strcmp(str, word)) - return exact_match; - break; - - default: - assert (0); - } - - return no_match; -} - -struct cmd_matcher -{ - struct cmd_element *cmd; /* The command element the matcher is using */ - enum filter_type filter; /* Whether to use strict or relaxed matching */ - vector vline; /* The tokenized commandline which is to be matched */ - unsigned int index; /* The index up to which matching should be done */ - - /* If set, construct a list of matches at the position given by index */ - enum match_type *match_type; - vector *match; - - unsigned int word_index; /* iterating over vline */ -}; - -static int -push_argument(int *argc, const char **argv, const char *arg) -{ - if (!arg || !strlen(arg)) - arg = NULL; - - if (!argc || !argv) - return 0; - - if (*argc >= CMD_ARGC_MAX) - return -1; - - argv[(*argc)++] = arg; return 0; } -static void -cmd_matcher_record_match(struct cmd_matcher *matcher, - enum match_type match_type, - struct cmd_token *token) -{ - if (matcher->word_index != matcher->index) - return; - - if (matcher->match) - { - if (!*matcher->match) - *matcher->match = vector_init(VECTOR_MIN_SIZE); - vector_set(*matcher->match, token); - } - - if (matcher->match_type) - { - if (match_type > *matcher->match_type) - *matcher->match_type = match_type; - } -} - -static int -cmd_matcher_words_left(struct cmd_matcher *matcher) -{ - return matcher->word_index < vector_active(matcher->vline); -} - -static const char* -cmd_matcher_get_word(struct cmd_matcher *matcher) -{ - assert(cmd_matcher_words_left(matcher)); - - return vector_slot(matcher->vline, matcher->word_index); -} - -static enum matcher_rv -cmd_matcher_match_terminal(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - const char *word; - enum match_type word_match; - - assert(token->type == TOKEN_TERMINAL); - - if (!cmd_matcher_words_left(matcher)) - { - if (token->terminal == TERMINAL_OPTION) - return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ - else - return MATCHER_INCOMPLETE; - } - - word = cmd_matcher_get_word(matcher); - word_match = cmd_word_match(token, matcher->filter, word); - if (word_match == no_match) - return MATCHER_NO_MATCH; - - /* We have to record the input word as argument if it matched - * against a variable. */ - if (TERMINAL_RECORD (token->terminal)) - { - if (push_argument(argc, argv, word)) - return MATCHER_EXCEED_ARGC_MAX; - } - - cmd_matcher_record_match(matcher, word_match, token); - - matcher->word_index++; - - /* A vararg token should consume all left over words as arguments */ - if (token->terminal == TERMINAL_VARARG) - while (cmd_matcher_words_left(matcher)) - { - word = cmd_matcher_get_word(matcher); - if (word && strlen(word)) - push_argument(argc, argv, word); - matcher->word_index++; - } - - return MATCHER_OK; -} - -static enum matcher_rv -cmd_matcher_match_multiple(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - enum match_type multiple_match; - unsigned int multiple_index; - const char *word; - const char *arg = NULL; - struct cmd_token *word_token; - enum match_type word_match; - - assert(token->type == TOKEN_MULTIPLE); - - multiple_match = no_match; - - if (!cmd_matcher_words_left(matcher)) - return MATCHER_INCOMPLETE; - - word = cmd_matcher_get_word(matcher); - for (multiple_index = 0; - multiple_index < vector_active(token->multiple); - multiple_index++) - { - word_token = vector_slot(token->multiple, multiple_index); - - word_match = cmd_word_match(word_token, matcher->filter, word); - if (word_match == no_match) - continue; - - cmd_matcher_record_match(matcher, word_match, word_token); - - if (word_match > multiple_match) - { - multiple_match = word_match; - arg = word; - } - /* To mimic the behavior of the old command implementation, we - * tolerate any ambiguities here :/ */ - } - - matcher->word_index++; - - if (multiple_match == no_match) - return MATCHER_NO_MATCH; - - if (push_argument(argc, argv, arg)) - return MATCHER_EXCEED_ARGC_MAX; - - return MATCHER_OK; -} - -static enum matcher_rv -cmd_matcher_read_keywords(struct cmd_matcher *matcher, - struct cmd_token *token, - vector args_vector) -{ - unsigned int i; - unsigned long keyword_mask; - unsigned int keyword_found; - enum match_type keyword_match; - enum match_type word_match; - vector keyword_vector; - struct cmd_token *word_token; - const char *word; - int keyword_argc; - const char **keyword_argv; - enum matcher_rv rv = MATCHER_OK; - - keyword_mask = 0; - while (1) - { - if (!cmd_matcher_words_left(matcher)) - return MATCHER_OK; - - word = cmd_matcher_get_word(matcher); - - keyword_found = -1; - keyword_match = no_match; - for (i = 0; i < vector_active(token->keyword); i++) - { - if (keyword_mask & (1 << i)) - continue; - - keyword_vector = vector_slot(token->keyword, i); - word_token = vector_slot(keyword_vector, 0); - - word_match = cmd_word_match(word_token, matcher->filter, word); - if (word_match == no_match) - continue; - - cmd_matcher_record_match(matcher, word_match, word_token); - - if (word_match > keyword_match) - { - keyword_match = word_match; - keyword_found = i; - } - else if (word_match == keyword_match) - { - if (matcher->word_index != matcher->index || args_vector) - return MATCHER_AMBIGUOUS; - } - } - - if (keyword_found == (unsigned int)-1) - return MATCHER_NO_MATCH; - - matcher->word_index++; - - if (matcher->word_index > matcher->index) - return MATCHER_OK; - - keyword_mask |= (1 << keyword_found); - - if (args_vector) - { - keyword_argc = 0; - keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*)); - /* We use -1 as a marker for unused fields as NULL might be a valid value */ - for (i = 0; i < CMD_ARGC_MAX + 1; i++) - keyword_argv[i] = (void*)-1; - vector_set_index(args_vector, keyword_found, keyword_argv); - } - else - { - keyword_argv = NULL; - } - - keyword_vector = vector_slot(token->keyword, keyword_found); - /* the keyword itself is at 0. We are only interested in the arguments, - * so start counting at 1. */ - for (i = 1; i < vector_active(keyword_vector); i++) - { - word_token = vector_slot(keyword_vector, i); - - switch (word_token->type) - { - case TOKEN_TERMINAL: - rv = cmd_matcher_match_terminal(matcher, word_token, - &keyword_argc, keyword_argv); - break; - case TOKEN_MULTIPLE: - rv = cmd_matcher_match_multiple(matcher, word_token, - &keyword_argc, keyword_argv); - break; - case TOKEN_KEYWORD: - assert(!"Keywords should never be nested."); - break; - } - - if (MATCHER_ERROR(rv)) - return rv; - - if (matcher->word_index > matcher->index) - return MATCHER_OK; - } - } - /* not reached */ -} - -static enum matcher_rv -cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv, - vector keyword_args_vector) -{ - unsigned int i, j; - const char **keyword_args; - vector keyword_vector; - struct cmd_token *word_token; - const char *arg; - enum matcher_rv rv; - - rv = MATCHER_OK; - - if (keyword_args_vector == NULL) - return rv; - - for (i = 0; i < vector_active(token->keyword); i++) - { - keyword_vector = vector_slot(token->keyword, i); - keyword_args = vector_lookup(keyword_args_vector, i); - - if (vector_active(keyword_vector) == 1) - { - /* this is a keyword without arguments */ - if (keyword_args) - { - word_token = vector_slot(keyword_vector, 0); - arg = word_token->cmd; - XFREE (MTYPE_TMP, keyword_args); - } - else - { - arg = NULL; - } - - if (push_argument(argc, argv, arg)) - rv = MATCHER_EXCEED_ARGC_MAX; - } - else - { - /* this is a keyword with arguments */ - if (keyword_args) - { - /* the keyword was present, so just fill in the arguments */ - for (j = 0; keyword_args[j] != (void*)-1; j++) - if (push_argument(argc, argv, keyword_args[j])) - rv = MATCHER_EXCEED_ARGC_MAX; - XFREE(MTYPE_TMP, keyword_args); - } - else - { - /* the keyword was not present, insert NULL for the arguments - * the keyword would have taken. */ - for (j = 1; j < vector_active(keyword_vector); j++) - { - word_token = vector_slot(keyword_vector, j); - if ((word_token->type == TOKEN_TERMINAL - && TERMINAL_RECORD (word_token->terminal)) - || word_token->type == TOKEN_MULTIPLE) - { - if (push_argument(argc, argv, NULL)) - rv = MATCHER_EXCEED_ARGC_MAX; - } - } - } - } - } - vector_free(keyword_args_vector); - return rv; -} - -static enum matcher_rv -cmd_matcher_match_keyword(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - vector keyword_args_vector; - enum matcher_rv reader_rv; - enum matcher_rv builder_rv; - - assert(token->type == TOKEN_KEYWORD); - - if (argc && argv) - keyword_args_vector = vector_init(VECTOR_MIN_SIZE); - else - keyword_args_vector = NULL; - - reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector); - builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc, - argv, keyword_args_vector); - /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */ - - if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv)) - return builder_rv; - - return reader_rv; -} - -static void -cmd_matcher_init(struct cmd_matcher *matcher, - struct cmd_element *cmd, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *match) -{ - memset(matcher, 0, sizeof(*matcher)); - - matcher->cmd = cmd; - matcher->filter = filter; - matcher->vline = vline; - matcher->index = index; - - matcher->match_type = match_type; - if (matcher->match_type) - *matcher->match_type = no_match; - matcher->match = match; - - matcher->word_index = 0; -} - -static enum matcher_rv -cmd_element_match(struct cmd_element *cmd_element, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *match, - int *argc, - const char **argv) -{ - struct cmd_matcher matcher; - unsigned int token_index; - enum matcher_rv rv = MATCHER_OK; - - cmd_matcher_init(&matcher, cmd_element, filter, - vline, index, match_type, match); - - if (argc != NULL) - *argc = 0; - - for (token_index = 0; - token_index < vector_active(cmd_element->tokens); - token_index++) - { - struct cmd_token *token = vector_slot(cmd_element->tokens, token_index); - - switch (token->type) - { - case TOKEN_TERMINAL: - rv = cmd_matcher_match_terminal(&matcher, token, argc, argv); - break; - case TOKEN_MULTIPLE: - rv = cmd_matcher_match_multiple(&matcher, token, argc, argv); - break; - case TOKEN_KEYWORD: - rv = cmd_matcher_match_keyword(&matcher, token, argc, argv); - } - - if (MATCHER_ERROR(rv)) - return rv; - - if (matcher.word_index > index) - return MATCHER_OK; - } - - /* return MATCHER_COMPLETE if all words were consumed */ - if (matcher.word_index >= vector_active(vline)) - return MATCHER_COMPLETE; - - /* return MATCHER_COMPLETE also if only an empty word is left. */ - if (matcher.word_index == vector_active(vline) - 1 - && (!vector_slot(vline, matcher.word_index) - || !strlen((char*)vector_slot(vline, matcher.word_index)))) - return MATCHER_COMPLETE; - - return MATCHER_NO_MATCH; /* command is too long to match */ -} - /** - * Filter a given vector of commands against a given commandline and - * calculate possible completions. - * - * @param commands A vector of struct cmd_element*. Commands that don't - * match against the given command line will be overwritten - * with NULL in that vector. - * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically - * determines how incomplete commands are handled, compare with - * cmd_word_match for details. - * @param vline A vector of char* containing the tokenized commandline. - * @param index Only match up to the given token of the commandline. - * @param match_type Record the type of the best match here. - * @param matches Record the matches here. For each cmd_element in the commands - * vector, a match vector will be created in the matches vector. - * That vector will contain all struct command_token* of the - * cmd_element which matched against the given vline at the given - * index. - * @return A code specifying if an error occured. If all went right, it's - * CMD_SUCCESS. + * Compare function for cmd_token. + * Used with qsort to sort command completions. */ static int -cmd_vector_filter(vector commands, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *matches) +compare_completions (const void *fst, const void *snd) { - unsigned int i; - struct cmd_element *cmd_element; - enum match_type best_match; - enum match_type element_match; - enum matcher_rv matcher_rv; - - best_match = no_match; - *matches = vector_init(VECTOR_MIN_SIZE); - - for (i = 0; i < vector_active (commands); i++) - if ((cmd_element = vector_slot (commands, i)) != NULL) - { - vector_set_index(*matches, i, NULL); - matcher_rv = cmd_element_match(cmd_element, filter, - vline, index, - &element_match, - (vector*)&vector_slot(*matches, i), - NULL, NULL); - if (MATCHER_ERROR(matcher_rv)) - { - vector_slot(commands, i) = NULL; - if (matcher_rv == MATCHER_AMBIGUOUS) - return CMD_ERR_AMBIGUOUS; - if (matcher_rv == MATCHER_EXCEED_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } - else if (element_match > best_match) - { - best_match = element_match; - } - } - *match_type = best_match; - return CMD_SUCCESS; + struct cmd_token *first = *(struct cmd_token **) fst, + *secnd = *(struct cmd_token **) snd; + return strcmp (first->text, secnd->text); } /** - * Check whether a given commandline is complete if used for a specific - * cmd_element. - * - * @param cmd_element A cmd_element against which the commandline should be - * checked. - * @param vline The tokenized commandline. - * @return 1 if the given commandline is complete, 0 otherwise. + * Takes a list of completions returned by command_complete, + * dedeuplicates them based on both text and description, + * and returns them as a vector. */ -static int -cmd_is_complete(struct cmd_element *cmd_element, - vector vline) +static vector +completions_to_vec (struct list *completions) { - enum matcher_rv rv; - - rv = cmd_element_match(cmd_element, - FILTER_RELAXED, - vline, -1, - NULL, NULL, - NULL, NULL); - return (rv == MATCHER_COMPLETE); -} + vector comps = vector_init (VECTOR_MIN_SIZE); -/** - * Parse a given commandline and construct a list of arguments for the - * given command_element. - * - * @param cmd_element The cmd_element for which we want to construct arguments. - * @param vline The tokenized commandline. - * @param argc Where to store the argument count. - * @param argv Where to store the argument list. Should be at least - * CMD_ARGC_MAX elements long. - * @return CMD_SUCCESS if everything went alright, an error otherwise. - */ -static int -cmd_parse(struct cmd_element *cmd_element, - vector vline, - int *argc, const char **argv) -{ - enum matcher_rv rv = cmd_element_match(cmd_element, - FILTER_RELAXED, - vline, -1, - NULL, NULL, - argc, argv); - switch (rv) + struct listnode *ln; + struct cmd_token *token; + unsigned int i, exists; + for (ALL_LIST_ELEMENTS_RO(completions,ln,token)) + { + // linear search for token in completions vector + exists = 0; + for (i = 0; i < vector_active (comps) && !exists; i++) { - case MATCHER_COMPLETE: - return CMD_SUCCESS; - - case MATCHER_NO_MATCH: - return CMD_ERR_NO_MATCH; - - case MATCHER_AMBIGUOUS: - return CMD_ERR_AMBIGUOUS; - - case MATCHER_EXCEED_ARGC_MAX: - return CMD_ERR_EXEED_ARGC_MAX; - - default: - return CMD_ERR_INCOMPLETE; + struct cmd_token *curr = vector_slot (comps, i); + exists = !strcmp (curr->text, token->text) && + !strcmp (curr->desc, token->desc); } -} - -/* Check ambiguous match */ -static int -is_cmd_ambiguous (vector cmd_vector, - const char *command, - vector matches, - enum match_type type) -{ - unsigned int i; - unsigned int j; - const char *str = NULL; - const char *matched = NULL; - vector match_vector; - struct cmd_token *cmd_token; - - if (command == NULL) - command = ""; - - for (i = 0; i < vector_active (matches); i++) - if ((match_vector = vector_slot (matches, i)) != NULL) - { - int match = 0; - - for (j = 0; j < vector_active (match_vector); j++) - if ((cmd_token = vector_slot (match_vector, j)) != NULL) - { - enum match_type ret; - - assert(cmd_token->type == TOKEN_TERMINAL); - if (cmd_token->type != TOKEN_TERMINAL) - continue; - - str = cmd_token->cmd; - - switch (type) - { - case exact_match: - if (!TERMINAL_RECORD (cmd_token->terminal) - && strcmp (command, str) == 0) - match++; - break; - case partly_match: - if (!TERMINAL_RECORD (cmd_token->terminal) - && strncmp (command, str, strlen (command)) == 0) - { - if (matched && strcmp (matched, str) != 0) - return 1; /* There is ambiguous match. */ - else - matched = str; - match++; - } - break; - case range_match: - if (cmd_range_match (str, command)) - { - if (matched && strcmp (matched, str) != 0) - return 1; - else - matched = str; - match++; - } - break; -#ifdef HAVE_IPV6 - case ipv6_match: - if (cmd_token->terminal == TERMINAL_IPV6) - match++; - break; - case ipv6_prefix_match: - if ((ret = cmd_ipv6_prefix_match (command)) != no_match) - { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - - match++; - } - break; -#endif /* HAVE_IPV6 */ - case ipv4_match: - if (cmd_token->terminal == TERMINAL_IPV4) - match++; - break; - case ipv4_prefix_match: - if ((ret = cmd_ipv4_prefix_match (command)) != no_match) - { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - - match++; - } - break; - case extend_match: - if (TERMINAL_RECORD (cmd_token->terminal)) - match++; - break; - case no_match: - default: - break; - } - } - if (!match) - vector_slot (cmd_vector, i) = NULL; - } - return 0; -} -/* If src matches dst return dst string, otherwise return NULL */ -static const char * -cmd_entry_function (const char *src, struct cmd_token *token) -{ - const char *dst = token->cmd; - - /* Skip variable arguments. */ - if (TERMINAL_RECORD (token->terminal)) - return NULL; - - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - /* Matched with input string. */ - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - - return NULL; -} - -/* If src matches dst return dst string, otherwise return NULL */ -/* This version will return the dst string always if it is - CMD_VARIABLE for '?' key processing */ -static const char * -cmd_entry_function_desc (const char *src, struct cmd_token *token) -{ - const char *dst = token->cmd; + if (!exists) + vector_set (comps, copy_cmd_token (token)); + } - switch (token->terminal) - { - case TERMINAL_VARARG: - return dst; - - case TERMINAL_RANGE: - if (cmd_range_match (dst, src)) - return dst; - else - return NULL; - - case TERMINAL_IPV6: - if (cmd_ipv6_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV6_PREFIX: - if (cmd_ipv6_prefix_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV4: - if (cmd_ipv4_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV4_PREFIX: - if (cmd_ipv4_prefix_match (src)) - return dst; - else - return NULL; - - /* Optional or variable commands always match on '?' */ - case TERMINAL_OPTION: - case TERMINAL_VARIABLE: - return dst; - - case TERMINAL_LITERAL: - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - else - return NULL; + // sort completions + qsort (comps->index, + vector_active (comps), + sizeof (void *), + &compare_completions); - default: - assert(0); - return NULL; - } + return comps; } - /** - * Check whether a string is already present in a vector of strings. - * @param v A vector of char*. - * @param str A char*. - * @return 0 if str is already present in the vector, 1 otherwise. - */ -static int -cmd_unique_string (vector v, const char *str) -{ - unsigned int i; - char *match; - - for (i = 0; i < vector_active (v); i++) - if ((match = vector_slot (v, i)) != NULL) - if (strcmp (match, str) == 0) - return 0; - return 1; -} - -/** - * Check whether a struct cmd_token matching a given string is already - * present in a vector of struct cmd_token. - * @param v A vector of struct cmd_token*. - * @param str A char* which should be searched for. - * @return 0 if there is a struct cmd_token* with its cmd matching str, - * 1 otherwise. + * Generates a vector of cmd_token representing possible completions + * on the current input. + * + * @param vline the vectorized input line + * @param vty the vty with the node to match on + * @param status pointer to matcher status code */ -static int -desc_unique_string (vector v, const char *str) -{ - unsigned int i; - struct cmd_token *token; - - for (i = 0; i < vector_active (v); i++) - if ((token = vector_slot (v, i)) != NULL) - if (strcmp (token->cmd, str) == 0) - return 0; - return 1; -} - -static int -cmd_try_do_shortcut (enum node_type node, char* first_word) { - if ( first_word != NULL && - node != AUTH_NODE && - node != VIEW_NODE && - node != AUTH_ENABLE_NODE && - node != ENABLE_NODE && - node != RESTRICTED_NODE && - 0 == strcmp( "do", first_word ) ) - return 1; - return 0; -} - -static void -cmd_matches_free(vector *matches) -{ - unsigned int i; - vector cmd_matches; - - for (i = 0; i < vector_active(*matches); i++) - if ((cmd_matches = vector_slot(*matches, i)) != NULL) - vector_free(cmd_matches); - vector_free(*matches); - *matches = NULL; -} - -static int -cmd_describe_cmp(const void *a, const void *b) -{ - const struct cmd_token *first = *(struct cmd_token * const *)a; - const struct cmd_token *second = *(struct cmd_token * const *)b; - - return strcmp(first->cmd, second->cmd); -} - -static void -cmd_describe_sort(vector matchvec) -{ - qsort(matchvec->index, vector_active(matchvec), - sizeof(void*), cmd_describe_cmp); -} - -/* '?' describe command support. */ static vector -cmd_describe_command_real (vector vline, struct vty *vty, int *status) +cmd_complete_command_real (vector vline, struct vty *vty, int *status) { - unsigned int i; - vector cmd_vector; -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - struct cmd_element *cmd_element; - unsigned int index; - int ret; - enum match_type match; - char *command; - vector matches = NULL; - vector match_vector; - uint32_t command_found = 0; - const char *last_word; - - /* Set index. */ - if (vector_active (vline) == 0) - { - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - index = vector_active (vline) - 1; - - /* Make copy vector of current node's command vector. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + struct list *completions; + struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); - /* Prepare match vector */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); + enum matcher_rv rv = command_complete (cmdgraph, vline, &completions); - /* Filter commands and build a list how they could possibly continue. */ - for (i = 0; i <= index; i++) + if (MATCHER_ERROR(rv)) + { + switch (rv) { - command = vector_slot (vline, i); - - if (matches) - cmd_matches_free(&matches); - - ret = cmd_vector_filter(cmd_vector, - FILTER_RELAXED, - vline, i, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = ret; - return NULL; - } - - /* The last match may well be ambigious, so break here */ - if (i == index) - break; - - if (match == vararg_match) - { - /* We found a vararg match - so we can throw out the current matches here - * and don't need to continue checking the command input */ - unsigned int j, k; - - for (j = 0; j < vector_active (matches); j++) - if ((match_vector = vector_slot (matches, j)) != NULL) - for (k = 0; k < vector_active (match_vector); k++) - { - struct cmd_token *token = vector_slot (match_vector, k); - vector_set (matchvec, token); - } - - *status = CMD_SUCCESS; - vector_set(matchvec, &token_cr); - vector_free (cmd_vector); - cmd_matches_free(&matches); - cmd_describe_sort(matchvec); - return matchvec; - } - - ret = is_cmd_ambiguous(cmd_vector, command, matches, match); - if (ret == 1) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - else if (ret == 2) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = CMD_ERR_NO_MATCH; - return NULL; - } + case MATCHER_AMBIGUOUS: + *status = CMD_ERR_AMBIGUOUS; + default: + *status = CMD_ERR_NO_MATCH; } - - /* Make description vector. */ - for (i = 0; i < vector_active (matches); i++) { - if ((cmd_element = vector_slot (cmd_vector, i)) != NULL && - !(cmd_element->attr == CMD_ATTR_DEPRECATED || - cmd_element->attr == CMD_ATTR_HIDDEN)) - { - unsigned int j; - vector vline_trimmed; - - command_found++; - last_word = vector_slot(vline, vector_active(vline) - 1); - if (last_word == NULL || !strlen(last_word)) - { - vline_trimmed = vector_copy(vline); - vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); - - if (cmd_is_complete(cmd_element, vline_trimmed) - && desc_unique_string(matchvec, command_cr)) - { - if (match != vararg_match) - vector_set(matchvec, &token_cr); - } - - vector_free(vline_trimmed); - } - - match_vector = vector_slot (matches, i); - if (match_vector) - for (j = 0; j < vector_active(match_vector); j++) - { - struct cmd_token *token = vector_slot(match_vector, j); - const char *string; - - string = cmd_entry_function_desc(command, token); - if (string && desc_unique_string(matchvec, string)) - vector_set(matchvec, token); - } - } - } - - /* - * We can get into this situation when the command is complete - * but the last part of the command is an optional piece of - * cli. - */ - last_word = vector_slot(vline, vector_active(vline) - 1); - if (command_found == 0 && (last_word == NULL || !strlen(last_word))) { - vector_set(matchvec, &token_cr); + return NULL; } - vector_free (cmd_vector); - cmd_matches_free(&matches); + vector comps = completions_to_vec (completions); + list_delete (completions); - if (vector_slot (matchvec, 0) == NULL) - { - vector_free (matchvec); + // set status code appropriately + switch (vector_active (comps)) + { + case 0: *status = CMD_ERR_NO_MATCH; - return NULL; - } + break; + case 1: + *status = CMD_COMPLETE_FULL_MATCH; + break; + default: + *status = CMD_COMPLETE_LIST_MATCH; + } - *status = CMD_SUCCESS; - cmd_describe_sort(matchvec); - return matchvec; + return comps; } vector @@ -2236,253 +570,19 @@ cmd_describe_command (vector vline, struct vty *vty, int *status) shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } + for (index = 1; index < vector_active (vline); index++) + { + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } - ret = cmd_describe_command_real (shifted_vline, vty, status); + ret = cmd_complete_command_real (shifted_vline, vty, status); vector_free(shifted_vline); vty->node = onode; return ret; } - - return cmd_describe_command_real (vline, vty, status); -} - - -/* Check LCD of matched command. */ -static int -cmd_lcd (char **matched) -{ - int i; - int j; - int lcd = -1; - char *s1, *s2; - char c1, c2; - - if (matched[0] == NULL || matched[1] == NULL) - return 0; - - for (i = 1; matched[i] != NULL; i++) - { - s1 = matched[i - 1]; - s2 = matched[i]; - - for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) - if (c1 != c2) - break; - - if (lcd < 0) - lcd = j; - else - { - if (lcd > j) - lcd = j; - } - } - return lcd; -} - -static int -cmd_complete_cmp(const void *a, const void *b) -{ - const char *first = *(char * const *)a; - const char *second = *(char * const *)b; - - if (!first) - { - if (!second) - return 0; - return 1; - } - if (!second) - return -1; - - return strcmp(first, second); -} - -static void -cmd_complete_sort(vector matchvec) -{ - qsort(matchvec->index, vector_active(matchvec), - sizeof(void*), cmd_complete_cmp); -} - -/* Command line completion support. */ -static char ** -cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib) -{ - unsigned int i; - vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - unsigned int index; - char **match_str; - struct cmd_token *token; - char *command; - int lcd; - vector matches = NULL; - vector match_vector; - - if (vector_active (vline) == 0) - { - vector_free (cmd_vector); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - else - index = vector_active (vline) - 1; - - /* First, filter by command string */ - for (i = 0; i <= index; i++) - { - command = vector_slot (vline, i); - enum match_type match; - int ret; - - if (matches) - cmd_matches_free(&matches); - - /* First try completion match, if there is exactly match return 1 */ - ret = cmd_vector_filter(cmd_vector, - FILTER_RELAXED, - vline, i, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - vector_free(cmd_vector); - cmd_matches_free(&matches); - *status = ret; - return NULL; - } - - /* Break here - the completion mustn't be checked to be non-ambiguous */ - if (i == index) - break; - - /* If there is exact match then filter ambiguous match else check - ambiguousness. */ - ret = is_cmd_ambiguous (cmd_vector, command, matches, match); - if (ret == 1) - { - vector_free (cmd_vector); - cmd_matches_free(&matches); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - } - - /* Prepare match vector. */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - - /* Build the possible list of continuations into a list of completions */ - for (i = 0; i < vector_active (matches); i++) - if ((match_vector = vector_slot (matches, i))) - { - const char *string; - unsigned int j; - - for (j = 0; j < vector_active (match_vector); j++) - if ((token = vector_slot (match_vector, j))) - { - string = cmd_entry_function (vector_slot (vline, index), - token); - if (string && cmd_unique_string (matchvec, string)) - vector_set (matchvec, (islib != 0 ? - XSTRDUP (MTYPE_TMP, string) : - strdup (string) /* rl freed */)); - } - } - - /* We don't need cmd_vector any more. */ - vector_free (cmd_vector); - cmd_matches_free(&matches); - - /* No matched command */ - if (vector_slot (matchvec, 0) == NULL) - { - vector_free (matchvec); - - /* In case of 'command \t' pattern. Do you need '?' command at - the end of the line. */ - if (vector_slot (vline, index) == '\0') - *status = CMD_ERR_NOTHING_TODO; - else - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - /* Only one matched */ - if (vector_slot (matchvec, 1) == NULL) - { - size_t index_size = matchvec->alloced * sizeof (void *); - match_str = XMALLOC (MTYPE_TMP, index_size); - memcpy (match_str, matchvec->index, index_size); - vector_free (matchvec); - - *status = CMD_COMPLETE_FULL_MATCH; - return match_str; - } - /* Make it sure last element is NULL. */ - vector_set (matchvec, NULL); - - /* Check LCD of matched strings. */ - if (vector_slot (vline, index) != NULL) - { - lcd = cmd_lcd ((char **) matchvec->index); - - if (lcd) - { - int len = strlen (vector_slot (vline, index)); - - if (len < lcd) - { - char *lcdstr; - - lcdstr = (islib != 0 ? - XMALLOC (MTYPE_TMP, lcd + 1) : - malloc(lcd + 1)); - memcpy (lcdstr, matchvec->index[0], lcd); - lcdstr[lcd] = '\0'; - - /* Free matchvec. */ - for (i = 0; i < vector_active (matchvec); i++) - { - if (vector_slot (matchvec, i)) - { - if (islib != 0) - XFREE (MTYPE_TMP, vector_slot (matchvec, i)); - else - free (vector_slot (matchvec, i)); - } - } - vector_free (matchvec); - - /* Make new matchvec. */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - vector_set (matchvec, lcdstr); - - size_t index_size = matchvec->alloced * sizeof (void *); - match_str = XMALLOC (MTYPE_TMP, index_size); - memcpy (match_str, matchvec->index, index_size); - vector_free (matchvec); - - *status = CMD_COMPLETE_MATCH; - return match_str; - } - } - } - - match_str = (char **) matchvec->index; - cmd_complete_sort(matchvec); - vector_only_wrapper_free (matchvec); - *status = CMD_COMPLETE_LIST_MATCH; - return match_str; + return cmd_complete_command_real (vline, vty, status); } char ** @@ -2502,19 +602,41 @@ cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } + for (index = 1; index < vector_active (vline); index++) + { + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } - ret = cmd_complete_command_real (shifted_vline, vty, status, islib); + // get token completions + vector comps = cmd_complete_command_real (shifted_vline, vty, status); + ret = XMALLOC (MTYPE_TMP, vector_active (comps) * sizeof (char *)); + for (unsigned int i = 0; i < vector_active (comps); i++) + { + struct cmd_token *token = vector_slot (comps, i); + ret[i] = XSTRDUP (MTYPE_TMP, token->text); + vector_unset (comps, i); + del_cmd_token (token); + } + vector_free (comps); vector_free(shifted_vline); vty->node = onode; return ret; } - return cmd_complete_command_real (vline, vty, status, islib); + // get token completions + vector comps = cmd_complete_command_real (vline, vty, status); + ret = XMALLOC (MTYPE_TMP, vector_active (comps) * sizeof (char *)); + for (unsigned int i = 0; i < vector_active (comps); i++) + { + struct cmd_token *token = vector_slot (comps, i); + ret[i] = XSTRDUP (MTYPE_TMP, token->text); + vector_unset (comps, i); + del_cmd_token (token); + } + vector_free (comps); + + return ret; } char ** @@ -2561,109 +683,47 @@ node_parent ( enum node_type node ) /* Execute command by argument vline vector. */ static int cmd_execute_command_real (vector vline, - enum filter_type filter, - struct vty *vty, - struct cmd_element **cmd) -{ - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - enum match_type match = 0; - char *command; - int ret; - vector matches; - - /* Make copy of command elements. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - - for (index = 0; index < vector_active (vline); index++) - { - command = vector_slot (vline, index); - ret = cmd_vector_filter(cmd_vector, - filter, - vline, index, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - cmd_matches_free(&matches); - return ret; - } - - if (match == vararg_match) - { - cmd_matches_free(&matches); - break; - } - - ret = is_cmd_ambiguous (cmd_vector, command, matches, match); - cmd_matches_free(&matches); - - if (ret == 1) - { - vector_free(cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - else if (ret == 2) - { - vector_free(cmd_vector); - return CMD_ERR_NO_MATCH; - } - } - - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) - { - if (cmd_is_complete(cmd_element, vline)) - { - matched_element = cmd_element; - matched_count++; - } - else - { - incomplete_count++; - } - } - - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); - - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) - { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; + enum filter_type filter, + struct vty *vty, + struct cmd_element **cmd) +{ + struct list *argv_list; + enum matcher_rv status; + struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); + status = command_match (cmdgraph, vline, &argv_list, cmd); + + // if matcher error, return corresponding CMD_ERR + if (MATCHER_ERROR(status)) + switch (status) + { + case MATCHER_INCOMPLETE: + return CMD_ERR_INCOMPLETE; + case MATCHER_AMBIGUOUS: + return CMD_ERR_AMBIGUOUS; + default: + return CMD_ERR_NO_MATCH; } - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; + // build argv array from argv list + struct cmd_token **argv = XMALLOC (MTYPE_TMP, argv_list->count * sizeof (struct cmd_token *)); + struct listnode *ln; + struct cmd_token *token; + unsigned int i = 0; + for (ALL_LIST_ELEMENTS_RO(argv_list,ln,token)) + argv[i++] = token; - ret = cmd_parse(matched_element, vline, &argc, argv); - if (ret != CMD_SUCCESS) - return ret; + int argc = argv_list->count; - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; + int ret; + if ((*cmd)->daemon) + ret = CMD_SUCCESS_DAEMON; + else + ret = (*cmd)->func (*cmd, vty, argc, argv); - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; + // delete list and cmd_token's in it + list_delete (argv_list); - /* Execute matched command. */ - return (*matched_element->func) (matched_element, vty, argc, argv); + return ret; } /** @@ -2683,8 +743,8 @@ cmd_execute_command_real (vector vline, */ int cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, - int vtysh) { - int ret, saved_ret, tried = 0; + int vtysh) { + int ret, saved_ret = 0; enum node_type onode, try_node; onode = try_node = vty->node; @@ -2699,10 +759,8 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } + for (index = 1; index < vector_active (vline); index++) + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); @@ -2717,24 +775,22 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, if (vtysh) return saved_ret; - /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ - while ( ret != CMD_SUCCESS && ret != CMD_WARNING - && vty->node > CONFIG_NODE ) + if (ret != CMD_SUCCESS && ret != CMD_WARNING) { - try_node = node_parent(try_node); - vty->node = try_node; - ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); - tried = 1; - if (ret == CMD_SUCCESS || ret == CMD_WARNING) - { - /* succesfull command, leave the node as is */ - return ret; - } + /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ + while (vty->node > CONFIG_NODE) + { + try_node = node_parent(try_node); + vty->node = try_node; + ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); + if (ret == CMD_SUCCESS || ret == CMD_WARNING) + return ret; + } + /* no command succeeded, reset the vty to the original node */ + vty->node = onode; } - /* no command succeeded, reset the vty to the original node and - return the error for this node */ - if ( tried ) - vty->node = onode; + + /* return command status for original node */ return saved_ret; } @@ -2753,7 +809,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, */ int cmd_execute_command_strict (vector vline, struct vty *vty, - struct cmd_element **cmd) + struct cmd_element **cmd) { return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); } @@ -2797,7 +853,7 @@ command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) && - ret != CMD_SUCCESS && + ret != CMD_SUCCESS && ret != CMD_WARNING && vty->node > CONFIG_NODE) { vty->node = node_parent(vty->node); @@ -2808,11 +864,11 @@ command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use // stay at the same node if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) && - ret != CMD_SUCCESS && + ret != CMD_SUCCESS && ret != CMD_WARNING) { - vty->node = saved_node; - memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ); + vty->node = saved_node; + memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ); } } @@ -2831,13 +887,13 @@ config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num) while (fgets (vty->buf, VTY_BUFSIZ, fp)) { if (!error_ret) - ++(*line_num); + ++(*line_num); ret = command_config_read_one_line (vty, NULL, 0); if (ret != CMD_SUCCESS && ret != CMD_WARNING && - ret != CMD_ERR_NOTHING_TODO) - error_ret = ret; + ret != CMD_ERR_NOTHING_TODO) + error_ret = ret; } if (error_ret) { @@ -2865,7 +921,7 @@ DEFUN (config_terminal, } /* Enable command */ -DEFUN (enable, +DEFUN (enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") @@ -2881,7 +937,7 @@ DEFUN (enable, } /* Disable command */ -DEFUN (disable, +DEFUN (disable, config_disable_cmd, "disable", "Turn off privileged mode command\n") @@ -2903,9 +959,9 @@ DEFUN (config_exit, case ENABLE_NODE: case RESTRICTED_NODE: if (vty_shell (vty)) - exit (0); + exit (0); else - vty->status = VTY_CLOSE; + vty->status = VTY_CLOSE; break; case CONFIG_NODE: vty->node = ENABLE_NODE; @@ -2955,7 +1011,7 @@ ALIAS (config_exit, config_quit_cmd, "quit", "Exit current mode and down to previous mode\n") - + /* End of configuration. */ DEFUN (config_end, config_end_cmd, @@ -3012,7 +1068,7 @@ DEFUN (show_version, "Displays zebra version\n") { vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", - VTY_NEWLINE); + VTY_NEWLINE); vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE); vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE, QUAGGA_CONFIG_ARGS, VTY_NEWLINE); @@ -3026,8 +1082,8 @@ DEFUN (config_help, "help", "Description of the interactive help system\n") { - vty_out (vty, - "Quagga VTY provides advanced help feature. When you need help,%s\ + vty_out (vty, + "Quagga VTY provides advanced help feature. When you need help,%s\ anytime at the command line please press '?'.%s\ %s\ If nothing matches, the help list will be empty and you must backup%s\ @@ -3039,8 +1095,8 @@ argument.%s\ 2. Partial help is provided when an abbreviated argument is entered%s\ and you want to know what arguments match the input%s\ (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); return CMD_SUCCESS; } @@ -3059,16 +1115,18 @@ DEFUN (config_list, && !(cmd->attr == CMD_ATTR_DEPRECATED || cmd->attr == CMD_ATTR_HIDDEN)) vty_out (vty, " %s%s", cmd->string, - VTY_NEWLINE); + VTY_NEWLINE); return CMD_SUCCESS; } /* Write current configuration into file. */ -DEFUN (config_write_file, - config_write_file_cmd, - "write file", +DEFUN (config_write, + config_write_cmd, + "write [<file|memory|terminal>]", "Write running configuration to memory, network, or terminal\n" - "Write to configuration file\n") + "Write to configuration file\n" + "Write configuration currently in memory\n" + "Write configuration to terminal\n") { unsigned int i; int fd; @@ -3080,17 +1138,47 @@ DEFUN (config_write_file, struct vty *file_vty; struct stat conf_stat; + // if command was 'write terminal' or 'show running-config' + if (argc == 2 && (!strcmp(argv[1]->arg, "terminal") || + !strcmp(argv[1]->arg, "running-config"))) + { + if (vty->type == VTY_SHELL_SERV) + { + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + } + else + { + vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + vty_out (vty, "end%s",VTY_NEWLINE); + } + return CMD_SUCCESS; + } + /* Check and see if we are operating under vtysh configuration */ if (host.config == NULL) { vty_out (vty, "Can't save to configuration file, using vtysh.%s", - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; } /* Get filename. */ config_file = host.config; - + config_file_sav = XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1); strcpy (config_file_sav, config_file); @@ -3099,16 +1187,16 @@ DEFUN (config_write_file, config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8); sprintf (config_file_tmp, "%s.XXXXXX", config_file); - + /* Open file to configuration write. */ fd = mkstemp (config_file_tmp); if (fd < 0) { vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp, - VTY_NEWLINE); + VTY_NEWLINE); goto finished; } - + /* Make vty for configuration file. */ file_vty = vty_new (); file_vty->wfd = fd; @@ -3122,51 +1210,51 @@ DEFUN (config_write_file, for (i = 0; i < vector_active (cmdvec); i++) if ((node = vector_slot (cmdvec, i)) && node->func) { - if ((*node->func) (file_vty)) - vty_out (file_vty, "!\n"); + if ((*node->func) (file_vty)) + vty_out (file_vty, "!\n"); } vty_close (file_vty); if (stat(config_file, &conf_stat) >= 0) { if (unlink (config_file_sav) != 0) - if (errno != ENOENT) - { - vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, - VTY_NEWLINE); - goto finished; - } + if (errno != ENOENT) + { + vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } if (link (config_file, config_file_sav) != 0) - { - vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, - VTY_NEWLINE); - goto finished; - } + { + vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } sync (); if (unlink (config_file) != 0) - { - vty_out (vty, "Can't unlink configuration file %s.%s", config_file, - VTY_NEWLINE); - goto finished; - } + { + vty_out (vty, "Can't unlink configuration file %s.%s", config_file, + VTY_NEWLINE); + goto finished; + } } if (link (config_file_tmp, config_file) != 0) { vty_out (vty, "Can't save configuration file %s.%s", config_file, - VTY_NEWLINE); + VTY_NEWLINE); goto finished; } sync (); - + if (chmod (config_file, CONFIGFILE_MASK) != 0) { - vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s", - config_file, safe_strerror(errno), errno, VTY_NEWLINE); + vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s", + config_file, safe_strerror(errno), errno, VTY_NEWLINE); goto finished; } vty_out (vty, "Configuration saved to %s%s", config_file, - VTY_NEWLINE); + VTY_NEWLINE); ret = CMD_SUCCESS; finished: @@ -3176,62 +1264,15 @@ finished: return ret; } -ALIAS (config_write_file, - config_write_cmd, - "write", - "Write running configuration to memory, network, or terminal\n") - -ALIAS (config_write_file, - config_write_memory_cmd, - "write memory", - "Write running configuration to memory, network, or terminal\n" - "Write configuration to the file (same as write file)\n") - -ALIAS (config_write_file, +ALIAS (config_write, copy_runningconfig_startupconfig_cmd, - "copy running-config startup-config", + "copy running-config startup-config", "Copy configuration\n" "Copy running config to... \n" "Copy running config to startup config (same as write file)\n") /* Write current configuration into the terminal. */ -DEFUN (config_write_terminal, - config_write_terminal_cmd, - "write terminal", - "Write running configuration to memory, network, or terminal\n" - "Write to terminal\n") -{ - unsigned int i; - struct cmd_node *node; - - if (vty->type == VTY_SHELL_SERV) - { - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) - { - if ((*node->func) (vty)) - vty_out (vty, "!%s", VTY_NEWLINE); - } - } - else - { - vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, - VTY_NEWLINE); - vty_out (vty, "!%s", VTY_NEWLINE); - - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func) - { - if ((*node->func) (vty)) - vty_out (vty, "!%s", VTY_NEWLINE); - } - vty_out (vty, "end%s",VTY_NEWLINE); - } - return CMD_SUCCESS; -} - -/* Write current configuration into the terminal. */ -ALIAS (config_write_terminal, +ALIAS (config_write, show_running_config_cmd, "show running-config", SHOW_STR @@ -3242,7 +1283,7 @@ DEFUN (show_startup_config, show_startup_config_cmd, "show startup-config", SHOW_STR - "Contentes of startup configuration\n") + "Contents of startup configuration\n") { char buf[BUFSIZ]; FILE *confp; @@ -3251,7 +1292,7 @@ DEFUN (show_startup_config, if (confp == NULL) { vty_out (vty, "Can't open configuration file [%s] due to '%s'%s", - host.config, safe_strerror(errno), VTY_NEWLINE); + host.config, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3260,7 +1301,7 @@ DEFUN (show_startup_config, char *cp = buf; while (*cp != '\r' && *cp != '\n' && *cp != '\0') - cp++; + cp++; *cp = '\0'; vty_out (vty, "%s%s", buf, VTY_NEWLINE); @@ -3272,13 +1313,15 @@ DEFUN (show_startup_config, } /* Hostname configuration */ -DEFUN (config_hostname, +DEFUN (config_hostname, hostname_cmd, "hostname WORD", "Set system's network name\n" "This system's network name\n") { - if (!isalpha((int) *argv[0])) + struct cmd_token *word = argv[1]; + + if (!isalpha((int) word->arg[0])) { vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE); return CMD_WARNING; @@ -3286,12 +1329,12 @@ DEFUN (config_hostname, if (host.name) XFREE (MTYPE_HOST, host.name); - - host.name = XSTRDUP (MTYPE_HOST, argv[0]); + + host.name = XSTRDUP (MTYPE_HOST, word->arg); return CMD_SUCCESS; } -DEFUN (config_no_hostname, +DEFUN (config_no_hostname, no_hostname_cmd, "no hostname [HOSTNAME]", NO_STR @@ -3306,42 +1349,26 @@ DEFUN (config_no_hostname, /* VTY interface password set. */ DEFUN (config_password, password_cmd, - "password (8|) WORD", + "password [8] WORD", "Assign the terminal connection password\n" "Specifies a HIDDEN password will follow\n" - "dummy string \n" - "The HIDDEN line password string\n") + "The password string\n") { - /* Argument check. */ - if (argc == 0) - { - vty_out (vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (argc == 2) - { - if (*argv[0] == '8') - { - if (host.password) - XFREE (MTYPE_HOST, host.password); - host.password = NULL; - if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - return CMD_SUCCESS; - } - else - { - vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } - } + if (argc == 3) // '8' was specified + { + if (host.password) + XFREE (MTYPE_HOST, host.password); + host.password = NULL; + if (host.password_encrypt) + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[2]->arg); + return CMD_SUCCESS; + } - if (!isalnum ((int) *argv[0])) + if (!isalnum (argv[1]->arg[0])) { - vty_out (vty, - "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); return CMD_WARNING; } @@ -3352,62 +1379,50 @@ DEFUN (config_password, password_cmd, if (host.encrypt) { if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[1]->arg)); } else - host.password = XSTRDUP (MTYPE_HOST, argv[0]); + host.password = XSTRDUP (MTYPE_HOST, argv[1]->arg); return CMD_SUCCESS; } -ALIAS (config_password, password_text_cmd, - "password LINE", - "Assign the terminal connection password\n" - "The UNENCRYPTED (cleartext) line password\n") - /* VTY enable password set. */ DEFUN (config_enable_password, enable_password_cmd, - "enable password (8|) WORD", + "enable password [8] WORD", "Modify enable password parameters\n" "Assign the privileged level password\n" "Specifies a HIDDEN password will follow\n" "dummy string \n" "The HIDDEN 'enable' password string\n") { - /* Argument check. */ - if (argc == 0) - { - vty_out (vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } - /* Crypt type is specified. */ - if (argc == 2) + if (argc == 4) { - if (*argv[0] == '8') - { - if (host.enable) - XFREE (MTYPE_HOST, host.enable); - host.enable = NULL; - - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - - return CMD_SUCCESS; - } + if (argv[2]->arg[0] == '8') + { + if (host.enable) + XFREE (MTYPE_HOST, host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[3]->arg); + + return CMD_SUCCESS; + } else - { - vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } + { + vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } } - if (!isalnum ((int) *argv[0])) + if (!isalnum (argv[2]->arg[0])) { - vty_out (vty, - "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); return CMD_WARNING; } @@ -3419,22 +1434,15 @@ DEFUN (config_enable_password, enable_password_cmd, if (host.encrypt) { if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[2]->arg)); } else - host.enable = XSTRDUP (MTYPE_HOST, argv[0]); + host.enable = XSTRDUP (MTYPE_HOST, argv[2]->arg); return CMD_SUCCESS; } -ALIAS (config_enable_password, - enable_password_text_cmd, - "enable password LINE", - "Modify enable password parameters\n" - "Assign the privileged level password\n" - "The UNENCRYPTED (cleartext) 'enable' password\n") - /* VTY enable password delete. */ DEFUN (no_config_enable_password, no_enable_password_cmd, "no enable password", @@ -3452,7 +1460,7 @@ DEFUN (no_config_enable_password, no_enable_password_cmd, return CMD_SUCCESS; } - + DEFUN (service_password_encrypt, service_password_encrypt_cmd, "service password-encryption", @@ -3467,13 +1475,13 @@ DEFUN (service_password_encrypt, if (host.password) { if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); + XFREE (MTYPE_HOST, host.password_encrypt); host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password)); } if (host.enable) { if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); + XFREE (MTYPE_HOST, host.enable_encrypt); host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable)); } @@ -3504,7 +1512,7 @@ DEFUN (no_service_password_encrypt, } DEFUN (config_terminal_length, config_terminal_length_cmd, - "terminal length <0-512>", + "terminal length (0-512)", "Set terminal line parameters\n" "Set number of lines on a screen\n" "Number of lines on screen (0 for no pausing)\n") @@ -3512,7 +1520,7 @@ DEFUN (config_terminal_length, config_terminal_length_cmd, int lines; char *endptr = NULL; - lines = strtol (argv[0], &endptr, 10); + lines = strtol (argv[2]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3534,7 +1542,7 @@ DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, } DEFUN (service_terminal_length, service_terminal_length_cmd, - "service terminal-length <0-512>", + "service terminal-length (0-512)", "Set up miscellaneous service\n" "System wide terminal length configuration\n" "Number of lines of VTY (0 means no line control)\n") @@ -3542,7 +1550,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd, int lines; char *endptr = NULL; - lines = strtol (argv[0], &endptr, 10); + lines = strtol (argv[2]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3554,7 +1562,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd, } DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, - "no service terminal-length [<0-512>]", + "no service terminal-length [(0-512)]", NO_STR "Set up miscellaneous service\n" "System wide terminal length configuration\n" @@ -3565,15 +1573,15 @@ DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, } DEFUN_HIDDEN (do_echo, - echo_cmd, - "echo .MESSAGE", - "Echo a message back to the vty\n" - "The message to echo\n") + echo_cmd, + "echo MESSAGE...", + "Echo a message back to the vty\n" + "The message to echo\n") { char *message; - vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""), - VTY_NEWLINE); + vty_out (vty, "%s%s", ((message = argv_concat (argv, argc, 0)) ? message : ""), + VTY_NEWLINE); if (message) XFREE(MTYPE_TMP, message); return CMD_SUCCESS; @@ -3581,7 +1589,7 @@ DEFUN_HIDDEN (do_echo, DEFUN (config_logmsg, config_logmsg_cmd, - "logmsg "LOG_LEVELS" .MESSAGE", + "logmsg "LOG_LEVELS" MESSAGE...", "Send a message to enabled logging destinations\n" LOG_LEVEL_DESC "The message to send\n") @@ -3589,7 +1597,7 @@ DEFUN (config_logmsg, int level; char *message; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[1]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); @@ -3611,8 +1619,8 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s, facility %s, ident %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], - facility_name(zl->facility), zl->ident); + zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], + facility_name(zl->facility), zl->ident); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Stdout logging: "); @@ -3620,7 +1628,7 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); + zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Monitor logging: "); @@ -3628,7 +1636,7 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); + zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "File logging: "); @@ -3637,40 +1645,35 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s, filename %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], - zl->filename); + zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], + zl->filename); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Protocol name: %s%s", - zlog_proto_names[zl->protocol], VTY_NEWLINE); + zlog_proto_names[zl->protocol], VTY_NEWLINE); vty_out (vty, "Record priority: %s%s", - (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); + (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); vty_out (vty, "Timestamp precision: %d%s", - zl->timestamp_precision, VTY_NEWLINE); + zl->timestamp_precision, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN (config_log_stdout, config_log_stdout_cmd, - "log stdout", - "Logging control\n" - "Set stdout logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_stdout_level, - config_log_stdout_level_cmd, - "log stdout "LOG_LEVELS, + "log stdout ["LOG_LEVELS"]", "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC) { + if (argc == 2) + { + zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); + return CMD_SUCCESS; + } int level; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[2]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_set_level (NULL, ZLOG_DEST_STDOUT, level); return CMD_SUCCESS; @@ -3678,11 +1681,11 @@ DEFUN (config_log_stdout_level, DEFUN (no_config_log_stdout, no_config_log_stdout_cmd, - "no log stdout [LEVEL]", + "no log stdout ["LOG_LEVELS"]", NO_STR "Logging control\n" "Cancel logging to stdout\n" - "Logging level\n") + LOG_LEVEL_DESC) { zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); return CMD_SUCCESS; @@ -3690,24 +1693,19 @@ DEFUN (no_config_log_stdout, DEFUN (config_log_monitor, config_log_monitor_cmd, - "log monitor", - "Logging control\n" - "Set terminal line (monitor) logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_monitor_level, - config_log_monitor_level_cmd, - "log monitor "LOG_LEVELS, + "log monitor ["LOG_LEVELS"]", "Logging control\n" "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) { + if (argc == 2) + { + zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); + return CMD_SUCCESS; + } int level; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[2]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_set_level (NULL, ZLOG_DEST_MONITOR, level); return CMD_SUCCESS; @@ -3715,11 +1713,11 @@ DEFUN (config_log_monitor_level, DEFUN (no_config_log_monitor, no_config_log_monitor_cmd, - "no log monitor [LEVEL]", + "no log monitor ["LOG_LEVELS"]", NO_STR "Logging control\n" "Disable terminal line (monitor) logging\n" - "Logging level\n") + LOG_LEVEL_DESC) { zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); return CMD_SUCCESS; @@ -3731,19 +1729,19 @@ set_log_file(struct vty *vty, const char *fname, int loglevel) int ret; char *p = NULL; const char *fullpath; - + /* Path detection. */ if (! IS_DIRECTORY_SEP (*fname)) { char cwd[MAXPATHLEN+1]; cwd[MAXPATHLEN] = '\0'; - + if (getcwd (cwd, MAXPATHLEN) == NULL) { zlog_err ("config_log_file: Unable to alloc mem!"); return CMD_WARNING; } - + if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2)) == NULL) { @@ -3781,36 +1779,32 @@ set_log_file(struct vty *vty, const char *fname, int loglevel) DEFUN (config_log_file, config_log_file_cmd, - "log file FILENAME", - "Logging control\n" - "Logging to file\n" - "Logging filename\n") -{ - return set_log_file(vty, argv[0], zlog_default->default_lvl); -} - -DEFUN (config_log_file_level, - config_log_file_level_cmd, - "log file FILENAME "LOG_LEVELS, + "log file FILENAME [" LOG_LEVELS "]", "Logging control\n" "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC) { - int level; - - if ((level = level_match(argv[1])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - return set_log_file(vty, argv[0], level); + if (argc == 4) + { + int level; + if ((level = level_match(argv[3]->arg)) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[2]->arg, level); + } + else + return set_log_file(vty, argv[2]->arg, zlog_default->default_lvl); } DEFUN (no_config_log_file, no_config_log_file_cmd, - "no log file [FILENAME]", + "no log file [FILENAME [LEVEL]]", NO_STR "Logging control\n" "Cancel logging to file\n" - "Logging file name\n") + "Logging file name\n" + "Logging file name\n" + "Logging level\n") { zlog_reset_file (NULL); @@ -3822,52 +1816,37 @@ DEFUN (no_config_log_file, return CMD_SUCCESS; } -ALIAS (no_config_log_file, - no_config_log_file_level_cmd, - "no log file FILENAME LEVEL", - NO_STR - "Logging control\n" - "Cancel logging to file\n" - "Logging file name\n" - "Logging level\n") - DEFUN (config_log_syslog, config_log_syslog_cmd, - "log syslog", - "Logging control\n" - "Set syslog logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_syslog_level, - config_log_syslog_level_cmd, - "log syslog "LOG_LEVELS, + "log syslog [" LOG_LEVELS "]", "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC) { - int level; - - if ((level = level_match(argv[0])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level); - return CMD_SUCCESS; + if (argc == 3) + { + int level; + if ((level = level_match (argv[2]->arg)) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level); + return CMD_SUCCESS; + } + else + { + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + return CMD_SUCCESS; + } } DEFUN_DEPRECATED (config_log_syslog_facility, - config_log_syslog_facility_cmd, - "log syslog facility "LOG_FACILITIES, - "Logging control\n" - "Logging goes to syslog\n" - "(Deprecated) Facility parameter for syslog messages\n" - LOG_FACILITY_DESC) + config_log_syslog_facility_cmd, + "log syslog facility "LOG_FACILITIES, + "Logging control\n" + "Logging goes to syslog\n" + "(Deprecated) Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) { - int facility; - - if ((facility = facility_match(argv[0])) < 0) - return CMD_ERR_NO_MATCH; + int facility = facility_match(argv[3]->arg); zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); zlog_default->facility = facility; @@ -3876,25 +1855,17 @@ DEFUN_DEPRECATED (config_log_syslog_facility, DEFUN (no_config_log_syslog, no_config_log_syslog_cmd, - "no log syslog [LEVEL]", + "no log syslog [" LOG_FACILITIES "] ["LOG_LEVELS"]", NO_STR "Logging control\n" "Cancel logging to syslog\n" - "Logging level\n") + LOG_FACILITY_DESC + LOG_LEVEL_DESC) { zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); return CMD_SUCCESS; } -ALIAS (no_config_log_syslog, - no_config_log_syslog_facility_cmd, - "no log syslog facility "LOG_FACILITIES, - NO_STR - "Logging control\n" - "Logging goes to syslog\n" - "Facility parameter for syslog messages\n" - LOG_FACILITY_DESC) - DEFUN (config_log_facility, config_log_facility_cmd, "log facility "LOG_FACILITIES, @@ -3902,37 +1873,35 @@ DEFUN (config_log_facility, "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) { - int facility; + int facility = facility_match(argv[2]->arg); - if ((facility = facility_match(argv[0])) < 0) - return CMD_ERR_NO_MATCH; zlog_default->facility = facility; return CMD_SUCCESS; } DEFUN (no_config_log_facility, no_config_log_facility_cmd, - "no log facility [FACILITY]", + "no log facility ["LOG_FACILITIES"]", NO_STR "Logging control\n" "Reset syslog facility to default (daemon)\n" - "Syslog facility\n") + LOG_FACILITY_DESC) { zlog_default->facility = LOG_DAEMON; return CMD_SUCCESS; } DEFUN_DEPRECATED (config_log_trap, - config_log_trap_cmd, - "log trap "LOG_LEVELS, - "Logging control\n" - "(Deprecated) Set logging level and default for all destinations\n" - LOG_LEVEL_DESC) + config_log_trap_cmd, + "log trap "LOG_LEVELS, + "Logging control\n" + "(Deprecated) Set logging level and default for all destinations\n" + LOG_LEVEL_DESC) { int new_level ; int i; - - if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) + + if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_default->default_lvl = new_level; @@ -3943,12 +1912,12 @@ DEFUN_DEPRECATED (config_log_trap, } DEFUN_DEPRECATED (no_config_log_trap, - no_config_log_trap_cmd, - "no log trap [LEVEL]", - NO_STR - "Logging control\n" - "Permit all logging information\n" - "Logging level\n") + no_config_log_trap_cmd, + "no log trap ["LOG_LEVELS"]", + NO_STR + "Logging control\n" + "Permit all logging information\n" + LOG_LEVEL_DESC) { zlog_default->default_lvl = LOG_DEBUG; return CMD_SUCCESS; @@ -3983,14 +1952,8 @@ DEFUN (config_log_timestamp_precision, "Set the timestamp precision\n" "Number of subsecond digits\n") { - if (argc != 1) - { - vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); - return CMD_WARNING; - } - VTY_GET_INTEGER_RANGE("Timestamp Precision", - zlog_default->timestamp_precision, argv[0], 0, 6); + zlog_default->timestamp_precision, argv[3]->arg, 0, 6); return CMD_SUCCESS; } @@ -4021,7 +1984,7 @@ cmd_banner_motd_file (const char *file) if (in == rpath) { if (host.motdfile) - XFREE (MTYPE_HOST, host.motdfile); + XFREE (MTYPE_HOST, host.motdfile); host.motdfile = XSTRDUP (MTYPE_HOST, file); } else @@ -4038,13 +2001,13 @@ DEFUN (banner_motd_file, "Banner from a file\n" "Filename\n") { - int cmd = cmd_banner_motd_file (argv[0]); + int cmd = cmd_banner_motd_file (argv[3]->arg); if (cmd == CMD_ERR_NO_FILE) - vty_out (vty, "%s does not exist", argv[0]); + vty_out (vty, "%s does not exist", argv[3]->arg); else if (cmd == CMD_WARNING) vty_out (vty, "%s must be in %s", - argv[0], SYSCONFDIR); + argv[0], SYSCONFDIR); return cmd; } @@ -4068,7 +2031,7 @@ DEFUN (no_banner_motd, "Strings for motd\n") { host.motd = NULL; - if (host.motdfile) + if (host.motdfile) XFREE (MTYPE_HOST, host.motdfile); host.motdfile = NULL; return CMD_SUCCESS; @@ -4123,9 +2086,6 @@ install_default (enum node_type node) install_element (node, &config_help_cmd); install_element (node, &config_list_cmd); - install_element (node, &config_write_terminal_cmd); - install_element (node, &config_write_file_cmd); - install_element (node, &config_write_memory_cmd); install_element (node, &config_write_cmd); install_element (node, &show_running_config_cmd); } @@ -4134,12 +2094,6 @@ install_default (enum node_type node) void cmd_init (int terminal) { - command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>"); - token_cr.type = TOKEN_TERMINAL; - token_cr.terminal = TERMINAL_LITERAL; - token_cr.cmd = command_cr; - token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); - /* Allocate initial top vector of commands. */ cmdvec = vector_init (VECTOR_MIN_SIZE); @@ -4207,33 +2161,24 @@ cmd_init (int terminal) install_default (CONFIG_NODE); } - + install_element (CONFIG_NODE, &hostname_cmd); install_element (CONFIG_NODE, &no_hostname_cmd); if (terminal) { install_element (CONFIG_NODE, &password_cmd); - install_element (CONFIG_NODE, &password_text_cmd); install_element (CONFIG_NODE, &enable_password_cmd); - install_element (CONFIG_NODE, &enable_password_text_cmd); install_element (CONFIG_NODE, &no_enable_password_cmd); install_element (CONFIG_NODE, &config_log_stdout_cmd); - install_element (CONFIG_NODE, &config_log_stdout_level_cmd); install_element (CONFIG_NODE, &no_config_log_stdout_cmd); install_element (CONFIG_NODE, &config_log_monitor_cmd); - install_element (CONFIG_NODE, &config_log_monitor_level_cmd); install_element (CONFIG_NODE, &no_config_log_monitor_cmd); install_element (CONFIG_NODE, &config_log_file_cmd); - install_element (CONFIG_NODE, &config_log_file_level_cmd); install_element (CONFIG_NODE, &no_config_log_file_cmd); - install_element (CONFIG_NODE, &no_config_log_file_level_cmd); install_element (CONFIG_NODE, &config_log_syslog_cmd); - install_element (CONFIG_NODE, &config_log_syslog_level_cmd); - install_element (CONFIG_NODE, &config_log_syslog_facility_cmd); install_element (CONFIG_NODE, &no_config_log_syslog_cmd); - install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd); install_element (CONFIG_NODE, &config_log_facility_cmd); install_element (CONFIG_NODE, &no_config_log_facility_cmd); install_element (CONFIG_NODE, &config_log_trap_cmd); @@ -4253,7 +2198,7 @@ cmd_init (int terminal) install_element (VIEW_NODE, &show_thread_cpu_cmd); install_element (ENABLE_NODE, &show_thread_cpu_cmd); install_element (RESTRICTED_NODE, &show_thread_cpu_cmd); - + install_element (ENABLE_NODE, &clear_thread_cpu_cmd); install_element (VIEW_NODE, &show_work_queues_cmd); install_element (ENABLE_NODE, &show_work_queues_cmd); @@ -4263,84 +2208,87 @@ cmd_init (int terminal) srandom(time(NULL)); } -static void -cmd_terminate_token(struct cmd_token *token) +struct cmd_token * +new_cmd_token (enum cmd_token_type type, char *text, char *desc) { - unsigned int i, j; - vector keyword_vect; + struct cmd_token *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token)); + token->type = type; + token->text = text; + token->desc = desc; + token->arg = NULL; - if (token->multiple) - { - for (i = 0; i < vector_active(token->multiple); i++) - cmd_terminate_token(vector_slot(token->multiple, i)); - vector_free(token->multiple); - token->multiple = NULL; - } + return token; +} - if (token->keyword) - { - for (i = 0; i < vector_active(token->keyword); i++) - { - keyword_vect = vector_slot(token->keyword, i); - for (j = 0; j < vector_active(keyword_vect); j++) - cmd_terminate_token(vector_slot(keyword_vect, j)); - vector_free(keyword_vect); - } - vector_free(token->keyword); - token->keyword = NULL; - } +void +del_cmd_token (struct cmd_token *token) +{ + if (!token) return; - XFREE(MTYPE_CMD_TOKENS, token->cmd); - XFREE(MTYPE_CMD_TOKENS, token->desc); + if (token->text) + XFREE (MTYPE_CMD_TOKENS, token->text); + if (token->desc) + XFREE (MTYPE_CMD_TOKENS, token->desc); + if (token->arg) + XFREE (MTYPE_CMD_TOKENS, token->arg); - XFREE(MTYPE_CMD_TOKENS, token); + XFREE (MTYPE_CMD_TOKENS, token); } -static void -cmd_terminate_element(struct cmd_element *cmd) +struct cmd_token * +copy_cmd_token (struct cmd_token *token) { - unsigned int i; + struct cmd_token *copy = new_cmd_token (token->type, NULL, NULL); + copy->value = token->value; + copy->max = token->max; + copy->min = token->min; + copy->text = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL; + copy->desc = token->desc ? XSTRDUP (MTYPE_CMD_TOKENS, token->desc) : NULL; + copy->arg = token->arg ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL; - if (cmd->tokens == NULL) - return; + return copy; +} - for (i = 0; i < vector_active(cmd->tokens); i++) - cmd_terminate_token(vector_slot(cmd->tokens, i)); +void +del_cmd_element(struct cmd_element *cmd) +{ + if (!cmd) return; + XFREE (MTYPE_CMD_TOKENS, cmd->string); + XFREE (MTYPE_CMD_TOKENS, cmd->doc); + XFREE (MTYPE_CMD_TOKENS, cmd); +} - vector_free(cmd->tokens); - cmd->tokens = NULL; +struct cmd_element * +copy_cmd_element(struct cmd_element *cmd) +{ + struct cmd_element *el = XMALLOC(MTYPE_CMD_TOKENS, sizeof (struct cmd_element)); + el->string = cmd->string ? XSTRDUP(MTYPE_CMD_TOKENS, cmd->string) : NULL; + el->func = cmd->func; + el->doc = cmd->doc ? XSTRDUP(MTYPE_CMD_TOKENS, cmd->doc) : NULL; + el->daemon = cmd->daemon; + el->attr = cmd->attr; + return el; } void cmd_terminate () { - unsigned int i, j; struct cmd_node *cmd_node; - struct cmd_element *cmd_element; - vector cmd_node_v; if (cmdvec) { - for (i = 0; i < vector_active (cmdvec); i++) + for (unsigned int i = 0; i < vector_active (cmdvec); i++) if ((cmd_node = vector_slot (cmdvec, i)) != NULL) - { - cmd_node_v = cmd_node->cmd_vector; - - for (j = 0; j < vector_active (cmd_node_v); j++) - if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) - cmd_terminate_element(cmd_element); - - vector_free (cmd_node_v); - } + { + // deleting the graph delets the cmd_element as well + graph_delete_graph (cmd_node->cmdgraph); + vector_free (cmd_node->cmd_vector); + } vector_free (cmdvec); cmdvec = NULL; } - if (command_cr) - XFREE(MTYPE_CMD_TOKENS, command_cr); - if (token_cr.desc) - XFREE(MTYPE_CMD_TOKENS, token_cr.desc); if (host.name) XFREE (MTYPE_HOST, host.name); if (host.password) |
