diff options
Diffstat (limited to 'lib/command.c')
| -rw-r--r-- | lib/command.c | 843 |
1 files changed, 391 insertions, 452 deletions
diff --git a/lib/command.c b/lib/command.c index 4e8e24f491..34864e9d3f 100644 --- a/lib/command.c +++ b/lib/command.c @@ -41,12 +41,19 @@ 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, @@ -57,6 +64,11 @@ enum matcher_rv 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 \ @@ -182,6 +194,7 @@ print_version (const char *progname) { printf ("%s version %s\n", progname, QUAGGA_VERSION); printf ("%s\n", QUAGGA_COPYRIGHT); + printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS); } @@ -291,24 +304,29 @@ 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 red was a - * real word and not some abstract token */ + 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 @@ -323,6 +341,14 @@ format_parser_error(struct format_parser_state *state, const char *message) 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) { @@ -358,6 +384,21 @@ format_parser_desc_str(struct format_parser_state *state) 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) { @@ -382,6 +423,23 @@ format_parser_begin_keyword(struct format_parser_state *state) 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) { @@ -407,6 +465,14 @@ format_parser_begin_multiple(struct format_parser_state *state) 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) { @@ -422,13 +488,22 @@ format_parser_end_keyword(struct format_parser_state *state) 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, "Unepexted ')'"); + format_parser_error(state, "Unexpected ')'"); if (vector_active(state->curvect) == 0) format_parser_error(state, "Empty multiple section"); @@ -456,6 +531,21 @@ format_parser_end_multiple(struct format_parser_state *state) 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) { @@ -484,6 +574,13 @@ format_parser_handle_pipe(struct format_parser_state *state) } } +/** + * 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) { @@ -506,6 +603,25 @@ format_parser_read_word(struct format_parser_state *state) 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); @@ -734,54 +850,6 @@ cmd_node_vector (vector v, enum node_type ntype) return cnode->cmd_vector; } -#if 0 -/* Filter command vector by symbol. This function is not actually used; - * should it be deleted? */ -static int -cmd_filter_by_symbol (char *command, char *symbol) -{ - int i, lim; - - if (strcmp (symbol, "IPV4_ADDRESS") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/')) - return 1; - i++; - } - return 0; - } - if (strcmp (symbol, "STRING") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-')) - return 1; - i++; - } - return 0; - } - if (strcmp (symbol, "IFNAME") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! isalnum ((int) command[i])) - return 1; - i++; - } - return 0; - } - return 0; -} -#endif - /* Completion match types. */ enum match_type { @@ -797,59 +865,29 @@ enum match_type exact_match }; +#define IPV4_ADDR_STR "0123456789." +#define IPV4_PREFIX_STR "0123456789./" + +/** + * Determines whether a string is a valid ipv4 token. + * + * @param[in] str the string to match + * @return exact_match if the string is an exact match, no_match/partly_match + * otherwise + */ static enum match_type cmd_ipv4_match (const char *str) { - const char *sp; - int dots = 0, nums = 0; - char buf[4]; + struct sockaddr_in sin_dummy; 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 (strspn (str, IPV4_ADDR_STR) != strlen (str)) + return no_match; - if (nums < 4) - return partly_match; + if (inet_pton(AF_INET, str, &sin_dummy.sin_addr) != 1) + return no_match; return exact_match; } @@ -857,91 +895,42 @@ cmd_ipv4_match (const char *str) static enum match_type cmd_ipv4_prefix_match (const char *str) { - const char *sp; - int dots = 0; - char buf[4]; + struct sockaddr_in sin_dummy; + const char *delim = "/\0"; + char *dupe, *prefix, *mask, *context, *endptr; + int nmask = -1; 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; + if (strspn (str, IPV4_PREFIX_STR) != strlen (str)) + return no_match; - str++; - } + /* 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); - sp = str; - while (*str != '\0') - { - if (!isdigit ((int) *str)) - return no_match; + if (!mask) + return partly_match; - str++; - } + /* validate prefix */ + if (inet_pton(AF_INET, prefix, &sin_dummy.sin_addr) != 1) + return no_match; - if (atoi (sp) > 32) + /* validate mask */ + nmask = strtol (mask, &endptr, 10); + if (*endptr != '\0' || nmask < 0 || nmask > 32) return no_match; + XFREE(MTYPE_TMP, dupe); + return exact_match; } -#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" -#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" -#define STATE_START 1 -#define STATE_COLON 2 -#define STATE_DOUBLE 3 -#define STATE_ADDR 4 -#define STATE_DOT 5 -#define STATE_SLASH 6 -#define STATE_MASK 7 +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:." +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" #ifdef HAVE_IPV6 @@ -972,11 +961,10 @@ cmd_ipv6_match (const char *str) static enum match_type cmd_ipv6_prefix_match (const char *str) { - int state = STATE_START; - int colons = 0, nums = 0, double_colon = 0; - int mask; - const char *sp = NULL; - char *endptr = NULL; + struct sockaddr_in6 sin6_dummy; + const char *delim = "/\0"; + char *dupe, *prefix, *mask, *context, *endptr; + int nmask = -1; if (str == NULL) return partly_match; @@ -984,123 +972,25 @@ cmd_ipv6_prefix_match (const char *str) if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) return no_match; - while (*str != '\0' && state != STATE_MASK) - { - switch (state) - { - case STATE_START: - if (*str == ':') - { - if (*(str + 1) != ':' && *(str + 1) != '\0') - return no_match; - colons--; - state = STATE_COLON; - } - else - { - sp = str; - state = STATE_ADDR; - } - - continue; - case STATE_COLON: - colons++; - if (*(str + 1) == '/') - return no_match; - else if (*(str + 1) == ':') - state = STATE_DOUBLE; - else - { - sp = str + 1; - state = STATE_ADDR; - } - break; - case STATE_DOUBLE: - if (double_colon) - return no_match; - - if (*(str + 1) == ':') - return no_match; - else - { - if (*(str + 1) != '\0' && *(str + 1) != '/') - colons++; - sp = str + 1; - - if (*(str + 1) == '/') - state = STATE_SLASH; - else - state = STATE_ADDR; - } - - double_colon++; - nums += 1; - break; - case STATE_ADDR: - if (*(str + 1) == ':' || *(str + 1) == '.' - || *(str + 1) == '\0' || *(str + 1) == '/') - { - if (str - sp > 3) - return no_match; - - for (; sp <= str; sp++) - if (*sp == '/') - return no_match; - - nums++; - - if (*(str + 1) == ':') - state = STATE_COLON; - else if (*(str + 1) == '.') - { - if (colons || double_colon) - state = STATE_DOT; - else - return no_match; - } - else if (*(str + 1) == '/') - state = STATE_SLASH; - } - break; - case STATE_DOT: - state = STATE_ADDR; - break; - case STATE_SLASH: - if (*(str + 1) == '\0') - return partly_match; - - state = STATE_MASK; - break; - default: - break; - } - - if (nums > 11) - 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 (colons > 7) - return no_match; - - str++; - } - - if (state < STATE_MASK) + if (!mask) return partly_match; - mask = strtol (str, &endptr, 10); - if (*endptr != '\0') + /* validate prefix */ + if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1) return no_match; - if (mask < 0 || mask > 128) + /* validate mask */ + nmask = strtol (mask, &endptr, 10); + if (*endptr != '\0' || nmask < 0 || nmask > 128) return no_match; - -/* I don't know why mask < 13 makes command match partly. - Forgive me to make this comments. I Want to set static default route - because of lack of function to originate default in ospf6d; sorry - yasu - if (mask < 13) - return partly_match; -*/ + + XFREE(MTYPE_TMP, dupe); return exact_match; } @@ -1172,59 +1062,61 @@ cmd_word_match(struct cmd_token *token, if (!word) return no_match; - if (CMD_VARARG(str)) - { - return vararg_match; - } - else if (CMD_RANGE(str)) - { - if (cmd_range_match(str, word)) - return range_match; - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6(str)) - { - match_type = cmd_ipv6_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_match; - } - else if (CMD_IPV6_PREFIX(str)) + switch (token->terminal) { - 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; - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4(str)) - { - match_type = cmd_ipv4_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_match; - } - else if (CMD_IPV4_PREFIX(str)) - { - match_type = cmd_ipv4_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) + 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 ipv4_prefix_match; - } - else if (CMD_OPTION(str) || CMD_VARIABLE(str)) - { - return extend_match; - } - else - { - 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; + 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; @@ -1308,7 +1200,7 @@ cmd_matcher_match_terminal(struct cmd_matcher *matcher, if (!cmd_matcher_words_left(matcher)) { - if (CMD_OPTION(token->cmd)) + if (token->terminal == TERMINAL_OPTION) return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ else return MATCHER_INCOMPLETE; @@ -1321,9 +1213,7 @@ cmd_matcher_match_terminal(struct cmd_matcher *matcher, /* We have to record the input word as argument if it matched * against a variable. */ - if (CMD_VARARG(token->cmd) - || CMD_VARIABLE(token->cmd) - || CMD_OPTION(token->cmd)) + if (TERMINAL_RECORD (token->terminal)) { if (push_argument(argc, argv, word)) return MATCHER_EXCEED_ARGC_MAX; @@ -1334,7 +1224,7 @@ cmd_matcher_match_terminal(struct cmd_matcher *matcher, matcher->word_index++; /* A vararg token should consume all left over words as arguments */ - if (CMD_VARARG(token->cmd)) + if (token->terminal == TERMINAL_VARARG) while (cmd_matcher_words_left(matcher)) { word = cmd_matcher_get_word(matcher); @@ -1354,7 +1244,7 @@ cmd_matcher_match_multiple(struct cmd_matcher *matcher, enum match_type multiple_match; unsigned int multiple_index; const char *word; - const char *arg; + const char *arg = NULL; struct cmd_token *word_token; enum match_type word_match; @@ -1565,9 +1455,7 @@ cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, { word_token = vector_slot(keyword_vector, j); if ((word_token->type == TOKEN_TERMINAL - && (CMD_VARARG(word_token->cmd) - || CMD_VARIABLE(word_token->cmd) - || CMD_OPTION(word_token->cmd))) + && TERMINAL_RECORD (word_token->terminal)) || word_token->type == TOKEN_MULTIPLE) { if (push_argument(argc, argv, NULL)) @@ -1853,12 +1741,12 @@ is_cmd_ambiguous (vector cmd_vector, switch (type) { case exact_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + if (!TERMINAL_RECORD (cmd_token->terminal) && strcmp (command, str) == 0) match++; break; case partly_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + if (!TERMINAL_RECORD (cmd_token->terminal) && strncmp (command, str, strlen (command)) == 0) { if (matched && strcmp (matched, str) != 0) @@ -1880,7 +1768,7 @@ is_cmd_ambiguous (vector cmd_vector, break; #ifdef HAVE_IPV6 case ipv6_match: - if (CMD_IPV6 (str)) + if (cmd_token->terminal == TERMINAL_IPV6) match++; break; case ipv6_prefix_match: @@ -1894,7 +1782,7 @@ is_cmd_ambiguous (vector cmd_vector, break; #endif /* HAVE_IPV6 */ case ipv4_match: - if (CMD_IPV4 (str)) + if (cmd_token->terminal == TERMINAL_IPV4) match++; break; case ipv4_prefix_match: @@ -1907,7 +1795,7 @@ is_cmd_ambiguous (vector cmd_vector, } break; case extend_match: - if (CMD_OPTION (str) || CMD_VARIABLE (str)) + if (TERMINAL_RECORD (cmd_token->terminal)) match++; break; case no_match: @@ -1923,11 +1811,12 @@ is_cmd_ambiguous (vector cmd_vector, /* If src matches dst return dst string, otherwise return NULL */ static const char * -cmd_entry_function (const char *src, const char *dst) +cmd_entry_function (const char *src, struct cmd_token *token) { + const char *dst = token->cmd; + /* Skip variable arguments. */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) || - CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst)) + if (TERMINAL_RECORD (token->terminal)) return NULL; /* In case of 'command \t', given src is NULL string. */ @@ -1945,65 +1834,64 @@ cmd_entry_function (const char *src, const char *dst) /* 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, const char *dst) +cmd_entry_function_desc (const char *src, struct cmd_token *token) { - if (CMD_VARARG (dst)) - return dst; - - if (CMD_RANGE (dst)) - { - if (cmd_range_match (dst, src)) - return dst; - else - return NULL; - } - -#ifdef HAVE_IPV6 - if (CMD_IPV6 (dst)) - { - if (cmd_ipv6_match (src)) - return dst; - else - return NULL; - } - - if (CMD_IPV6_PREFIX (dst)) - { - if (cmd_ipv6_prefix_match (src)) - return dst; - else - return NULL; - } -#endif /* HAVE_IPV6 */ + const char *dst = token->cmd; - if (CMD_IPV4 (dst)) + switch (token->terminal) { - if (cmd_ipv4_match (src)) - return dst; - else - return NULL; + 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; + + default: + assert(0); + return NULL; } - - if (CMD_IPV4_PREFIX (dst)) - { - if (cmd_ipv4_prefix_match (src)) - return dst; - else - return NULL; - } - - /* Optional or variable commands always match on '?' */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst)) - return dst; - - /* 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; } /** @@ -2222,7 +2110,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) struct cmd_token *token = vector_slot(match_vector, j); const char *string; - string = cmd_entry_function_desc(command, token->cmd); + string = cmd_entry_function_desc(command, token); if (string && desc_unique_string(matchvec, string)) vector_set(matchvec, token); } @@ -2348,7 +2236,7 @@ cmd_complete_sort(vector matchvec) /* Command line completion support. */ static char ** -cmd_complete_command_real (vector vline, struct vty *vty, int *status) +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)); @@ -2410,15 +2298,6 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) *status = CMD_ERR_AMBIGUOUS; return NULL; } - /* - else if (ret == 2) - { - vector_free (cmd_vector); - cmd_matches_free(&matches); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - */ } /* Prepare match vector. */ @@ -2433,13 +2312,14 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) for (j = 0; j < vector_active (match_vector); j++) if ((token = vector_slot (match_vector, j))) - { - if ((string = - cmd_entry_function (vector_slot (vline, index), - token->cmd))) - if (cmd_unique_string (matchvec, string)) - vector_set (matchvec, XSTRDUP (MTYPE_TMP, string)); - } + { + 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. */ @@ -2484,18 +2364,23 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) { char *lcdstr; - lcdstr = XMALLOC (MTYPE_TMP, lcd + 1); + lcdstr = (islib != 0 ? + XMALLOC (MTYPE_TMP, lcd + 1) : + malloc(lcd + 1)); memcpy (lcdstr, matchvec->index[0], lcd); lcdstr[lcd] = '\0'; - /* match_str = (char **) &lcdstr; */ - /* Free matchvec. */ for (i = 0; i < vector_active (matchvec); i++) - { - if (vector_slot (matchvec, i)) - XFREE (MTYPE_TMP, vector_slot (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. */ @@ -2518,7 +2403,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) } char ** -cmd_complete_command (vector vline, struct vty *vty, int *status) +cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) { char **ret; @@ -2539,15 +2424,20 @@ cmd_complete_command (vector vline, struct vty *vty, int *status) vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } - ret = cmd_complete_command_real (shifted_vline, vty, status); + ret = cmd_complete_command_real (shifted_vline, vty, status, islib); vector_free(shifted_vline); vty->node = onode; return ret; } + return cmd_complete_command_real (vline, vty, status, islib); +} - return cmd_complete_command_real (vline, vty, status); +char ** +cmd_complete_command (vector vline, struct vty *vty, int *status) +{ + return cmd_complete_command_lib (vline, vty, status, 0); } /* return parent node */ @@ -2562,6 +2452,9 @@ node_parent ( enum node_type node ) switch (node) { case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -2943,12 +2836,16 @@ DEFUN (config_exit, case KEYCHAIN_NODE: case MASC_NODE: case RMAP_NODE: + case PIM_NODE: case VTY_NODE: vty->node = CONFIG_NODE; break; - case BGP_VPNV4_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: + case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: vty->node = BGP_NODE; @@ -2988,7 +2885,10 @@ DEFUN (config_end, case RIP_NODE: case RIPNG_NODE: case BGP_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -3000,6 +2900,7 @@ DEFUN (config_end, case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; @@ -3020,6 +2921,8 @@ DEFUN (show_version, vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", 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); return CMD_SUCCESS; } @@ -3115,7 +3018,7 @@ DEFUN (config_write_file, /* Make vty for configuration file. */ file_vty = vty_new (); - file_vty->fd = fd; + file_vty->wfd = fd; file_vty->type = VTY_FILE; /* Config file header print. */ @@ -4052,6 +3955,37 @@ DEFUN (no_banner_motd, return CMD_SUCCESS; } +DEFUN (show_commandtree, + show_commandtree_cmd, + "show commandtree", + NO_STR + "Show command tree\n") +{ + /* TBD */ + vector cmd_vector; + unsigned int i; + + vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE); + + /* vector of all commands installed at this node */ + cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + + /* loop over all commands at this node */ + for (i = 0; i < vector_active(cmd_vector); ++i) + { + struct cmd_element *cmd_element; + + /* A cmd_element (seems to be) is an individual command */ + if ((cmd_element = vector_slot (cmd_vector, i)) == NULL) + continue; + + vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE); + } + + vector_free (cmd_vector); + return CMD_SUCCESS; +} + /* Set config filename. Called from vty.c */ void host_config_set (const char *filename) @@ -4083,6 +4017,7 @@ 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, ""); @@ -4119,6 +4054,7 @@ cmd_init (int terminal) install_element (VIEW_NODE, &config_terminal_length_cmd); install_element (VIEW_NODE, &config_terminal_no_length_cmd); install_element (VIEW_NODE, &show_logging_cmd); + install_element (VIEW_NODE, &show_commandtree_cmd); install_element (VIEW_NODE, &echo_cmd); install_element (RESTRICTED_NODE, &config_list_cmd); @@ -4128,6 +4064,7 @@ cmd_init (int terminal) install_element (RESTRICTED_NODE, &config_enable_cmd); install_element (RESTRICTED_NODE, &config_terminal_length_cmd); install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd); + install_element (RESTRICTED_NODE, &show_commandtree_cmd); install_element (RESTRICTED_NODE, &echo_cmd); } @@ -4140,6 +4077,7 @@ cmd_init (int terminal) } install_element (ENABLE_NODE, &show_startup_config_cmd); install_element (ENABLE_NODE, &show_version_cmd); + install_element (ENABLE_NODE, &show_commandtree_cmd); if (terminal) { @@ -4204,7 +4142,8 @@ cmd_init (int terminal) vrf_install_commands (); } - srand(time(NULL)); + install_element (CONFIG_NODE, &show_commandtree_cmd); + srandom(time(NULL)); } static void |
