summaryrefslogtreecommitdiff
path: root/lib/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/command.c')
-rw-r--r--lib/command.c843
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