summaryrefslogtreecommitdiffstats
path: root/lib/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/command.c')
-rw-r--r--lib/command.c1383
1 files changed, 860 insertions, 523 deletions
diff --git a/lib/command.c b/lib/command.c
index 2c9ca64a..251c8963 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -30,6 +30,8 @@ Boston, MA 02111-1307, USA. */
#include "thread.h"
#include "vector.h"
#include "vty.h"
+#include "uty.h"
+#include "qstring.h"
#include "command.h"
#include "workqueue.h"
#include "command_queue.h"
@@ -242,10 +244,29 @@ sort_node ()
}
}
-/* 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. */
-vector
+/*------------------------------------------------------------------------------
+ * Take string and break it into tokens.
+ *
+ * Discards leading and trailing white-space.
+ *
+ * Treats lines that start with '!' or '#' (after any leading white-space)
+ * as empty -- these are comment lines.
+ *
+ * Tokens are non-whitespace separated by one or more white-space.
+ *
+ * White-space is anything that isspace() thinks is a space. (Which in the
+ * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.)
+ *
+ * Returns: NULL => empty line (after white-space trimming) or comment line.
+ * otherwise: is vector containing one or more tokens.
+ *
+ * Note: all the tokens in the vector have at least one character, and no
+ * entries are NULL.
+ *
+ * NB: it is the caller's responsibility to release the vector and its contents,
+ * see cmd_free_strvec().
+ */
+extern vector
cmd_make_strvec (const char *string)
{
const char *cp, *start;
@@ -258,58 +279,69 @@ cmd_make_strvec (const char *string)
cp = string;
- /* Skip white spaces. */
- while (isspace ((int) *cp) && *cp != '\0')
+ /* Skip white spaces. */
+ while (isspace((int) *cp))
cp++;
- /* Return if there is only white spaces */
- if (*cp == '\0')
+ /* Return if line is empty or effectively empty */
+ if ((*cp == '\0') || (*cp == '!') || (*cp == '#'))
return NULL;
- if (*cp == '!' || *cp == '#')
- return NULL;
+ /* Prepare return vector -- expect some reasonable number of tokens. */
+ strvec = vector_init (10) ;
- /* Prepare return vector. */
- strvec = vector_init (0);
-
- /* Copy each command piece and set into vector. */
+ /* Copy each command piece and set into vector. */
while (1)
{
start = cp;
- while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
- *cp != '\0')
- cp++;
+ while (!isspace((int) *cp) && (*cp != '\0'))
+ cp++ ; /* eat token characters */
strlen = cp - start;
token = XMALLOC (MTYPE_STRVEC, strlen + 1);
memcpy (token, start, strlen);
*(token + strlen) = '\0';
- vector_set (strvec, token);
+ vector_push_item(strvec, token);
- while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
- *cp != '\0')
- cp++;
+ while (isspace((int) *cp))
+ cp++ ; /* skip white-space */
if (*cp == '\0')
return strvec;
}
}
-/* Free allocated string vector. */
-void
-cmd_free_strvec (vector v)
+/*------------------------------------------------------------------------------
+ * Add given string to vector of strings.
+ *
+ * Create vector if required.
+ */
+extern vector
+cmd_add_to_strvec (vector strvec, const char* str)
{
- unsigned int i;
- char *cp;
+ if (strvec == NULL)
+ strvec = vector_init(1) ;
- if (!v)
- return;
+ vector_push_item(strvec, XSTRDUP(MTYPE_STRVEC, str));
- for (i = 0; i < vector_active (v); i++)
- if ((cp = vector_slot (v, i)) != NULL)
- XFREE (MTYPE_STRVEC, cp);
+ return strvec ;
+} ;
- vector_free (v);
-}
+/*------------------------------------------------------------------------------
+ * Free allocated string vector (if any) and all its contents.
+ *
+ * Note that this is perfectly happy with strvec == NULL.
+ */
+extern void
+cmd_free_strvec (vector strvec)
+{
+ char *cp;
+
+ /* Note that vector_ream_free() returns NULL if strvec == NULL */
+ while((cp = vector_ream_free(strvec)) != NULL)
+ XFREE (MTYPE_STRVEC, cp);
+} ;
+
+/*----------------------------------------------------------------------------*/
/* Fetch next description. Used in cmd_make_descvec(). */
static char *
@@ -472,7 +504,19 @@ cmd_prompt (enum node_type node)
{
struct cmd_node *cnode;
- cnode = vector_slot (cmdvec, node);
+ assert(cmdvec != NULL) ;
+ assert(cmdvec->p_items != NULL) ;
+
+ cnode = NULL ;
+ if (node < cmdvec->limit)
+ cnode = vector_slot (cmdvec, node);
+
+ if (cnode == NULL)
+ {
+ zlog_err("Could not find prompt for node %d for", node) ;
+ return NULL ;
+ } ;
+
return cnode->prompt;
}
@@ -683,21 +727,41 @@ cmd_filter_by_symbol (char *command, char *symbol)
}
#endif
+/*==============================================================================
+ * Match functions.
+ *
+ * Is the given string a, possibly incomplete, value of the required kind ?
+ */
+
/* Completion match types. */
enum match_type
{
- no_match,
+ no_match, /* nope */
extend_match,
+
ipv4_prefix_match,
ipv4_match,
ipv6_prefix_match,
ipv6_match,
range_match,
vararg_match,
- partly_match,
- exact_match
+
+ partly_match, /* OK as far as it went */
+ exact_match /* Syntactically complete */
};
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Address:
+ *
+ * 999.999.999.999 -- where no part may be > 255
+ *
+ * TODO: cmd_ipv4_match() seems to accept leading '.' ?
+ * TODO: cmd_ipv4_match() seems to accept leading zeros ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ */
static enum match_type
cmd_ipv4_match (const char *str)
{
@@ -755,6 +819,22 @@ cmd_ipv4_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Prefix:
+ *
+ * 999.999.999.999/99 -- where no part may be > 255,
+ * and prefix length may not be > 32
+ *
+ * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ?
+ * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
static enum match_type
cmd_ipv4_prefix_match (const char *str)
{
@@ -834,6 +914,16 @@ cmd_ipv4_prefix_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Address:
+ *
+ * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ */
+
#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
#define STATE_START 1
@@ -952,6 +1042,19 @@ cmd_ipv6_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Prefix:
+ *
+ * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ?
+ * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
static enum match_type
cmd_ipv6_prefix_match (const char *str)
{
@@ -1085,6 +1188,14 @@ cmd_ipv6_prefix_match (const char *str)
#endif /* HAVE_IPV6 */
+/*------------------------------------------------------------------------------
+ * Is this a decimal number in the allowed range:
+ *
+ * Returns: 1 => OK -- *including* empty string
+ * 0 => not a valid number, or not in required range
+ * (or invalid range !!)
+ */
+
#define DECIMAL_STRLEN_MAX 10
static int
@@ -1132,124 +1243,182 @@ cmd_range_match (const char *range, const char *str)
return 1;
}
-/* Make completion match and return match type flag. */
+/*==============================================================================
+ * Command "filtering".
+ *
+ * The command parsing process starts with a (shallow) copy of the cmd_vector
+ * entry for the current "node".
+ *
+ * So cmd_v contains pointers to struct cmd_element values. When match fails,
+ * the pointer is set NULL -- so parsing is a process of reducing the cmd_v
+ * down to just the entries that match.
+ *
+ * Each cmd_element has a vector "strvec", which contains an entry for each
+ * "token" position. That entry is a vector containing the possible values at
+ * that position.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Make completion match and return match type flag.
+ *
+ * Takes: command -- address of candidate token
+ * cmd_v -- vector of commands that is being reduced/filtered
+ * index -- index of token (position in line -- 0 == first)
+ *
+ * Returns: any of the enum match_type values:
+ *
+ * no_match => no match of any kind
+ *
+ * extend_match => saw an optional token
+ * ipv4_prefix_match )
+ * ipv4_match )
+ * ipv6_prefix_match ) saw full or partial match for this
+ * ipv6_match )
+ * range_match )
+ * vararg_match )
+ *
+ * partly_match => saw partial match for a keyword
+ * exact_match => saw exact match for a keyword
+ *
+ * Note that these return values are in ascending order of preference. So,
+ * if there are multiple possibilities at this position, will return the one
+ * furthest down this list.
+ */
static enum match_type
-cmd_filter_by_completion (char *command, vector v, unsigned int index)
+cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
{
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;
+ for (i = 0; i < vector_active (cmd_v); i++)
+ {
+ const char *str;
+ struct cmd_element *cmd_element;
+ vector descvec;
+ struct desc *desc;
+ unsigned int j;
+ int matched ;
- descvec = vector_slot (cmd_element->strvec, index);
+ /* Skip past cmd_v entries that have already been set NULL */
+ if ((cmd_element = vector_slot (cmd_v, i)) == NULL)
+ continue ;
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
+ /* Discard cmd_v entry that has no token at the current position */
+ if (index >= vector_active (cmd_element->strvec))
+ {
+ vector_slot (cmd_v, i) = NULL;
+ continue ;
+ } ;
- 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;
+ /* See if get any sort of match at current position */
+ matched = 0 ;
+ descvec = vector_slot (cmd_element->strvec, index);
- matched++;
- }
- }
+ 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;
+
+ matched++;
+ }
+ }
#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command))
- {
- if (match_type < ipv6_match)
- match_type = ipv6_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;
-
- matched++;
- }
- }
+ else if (CMD_IPV6 (str))
+ {
+ if (cmd_ipv6_match (command))
+ {
+ if (match_type < ipv6_match)
+ match_type = ipv6_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;
+
+ matched++;
+ }
+ }
#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command))
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
+ else if (CMD_IPV4 (str))
+ {
+ if (cmd_ipv4_match (command))
+ {
+ if (match_type < ipv4_match)
+ match_type = ipv4_match;
+
+ 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 if (CMD_OPTION (str) || CMD_VARIABLE (str))
+ /* Check is this point's argument optional ? */
+ {
+ 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++;
+ } ;
+ } ;
+
+ /* Discard cmd_v entry that has no match at this position */
+ if (!matched)
+ vector_slot (cmd_v, i) = NULL;
+ }
- 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;
- }
- }
return match_type;
}
-/* Filter vector by command character with index. */
+/*------------------------------------------------------------------------------
+ * Filter vector by command character with index.
+ *
+ * This appears to be identical to cmd_filter_by_completion(), except that
+ * when matching keywords, requires an exact match.
+ *
+ * TODO: see if can merge cmd_filter_by_completion() & cmd_filter_by_string()
+ */
static enum match_type
-cmd_filter_by_string (char *command, vector v, unsigned int index)
+cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
{
unsigned int i;
const char *str;
@@ -1261,13 +1430,13 @@ cmd_filter_by_string (char *command, vector v, unsigned int index)
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)
+ for (i = 0; i < vector_active (cmd_v); i++)
+ if ((cmd_element = vector_slot (cmd_v, 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;
+ vector_slot (cmd_v, i) = NULL;
else
{
unsigned int j;
@@ -1349,15 +1518,49 @@ cmd_filter_by_string (char *command, vector v, unsigned int index)
}
}
if (!matched)
- vector_slot (v, i) = NULL;
+ vector_slot (cmd_v, i) = NULL;
}
}
return match_type;
}
-/* Check ambiguous match */
+/*------------------------------------------------------------------------------
+ * Check for ambiguous match
+ *
+ * Given the best that cmd_filter_by_completion() or cmd_filter_by_string()
+ * found, check as follows:
+ *
+ * 1. discard all commands for which do not have the type of match selected.
+ *
+ * See above for the ranking of matches.
+ *
+ * 2. for "partial match", look out for matching more than one keyword, and
+ * return 1 if finds that.
+ *
+ * 3. for "range match", look out for matching more than one range, and
+ * return 1 if finds that.
+ *
+ * 4. for ipv4_prefix_match and ipv6_prefix_match, if get a "partial match",
+ * return 2.
+ *
+ * This appears to catch things which are supposed to be prefixes, but
+ * do not have a '/' or do not have any digits after the '/'.
+ *
+ * Takes: command -- address of candidate token
+ * cmd_v -- vector of commands that is being reduced/filtered
+ * index -- index of token (position in line -- 0 == first)
+ * type -- as returned by cmd_filter_by_completion()
+ * or cmd_filter_by_string()
+ *
+ * Returns: 0 => not ambiguous
+ * 1 => ambiguous -- the candidate token matches more than one
+ * keyword, or the candidate number matches more
+ * than one number range.
+ * 2 => partial match for ipv4_prefix or ipv6_prefix
+ * (missing '/' or no digits after '/').
+ */
static int
-is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
+is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
{
unsigned int i;
unsigned int j;
@@ -1367,8 +1570,8 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
vector descvec;
struct desc *desc;
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
+ for (i = 0; i < vector_active (cmd_v); i++)
+ if ((cmd_element = vector_slot (cmd_v, i)) != NULL)
{
int match = 0;
@@ -1447,25 +1650,27 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
}
}
if (!match)
- vector_slot (v, i) = NULL;
+ vector_slot (cmd_v, i) = NULL;
}
return 0;
}
-/* If src matches dst return dst string, otherwise return NULL */
+/*------------------------------------------------------------------------------
+ * If src matches dst return dst string, otherwise return NULL
+ *
+ * Returns NULL if dst is an option, variable of vararg.
+ *
+ * NULL or empty src are deemed to match.
+ */
static const char *
cmd_entry_function (const char *src, const char *dst)
{
- /* Skip variable arguments. */
- if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) ||
- CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst))
+ if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst))
return NULL;
- /* In case of 'command \t', given src is NULL string. */
- if (src == NULL)
+ if ((src == NULL) || (*src == '\0'))
return dst;
- /* Matched with input string. */
if (strncmp (src, dst, strlen (src)) == 0)
return dst;
@@ -1537,8 +1742,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 same string element existence.
+ *
+ * Returns: 0 => found same string in the vector
+ * 1 => NOT found same string in the vector
+ */
static int
cmd_unique_string (vector v, const char *str)
{
@@ -1569,15 +1778,9 @@ desc_unique_string (vector v, const char *str)
static int
cmd_try_do_shortcut (enum node_type node, char* first_word) {
- if ( first_word != NULL &&
- node != AUTH_NODE &&
- node != VIEW_NODE &&
- node != AUTH_ENABLE_NODE &&
- node != ENABLE_NODE &&
- node != RESTRICTED_NODE &&
- 0 == strcmp( "do", first_word ) )
- return 1;
- return 0;
+ return (node >= MIN_DO_SHORTCUT_NODE)
+ && (first_word != NULL)
+ && (strcmp( "do", first_word) == 0) ? 1 : 0 ;
}
/* '?' describe command support. */
@@ -1717,6 +1920,19 @@ cmd_describe_command_real (vector vline, int node, int *status)
return matchvec;
}
+/*------------------------------------------------------------------------------
+ * Get description of current (partial) command
+ *
+ * Returns: NULL => no description available
+ *
+ * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
+ *
+ * or: address of vector of "struct desc" values available.
+ *
+ * NB: when a vector is returned it is the caller's responsibility to
+ * vector_free() it. (The contents are all effectively const, so do not
+ * themselves need to be freed.)
+ */
vector
cmd_describe_command (vector vline, int node, int *status)
{
@@ -1745,134 +1961,166 @@ cmd_describe_command (vector vline, int node, int *status)
return cmd_describe_command_real (vline, node, status);
}
-
-/* Check LCD of matched command. */
+/*------------------------------------------------------------------------------
+ * Check LCD of matched command.
+ *
+ * Scan list of matched keywords, and by comparing them pair-wise, find the
+ * longest common leading substring.
+ *
+ * Returns: 0 if zero or one matched keywords
+ * length of longest common leading substring, otherwise.
+ */
static int
-cmd_lcd (char **matched)
+cmd_lcd (vector matchvec)
{
- int i;
- int j;
- int lcd = -1;
- char *s1, *s2;
- char c1, c2;
+ int n ;
+ int i ;
+ int lcd ;
+ char *sp, *sq, *ss ;
- if (matched[0] == NULL || matched[1] == NULL)
- return 0;
+ n = vector_end(matchvec) ;
+ if (n < 2)
+ return 0 ;
- for (i = 1; matched[i] != NULL; i++)
+ ss = vector_get_item(matchvec, 0) ;
+ lcd = strlen(ss) ;
+
+ for (i = 1 ; i < n ; i++)
{
- s1 = matched[i - 1];
- s2 = matched[i];
+ sq = ss ;
+ ss = vector_get_item(matchvec, i) ;
+ sp = ss ;
- for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
- if (c1 != c2)
- break;
+ while ((*sp == *sq) && (*sp != '\0'))
+ {
+ ++sp ;
+ ++sq ;
+ } ;
- if (lcd < 0)
- lcd = j;
- else
- {
- if (lcd > j)
- lcd = j;
- }
+ if (lcd > (sp - ss))
+ lcd = (sp - ss) ;
}
return lcd;
}
-/* Command line completion support. */
-static char **
+/*------------------------------------------------------------------------------
+ * Command line completion support.
+ */
+static vector
cmd_complete_command_real (vector vline, int node, int *status)
{
unsigned int i;
- vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
+ unsigned int ivl ;
+ unsigned int last_ivl ;
+ vector cmd_v ;
#define INIT_MATCHVEC_SIZE 10
vector matchvec;
struct cmd_element *cmd_element;
unsigned int index;
- char **match_str;
struct desc *desc;
vector descvec;
- char *command;
- int lcd;
+ char *token;
+ int n ;
+ /* Stop immediately if the vline is empty. */
if (vector_active (vline) == 0)
{
- vector_free (cmd_vector);
*status = CMD_ERR_NO_MATCH;
return NULL;
}
- else
- index = vector_active (vline) - 1;
- /* First, filter by preceeding command string */
- for (i = 0; i < index; i++)
- if ((command = vector_slot (vline, i)))
- {
- enum match_type match;
- int ret;
+ /* Take (shallow) copy of cmdvec for given node. */
+ cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
- /* First try completion match, if there is exactly match return 1 */
- match = cmd_filter_by_completion (command, cmd_vector, i);
+ /* First, filter upto, but excluding last token */
+ last_ivl = vector_active (vline) - 1;
- /* 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;
- }
- /*
- else if (ret == 2)
- {
- vector_free (cmd_vector);
+ for (ivl = 0; ivl < last_ivl; ivl++)
+ {
+ enum match_type match;
+ int ret;
+
+ /* TODO: does this test make any sense ? */
+ if ((token = vector_slot (vline, ivl)) == NULL)
+ continue ;
+
+ /* First try completion match, return best kind of match */
+ index = ivl ;
+ match = cmd_filter_by_completion (token, cmd_v, index) ;
+
+ /* Eliminate all but the selected kind of match */
+ ret = is_cmd_ambiguous (token, cmd_v, index, match) ;
+
+ if (ret == 1)
+ {
+ /* ret == 1 => either token matches more than one keyword
+ * or token matches more than one number range
+ */
+ vector_free (cmd_v);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ }
+#if 0
+ /* For command completion purposes do not appear to care about
+ * incomplete ipv4 or ipv6 prefixes (missing '/' or digits after).
+ */
+ else if (ret == 2)
+ {
+ vector_free (cmd_v);
*status = CMD_ERR_NO_MATCH;
return NULL;
- }
- */
+ }
+#endif
}
- /* Prepare match vector. */
+ /* 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)))
- {
- const char *string;
- vector strvec = cmd_element->strvec;
+ /* Now we got into completion */
+ index = last_ivl ;
+ token = vector_slot(vline, last_ivl) ; /* is now the last token */
- /* Check field length */
- if (index >= vector_active (strvec))
- vector_slot (cmd_vector, i) = NULL;
- else
- {
- unsigned int j;
+ for (i = 0; i < vector_active (cmd_v); i++)
+ {
+ unsigned int j;
+ const char *string;
- descvec = vector_slot (strvec, index);
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- if ((string =
- cmd_entry_function (vector_slot (vline, index),
- desc->cmd)))
- if (cmd_unique_string (matchvec, string))
- vector_set (matchvec, XSTRDUP (MTYPE_TMP, string));
- }
- }
- }
+ if ((cmd_element = vector_slot (cmd_v, i)) == NULL)
+ continue ;
- /* We don't need cmd_vector any more. */
- vector_free (cmd_vector);
+ vector strvec = cmd_element->strvec;
+
+ /* Check field length */
+ if (index >= vector_active (strvec))
+ {
+ vector_slot (cmd_v, i) = NULL;
+ continue ;
+ }
+
+ descvec = vector_slot (strvec, index);
+
+ for (j = 0; j < vector_active (descvec); j++)
+ if ((desc = vector_slot (descvec, j)) != NULL)
+ {
+ string = cmd_entry_function(token, desc->cmd) ;
+ if ((string != NULL) && cmd_unique_string(matchvec, string))
+ cmd_add_to_strvec (matchvec, string) ;
+ }
+ } ;
+
+ n = vector_end(matchvec) ; /* number of entries in the matchvec */
+
+ /* We don't need cmd_v any more. */
+ vector_free (cmd_v);
/* No matched command */
- if (vector_slot (matchvec, 0) == NULL)
+ if (n == 0)
{
vector_free (matchvec);
/* In case of 'command \t' pattern. Do you need '?' command at
the end of the line. */
- if (vector_slot (vline, index) == '\0')
+ if (*token == '\0')
*status = CMD_ERR_NOTHING_TODO;
else
*status = CMD_ERR_NO_MATCH;
@@ -1880,66 +2128,50 @@ cmd_complete_command_real (vector vline, int node, int *status)
}
/* XXX: TODO: stop poking around inside vector */
- /* Only one matched */
- if (vector_slot (matchvec, 1) == NULL)
+ /* Only one matched */
+ if (n == 1)
{
- match_str = (char **) matchvec->VECTOR_INDEX;
- vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_FULL_MATCH;
- return match_str;
+ return matchvec ;
}
- /* Make it sure last element is NULL. */
- vector_set (matchvec, NULL);
- /* Check LCD of matched strings. */
- if (vector_slot (vline, index) != NULL)
+ /* Check LCD of matched strings. */
+ if (token != NULL)
{
- lcd = cmd_lcd ((char **) matchvec->VECTOR_INDEX);
+ unsigned lcd = cmd_lcd (matchvec) ;
- if (lcd)
+ if (lcd != 0)
{
- int len = strlen (vector_slot (vline, index));
-
- if (len < lcd)
+ if (strlen(token) < lcd)
{
char *lcdstr;
lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
- memcpy (lcdstr, matchvec->VECTOR_INDEX[0], lcd);
+ memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ;
lcdstr[lcd] = '\0';
- /* match_str = (char **) &lcdstr; */
+ cmd_free_strvec(matchvec) ; /* discard the match vector */
- /* Free matchvec. */
- for (i = 0; i < vector_active (matchvec); i++)
- {
- if (vector_slot (matchvec, i))
- XFREE (MTYPE_STRVEC, vector_slot (matchvec, i));
- }
- vector_free (matchvec);
-
- /* Make new matchvec. */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
- vector_set (matchvec, lcdstr);
- match_str = (char **) matchvec->VECTOR_INDEX;
- vector_only_wrapper_free (matchvec);
+ vector_push_item(matchvec, lcdstr) ;
*status = CMD_COMPLETE_MATCH;
- return match_str;
+ return matchvec ;
}
}
}
- match_str = (char **) matchvec->VECTOR_INDEX;
- vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_LIST_MATCH;
- return match_str;
+ return matchvec ;
}
-char **
+/*------------------------------------------------------------------------------
+ * Can the current command be completed ?
+ */
+extern vector
cmd_complete_command (vector vline, int node, int *status)
{
- char **ret;
+ vector ret;
if ( cmd_try_do_shortcut(node, vector_slot(vline, 0) ) )
{
@@ -1964,13 +2196,14 @@ cmd_complete_command (vector vline, int node, int *status)
return cmd_complete_command_real (vline, node, status);
}
-/* return parent node */
-/* MUST eventually converge on CONFIG_NODE */
+/*------------------------------------------------------------------------------
+ * Return parent node
+ *
+ * All nodes > CONFIG_NODE are descended from CONFIG_NODE
+ */
enum node_type
node_parent ( enum node_type node )
{
- enum node_type ret;
-
assert (node > CONFIG_NODE);
switch (node)
@@ -1980,69 +2213,101 @@ node_parent ( enum node_type node )
case BGP_IPV4M_NODE:
case BGP_IPV6_NODE:
case BGP_IPV6M_NODE:
- ret = BGP_NODE;
- break;
+ return BGP_NODE;
+
case KEYCHAIN_KEY_NODE:
- ret = KEYCHAIN_NODE;
- break;
- default:
- ret = CONFIG_NODE;
- }
+ return KEYCHAIN_NODE;
- return ret;
-}
+ default:
+ return CONFIG_NODE;
+ }
+}
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", if possible, ready for execution.
+ *
+ * If "strict" use: cmd_filter_by_string()
+ * otherwise use: cmd_filter_by_completion()
+ *
+ * Takes the node from parsed->cnode.
+ *
+ * Returns: CMD_SUCCESS => successfully parsed command, and the result is
+ * in the given parsed structure, ready for execution.
+ *
+ * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a
+ * "daemon" value.
+ *
+ * otherwise => some failure to parse
+ *
+ * NB: the argv[] in the parsed structure contains *copies* of the char*
+ * pointers in the given vline.
+ *
+ * The vline may not be released until the parsed structure is.
+ *
+ * The parsed structure may be released without worrying about the contents
+ * of the argv[].
+ */
+enum cmd_parse_type
+{
+ cmd_parse_completion = 0,
+ cmd_parse_strict = 1,
+};
-/* Execute command by argument vline vector. */
static int
-cmd_execute_command_real (vector vline, struct vty *vty,
- struct cmd_element **cmd, qpn_nexus dest_nexus)
+cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed,
+ enum cmd_parse_type type)
{
- unsigned int i;
- unsigned int index;
- vector cmd_vector;
+ unsigned int i ;
+ unsigned int ivl ;
+ unsigned index ;
+ vector cmd_v;
struct cmd_element *cmd_element;
struct cmd_element *matched_element;
unsigned int matched_count, incomplete_count;
int argc;
- const char *argv[CMD_ARGC_MAX];
enum match_type match = 0;
int varflag;
char *command;
- /* Make copy of command elements. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty_get_node(vty)));
+ int strict = (type == cmd_parse_strict) ;
- for (index = 0; index < vector_active (vline); index++)
- if ((command = vector_slot (vline, index)))
+ parsed->cmd = NULL ; /* return this if parsing fails */
+ parsed->argc = 0 ;
+
+ /* Make copy of command elements. */
+ cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode));
+
+ /* Look for an unambiguous result */
+ index = 0 ;
+ for (ivl = first; ivl < vector_active (vline); ivl++)
+ if ((command = vector_slot (vline, ivl)) != NULL)
{
- int ret;
+ int ret ;
- match = cmd_filter_by_completion (command, cmd_vector, index);
+ index = ivl - first ;
+ match = strict ? cmd_filter_by_string(command, cmd_v, index)
+ : cmd_filter_by_completion(command, cmd_v, index) ;
if (match == vararg_match)
break;
- ret = is_cmd_ambiguous (command, cmd_vector, index, match);
+ ret = is_cmd_ambiguous (command, cmd_v, index, match);
- if (ret == 1)
+ if (ret != 0)
{
- vector_free (cmd_vector);
- return CMD_ERR_AMBIGUOUS;
- }
- else if (ret == 2)
- {
- vector_free (cmd_vector);
- return CMD_ERR_NO_MATCH;
+ assert((ret == 1) || (ret == 2)) ;
+ vector_free (cmd_v);
+ return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ;
}
}
- /* Check matched count. */
- matched_element = NULL;
- matched_count = 0;
+ /* Check matched count. */
+ matched_element = NULL;
+ matched_count = 0;
incomplete_count = 0;
- for (i = 0; i < vector_active (cmd_vector); i++)
- if ((cmd_element = vector_slot (cmd_vector, i)))
+ for (i = 0; i < vector_active (cmd_v); i++)
+ if ((cmd_element = vector_slot (cmd_v, i)) != NULL)
{
if (match == vararg_match || index >= cmd_element->cmdsize)
{
@@ -2058,133 +2323,255 @@ cmd_execute_command_real (vector vline, struct vty *vty,
}
}
- /* Finish of using cmd_vector. */
- vector_free (cmd_vector);
+ /* Finished with cmd_v. */
+ vector_free (cmd_v);
- /* To execute command, matched_count must be 1. */
- if (matched_count == 0)
+ /* To execute command, matched_count must be 1. */
+ if (matched_count != 1)
{
- if (incomplete_count)
- return CMD_ERR_INCOMPLETE;
+ if (matched_count == 0)
+ return (incomplete_count) ? CMD_ERR_INCOMPLETE : CMD_ERR_NO_MATCH ;
else
- return CMD_ERR_NO_MATCH;
- }
-
- if (matched_count > 1)
- return CMD_ERR_AMBIGUOUS;
+ return CMD_ERR_AMBIGUOUS ;
+ } ;
- /* Argument treatment */
- varflag = 0;
- argc = 0;
+ /* Found command -- process the arguments ready for execution */
+ varflag = 0 ;
+ argc = 0 ;
- for (i = 0; i < vector_active (vline); i++)
+ for (ivl = first; ivl < vector_active (vline); ivl++)
{
- if (varflag)
- argv[argc++] = vector_slot (vline, i);
- else
+ int take = varflag ;
+
+ if (!varflag)
{
- vector descvec = vector_slot (matched_element->strvec, i);
+ int index = ivl - first ;
+ vector descvec = vector_slot (matched_element->strvec, index);
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);
+ take = varflag = 1 ;
+ else
+ take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ;
}
else
- argv[argc++] = vector_slot (vline, i);
+ take = 1 ;
}
- if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXEED_ARGC_MAX;
- }
+ if (take)
+ {
+ if (argc >= CMD_ARGC_MAX)
+ return CMD_ERR_EXCEED_ARGC_MAX ;
+ parsed->argv[argc++] = vector_slot (vline, ivl);
+ } ;
+ } ;
- /* For vtysh execution. */
- if (cmd)
- *cmd = matched_element;
+ /* Everything checks out... ready to execute command */
+ parsed->cmd = matched_element ;
+ parsed->argc = argc ;
- if (matched_element->daemon)
+ if (parsed->cmd->daemon)
return CMD_SUCCESS_DAEMON;
- /* Execute matched command. */
- if (qpthreads_enabled && !(matched_element->attr & CMD_ATTR_CALL))
- {
- /* Don't do it now, but send to bgp qpthread */
- cq_enqueue(matched_element, vty, argc, argv, dest_nexus);
- return CMD_QUEUED;
- }
- else
- {
- return (*matched_element->func) (matched_element, vty, argc, argv);
- }
-}
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", or any of its ancestors.
+ *
+ * Takes the node to start in from parsed->cnode.
+ *
+ * Returns: CMD_SUCCESS => successfully parsed command, and the result is
+ * in the given parsed structure, ready for execution.
+ *
+ * NB: parsed->cnode may have changed.
+ *
+ * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a
+ * "daemon" value.
+ *
+ * otherwise => some failure to parse
+ *
+ * NB: returns error from attempt to parse in the
+ * starting parsed->cnode (which is returned
+ * unchanged).
+ *
+ * See cmd_parse_command() for more detail.
+ */
+static int
+cmd_parse_command_tree(vector vline, int first, struct cmd_parsed* parsed,
+ enum cmd_parse_type type)
+{
+ int ret ;
+ int first_ret ;
+ enum node_type first_node ;
-int
-cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
- qpn_nexus dest_nexus, int vtysh) {
- int ret, saved_ret, tried = 0;
- enum node_type onode, try_node;
+ /* Try in the current node */
+ ret = cmd_parse_command(vline, first, parsed, type) ;
- onode = try_node = vty_get_node(vty);
+ if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON))
+ return ret ; /* done if found command */
- if ( cmd_try_do_shortcut(vty_get_node(vty), vector_slot(vline, 0) ) )
- {
- vector shifted_vline;
- unsigned int index;
+ /* Try in parent node(s) */
+ first_node = parsed->cnode ;
+ first_ret = ret ;
- vty_set_node(vty, ENABLE_NODE);
- /* We can try it on enable node, cos' the vty is authenticated */
+ while (parsed->cnode <= CONFIG_NODE)
+ {
+ parsed->cnode = node_parent(parsed->cnode) ;
+ ret = cmd_parse_command(vline, first, parsed, type) ;
- shifted_vline = vector_init (vector_count(vline));
- /* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
- {
- vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
- }
+ if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON))
+ return ret ; /* done if found command */
+ } ;
- ret = cmd_execute_command_real (shifted_vline, vty, cmd, dest_nexus);
+ parsed->cnode = first_node ; /* restore node state */
+ return first_ret ; /* return original result */
+}
- vector_free(shifted_vline);
- vty_set_node(vty, onode);
- return ret;
- }
+/*------------------------------------------------------------------------------
+ * Prepare to parse command -- deal with possible "do" shortcut.
+ *
+ * Initialises parsed structure, setting cnode and onode to vty->node.
+ *
+ * Checks to see if this is a valid "do" shortcut, and adjusts cnode if so.
+ *
+ * Returns: 0 => not "do" -- first word of actual command is first word
+ * 1 => is "do" -- first word of actual command is second word
+ */
+static int
+cmd_pre_command(struct vty* vty, struct cmd_parsed* parsed, vector vline)
+{
+ int first ;
+ parsed->onode = parsed->cnode = vty_get_node(vty) ;
- saved_ret = ret = cmd_execute_command_real (vline, vty, cmd, dest_nexus);
+ parsed->cmd = NULL ;
+ parsed->argc = 0 ;
+ /* 'do' forces command to be parsed in ENABLE_NODE (if allowed) */
+ if (cmd_try_do_shortcut(parsed->cnode, vector_slot(vline, 0)))
+ {
+ parsed->cnode = ENABLE_NODE ;
+ parsed->do_shortcut = 1 ;
+ first = 1 ;
+ }
+ else
+ {
+ parsed->do_shortcut = 0 ;
+ first = 0 ;
+ } ;
+
+ return first ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Tidy up after executing command.
+ *
+ * This is separated out so that can be called when queued command completes.
+ *
+ * If have just processed a "do" shortcut command, and it has not set the
+ * vty->node to something other than ENABLE_NODE, then restore to the original
+ * state.
+ *
+ * Arguments: ret = CMD_XXXX -- NB: CMD_QUEUED => command revoked
+ * action = true unless command return is being revoked
+ */
+extern void
+cmd_post_command(struct vty* vty, struct cmd_parsed* parsed, int ret,
+ int action)
+{
+ if (parsed->do_shortcut)
+ {
+ if (vty_get_node(vty) == ENABLE_NODE)
+ vty_set_node(vty, parsed->onode) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Execute command:
+ *
+ * -- use cmd_parse_completion type parsing.
+ *
+ * -- unless vtysh: if does not parse in current node, try ancestors
+ *
+ * If does not find in any ancestor, return error from current node.
+ *
+ * -- implement the "do" shortcut
+ *
+ * If qpthreads_enabled, then may queue the command rather than execute it
+ * here.
+ *
+ * The vty->node may be changed during the execution of the command, and may
+ * be returned changed once the command has completed.
+ */
+extern int
+cmd_execute_command (vector vline, struct vty *vty,
+ struct cmd_element **cmd, qpn_nexus to_nexus,
+ qpn_nexus from_nexus, int vtysh)
+{
+ int ret ;
+ int first ;
+ struct cmd_parsed parsed ;
+
+ /* Set up parsed structure & deal with "do" shortcut prefix */
+ first = cmd_pre_command(vty, &parsed, vline) ;
+
+ /* Try to parse in parsed.cnode node or, if required, ancestors thereof. */
if (vtysh)
- return saved_ret;
+ ret = cmd_parse_command(vline, first, &parsed, cmd_parse_completion) ;
+ else
+ ret = cmd_parse_command_tree(vline, first, &parsed, cmd_parse_completion) ;
- /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
- while ( ret != CMD_SUCCESS && ret != CMD_WARNING
- && vty_get_node(vty) > CONFIG_NODE )
+ if (cmd)
+ *cmd = parsed.cmd ; /* for vtysh */
+
+ if (ret != CMD_SUCCESS)
+ return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */
+
+ /* Execute the parsed command */
+
+ vty_set_node(vty, parsed.cnode) ;
+
+ if (qpthreads_enabled && !(parsed.cmd->attr & CMD_ATTR_CALL))
{
- try_node = node_parent(try_node);
- vty_set_node(vty, try_node);
- ret = cmd_execute_command_real (vline, vty, cmd, dest_nexus);
- tried = 1;
- if (ret == CMD_SUCCESS || ret == CMD_WARNING)
- {
- /* succesfull command, leave the node as is */
- return ret;
- }
+ /* Don't do it now, but send to bgp qpthread */
+ cq_enqueue(vty, &parsed, to_nexus, from_nexus) ;
+ ret = CMD_QUEUED ;
}
- /* no command succeeded, reset the vty to the original node and
- return the error for this node */
- if ( tried )
- vty_set_node(vty, onode);
- return saved_ret;
-}
-
-/* Execute command by argument readline. */
-int
+ else
+ {
+ ret = (*(parsed.cmd->func))(parsed.cmd, vty, parsed.argc, parsed.argv) ;
+
+ cmd_post_command(vty, &parsed, ret, 1) ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Execute command by argument readline.
+ *
+ * -- use cmd_parse_strict type parsing.
+ *
+ * -- unless vtysh: if does not parse in current node, try ancestors
+ *
+ * If does not find in any ancestor, return error from current node.
+ *
+ * -- does NOT implement the "do" shortcut
+ *
+ * At all times executes the command immediately (no queueing, even if
+ * qpthreads_enabled).
+ *
+ * The vty->node may be changed either when parsing or executing the command.
+ */
+extern int
cmd_execute_command_strict (vector vline, struct vty *vty,
- struct cmd_element **cmd)
+ struct cmd_element **cmd, int vtysh)
{
+#if 0 /* replaced by cmd_parse_command() */
unsigned int i;
unsigned int index;
vector cmd_vector;
@@ -2285,62 +2672,74 @@ cmd_execute_command_strict (vector vline, struct vty *vty,
}
if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXEED_ARGC_MAX;
+ return CMD_ERR_EXCEED_ARGC_MAX;
}
+#endif
+
+ struct cmd_parsed parsed ;
+ int ret ;
+
+ /* Try to parse in parsed.cnode node or, if required, ancestors thereof. */
+ if (vtysh)
+ ret = cmd_parse_command(vline, 0, &parsed, cmd_parse_strict) ;
+ else
+ ret = cmd_parse_command_tree(vline, 0, &parsed, cmd_parse_strict) ;
- /* For vtysh execution. */
if (cmd)
- *cmd = matched_element;
+ *cmd = parsed.cmd ; /* for vtysh */
- if (matched_element->daemon)
- return CMD_SUCCESS_DAEMON;
+ if (ret != CMD_SUCCESS)
+ return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */
- /* Now execute matched command */
- return (*matched_element->func) (matched_element, vty, argc, argv);
+ /* Now execute matched command */
+ vty_set_node(vty, parsed.cnode) ;
+
+ return (*(parsed.cmd->func)) (parsed.cmd, vty, parsed.argc, parsed.argv);
}
-/* Configration make from file. */
+/*------------------------------------------------------------------------------
+ * Read configuration from file.
+ *
+ */
int
-config_from_file (struct vty *vty, FILE *fp, void (*after_first_cmd)(void))
+config_from_file (struct vty *vty, FILE *fp, void (*after_first_cmd)(void),
+ qstring buf)
{
int ret;
vector vline;
int first_cmd = 1;
- while (fgets (vty->buf, VTY_BUFSIZ, fp))
+ while (fgets (buf->body, buf->size, fp))
{
- vline = cmd_make_strvec (vty->buf);
+ vline = cmd_make_strvec (buf->body);
- /* In case of comment line */
+ /* In case of comment line */
if (vline == NULL)
continue;
- /* Execute configuration command : this is strict match */
- ret = cmd_execute_command_strict (vline, vty, NULL);
- /* special handling for after the first command */
+ /* Execute configuration command : this is strict match */
+ ret = cmd_execute_command_strict (vline, vty, NULL, 0);
+
+ /* special handling for after the first command */
if (first_cmd && after_first_cmd)
{
after_first_cmd();
first_cmd = 0;
}
- /* Try again with setting node to CONFIG_NODE */
- while (ret != CMD_SUCCESS && ret != CMD_WARNING
- && ret != CMD_ERR_NOTHING_TODO && vty_get_node(vty) != CONFIG_NODE)
- {
- vty_set_node(vty, node_parent(vty_get_node(vty)));
- ret = cmd_execute_command_strict (vline, vty, NULL);
- }
-
cmd_free_strvec (vline);
+ /* TODO: why does config file not stop on CMD_WARNING ?? */
if (ret != CMD_SUCCESS && ret != CMD_WARNING
- && ret != CMD_ERR_NOTHING_TODO)
+ && ret != CMD_ERR_NOTHING_TODO)
return ret;
- }
+ } ;
+
return CMD_SUCCESS;
}
+/*----------------------------------------------------------------------------*/
+
/* Configration from terminal */
DEFUN_CALL (config_terminal,
config_terminal_cmd,
@@ -2348,14 +2747,11 @@ DEFUN_CALL (config_terminal,
"Configuration from vty interface\n"
"Configuration terminal\n")
{
- if (vty_config_lock (vty))
- vty_set_node(vty, CONFIG_NODE);
- else
- {
- vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
+ if (vty_config_lock (vty, CONFIG_NODE))
+ return CMD_SUCCESS;
+
+ vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
+ return CMD_WARNING;
}
/* Enable command */
@@ -2391,47 +2787,7 @@ DEFUN_CALL (config_exit,
"exit",
"Exit current mode and down to previous mode\n")
{
- switch (vty_get_node(vty))
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- if (vty_shell (vty))
- exit (0);
- else
- vty_set_status(vty, VTY_CLOSE);
- break;
- case CONFIG_NODE:
- vty_set_node(vty, ENABLE_NODE);
- vty_config_unlock (vty);
- break;
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case BGP_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case MASC_NODE:
- case RMAP_NODE:
- case VTY_NODE:
- vty_set_node(vty, CONFIG_NODE);
- break;
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- vty_set_node(vty, BGP_NODE);
- break;
- case KEYCHAIN_KEY_NODE:
- vty_set_node(vty, KEYCHAIN_NODE);
- break;
- default:
- break;
- }
+ vty_cmd_exit(vty) ;
return CMD_SUCCESS;
}
@@ -2447,38 +2803,7 @@ DEFUN_CALL (config_end,
"end",
"End current mode and change to enable mode.")
{
- switch (vty_get_node(vty))
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- /* Nothing to do. */
- break;
- case CONFIG_NODE:
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case BGP_NODE:
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- case RMAP_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case KEYCHAIN_KEY_NODE:
- case MASC_NODE:
- case VTY_NODE:
- vty_config_unlock (vty);
- vty_set_node(vty, ENABLE_NODE);
- break;
- default:
- break;
- }
+ vty_cmd_end(vty) ;
return CMD_SUCCESS;
}
@@ -2539,7 +2864,7 @@ DEFUN_CALL (config_list,
}
/* Write current configuration into file. */
-DEFUN_CALL (config_write_file,
+DEFUN (config_write_file,
config_write_file_cmd,
"write file",
"Write running configuration to memory, network, or terminal\n"
@@ -2645,18 +2970,18 @@ finished:
return ret;
}
-ALIAS_CALL (config_write_file,
+ALIAS (config_write_file,
config_write_cmd,
"write",
"Write running configuration to memory, network, or terminal\n")
-ALIAS_CALL (config_write_file,
+ALIAS (config_write_file,
config_write_memory_cmd,
"write memory",
"Write running configuration to memory, network, or terminal\n"
"Write configuration to the file (same as write file)\n")
-ALIAS_CALL (config_write_file,
+ALIAS (config_write_file,
copy_runningconfig_startupconfig_cmd,
"copy running-config startup-config",
"Copy configuration\n"
@@ -2664,7 +2989,7 @@ ALIAS_CALL (config_write_file,
"Copy running config to startup config (same as write file)\n")
/* Write current configuration into the terminal. */
-DEFUN_CALL (config_write_terminal,
+DEFUN (config_write_terminal,
config_write_terminal_cmd,
"write terminal",
"Write running configuration to memory, network, or terminal\n"
@@ -2700,14 +3025,14 @@ DEFUN_CALL (config_write_terminal,
}
/* Write current configuration into the terminal. */
-ALIAS_CALL (config_write_terminal,
+ALIAS (config_write_terminal,
show_running_config_cmd,
"show running-config",
SHOW_STR
"running configuration\n")
/* Write startup configuration into the terminal. */
-DEFUN_CALL (show_startup_config,
+DEFUN (show_startup_config,
show_startup_config_cmd,
"show startup-config",
SHOW_STR
@@ -2753,10 +3078,16 @@ DEFUN_CALL (config_hostname,
return CMD_WARNING;
}
+ VTY_LOCK() ;
+
if (host.name)
XFREE (MTYPE_HOST, host.name);
host.name = XSTRDUP (MTYPE_HOST, argv[0]);
+ uty_set_host_name(host.name) ;
+
+ VTY_UNLOCK() ;
+
return CMD_SUCCESS;
}
@@ -2767,9 +3098,15 @@ DEFUN_CALL (config_no_hostname,
"Reset system's network name\n"
"Host name of this router\n")
{
+ VTY_LOCK() ;
+
if (host.name)
XFREE (MTYPE_HOST, host.name);
host.name = NULL;
+ uty_set_host_name(host.name) ;
+
+ VTY_UNLOCK() ;
+
return CMD_SUCCESS;
}