summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Franke <chris@opensourcerouting.org>2013-09-16 21:23:57 +0200
committerChristian Franke <chris@opensourcerouting.org>2014-01-08 00:49:41 +0100
commitfdc8614c306e5b61224d1ab4b63c00c558dbb07e (patch)
tree609374c0cafcba1e4f2bf6bd3dd44ae477b5265b
parentd30eb038466ae3d14862df08bf58ee80c00bf311 (diff)
lib/command.c: rewrite command matching/parsing
Add support for keyword commands. Signed-off-by: Christian Franke <chris@opensourcerouting.org>
-rw-r--r--babeld/babel_main.c3
-rw-r--r--bgpd/bgp_main.c3
-rw-r--r--isisd/isis_main.c2
-rw-r--r--lib/command.c1975
-rw-r--r--lib/command.h193
-rw-r--r--lib/memtypes.c2
-rw-r--r--lib/vty.c54
-rw-r--r--ospf6d/ospf6_main.c3
-rw-r--r--ospfd/ospf_main.c2
-rw-r--r--ripd/rip_main.c3
-rw-r--r--ripngd/ripng_main.c3
-rw-r--r--tests/main.c2
-rw-r--r--tests/test-commands.c6
-rw-r--r--vtysh/vtysh.c22
-rw-r--r--vtysh/vtysh_main.c2
-rw-r--r--zebra/main.c3
-rw-r--r--zebra/test_main.c3
17 files changed, 1494 insertions, 787 deletions
diff --git a/babeld/babel_main.c b/babeld/babel_main.c
index 7b1d8799..529c3f29 100644
--- a/babeld/babel_main.c
+++ b/babeld/babel_main.c
@@ -277,9 +277,6 @@ babel_init(int argc, char **argv)
/* this replace kernel_setup && kernel_setup_socket */
babelz_zebra_init ();
- /* Sort all installed commands. */
- sort_node ();
-
/* Get zebra configuration file. */
zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
vty_read_config (babel_config_file, babel_config_default);
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 1ff1ac95..1be65043 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -431,9 +431,6 @@ main (int argc, char **argv)
/* BGP related initialization. */
bgp_init ();
- /* Sort CLI commands. */
- sort_node ();
-
/* Parse config file. */
vty_read_config (config_file, config_default);
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index 96816ebb..283b7eaa 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -339,8 +339,6 @@ main (int argc, char **argv, char **envp)
isis_zebra_init ();
- sort_node ();
-
/* parse config file */
/* this is needed three times! because we have interfaces before the areas */
vty_read_config (config_file, config_default);
diff --git a/lib/command.c b/lib/command.c
index 3b3fadac..a2373644 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -1,6 +1,8 @@
/*
Command interpreter routine for virtual terminal [aka TeletYpe]
Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ Copyright (C) 2013 by Open Source Routing.
+ Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
This file is part of GNU Zebra.
@@ -35,9 +37,32 @@ Boston, MA 02111-1307, USA. */
each daemon maintains each own cmdvec. */
vector cmdvec = NULL;
-struct desc desc_cr;
+struct cmd_token token_cr;
char *command_cr = NULL;
+enum filter_type
+{
+ FILTER_RELAXED,
+ FILTER_STRICT
+};
+
+enum matcher_rv
+{
+ MATCHER_OK,
+ MATCHER_COMPLETE,
+ MATCHER_INCOMPLETE,
+ MATCHER_NO_MATCH,
+ MATCHER_AMBIGUOUS,
+ MATCHER_EXCEED_ARGC_MAX
+};
+
+#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;
@@ -196,53 +221,6 @@ install_node (struct cmd_node *node,
node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
}
-/* Compare two command's string. Used in sort_node (). */
-static int
-cmp_node (const void *p, const void *q)
-{
- const struct cmd_element *a = *(struct cmd_element * const *)p;
- const struct cmd_element *b = *(struct cmd_element * const *)q;
-
- return strcmp (a->string, b->string);
-}
-
-static int
-cmp_desc (const void *p, const void *q)
-{
- const struct desc *a = *(struct desc * const *)p;
- const struct desc *b = *(struct desc * const *)q;
-
- return strcmp (a->cmd, b->cmd);
-}
-
-/* Sort each node's command element according to command string. */
-void
-sort_node ()
-{
- unsigned int i, j;
- struct cmd_node *cnode;
- vector descvec;
- struct cmd_element *cmd_element;
-
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((cnode = vector_slot (cmdvec, i)) != NULL)
- {
- vector cmd_vector = cnode->cmd_vector;
- qsort (cmd_vector->index, vector_active (cmd_vector),
- sizeof (void *), cmp_node);
-
- for (j = 0; j < vector_active (cmd_vector); j++)
- if ((cmd_element = vector_slot (cmd_vector, j)) != NULL
- && vector_active (cmd_element->strvec))
- {
- descvec = vector_slot (cmd_element->strvec,
- vector_active (cmd_element->strvec) - 1);
- qsort (descvec->index, vector_active (descvec),
- sizeof (void *), cmp_desc);
- }
- }
-}
-
/* Breaking up string into each command piece. I assume given
character is separated by a space character. Return value is a
vector which includes char ** data element. */
@@ -312,15 +290,46 @@ cmd_free_strvec (vector v)
vector_free (v);
}
-/* Fetch next description. Used in cmd_make_descvec(). */
+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 */
+};
+
+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);
+}
+
static char *
-cmd_desc_str (const char **string)
+format_parser_desc_str(struct format_parser_state *state)
{
const char *cp, *start;
char *token;
int strlen;
-
- cp = *string;
+
+ cp = state->dp;
if (cp == NULL)
return NULL;
@@ -339,132 +348,226 @@ cmd_desc_str (const char **string)
cp++;
strlen = cp - start;
- token = XMALLOC (MTYPE_STRVEC, strlen + 1);
+ token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1);
memcpy (token, start, strlen);
*(token + strlen) = '\0';
- *string = cp;
+ state->dp = cp;
return token;
}
-/* New string vector. */
-static vector
-cmd_make_descvec (const char *string, const char *descstr)
+static void
+format_parser_begin_keyword(struct format_parser_state *state)
{
- int multiple = 0;
- const char *sp;
- char *token;
- int len;
- const char *cp;
- const char *dp;
- vector allvec;
- vector strvec = NULL;
- struct desc *desc;
+ struct cmd_token *token;
+ vector keyword_vect;
- cp = string;
- dp = descstr;
+ if (state->in_keyword
+ || state->in_multiple)
+ format_parser_error(state, "Unexpected '{'");
- if (cp == NULL)
- return NULL;
+ state->cp++;
+ state->in_keyword = 1;
- allvec = vector_init (VECTOR_MIN_SIZE);
+ token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
+ token->type = TOKEN_KEYWORD;
+ token->keyword = vector_init(VECTOR_MIN_SIZE);
- while (1)
- {
- while (isspace ((int) *cp) && *cp != '\0')
- cp++;
+ keyword_vect = vector_init(VECTOR_MIN_SIZE);
+ vector_set(token->keyword, keyword_vect);
- if (*cp == '(')
- {
- multiple = 1;
- cp++;
- }
- if (*cp == ')')
- {
- multiple = 0;
- cp++;
- }
- if (*cp == '|')
- {
- if (! multiple)
- {
- fprintf (stderr, "Command parse error!: %s\n", string);
- exit (1);
- }
- cp++;
- }
-
- while (isspace ((int) *cp) && *cp != '\0')
- cp++;
+ vector_set(state->curvect, token);
+ state->curvect = keyword_vect;
+}
- if (*cp == '(')
- {
- multiple = 1;
- cp++;
- }
+static void
+format_parser_begin_multiple(struct format_parser_state *state)
+{
+ struct cmd_token *token;
- if (*cp == '\0')
- return allvec;
+ if (state->in_keyword == 1)
+ format_parser_error(state, "Keyword starting with '('");
- sp = cp;
+ if (state->in_multiple)
+ format_parser_error(state, "Nested group");
- while (! (isspace ((int) *cp) || *cp == '\r' || *cp == '\n' || *cp == ')' || *cp == '|') && *cp != '\0')
- cp++;
+ state->cp++;
+ state->in_multiple = 1;
+ state->just_read_word = 0;
- len = cp - sp;
+ token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
+ token->type = TOKEN_MULTIPLE;
+ token->multiple = vector_init(VECTOR_MIN_SIZE);
- token = XMALLOC (MTYPE_STRVEC, len + 1);
- memcpy (token, sp, len);
- *(token + len) = '\0';
+ vector_set(state->curvect, token);
+ if (state->curvect != state->topvect)
+ state->intvect = state->curvect;
+ state->curvect = token->multiple;
+}
- desc = XCALLOC (MTYPE_DESC, sizeof (struct desc));
- desc->cmd = token;
- desc->str = cmd_desc_str (&dp);
+static void
+format_parser_end_keyword(struct format_parser_state *state)
+{
+ if (state->in_multiple
+ || !state->in_keyword)
+ format_parser_error(state, "Unexpected '}'");
- if (multiple)
- {
- if (multiple == 1)
- {
- strvec = vector_init (VECTOR_MIN_SIZE);
- vector_set (allvec, strvec);
- }
- multiple++;
- }
- else
- {
- strvec = vector_init (VECTOR_MIN_SIZE);
- vector_set (allvec, strvec);
- }
- vector_set (strvec, desc);
+ if (state->in_keyword == 1)
+ format_parser_error(state, "Empty keyword group");
+
+ state->cp++;
+ state->in_keyword = 0;
+ state->curvect = state->topvect;
+}
+
+static void
+format_parser_end_multiple(struct format_parser_state *state)
+{
+ char *dummy;
+
+ if (!state->in_multiple)
+ format_parser_error(state, "Unepexted ')'");
+
+ 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;
}
-/* Count mandantory string vector size. This is to determine inputed
- command has enough command length. */
-static int
-cmd_cmdsize (vector strvec)
+static void
+format_parser_handle_pipe(struct format_parser_state *state)
{
- unsigned int i;
- int size = 0;
- vector descvec;
- struct desc *desc;
+ struct cmd_token *keyword_token;
+ vector keyword_vect;
- for (i = 0; i < vector_active (strvec); i++)
- if ((descvec = vector_slot (strvec, i)) != NULL)
+ if (state->in_multiple)
{
- if ((vector_active (descvec)) == 1
- && (desc = vector_slot (descvec, 0)) != NULL)
- {
- if (desc->cmd == NULL || CMD_OPTION (desc->cmd))
- return size;
- else
- size++;
- }
- else
- size++;
+ 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 '|'");
+ }
+}
+
+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;
+ 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 size;
}
/* Return prompt character of specified node. */
@@ -497,11 +600,8 @@ install_element (enum node_type ntype, struct cmd_element *cmd)
}
vector_set (cnode->cmd_vector, cmd);
-
- if (cmd->strvec == NULL)
- cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc);
-
- cmd->cmdsize = cmd_cmdsize (cmd->strvec);
+ if (cmd->tokens == NULL)
+ cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
}
static const unsigned char itoa64[] =
@@ -847,9 +947,6 @@ cmd_ipv4_prefix_match (const char *str)
static enum match_type
cmd_ipv6_match (const char *str)
{
- int state = STATE_START;
- int colons = 0, nums = 0, double_colon = 0;
- const char *sp = NULL;
struct sockaddr_in6 sin6_dummy;
int ret;
@@ -1056,254 +1153,700 @@ cmd_range_match (const char *range, const char *str)
return 1;
}
-/* Make completion match and return match type flag. */
static enum match_type
-cmd_filter_by_completion (char *command, vector v, unsigned int index)
+cmd_word_match(struct cmd_token *token,
+ enum filter_type filter,
+ const char *word)
{
- unsigned int i;
const char *str;
- struct cmd_element *cmd_element;
enum match_type match_type;
- vector descvec;
- struct desc *desc;
-
- match_type = no_match;
- /* If command and cmd_element string does not match set NULL to vector */
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
- {
- if (index >= vector_active (cmd_element->strvec))
- vector_slot (v, i) = NULL;
- else
- {
- unsigned int j;
- int matched = 0;
+ str = token->cmd;
- descvec = vector_slot (cmd_element->strvec, index);
+ if (filter == FILTER_RELAXED)
+ if (!word || !strlen(word))
+ return partly_match;
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
-
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
+ if (!word)
+ return no_match;
- matched++;
- }
- }
+ 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))
- {
- if (cmd_ipv6_match (command))
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
+ 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))
+ {
+ 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)
+ || (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;
+ }
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command))
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
+ return no_match;
+}
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command))
- {
- if (match_type < ipv4_match)
- match_type = ipv4_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 */
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command))
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else
- /* Check is this point's argument optional ? */
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else if (strncmp (command, str, strlen (command)) == 0)
- {
- if (strcmp (command, str) == 0)
- match_type = exact_match;
- else
- {
- if (match_type < partly_match)
- match_type = partly_match;
- }
- matched++;
- }
- }
- if (!matched)
- vector_slot (v, i) = NULL;
- }
+ /* 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 (CMD_OPTION(token->cmd))
+ 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 (CMD_VARARG(token->cmd)
+ || CMD_VARIABLE(token->cmd)
+ || CMD_OPTION(token->cmd))
+ {
+ 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 (CMD_VARARG(token->cmd))
+ 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 match_type;
+
+ return MATCHER_OK;
}
-/* Filter vector by command character with index. */
-static enum match_type
-cmd_filter_by_string (char *command, vector v, unsigned int index)
+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;
+ 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;
+
+ 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;
+ }
+ 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
+ && (CMD_VARARG(word_token->cmd)
+ || CMD_VARIABLE(word_token->cmd)
+ || CMD_OPTION(word_token->cmd)))
+ || 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;
+
+ cmd_matcher_init(&matcher, cmd_element, filter,
+ vline, index, match_type, match);
+
+ if (argc != NULL)
+ *argc = 0;
+
+ for (token_index = 0;
+ token_index < vector_active(cmd_element->tokens);
+ token_index++)
+ {
+ struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
+
+ switch (token->type)
+ {
+ case TOKEN_TERMINAL:
+ rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
+ break;
+ case TOKEN_MULTIPLE:
+ rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
+ break;
+ case TOKEN_KEYWORD:
+ rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
+ }
+
+ if (MATCHER_ERROR(rv))
+ return rv;
+
+ if (matcher.word_index > index)
+ return MATCHER_OK;
+ }
+
+ /* return MATCHER_COMPLETE if all words were consumed */
+ if (matcher.word_index >= vector_active(vline))
+ return MATCHER_COMPLETE;
+
+ /* return MATCHER_COMPLETE also if only an empty word is left. */
+ if (matcher.word_index == vector_active(vline) - 1
+ && (!vector_slot(vline, matcher.word_index)
+ || !strlen((char*)vector_slot(vline, matcher.word_index))))
+ return MATCHER_COMPLETE;
+
+ return MATCHER_NO_MATCH; /* command is too long to match */
+}
+
+/**
+ * Filter a given vector of commands against a given commandline and
+ * calculate possible completions.
+ *
+ * @param commands A vector of struct cmd_element*. Commands that don't
+ * match against the given command line will be overwritten
+ * with NULL in that vector.
+ * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
+ * determines how incomplete commands are handled, compare with
+ * cmd_word_match for details.
+ * @param vline A vector of char* containing the tokenized commandline.
+ * @param index Only match up to the given token of the commandline.
+ * @param match_type Record the type of the best match here.
+ * @param matches Record the matches here. For each cmd_element in the commands
+ * vector, a match vector will be created in the matches vector.
+ * That vector will contain all struct command_token* of the
+ * cmd_element which matched against the given vline at the given
+ * index.
+ * @return A code specifying if an error occured. If all went right, it's
+ * CMD_SUCCESS.
+ */
+static int
+cmd_vector_filter(vector commands,
+ enum filter_type filter,
+ vector vline,
+ unsigned int index,
+ enum match_type *match_type,
+ vector *matches)
{
unsigned int i;
- const char *str;
struct cmd_element *cmd_element;
- enum match_type match_type;
- vector descvec;
- struct desc *desc;
+ enum match_type best_match;
+ enum match_type element_match;
+ enum matcher_rv matcher_rv;
- match_type = no_match;
+ best_match = no_match;
+ *matches = vector_init(VECTOR_MIN_SIZE);
- /* If command and cmd_element string does not match set NULL to vector */
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
+ for (i = 0; i < vector_active (commands); i++)
+ if ((cmd_element = vector_slot (commands, i)) != NULL)
{
- /* If given index is bigger than max string vector of command,
- set NULL */
- if (index >= vector_active (cmd_element->strvec))
- vector_slot (v, i) = NULL;
- else
- {
- unsigned int j;
- int matched = 0;
+ 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;
+}
- descvec = vector_slot (cmd_element->strvec, index);
+/**
+ * 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.
+ */
+static int
+cmd_is_complete(struct cmd_element *cmd_element,
+ vector vline)
+{
+ enum matcher_rv rv;
+
+ rv = cmd_element_match(cmd_element,
+ FILTER_RELAXED,
+ vline, -1,
+ NULL, NULL,
+ NULL, NULL);
+ return (rv == MATCHER_COMPLETE);
+}
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
+/**
+ * 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)
+ {
+ case MATCHER_COMPLETE:
+ return CMD_SUCCESS;
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command) == exact_match)
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command) == exact_match)
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command) == exact_match)
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command) == exact_match)
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else
- {
- if (strcmp (command, str) == 0)
- {
- match_type = exact_match;
- matched++;
- }
- }
- }
- if (!matched)
- vector_slot (v, i) = NULL;
- }
- }
- return match_type;
+ 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;
+ }
}
/* Check ambiguous match */
static int
-is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
+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;
- struct cmd_element *cmd_element;
const char *matched = NULL;
- vector descvec;
- struct desc *desc;
+ vector match_vector;
+ struct cmd_token *cmd_token;
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
+ if (command == NULL)
+ command = "";
+
+ for (i = 0; i < vector_active (matches); i++)
+ if ((match_vector = vector_slot (matches, i)) != NULL)
{
int match = 0;
- descvec = vector_slot (cmd_element->strvec, index);
-
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
+ for (j = 0; j < vector_active (match_vector); j++)
+ if ((cmd_token = vector_slot (match_vector, j)) != NULL)
{
enum match_type ret;
-
- str = desc->cmd;
+
+ assert(cmd_token->type == TOKEN_TERMINAL);
+ if (cmd_token->type != TOKEN_TERMINAL)
+ continue;
+
+ str = cmd_token->cmd;
switch (type)
{
@@ -1371,7 +1914,7 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
}
}
if (!match)
- vector_slot (v, i) = NULL;
+ vector_slot (cmd_vector, i) = NULL;
}
return 0;
}
@@ -1461,8 +2004,12 @@ cmd_entry_function_desc (const char *src, const char *dst)
return NULL;
}
-/* Check same string element existence. If it isn't there return
- 1. */
+/**
+ * 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)
{
@@ -1476,19 +2023,25 @@ cmd_unique_string (vector v, const char *str)
return 1;
}
-/* Compare string to description vector. If there is same string
- return 1 else return 0. */
+/**
+ * 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.
+ */
static int
desc_unique_string (vector v, const char *str)
{
unsigned int i;
- struct desc *desc;
+ struct cmd_token *token;
for (i = 0; i < vector_active (v); i++)
- if ((desc = vector_slot (v, i)) != NULL)
- if (strcmp (desc->cmd, str) == 0)
- return 1;
- return 0;
+ if ((token = vector_slot (v, i)) != NULL)
+ if (strcmp (token->cmd, str) == 0)
+ return 0;
+ return 1;
}
static int
@@ -1504,6 +2057,35 @@ cmd_try_do_shortcut (enum node_type node, char* first_word) {
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)
@@ -1517,6 +2099,8 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
int ret;
enum match_type match;
char *command;
+ vector matches = NULL;
+ vector match_vector;
/* Set index. */
if (vector_active (vline) == 0)
@@ -1524,111 +2108,121 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
*status = CMD_ERR_NO_MATCH;
return NULL;
}
- else
- index = vector_active (vline) - 1;
-
+
+ index = vector_active (vline) - 1;
+
/* Make copy vector of current node's command vector. */
cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
/* Prepare match vector */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
- /* Filter commands. */
- /* Only words precedes current word will be checked in this loop. */
- for (i = 0; i < index; i++)
- if ((command = vector_slot (vline, i)))
- {
- match = cmd_filter_by_completion (command, cmd_vector, i);
-
- if (match == vararg_match)
- {
- struct cmd_element *cmd_element;
- vector descvec;
- unsigned int j, k;
+ /* Filter commands and build a list how they could possibly continue. */
+ for (i = 0; i <= index; i++)
+ {
+ command = vector_slot (vline, i);
- for (j = 0; j < vector_active (cmd_vector); j++)
- if ((cmd_element = vector_slot (cmd_vector, j)) != NULL
- && (vector_active (cmd_element->strvec)))
- {
- descvec = vector_slot (cmd_element->strvec,
- vector_active (cmd_element->strvec) - 1);
- for (k = 0; k < vector_active (descvec); k++)
- {
- struct desc *desc = vector_slot (descvec, k);
- vector_set (matchvec, desc);
- }
- }
-
- vector_set (matchvec, &desc_cr);
- vector_free (cmd_vector);
+ if (matches)
+ cmd_matches_free(&matches);
- return matchvec;
- }
+ ret = cmd_vector_filter(cmd_vector,
+ FILTER_RELAXED,
+ vline, i,
+ &match,
+ &matches);
- if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1)
- {
- vector_free (cmd_vector);
- vector_free (matchvec);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- }
- else if (ret == 2)
- {
- vector_free (cmd_vector);
- vector_free (matchvec);
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
- }
+ if (ret != CMD_SUCCESS)
+ {
+ vector_free (cmd_vector);
+ vector_free (matchvec);
+ cmd_matches_free(&matches);
+ *status = ret;
+ return NULL;
+ }
- /* Prepare match vector */
- /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
+ /* 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;
+ }
- /* Make sure that cmd_vector is filtered based on current word */
- command = vector_slot (vline, index);
- if (command)
- match = cmd_filter_by_completion (command, cmd_vector, index);
+ ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
+ if (ret == 1)
+ {
+ vector_free (cmd_vector);
+ vector_free (matchvec);
+ cmd_matches_free(&matches);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ }
+ else if (ret == 2)
+ {
+ vector_free (cmd_vector);
+ vector_free (matchvec);
+ cmd_matches_free(&matches);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ }
/* Make description vector. */
- for (i = 0; i < vector_active (cmd_vector); i++)
+ for (i = 0; i < vector_active (matches); i++)
if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
{
- vector strvec = cmd_element->strvec;
+ unsigned int j;
+ const char *last_word;
+ vector vline_trimmed;
- /* if command is NULL, index may be equal to vector_active */
- if (command && index >= vector_active (strvec))
- vector_slot (cmd_vector, i) = NULL;
- else
- {
- /* Check if command is completed. */
- if (command == NULL && index == vector_active (strvec))
- {
- if (!desc_unique_string (matchvec, command_cr))
- vector_set (matchvec, &desc_cr);
- }
- else
- {
- unsigned int j;
- vector descvec = vector_slot (strvec, index);
- struct desc *desc;
-
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- const char *string;
-
- string = cmd_entry_function_desc (command, desc->cmd);
- if (string)
- {
- /* Uniqueness check */
- if (!desc_unique_string (matchvec, string))
- vector_set (matchvec, desc);
- }
- }
- }
- }
+ 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->cmd);
+ if (string && desc_unique_string(matchvec, string))
+ vector_set(matchvec, token);
+ }
}
vector_free (cmd_vector);
+ cmd_matches_free(&matches);
if (vector_slot (matchvec, 0) == NULL)
{
@@ -1638,6 +2232,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
}
*status = CMD_SUCCESS;
+ cmd_describe_sort(matchvec);
return matchvec;
}
@@ -1708,6 +2303,31 @@ cmd_lcd (char **matched)
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)
@@ -1716,13 +2336,13 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
#define INIT_MATCHVEC_SIZE 10
vector matchvec;
- struct cmd_element *cmd_element;
unsigned int index;
char **match_str;
- struct desc *desc;
- vector descvec;
+ struct cmd_token *token;
char *command;
int lcd;
+ vector matches = NULL;
+ vector match_vector;
if (vector_active (vline) == 0)
{
@@ -1733,66 +2353,80 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
else
index = vector_active (vline) - 1;
- /* First, filter by preceeding command string */
- for (i = 0; i < index; i++)
- if ((command = vector_slot (vline, i)))
- {
- enum match_type match;
- int ret;
+ /* First, filter by command string */
+ for (i = 0; i <= index; i++)
+ {
+ command = vector_slot (vline, i);
+ enum match_type match;
+ int ret;
- /* First try completion match, if there is exactly match return 1 */
- match = cmd_filter_by_completion (command, cmd_vector, i);
+ if (matches)
+ cmd_matches_free(&matches);
- /* If there is exact match then filter ambiguous match else check
- ambiguousness. */
- if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1)
- {
- vector_free (cmd_vector);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- }
- /*
+ /* 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;
+ }
+ /*
else if (ret == 2)
{
vector_free (cmd_vector);
+ cmd_matches_free(&matches);
*status = CMD_ERR_NO_MATCH;
return NULL;
}
*/
- }
+ }
/* Prepare match vector. */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
- /* Now we got into completion */
- for (i = 0; i < vector_active (cmd_vector); i++)
- if ((cmd_element = vector_slot (cmd_vector, i)))
+ /* 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;
- vector strvec = cmd_element->strvec;
+ unsigned int j;
- /* Check field length */
- if (index >= vector_active (strvec))
- vector_slot (cmd_vector, i) = NULL;
- else
- {
- unsigned int j;
-
- descvec = vector_slot (strvec, index);
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
+ 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),
- desc->cmd)))
+ token->cmd)))
if (cmd_unique_string (matchvec, string))
vector_set (matchvec, XSTRDUP (MTYPE_TMP, string));
}
- }
}
/* 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)
@@ -1832,7 +2466,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
{
char *lcdstr;
- lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
+ lcdstr = XMALLOC (MTYPE_TMP, lcd + 1);
memcpy (lcdstr, matchvec->index[0], lcd);
lcdstr[lcd] = '\0';
@@ -1842,7 +2476,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
for (i = 0; i < vector_active (matchvec); i++)
{
if (vector_slot (matchvec, i))
- XFREE (MTYPE_STRVEC, vector_slot (matchvec, i));
+ XFREE (MTYPE_TMP, vector_slot (matchvec, i));
}
vector_free (matchvec);
@@ -1859,6 +2493,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
}
match_str = (char **) matchvec->index;
+ cmd_complete_sort(matchvec);
vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_LIST_MATCH;
return match_str;
@@ -1927,7 +2562,9 @@ node_parent ( enum node_type node )
/* Execute command by argument vline vector. */
static int
-cmd_execute_command_real (vector vline, struct vty *vty,
+cmd_execute_command_real (vector vline,
+ enum filter_type filter,
+ struct vty *vty,
struct cmd_element **cmd)
{
unsigned int i;
@@ -1939,35 +2576,48 @@ cmd_execute_command_real (vector vline, struct vty *vty,
int argc;
const char *argv[CMD_ARGC_MAX];
enum match_type match = 0;
- int varflag;
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++)
- if ((command = vector_slot (vline, index)))
- {
- int ret;
-
- match = cmd_filter_by_completion (command, cmd_vector, 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)
+ if (match == vararg_match)
+ {
+ cmd_matches_free(&matches);
break;
-
- ret = is_cmd_ambiguous (command, cmd_vector, index, match);
+ }
- if (ret == 1)
- {
- vector_free (cmd_vector);
- return CMD_ERR_AMBIGUOUS;
- }
- else if (ret == 2)
- {
- vector_free (cmd_vector);
- return CMD_ERR_NO_MATCH;
- }
- }
+ 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;
@@ -1977,12 +2627,9 @@ cmd_execute_command_real (vector vline, struct vty *vty,
for (i = 0; i < vector_active (cmd_vector); i++)
if ((cmd_element = vector_slot (cmd_vector, i)))
{
- if (match == vararg_match || index >= cmd_element->cmdsize)
+ if (cmd_is_complete(cmd_element, vline))
{
matched_element = cmd_element;
-#if 0
- printf ("DEBUG: %s\n", cmd_element->string);
-#endif
matched_count++;
}
else
@@ -2006,35 +2653,9 @@ cmd_execute_command_real (vector vline, struct vty *vty,
if (matched_count > 1)
return CMD_ERR_AMBIGUOUS;
- /* Argument treatment */
- varflag = 0;
- argc = 0;
-
- for (i = 0; i < vector_active (vline); i++)
- {
- if (varflag)
- argv[argc++] = vector_slot (vline, i);
- else
- {
- vector descvec = vector_slot (matched_element->strvec, i);
-
- if (vector_active (descvec) == 1)
- {
- struct desc *desc = vector_slot (descvec, 0);
-
- if (CMD_VARARG (desc->cmd))
- varflag = 1;
-
- if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd))
- argv[argc++] = vector_slot (vline, i);
- }
- else
- argv[argc++] = vector_slot (vline, i);
- }
-
- if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXEED_ARGC_MAX;
- }
+ ret = cmd_parse(matched_element, vline, &argc, argv);
+ if (ret != CMD_SUCCESS)
+ return ret;
/* For vtysh execution. */
if (cmd)
@@ -2047,6 +2668,21 @@ cmd_execute_command_real (vector vline, struct vty *vty,
return (*matched_element->func) (matched_element, vty, argc, argv);
}
+/**
+ * Execute a given command, handling things like "do ..." and checking
+ * whether the given command might apply at a parent node if doesn't
+ * apply for the current node.
+ *
+ * @param vline Command line input, vector of char* where each element is
+ * one input token.
+ * @param vty The vty context in which the command should be executed.
+ * @param cmd Pointer where the struct cmd_element of the matched command
+ * will be stored, if any. May be set to NULL if this info is
+ * not needed.
+ * @param vtysh If set != 0, don't lookup the command at parent nodes.
+ * @return The status of the command that has been executed or an error code
+ * as to why no command could be executed.
+ */
int
cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
int vtysh) {
@@ -2070,7 +2706,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
- ret = cmd_execute_command_real (shifted_vline, vty, cmd);
+ ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
vector_free(shifted_vline);
vty->node = onode;
@@ -2078,7 +2714,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
}
- saved_ret = ret = cmd_execute_command_real (vline, vty, cmd);
+ saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
if (vtysh)
return saved_ret;
@@ -2089,7 +2725,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
{
try_node = node_parent(try_node);
vty->node = try_node;
- ret = cmd_execute_command_real (vline, vty, cmd);
+ ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
tried = 1;
if (ret == CMD_SUCCESS || ret == CMD_WARNING)
{
@@ -2104,123 +2740,24 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
return saved_ret;
}
-/* Execute command by argument readline. */
+/**
+ * Execute a given command, matching it strictly against the current node.
+ * This mode is used when reading config files.
+ *
+ * @param vline Command line input, vector of char* where each element is
+ * one input token.
+ * @param vty The vty context in which the command should be executed.
+ * @param cmd Pointer where the struct cmd_element* of the matched command
+ * will be stored, if any. May be set to NULL if this info is
+ * not needed.
+ * @return The status of the command that has been executed or an error code
+ * as to why no command could be executed.
+ */
int
cmd_execute_command_strict (vector vline, 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];
- int varflag;
- enum match_type match = 0;
- char *command;
-
- /* Make copy of command element */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
-
- for (index = 0; index < vector_active (vline); index++)
- if ((command = vector_slot (vline, index)))
- {
- int ret;
-
- match = cmd_filter_by_string (vector_slot (vline, index),
- cmd_vector, index);
-
- /* If command meets '.VARARG' then finish matching. */
- if (match == vararg_match)
- break;
-
- ret = is_cmd_ambiguous (command, cmd_vector, index, match);
- if (ret == 1)
- {
- vector_free (cmd_vector);
- return CMD_ERR_AMBIGUOUS;
- }
- 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 (vector_slot (cmd_vector, i) != NULL)
- {
- cmd_element = vector_slot (cmd_vector, i);
-
- if (match == vararg_match || index >= cmd_element->cmdsize)
- {
- 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;
- }
-
- if (matched_count > 1)
- return CMD_ERR_AMBIGUOUS;
-
- /* Argument treatment */
- varflag = 0;
- argc = 0;
-
- for (i = 0; i < vector_active (vline); i++)
- {
- if (varflag)
- argv[argc++] = vector_slot (vline, i);
- else
- {
- vector descvec = vector_slot (matched_element->strvec, i);
-
- if (vector_active (descvec) == 1)
- {
- struct desc *desc = vector_slot (descvec, 0);
-
- if (CMD_VARARG (desc->cmd))
- varflag = 1;
-
- if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd))
- argv[argc++] = vector_slot (vline, i);
- }
- else
- argv[argc++] = vector_slot (vline, i);
- }
-
- if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXEED_ARGC_MAX;
- }
-
- /* For vtysh execution. */
- if (cmd)
- *cmd = matched_element;
-
- if (matched_element->daemon)
- return CMD_SUCCESS_DAEMON;
-
- /* Now execute matched command */
- return (*matched_element->func) (matched_element, vty, argc, argv);
+ return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
}
/* Configration make from file. */
@@ -3461,9 +3998,10 @@ install_default (enum node_type node)
void
cmd_init (int terminal)
{
- command_cr = XSTRDUP(MTYPE_STRVEC, "<cr>");
- desc_cr.cmd = command_cr;
- desc_cr.str = XSTRDUP(MTYPE_STRVEC, "");
+ command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
+ token_cr.type = TOKEN_TERMINAL;
+ token_cr.cmd = command_cr;
+ token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
/* Allocate initial top vector of commands. */
cmdvec = vector_init (VECTOR_MIN_SIZE);
@@ -3584,14 +4122,61 @@ cmd_init (int terminal)
srand(time(NULL));
}
+static void
+cmd_terminate_token(struct cmd_token *token)
+{
+ unsigned int i, j;
+ vector keyword_vect;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ XFREE(MTYPE_CMD_TOKENS, token->cmd);
+ XFREE(MTYPE_CMD_TOKENS, token->desc);
+
+ XFREE(MTYPE_CMD_TOKENS, token);
+}
+
+static void
+cmd_terminate_element(struct cmd_element *cmd)
+{
+ unsigned int i;
+
+ if (cmd->tokens == NULL)
+ return;
+
+ for (i = 0; i < vector_active(cmd->tokens); i++)
+ cmd_terminate_token(vector_slot(cmd->tokens, i));
+
+ vector_free(cmd->tokens);
+ cmd->tokens = NULL;
+}
+
void
cmd_terminate ()
{
- unsigned int i, j, k, l;
+ unsigned int i, j;
struct cmd_node *cmd_node;
struct cmd_element *cmd_element;
- struct desc *desc;
- vector cmd_node_v, cmd_element_v, desc_v;
+ vector cmd_node_v;
if (cmdvec)
{
@@ -3601,30 +4186,8 @@ cmd_terminate ()
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_element->strvec != NULL)
- {
- cmd_element_v = cmd_element->strvec;
-
- for (k = 0; k < vector_active (cmd_element_v); k++)
- if ((desc_v = vector_slot (cmd_element_v, k)) != NULL)
- {
- for (l = 0; l < vector_active (desc_v); l++)
- if ((desc = vector_slot (desc_v, l)) != NULL)
- {
- if (desc->cmd)
- XFREE (MTYPE_STRVEC, desc->cmd);
- if (desc->str)
- XFREE (MTYPE_STRVEC, desc->str);
-
- XFREE (MTYPE_DESC, desc);
- }
- vector_free (desc_v);
- }
-
- cmd_element->strvec = NULL;
- vector_free (cmd_element_v);
- }
+ if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
+ cmd_terminate_element(cmd_element);
vector_free (cmd_node_v);
}
@@ -3634,9 +4197,9 @@ cmd_terminate ()
}
if (command_cr)
- XFREE(MTYPE_STRVEC, command_cr);
- if (desc_cr.str)
- XFREE(MTYPE_STRVEC, desc_cr.str);
+ XFREE(MTYPE_CMD_TOKENS, command_cr);
+ if (token_cr.desc)
+ XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
if (host.name)
XFREE (MTYPE_HOST, host.name);
if (host.password)
diff --git a/lib/command.h b/lib/command.h
index 2d708d8e..e47c4255 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -138,18 +138,32 @@ struct cmd_element
int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
const char *doc; /* Documentation of this command. */
int daemon; /* Daemon to which this command belong. */
- vector strvec; /* Pointing out each description vector. */
- unsigned int cmdsize; /* Command index count. */
- char *config; /* Configuration string */
- vector subconfig; /* Sub configuration string */
+ vector tokens; /* Vector of cmd_tokens */
u_char attr; /* Command attributes */
};
+
+enum cmd_token_type
+{
+ TOKEN_TERMINAL = 0,
+ TOKEN_MULTIPLE,
+ TOKEN_KEYWORD,
+};
+
/* Command description structure. */
-struct desc
+struct cmd_token
{
+ enum cmd_token_type type;
+
+ /* Used for type == MULTIPLE */
+ vector multiple; /* vector of cmd_token, type == FINAL */
+
+ /* Used for type == KEYWORD */
+ vector keyword; /* vector of vector of cmd_tokens */
+
+ /* Used for type == TERMINAL */
char *cmd; /* Command string. */
- char *str; /* Command's description. */
+ char *desc; /* Command's description. */
};
/* Return value of the commands. */
@@ -192,7 +206,170 @@ struct desc
int argc __attribute__ ((unused)), \
const char *argv[] __attribute__ ((unused)) )
-/* DEFUN for vty command interafce. Little bit hacky ;-). */
+/* DEFUN for vty command interafce. Little bit hacky ;-).
+ *
+ * DEFUN(funcname, cmdname, cmdstr, helpstr)
+ *
+ * funcname
+ * ========
+ *
+ * Name of the function that will be defined.
+ *
+ * cmdname
+ * =======
+ *
+ * Name of the struct that will be defined for the command.
+ *
+ * cmdstr
+ * ======
+ *
+ * The cmdstr defines the command syntax. It is used by the vty subsystem
+ * and vtysh to perform matching and completion in the cli. So you have to take
+ * care to construct it adhering to the following grammar. The names used
+ * for the production rules losely represent the names used in lib/command.c
+ *
+ * cmdstr = cmd_token , { " " , cmd_token } ;
+ *
+ * cmd_token = cmd_terminal
+ * | cmd_multiple
+ * | cmd_keyword ;
+ *
+ * cmd_terminal_fixed = fixed_string
+ * | variable
+ * | range
+ * | ipv4
+ * | ipv4_prefix
+ * | ipv6
+ * | ipv6_prefix ;
+ *
+ * cmd_terminal = cmd_terminal_fixed
+ * | option
+ * | vararg ;
+ *
+ * multiple_part = cmd_terminal_fixed ;
+ * cmd_multiple = "(" , multiple_part , ( "|" | { "|" , multiple_part } ) , ")" ;
+ *
+ * keyword_part = fixed_string , { " " , ( cmd_terminal_fixed | cmd_multiple ) } ;
+ * cmd_keyword = "{" , keyword_part , { "|" , keyword_part } , "}" ;
+ *
+ * lowercase = "a" | ... | "z" ;
+ * uppercase = "A" | ... | "Z" ;
+ * digit = "0" | ... | "9" ;
+ * number = digit , { digit } ;
+ *
+ * fixed_string = (lowercase | digit) , { lowercase | digit | uppercase | "-" | "_" } ;
+ * variable = uppercase , { uppercase | "_" } ;
+ * range = "<" , number , "-" , number , ">" ;
+ * ipv4 = "A.B.C.D" ;
+ * ipv4_prefix = "A.B.C.D/M" ;
+ * ipv6 = "X:X::X:X" ;
+ * ipv6_prefix = "X:X::X:X/M" ;
+ * option = "[" , variable , "]" ;
+ * vararg = "." , variable ;
+ *
+ * To put that all in a textual description: A cmdstr is a sequence of tokens,
+ * separated by spaces.
+ *
+ * Terminal Tokens:
+ *
+ * A very simple cmdstring would be something like: "show ip bgp". It consists
+ * of three Terminal Tokens, each containing a fixed string. When this command
+ * is called, no arguments will be passed down to the function implementing it,
+ * as it only consists of fixed strings.
+ *
+ * Apart from fixed strings, Terminal Tokens can also contain variables:
+ * An example would be "show ip bgp A.B.C.D". This command expects an IPv4
+ * as argument. As this is a variable, the IP address entered by the user will
+ * be passed down as an argument. Apart from two exceptions, the other options
+ * for Terminal Tokens behave exactly as we just discussed and only make a
+ * difference for the CLI. The two exceptions will be discussed in the next
+ * paragraphs.
+ *
+ * A Terminal Token can contain a so called option match. This is a simple
+ * string variable that the user may omit. An example would be:
+ * "show interface [IFNAME]". If the user calls this without an interface as
+ * argument, no arguments will be passed down to the function implementing
+ * this command. Otherwise, the interface name will be provided to the function
+ * as a regular argument.
+
+ * Also, a Terminal Token can contain a so called vararg. This is used e.g. in
+ * "show ip bgp regexp .LINE". The last token is a vararg match and will
+ * consume all the arguments the user inputs on the command line and append
+ * those to the list of arguments passed down to the function implementing this
+ * command. (Therefore, it doesn't make much sense to have any tokens after a
+ * vararg because the vararg will already consume all the words the user entered
+ * in the CLI)
+ *
+ * Multiple Tokens:
+ *
+ * The Multiple Token type can be used if there are multiple possibilities what
+ * arguments may be used for a command, but it should map to the same function
+ * nonetheless. An example would be "ip route A.B.C.D/M (reject|blackhole)"
+ * In that case both "reject" and "blackhole" would be acceptable as last
+ * arguments. The words matched by Multiple Tokens are always added to the
+ * argument list, even if they are matched by fixed strings. Such a Multiple
+ * Token can contain almost any type of token that would also be acceptable
+ * for a Terminal Token, the exception are optional variables and varag.
+ *
+ * There is one special case that is used in some places of Quagga that should be
+ * pointed out here shortly. An example would be "password (8|) WORD". This
+ * construct is used to have fixed strings communicated as arguments. (The "8"
+ * will be passed down as an argument in this case) It does not mean that
+ * the "8" is optional. Another historic and possibly surprising property of
+ * this construct is that it consumes two parts of helpstr. (Help
+ * strings will be explained later)
+ *
+ * Keyword Tokens:
+ *
+ * There are commands that take a lot of different and possibly optional arguments.
+ * An example from ospf would be the "default-information originate" command. This
+ * command takes a lot of optional arguments that may be provided in any order.
+ * To accomodate such commands, the Keyword Token has been implemented.
+ * Using the keyword token, the "default-information originate" command and all
+ * its possible options can be represented using this single cmdstr:
+ * "default-information originate \
+ * {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}"
+ *
+ * Keywords always start with a fixed string and may be followed by arguments.
+ * Except optional variables and vararg, everything is permitted here.
+ *
+ * For the special case of a keyword without arguments, either NULL or the
+ * keyword itself will be pushed as an argument, depending on whether the
+ * keyword is present.
+ * For the other keywords, arguments will be only pushed for
+ * variables/Multiple Tokens. If the keyword is not present, the arguments that
+ * would have been pushed will be substituted by NULL.
+ *
+ * A few examples:
+ * "default information originate metric-type 1 metric 1000"
+ * would yield the following arguments:
+ * { NULL, "1000", "1", NULL }
+ *
+ * "default information originate always route-map RMAP-DEFAULT"
+ * would yield the following arguments:
+ * { "always", NULL, NULL, "RMAP-DEFAULT" }
+ *
+ * helpstr
+ * =======
+ *
+ * The helpstr is used to show a short explantion for the commands that
+ * are available when the user presses '?' on the CLI. It is the concatenation
+ * of the helpstrings for all the tokens that make up the command.
+ *
+ * There should be one helpstring for each token in the cmdstr except those
+ * containing other tokens, like Multiple or Keyword Tokens. For those, there
+ * will only be the helpstrings of the contained tokens.
+ *
+ * The individual helpstrings are expected to be in the same order as their
+ * respective Tokens appear in the cmdstr. They should each be terminated with
+ * a linefeed. The last helpstring should be terminated with a linefeed as well.
+ *
+ * Care should also be taken to avoid having similar tokens with different
+ * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp".
+ * they both contain a helpstring for "show", but only one will be displayed
+ * when the user enters "sh?". If those two helpstrings differ, it is not
+ * defined which one will be shown and the behavior is therefore unpredictable.
+ */
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
@@ -330,7 +507,6 @@ struct desc
extern void install_node (struct cmd_node *, int (*) (struct vty *));
extern void install_default (enum node_type);
extern void install_element (enum node_type, struct cmd_element *);
-extern void sort_node (void);
/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
string with a space between each element (allocated using
@@ -346,7 +522,6 @@ extern int config_from_file (struct vty *, FILE *);
extern enum node_type node_parent (enum node_type);
extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int);
extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **);
-extern void config_replace_string (struct cmd_element *, char *, ...);
extern void cmd_init (int);
extern void cmd_terminate (void);
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 50b6fa42..47a34387 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -54,7 +54,7 @@ struct memory_list memory_list_lib[] =
{ MTYPE_ROUTE_MAP_RULE, "Route map rule" },
{ MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" },
{ MTYPE_ROUTE_MAP_COMPILED, "Route map compiled" },
- { MTYPE_DESC, "Command desc" },
+ { MTYPE_CMD_TOKENS, "Command desc" },
{ MTYPE_KEY, "Key" },
{ MTYPE_KEYCHAIN, "Key chain" },
{ MTYPE_IF_RMAP, "Interface route map" },
diff --git a/lib/vty.c b/lib/vty.c
index 0d6345c8..5b6fb0e3 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -931,23 +931,23 @@ vty_complete_command (struct vty *vty)
static void
vty_describe_fold (struct vty *vty, int cmd_width,
- unsigned int desc_width, struct desc *desc)
+ unsigned int desc_width, struct cmd_token *token)
{
char *buf;
const char *cmd, *p;
int pos;
- cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+ cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd;
if (desc_width <= 0)
{
- vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE);
+ vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE);
return;
}
- buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+ buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1);
- for (p = desc->str; strlen (p) > desc_width; p += pos + 1)
+ for (p = token->desc; strlen (p) > desc_width; p += pos + 1)
{
for (pos = desc_width; pos > 0; pos--)
if (*(p + pos) == ' ')
@@ -976,7 +976,7 @@ vty_describe_command (struct vty *vty)
vector vline;
vector describe;
unsigned int i, width, desc_width;
- struct desc *desc, *desc_cr = NULL;
+ struct cmd_token *token, *token_cr = NULL;
vline = cmd_make_strvec (vty->buf);
@@ -1010,15 +1010,15 @@ vty_describe_command (struct vty *vty)
/* Get width of command string. */
width = 0;
for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
+ if ((token = vector_slot (describe, i)) != NULL)
{
unsigned int len;
- if (desc->cmd[0] == '\0')
+ if (token->cmd[0] == '\0')
continue;
- len = strlen (desc->cmd);
- if (desc->cmd[0] == '.')
+ len = strlen (token->cmd);
+ if (token->cmd[0] == '.')
len--;
if (width < len)
@@ -1030,27 +1030,27 @@ vty_describe_command (struct vty *vty)
/* Print out description. */
for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
+ if ((token = vector_slot (describe, i)) != NULL)
{
- if (desc->cmd[0] == '\0')
+ if (token->cmd[0] == '\0')
continue;
- if (strcmp (desc->cmd, command_cr) == 0)
+ if (strcmp (token->cmd, command_cr) == 0)
{
- desc_cr = desc;
+ token_cr = token;
continue;
}
- if (!desc->str)
+ if (!token->desc)
vty_out (vty, " %-s%s",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
VTY_NEWLINE);
- else if (desc_width >= strlen (desc->str))
+ else if (desc_width >= strlen (token->desc))
vty_out (vty, " %-*s %s%s", width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str, VTY_NEWLINE);
+ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+ token->desc, VTY_NEWLINE);
else
- vty_describe_fold (vty, width, desc_width, desc);
+ vty_describe_fold (vty, width, desc_width, token);
#if 0
vty_out (vty, " %-*s %s%s", width
@@ -1059,18 +1059,18 @@ vty_describe_command (struct vty *vty)
#endif /* 0 */
}
- if ((desc = desc_cr))
+ if ((token = token_cr))
{
- if (!desc->str)
+ if (!token->desc)
vty_out (vty, " %-s%s",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
VTY_NEWLINE);
- else if (desc_width >= strlen (desc->str))
+ else if (desc_width >= strlen (token->desc))
vty_out (vty, " %-*s %s%s", width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str, VTY_NEWLINE);
+ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+ token->desc, VTY_NEWLINE);
else
- vty_describe_fold (vty, width, desc_width, desc);
+ vty_describe_fold (vty, width, desc_width, token);
}
out:
diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c
index 17d7654e..090d2934 100644
--- a/ospf6d/ospf6_main.c
+++ b/ospf6d/ospf6_main.c
@@ -319,9 +319,6 @@ main (int argc, char *argv[], char *envp[])
/* initialize ospf6 */
ospf6_init ();
- /* sort command vector */
- sort_node ();
-
/* parse config file */
vty_read_config (config_file, config_default);
diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
index 6d58b4ea..c68aa4dd 100644
--- a/ospfd/ospf_main.c
+++ b/ospfd/ospf_main.c
@@ -310,8 +310,6 @@ main (int argc, char **argv)
ospf_opaque_init ();
#endif /* HAVE_OPAQUE_LSA */
- sort_node ();
-
/* Get configuration file. */
vty_read_config (config_file, config_default);
diff --git a/ripd/rip_main.c b/ripd/rip_main.c
index 6a9fa71d..a512fbc2 100644
--- a/ripd/rip_main.c
+++ b/ripd/rip_main.c
@@ -287,9 +287,6 @@ main (int argc, char **argv)
rip_zclient_init ();
rip_peer_init ();
- /* Sort all installed commands. */
- sort_node ();
-
/* Get configuration file. */
vty_read_config (config_file, config_default);
diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
index 20225b7d..7525a267 100644
--- a/ripngd/ripng_main.c
+++ b/ripngd/ripng_main.c
@@ -282,9 +282,6 @@ main (int argc, char **argv)
zebra_init ();
ripng_peer_init ();
- /* Sort all installed commands. */
- sort_node ();
-
/* Get configuration file. */
vty_read_config (config_file, config_default);
diff --git a/tests/main.c b/tests/main.c
index e0fbb4d5..2d8cb0c5 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -171,8 +171,6 @@ main (int argc, char **argv)
/* OSPF vty inits. */
test_vty_init ();
- sort_node ();
-
/* Change to the daemon program. */
if (daemon_mode && daemon (0, 0) < 0)
{
diff --git a/tests/test-commands.c b/tests/test-commands.c
index e2f40c6a..18b3b50d 100644
--- a/tests/test-commands.c
+++ b/tests/test-commands.c
@@ -233,8 +233,6 @@ test_init(void)
cmd->daemon = 0;
cmd->func = test_callback;
}
- sort_node();
-
test_load();
vty_init_vtysh();
}
@@ -340,8 +338,8 @@ test_run(struct prng *prng, struct vty *vty, const char *cmd, unsigned int edit_
{
for (j = 0; j < vector_active(descriptions); j++)
{
- struct desc *cmd = vector_slot(descriptions, j);
- printf(" '%s' '%s'\n", cmd->cmd, cmd->str);
+ struct cmd_token *cmd = vector_slot(descriptions, j);
+ printf(" '%s' '%s'\n", cmd->cmd, cmd->desc);
}
vector_free(descriptions);
}
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index c575902c..34c3bd62 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -554,7 +554,7 @@ vtysh_rl_describe (void)
vector vline;
vector describe;
int width;
- struct desc *desc;
+ struct cmd_token *token;
vline = cmd_make_strvec (rl_line_buffer);
@@ -592,15 +592,15 @@ vtysh_rl_describe (void)
/* Get width of command string. */
width = 0;
for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
+ if ((token = vector_slot (describe, i)) != NULL)
{
int len;
- if (desc->cmd[0] == '\0')
+ if (token->cmd[0] == '\0')
continue;
- len = strlen (desc->cmd);
- if (desc->cmd[0] == '.')
+ len = strlen (token->cmd);
+ if (token->cmd[0] == '.')
len--;
if (width < len)
@@ -608,19 +608,19 @@ vtysh_rl_describe (void)
}
for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
+ if ((token = vector_slot (describe, i)) != NULL)
{
- if (desc->cmd[0] == '\0')
+ if (token->cmd[0] == '\0')
continue;
- if (! desc->str)
+ if (! token->desc)
fprintf (stdout," %-s\n",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd);
+ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd);
else
fprintf (stdout," %-*s %s\n",
width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str);
+ token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
+ token->desc);
}
cmd_free_strvec (vline);
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index 4a315a5c..48958f0f 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -299,8 +299,6 @@ main (int argc, char **argv, char **env)
vty_init_vtysh ();
- sort_node ();
-
/* Read vtysh configuration file before connecting to daemons. */
vtysh_read_config (config_default);
diff --git a/zebra/main.c b/zebra/main.c
index 742e0292..523b1911 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -343,9 +343,6 @@ main (int argc, char **argv)
interface_list ();
route_read ();
- /* Sort VTY commands. */
- sort_node ();
-
#ifdef HAVE_SNMP
zebra_snmp_init ();
#endif /* HAVE_SNMP */
diff --git a/zebra/test_main.c b/zebra/test_main.c
index a9518637..c6951729 100644
--- a/zebra/test_main.c
+++ b/zebra/test_main.c
@@ -298,9 +298,6 @@ main (int argc, char **argv)
route_read ();
zebra_vty_init();
- /* Sort VTY commands. */
- sort_node ();
-
/* Configuration file read*/
vty_read_config (config_file, config_default);