version 1.1, 2012/02/21 17:26:12
|
version 1.1.1.4, 2016/11/02 10:09:11
|
Line 1
|
Line 1
|
/* |
/* |
Command interpreter routine for virtual terminal [aka TeletYpe] |
Command interpreter routine for virtual terminal [aka TeletYpe] |
Copyright (C) 1997, 98, 99 Kunihiro Ishiguro |
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. |
This file is part of GNU Zebra. |
|
|
Line 35 Boston, MA 02111-1307, USA. */
|
Line 37 Boston, MA 02111-1307, USA. */
|
each daemon maintains each own cmdvec. */ |
each daemon maintains each own cmdvec. */ |
vector cmdvec = NULL; |
vector cmdvec = NULL; |
|
|
struct desc desc_cr; | struct cmd_token token_cr; |
char *command_cr = NULL; |
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. */ |
/* Host information structure. */ |
struct host host; |
struct host host; |
|
|
Line 84 static const char *default_motd =
|
Line 109 static const char *default_motd =
|
"\r\n\ |
"\r\n\ |
Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ |
Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ |
" QUAGGA_COPYRIGHT "\r\n\ |
" QUAGGA_COPYRIGHT "\r\n\ |
\r\n"; | " GIT_INFO "\r\n"; |
|
|
|
|
static const struct facility_map { |
static const struct facility_map { |
Line 156 print_version (const char *progname)
|
Line 181 print_version (const char *progname)
|
{ |
{ |
printf ("%s version %s\n", progname, QUAGGA_VERSION); |
printf ("%s version %s\n", progname, QUAGGA_VERSION); |
printf ("%s\n", QUAGGA_COPYRIGHT); |
printf ("%s\n", QUAGGA_COPYRIGHT); |
|
printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS); |
} |
} |
|
|
| |
/* Utility function to concatenate argv argument into a single string |
/* Utility function to concatenate argv argument into a single string |
with inserting ' ' character between each argument. */ |
with inserting ' ' character between each argument. */ |
char * |
char * |
Line 196 install_node (struct cmd_node *node,
|
Line 222 install_node (struct cmd_node *node,
|
node->cmd_vector = vector_init (VECTOR_MIN_SIZE); |
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 |
/* Breaking up string into each command piece. I assume given |
character is separated by a space character. Return value is a |
character is separated by a space character. Return value is a |
vector which includes char ** data element. */ |
vector which includes char ** data element. */ |
Line 312 cmd_free_strvec (vector v)
|
Line 291 cmd_free_strvec (vector v)
|
vector_free (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 * |
static char * |
cmd_desc_str (const char **string) | format_parser_desc_str(struct format_parser_state *state) |
{ |
{ |
const char *cp, *start; |
const char *cp, *start; |
char *token; |
char *token; |
int strlen; |
int strlen; |
|
|
cp = *string; |
|
|
|
|
cp = state->dp; |
|
|
if (cp == NULL) |
if (cp == NULL) |
return NULL; |
return NULL; |
|
|
Line 339 cmd_desc_str (const char **string)
|
Line 349 cmd_desc_str (const char **string)
|
cp++; |
cp++; |
|
|
strlen = cp - start; |
strlen = cp - start; |
token = XMALLOC (MTYPE_STRVEC, strlen + 1); | token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); |
memcpy (token, start, strlen); |
memcpy (token, start, strlen); |
*(token + strlen) = '\0'; |
*(token + strlen) = '\0'; |
|
|
*string = cp; | state->dp = cp; |
|
|
return token; |
return token; |
} |
} |
|
|
/* New string vector. */ | static void |
static vector | format_parser_begin_keyword(struct format_parser_state *state) |
cmd_make_descvec (const char *string, const char *descstr) | |
{ |
{ |
int multiple = 0; | struct cmd_token *token; |
const char *sp; | vector keyword_vect; |
char *token; | |
int len; | |
const char *cp; | |
const char *dp; | |
vector allvec; | |
vector strvec = NULL; | |
struct desc *desc; | |
|
|
cp = string; | if (state->in_keyword |
dp = descstr; | || state->in_multiple) |
| format_parser_error(state, "Unexpected '{'"); |
|
|
if (cp == NULL) | state->cp++; |
return NULL; | 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) | keyword_vect = vector_init(VECTOR_MIN_SIZE); |
{ | vector_set(token->keyword, keyword_vect); |
while (isspace ((int) *cp) && *cp != '\0') | |
cp++; | |
|
|
if (*cp == '(') | vector_set(state->curvect, token); |
{ | state->curvect = keyword_vect; |
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++; | |
|
|
if (*cp == '(') | static void |
{ | format_parser_begin_multiple(struct format_parser_state *state) |
multiple = 1; | { |
cp++; | struct cmd_token *token; |
} | |
|
|
if (*cp == '\0') | if (state->in_keyword == 1) |
return allvec; | 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') | state->cp++; |
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); | vector_set(state->curvect, token); |
memcpy (token, sp, len); | if (state->curvect != state->topvect) |
*(token + len) = '\0'; | state->intvect = state->curvect; |
| state->curvect = token->multiple; |
| } |
|
|
desc = XCALLOC (MTYPE_DESC, sizeof (struct desc)); | static void |
desc->cmd = token; | format_parser_end_keyword(struct format_parser_state *state) |
desc->str = cmd_desc_str (&dp); | { |
| if (state->in_multiple |
| || !state->in_keyword) |
| format_parser_error(state, "Unexpected '}'"); |
|
|
if (multiple) | if (state->in_keyword == 1) |
{ | format_parser_error(state, "Empty keyword group"); |
if (multiple == 1) | |
{ | state->cp++; |
strvec = vector_init (VECTOR_MIN_SIZE); | state->in_keyword = 0; |
vector_set (allvec, strvec); | state->curvect = state->topvect; |
} | } |
multiple++; | |
} | static void |
else | format_parser_end_multiple(struct format_parser_state *state) |
{ | { |
strvec = vector_init (VECTOR_MIN_SIZE); | char *dummy; |
vector_set (allvec, strvec); | |
} | if (!state->in_multiple) |
vector_set (strvec, desc); | 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 | static void |
command has enough command length. */ | format_parser_handle_pipe(struct format_parser_state *state) |
static int | |
cmd_cmdsize (vector strvec) | |
{ |
{ |
unsigned int i; | struct cmd_token *keyword_token; |
int size = 0; | vector keyword_vect; |
vector descvec; | |
struct desc *desc; | |
|
|
for (i = 0; i < vector_active (strvec); i++) | if (state->in_multiple) |
if ((descvec = vector_slot (strvec, i)) != NULL) | |
{ |
{ |
if ((vector_active (descvec)) == 1 | state->just_read_word = 0; |
&& (desc = vector_slot (descvec, 0)) != NULL) | state->cp++; |
{ | |
if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) | |
return size; | |
else | |
size++; | |
} | |
else | |
size++; | |
} |
} |
return size; | 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; |
|
if (strcmp (cmd, "A.B.C.D") == 0) |
|
token->terminal = TERMINAL_IPV4; |
|
else if (strcmp (cmd, "A.B.C.D/M") == 0) |
|
token->terminal = TERMINAL_IPV4_PREFIX; |
|
else if (strcmp (cmd, "X:X::X:X") == 0) |
|
token->terminal = TERMINAL_IPV6; |
|
else if (strcmp (cmd, "X:X::X:X/M") == 0) |
|
token->terminal = TERMINAL_IPV6_PREFIX; |
|
else if (cmd[0] == '[') |
|
token->terminal = TERMINAL_OPTION; |
|
else if (cmd[0] == '.') |
|
token->terminal = TERMINAL_VARARG; |
|
else if (cmd[0] == '<') |
|
token->terminal = TERMINAL_RANGE; |
|
else if (cmd[0] >= 'A' && cmd[0] <= 'Z') |
|
token->terminal = TERMINAL_VARIABLE; |
|
else |
|
token->terminal = TERMINAL_LITERAL; |
|
|
|
token->cmd = cmd; |
|
token->desc = format_parser_desc_str(state); |
|
vector_set(state->curvect, token); |
|
|
|
if (state->in_keyword == 1) |
|
state->in_keyword = 2; |
|
|
|
state->just_read_word = 1; |
|
} |
|
|
|
/** |
|
* Parse a given command format string and build a tree of tokens from |
|
* it that is suitable to be used by the command subsystem. |
|
* |
|
* @param string Command format string. |
|
* @param descstr Description string. |
|
* @return A vector of struct cmd_token representing the given command, |
|
* or NULL on error. |
|
*/ |
|
static vector |
|
cmd_parse_format(const char *string, const char *descstr) |
|
{ |
|
struct format_parser_state state; |
|
|
|
if (string == NULL) |
|
return NULL; |
|
|
|
memset(&state, 0, sizeof(state)); |
|
state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE); |
|
state.cp = state.string = string; |
|
state.dp = descstr; |
|
|
|
while (1) |
|
{ |
|
while (isspace((int)state.cp[0]) && state.cp[0] != '\0') |
|
state.cp++; |
|
|
|
switch (state.cp[0]) |
|
{ |
|
case '\0': |
|
if (state.in_keyword |
|
|| state.in_multiple) |
|
format_parser_error(&state, "Unclosed group/keyword"); |
|
return state.topvect; |
|
case '{': |
|
format_parser_begin_keyword(&state); |
|
break; |
|
case '(': |
|
format_parser_begin_multiple(&state); |
|
break; |
|
case '}': |
|
format_parser_end_keyword(&state); |
|
break; |
|
case ')': |
|
format_parser_end_multiple(&state); |
|
break; |
|
case '|': |
|
format_parser_handle_pipe(&state); |
|
break; |
|
default: |
|
format_parser_read_word(&state); |
|
} |
|
} |
|
} |
|
|
/* Return prompt character of specified node. */ |
/* Return prompt character of specified node. */ |
const char * |
const char * |
cmd_prompt (enum node_type node) |
cmd_prompt (enum node_type node) |
Line 497 install_element (enum node_type ntype, struct cmd_elem
|
Line 620 install_element (enum node_type ntype, struct cmd_elem
|
} |
} |
|
|
vector_set (cnode->cmd_vector, cmd); |
vector_set (cnode->cmd_vector, cmd); |
| if (cmd->tokens == NULL) |
if (cmd->strvec == NULL) | cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); |
cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); | |
| |
cmd->cmdsize = cmd_cmdsize (cmd->strvec); | |
} |
} |
|
|
static const unsigned char itoa64[] = |
static const unsigned char itoa64[] = |
Line 847 cmd_ipv4_prefix_match (const char *str)
|
Line 967 cmd_ipv4_prefix_match (const char *str)
|
static enum match_type |
static enum match_type |
cmd_ipv6_match (const char *str) |
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; |
struct sockaddr_in6 sin6_dummy; |
int ret; |
int ret; |
|
|
Line 868 cmd_ipv6_match (const char *str)
|
Line 985 cmd_ipv6_match (const char *str)
|
if (ret == 1) |
if (ret == 1) |
return exact_match; |
return exact_match; |
|
|
while (*str != '\0') | return no_match; |
{ | |
switch (state) | |
{ | |
case STATE_START: | |
if (*str == ':') | |
{ | |
if (*(str + 1) != ':' && *(str + 1) != '\0') | |
return no_match; | |
colons--; | |
state = STATE_COLON; | |
} | |
else | |
{ | |
sp = str; | |
state = STATE_ADDR; | |
} | |
| |
continue; | |
case STATE_COLON: | |
colons++; | |
if (*(str + 1) == ':') | |
state = STATE_DOUBLE; | |
else | |
{ | |
sp = str + 1; | |
state = STATE_ADDR; | |
} | |
break; | |
case STATE_DOUBLE: | |
if (double_colon) | |
return no_match; | |
| |
if (*(str + 1) == ':') | |
return no_match; | |
else | |
{ | |
if (*(str + 1) != '\0') | |
colons++; | |
sp = str + 1; | |
state = STATE_ADDR; | |
} | |
| |
double_colon++; | |
nums++; | |
break; | |
case STATE_ADDR: | |
if (*(str + 1) == ':' || *(str + 1) == '\0') | |
{ | |
if (str - sp > 3) | |
return no_match; | |
| |
nums++; | |
state = STATE_COLON; | |
} | |
if (*(str + 1) == '.') | |
state = STATE_DOT; | |
break; | |
case STATE_DOT: | |
state = STATE_ADDR; | |
break; | |
default: | |
break; | |
} | |
| |
if (nums > 8) | |
return no_match; | |
| |
if (colons > 7) | |
return no_match; | |
| |
str++; | |
} | |
| |
#if 0 | |
if (nums < 11) | |
return partly_match; | |
#endif /* 0 */ | |
| |
return exact_match; | |
} |
} |
|
|
static enum match_type |
static enum match_type |
Line 1033 cmd_ipv6_prefix_match (const char *str)
|
Line 1071 cmd_ipv6_prefix_match (const char *str)
|
if (*(str + 1) == ':') |
if (*(str + 1) == ':') |
state = STATE_COLON; |
state = STATE_COLON; |
else if (*(str + 1) == '.') |
else if (*(str + 1) == '.') |
state = STATE_DOT; | { |
| if (colons || double_colon) |
| state = STATE_DOT; |
| else |
| return no_match; |
| } |
else if (*(str + 1) == '/') |
else if (*(str + 1) == '/') |
state = STATE_SLASH; |
state = STATE_SLASH; |
} |
} |
Line 1130 cmd_range_match (const char *range, const char *str)
|
Line 1173 cmd_range_match (const char *range, const char *str)
|
return 1; |
return 1; |
} |
} |
|
|
/* Make completion match and return match type flag. */ |
|
static enum match_type |
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; |
const char *str; |
struct cmd_element *cmd_element; |
|
enum match_type match_type; |
enum match_type match_type; |
vector descvec; |
|
struct desc *desc; |
|
|
|
match_type = no_match; | str = token->cmd; |
|
|
/* If command and cmd_element string does not match set NULL to vector */ | if (filter == FILTER_RELAXED) |
for (i = 0; i < vector_active (v); i++) | if (!word || !strlen(word)) |
if ((cmd_element = vector_slot (v, i)) != NULL) | return partly_match; |
{ | |
if (index >= vector_active (cmd_element->strvec)) | |
vector_slot (v, i) = NULL; | |
else | |
{ | |
unsigned int j; | |
int matched = 0; | |
|
|
descvec = vector_slot (cmd_element->strvec, index); | if (!word) |
| return no_match; |
|
|
for (j = 0; j < vector_active (descvec); j++) | switch (token->terminal) |
if ((desc = vector_slot (descvec, j))) | { |
{ | case TERMINAL_VARARG: |
str = desc->cmd; | return vararg_match; |
| |
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++; | case TERMINAL_RANGE: |
} | if (cmd_range_match(str, word)) |
} | return range_match; |
#ifdef HAVE_IPV6 | break; |
else if (CMD_IPV6 (str)) | |
{ | |
if (cmd_ipv6_match (command)) | |
{ | |
if (match_type < ipv6_match) | |
match_type = ipv6_match; | |
|
|
matched++; | case TERMINAL_IPV6: |
} | match_type = cmd_ipv6_match(word); |
} | if ((filter == FILTER_RELAXED && match_type != no_match) |
else if (CMD_IPV6_PREFIX (str)) | || (filter == FILTER_STRICT && match_type == exact_match)) |
{ | return ipv6_match; |
if (cmd_ipv6_prefix_match (command)) | break; |
{ | |
if (match_type < ipv6_prefix_match) | |
match_type = ipv6_prefix_match; | |
|
|
matched++; | case TERMINAL_IPV6_PREFIX: |
} | match_type = cmd_ipv6_prefix_match(word); |
} | if ((filter == FILTER_RELAXED && match_type != no_match) |
#endif /* HAVE_IPV6 */ | || (filter == FILTER_STRICT && match_type == exact_match)) |
else if (CMD_IPV4 (str)) | return ipv6_prefix_match; |
{ | break; |
if (cmd_ipv4_match (command)) | |
{ | |
if (match_type < ipv4_match) | |
match_type = ipv4_match; | |
|
|
matched++; | case TERMINAL_IPV4: |
} | match_type = cmd_ipv4_match(word); |
} | if ((filter == FILTER_RELAXED && match_type != no_match) |
else if (CMD_IPV4_PREFIX (str)) | || (filter == FILTER_STRICT && match_type == exact_match)) |
{ | return ipv4_match; |
if (cmd_ipv4_prefix_match (command)) | break; |
{ | |
if (match_type < ipv4_prefix_match) | case TERMINAL_IPV4_PREFIX: |
match_type = ipv4_prefix_match; | match_type = cmd_ipv4_prefix_match(word); |
matched++; | if ((filter == FILTER_RELAXED && match_type != no_match) |
} | || (filter == FILTER_STRICT && match_type == exact_match)) |
} | return ipv4_prefix_match; |
else | break; |
/* Check is this point's argument optional ? */ | |
if (CMD_OPTION (str) || CMD_VARIABLE (str)) | case TERMINAL_OPTION: |
{ | case TERMINAL_VARIABLE: |
if (match_type < extend_match) | return extend_match; |
match_type = extend_match; | |
matched++; | case TERMINAL_LITERAL: |
} | if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) |
else if (strncmp (command, str, strlen (command)) == 0) | { |
{ | if (!strcmp(str, word)) |
if (strcmp (command, str) == 0) | return exact_match; |
match_type = exact_match; | return partly_match; |
else | } |
{ | if (filter == FILTER_STRICT && !strcmp(str, word)) |
if (match_type < partly_match) | return exact_match; |
match_type = partly_match; | break; |
} | |
matched++; | default: |
} | assert (0); |
} | } |
if (!matched) | |
vector_slot (v, i) = NULL; | return no_match; |
} | } |
| |
| struct cmd_matcher |
| { |
| struct cmd_element *cmd; /* The command element the matcher is using */ |
| enum filter_type filter; /* Whether to use strict or relaxed matching */ |
| vector vline; /* The tokenized commandline which is to be matched */ |
| unsigned int index; /* The index up to which matching should be done */ |
| |
| /* If set, construct a list of matches at the position given by index */ |
| enum match_type *match_type; |
| vector *match; |
| |
| unsigned int word_index; /* iterating over vline */ |
| }; |
| |
| static int |
| push_argument(int *argc, const char **argv, const char *arg) |
| { |
| if (!arg || !strlen(arg)) |
| arg = NULL; |
| |
| if (!argc || !argv) |
| return 0; |
| |
| if (*argc >= CMD_ARGC_MAX) |
| return -1; |
| |
| argv[(*argc)++] = arg; |
| return 0; |
| } |
| |
| static void |
| cmd_matcher_record_match(struct cmd_matcher *matcher, |
| enum match_type match_type, |
| struct cmd_token *token) |
| { |
| if (matcher->word_index != matcher->index) |
| return; |
| |
| if (matcher->match) |
| { |
| if (!*matcher->match) |
| *matcher->match = vector_init(VECTOR_MIN_SIZE); |
| vector_set(*matcher->match, token); |
| } |
| |
| if (matcher->match_type) |
| { |
| if (match_type > *matcher->match_type) |
| *matcher->match_type = match_type; |
| } |
| } |
| |
| static int |
| cmd_matcher_words_left(struct cmd_matcher *matcher) |
| { |
| return matcher->word_index < vector_active(matcher->vline); |
| } |
| |
| static const char* |
| cmd_matcher_get_word(struct cmd_matcher *matcher) |
| { |
| assert(cmd_matcher_words_left(matcher)); |
| |
| return vector_slot(matcher->vline, matcher->word_index); |
| } |
| |
| static enum matcher_rv |
| cmd_matcher_match_terminal(struct cmd_matcher *matcher, |
| struct cmd_token *token, |
| int *argc, const char **argv) |
| { |
| const char *word; |
| enum match_type word_match; |
| |
| assert(token->type == TOKEN_TERMINAL); |
| |
| if (!cmd_matcher_words_left(matcher)) |
| { |
| if (token->terminal == TERMINAL_OPTION) |
| return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ |
| else |
| return MATCHER_INCOMPLETE; |
| } |
| |
| word = cmd_matcher_get_word(matcher); |
| word_match = cmd_word_match(token, matcher->filter, word); |
| if (word_match == no_match) |
| return MATCHER_NO_MATCH; |
| |
| /* We have to record the input word as argument if it matched |
| * against a variable. */ |
| if (TERMINAL_RECORD (token->terminal)) |
| { |
| if (push_argument(argc, argv, word)) |
| return MATCHER_EXCEED_ARGC_MAX; |
| } |
| |
| cmd_matcher_record_match(matcher, word_match, token); |
| |
| matcher->word_index++; |
| |
| /* A vararg token should consume all left over words as arguments */ |
| if (token->terminal == TERMINAL_VARARG) |
| while (cmd_matcher_words_left(matcher)) |
| { |
| word = cmd_matcher_get_word(matcher); |
| if (word && strlen(word)) |
| push_argument(argc, argv, word); |
| matcher->word_index++; |
} |
} |
return match_type; | |
| return MATCHER_OK; |
} |
} |
|
|
/* Filter vector by command character with index. */ | static enum matcher_rv |
static enum match_type | cmd_matcher_match_multiple(struct cmd_matcher *matcher, |
cmd_filter_by_string (char *command, vector v, unsigned int index) | struct cmd_token *token, |
| int *argc, const char **argv) |
{ |
{ |
|
enum match_type multiple_match; |
|
unsigned int multiple_index; |
|
const char *word; |
|
const char *arg = NULL; |
|
struct cmd_token *word_token; |
|
enum match_type word_match; |
|
|
|
assert(token->type == TOKEN_MULTIPLE); |
|
|
|
multiple_match = no_match; |
|
|
|
if (!cmd_matcher_words_left(matcher)) |
|
return MATCHER_INCOMPLETE; |
|
|
|
word = cmd_matcher_get_word(matcher); |
|
for (multiple_index = 0; |
|
multiple_index < vector_active(token->multiple); |
|
multiple_index++) |
|
{ |
|
word_token = vector_slot(token->multiple, multiple_index); |
|
|
|
word_match = cmd_word_match(word_token, matcher->filter, word); |
|
if (word_match == no_match) |
|
continue; |
|
|
|
cmd_matcher_record_match(matcher, word_match, word_token); |
|
|
|
if (word_match > multiple_match) |
|
{ |
|
multiple_match = word_match; |
|
arg = word; |
|
} |
|
/* To mimic the behavior of the old command implementation, we |
|
* tolerate any ambiguities here :/ */ |
|
} |
|
|
|
matcher->word_index++; |
|
|
|
if (multiple_match == no_match) |
|
return MATCHER_NO_MATCH; |
|
|
|
if (push_argument(argc, argv, arg)) |
|
return MATCHER_EXCEED_ARGC_MAX; |
|
|
|
return MATCHER_OK; |
|
} |
|
|
|
static enum matcher_rv |
|
cmd_matcher_read_keywords(struct cmd_matcher *matcher, |
|
struct cmd_token *token, |
|
vector args_vector) |
|
{ |
unsigned int i; |
unsigned int i; |
const char *str; | unsigned long keyword_mask; |
| unsigned int keyword_found; |
| enum match_type keyword_match; |
| enum match_type word_match; |
| vector keyword_vector; |
| struct cmd_token *word_token; |
| const char *word; |
| int keyword_argc; |
| const char **keyword_argv; |
| enum matcher_rv rv = MATCHER_NO_MATCH; |
| |
| 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 |
| && TERMINAL_RECORD (word_token->terminal)) |
| || word_token->type == TOKEN_MULTIPLE) |
| { |
| if (push_argument(argc, argv, NULL)) |
| rv = MATCHER_EXCEED_ARGC_MAX; |
| } |
| } |
| } |
| } |
| } |
| vector_free(keyword_args_vector); |
| return rv; |
| } |
| |
| static enum matcher_rv |
| cmd_matcher_match_keyword(struct cmd_matcher *matcher, |
| struct cmd_token *token, |
| int *argc, const char **argv) |
| { |
| vector keyword_args_vector; |
| enum matcher_rv reader_rv; |
| enum matcher_rv builder_rv; |
| |
| assert(token->type == TOKEN_KEYWORD); |
| |
| if (argc && argv) |
| keyword_args_vector = vector_init(VECTOR_MIN_SIZE); |
| else |
| keyword_args_vector = NULL; |
| |
| reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector); |
| builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc, |
| argv, keyword_args_vector); |
| /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */ |
| |
| if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv)) |
| return builder_rv; |
| |
| return reader_rv; |
| } |
| |
| static void |
| cmd_matcher_init(struct cmd_matcher *matcher, |
| struct cmd_element *cmd, |
| enum filter_type filter, |
| vector vline, |
| unsigned int index, |
| enum match_type *match_type, |
| vector *match) |
| { |
| memset(matcher, 0, sizeof(*matcher)); |
| |
| matcher->cmd = cmd; |
| matcher->filter = filter; |
| matcher->vline = vline; |
| matcher->index = index; |
| |
| matcher->match_type = match_type; |
| if (matcher->match_type) |
| *matcher->match_type = no_match; |
| matcher->match = match; |
| |
| matcher->word_index = 0; |
| } |
| |
| static enum matcher_rv |
| cmd_element_match(struct cmd_element *cmd_element, |
| enum filter_type filter, |
| vector vline, |
| unsigned int index, |
| enum match_type *match_type, |
| vector *match, |
| int *argc, |
| const char **argv) |
| { |
| struct cmd_matcher matcher; |
| unsigned int token_index; |
| enum matcher_rv rv = MATCHER_NO_MATCH; |
| |
| 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; |
struct cmd_element *cmd_element; |
struct cmd_element *cmd_element; |
enum match_type match_type; | enum match_type best_match; |
vector descvec; | enum match_type element_match; |
struct desc *desc; | 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 (commands); i++) |
for (i = 0; i < vector_active (v); i++) | if ((cmd_element = vector_slot (commands, i)) != NULL) |
if ((cmd_element = vector_slot (v, i)) != NULL) | |
{ |
{ |
/* If given index is bigger than max string vector of command, | vector_set_index(*matches, i, NULL); |
set NULL */ | matcher_rv = cmd_element_match(cmd_element, filter, |
if (index >= vector_active (cmd_element->strvec)) | vline, index, |
vector_slot (v, i) = NULL; | &element_match, |
else | (vector*)&vector_slot(*matches, i), |
{ | NULL, NULL); |
unsigned int j; | if (MATCHER_ERROR(matcher_rv)) |
int matched = 0; | { |
| 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; |
|
|
for (j = 0; j < vector_active (descvec); j++) | rv = cmd_element_match(cmd_element, |
if ((desc = vector_slot (descvec, j))) | FILTER_RELAXED, |
{ | vline, -1, |
str = desc->cmd; | NULL, NULL, |
| NULL, NULL); |
| return (rv == MATCHER_COMPLETE); |
| } |
|
|
if (CMD_VARARG (str)) | /** |
{ | * Parse a given commandline and construct a list of arguments for the |
if (match_type < vararg_match) | * given command_element. |
match_type = vararg_match; | * |
matched++; | * @param cmd_element The cmd_element for which we want to construct arguments. |
} | * @param vline The tokenized commandline. |
else if (CMD_RANGE (str)) | * @param argc Where to store the argument count. |
{ | * @param argv Where to store the argument list. Should be at least |
if (cmd_range_match (str, command)) | * CMD_ARGC_MAX elements long. |
{ | * @return CMD_SUCCESS if everything went alright, an error otherwise. |
if (match_type < range_match) | */ |
match_type = range_match; | static int |
matched++; | cmd_parse(struct cmd_element *cmd_element, |
} | vector vline, |
} | int *argc, const char **argv) |
#ifdef HAVE_IPV6 | { |
else if (CMD_IPV6 (str)) | enum matcher_rv rv = cmd_element_match(cmd_element, |
{ | FILTER_RELAXED, |
if (cmd_ipv6_match (command) == exact_match) | vline, -1, |
{ | NULL, NULL, |
if (match_type < ipv6_match) | argc, argv); |
match_type = ipv6_match; | switch (rv) |
matched++; | { |
} | case MATCHER_COMPLETE: |
} | return CMD_SUCCESS; |
else if (CMD_IPV6_PREFIX (str)) | |
{ | case MATCHER_NO_MATCH: |
if (cmd_ipv6_prefix_match (command) == exact_match) | return CMD_ERR_NO_MATCH; |
{ | |
if (match_type < ipv6_prefix_match) | case MATCHER_AMBIGUOUS: |
match_type = ipv6_prefix_match; | return CMD_ERR_AMBIGUOUS; |
matched++; | |
} | case MATCHER_EXCEED_ARGC_MAX: |
} | return CMD_ERR_EXEED_ARGC_MAX; |
#endif /* HAVE_IPV6 */ | |
else if (CMD_IPV4 (str)) | default: |
{ | return CMD_ERR_INCOMPLETE; |
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; | |
} |
} |
|
|
/* Check ambiguous match */ |
/* Check ambiguous match */ |
static int |
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 i; |
unsigned int j; |
unsigned int j; |
const char *str = NULL; |
const char *str = NULL; |
struct cmd_element *cmd_element; |
|
const char *matched = NULL; |
const char *matched = NULL; |
vector descvec; | vector match_vector; |
struct desc *desc; | struct cmd_token *cmd_token; |
|
|
for (i = 0; i < vector_active (v); i++) | if (command == NULL) |
if ((cmd_element = vector_slot (v, i)) != NULL) | command = ""; |
| |
| for (i = 0; i < vector_active (matches); i++) |
| if ((match_vector = vector_slot (matches, i)) != NULL) |
{ |
{ |
int match = 0; |
int match = 0; |
|
|
descvec = vector_slot (cmd_element->strvec, index); | for (j = 0; j < vector_active (match_vector); j++) |
| if ((cmd_token = vector_slot (match_vector, j)) != NULL) |
for (j = 0; j < vector_active (descvec); j++) | |
if ((desc = vector_slot (descvec, j))) | |
{ |
{ |
enum match_type ret; |
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) |
switch (type) |
{ |
{ |
case exact_match: |
case exact_match: |
if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) | if (!TERMINAL_RECORD (cmd_token->terminal) |
&& strcmp (command, str) == 0) |
&& strcmp (command, str) == 0) |
match++; |
match++; |
break; |
break; |
case partly_match: |
case partly_match: |
if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) | if (!TERMINAL_RECORD (cmd_token->terminal) |
&& strncmp (command, str, strlen (command)) == 0) |
&& strncmp (command, str, strlen (command)) == 0) |
{ |
{ |
if (matched && strcmp (matched, str) != 0) |
if (matched && strcmp (matched, str) != 0) |
Line 1409 is_cmd_ambiguous (char *command, vector v, int index,
|
Line 1896 is_cmd_ambiguous (char *command, vector v, int index,
|
break; |
break; |
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
case ipv6_match: |
case ipv6_match: |
if (CMD_IPV6 (str)) | if (cmd_token->terminal == TERMINAL_IPV6) |
match++; |
match++; |
break; |
break; |
case ipv6_prefix_match: |
case ipv6_prefix_match: |
Line 1423 is_cmd_ambiguous (char *command, vector v, int index,
|
Line 1910 is_cmd_ambiguous (char *command, vector v, int index,
|
break; |
break; |
#endif /* HAVE_IPV6 */ |
#endif /* HAVE_IPV6 */ |
case ipv4_match: |
case ipv4_match: |
if (CMD_IPV4 (str)) | if (cmd_token->terminal == TERMINAL_IPV4) |
match++; |
match++; |
break; |
break; |
case ipv4_prefix_match: |
case ipv4_prefix_match: |
Line 1436 is_cmd_ambiguous (char *command, vector v, int index,
|
Line 1923 is_cmd_ambiguous (char *command, vector v, int index,
|
} |
} |
break; |
break; |
case extend_match: |
case extend_match: |
if (CMD_OPTION (str) || CMD_VARIABLE (str)) | if (TERMINAL_RECORD (cmd_token->terminal)) |
match++; |
match++; |
break; |
break; |
case no_match: |
case no_match: |
Line 1445 is_cmd_ambiguous (char *command, vector v, int index,
|
Line 1932 is_cmd_ambiguous (char *command, vector v, int index,
|
} |
} |
} |
} |
if (!match) |
if (!match) |
vector_slot (v, i) = NULL; | vector_slot (cmd_vector, i) = NULL; |
} |
} |
return 0; |
return 0; |
} |
} |
|
|
/* If src matches dst return dst string, otherwise return NULL */ |
/* If src matches dst return dst string, otherwise return NULL */ |
static const char * |
static const char * |
cmd_entry_function (const char *src, const char *dst) | cmd_entry_function (const char *src, struct cmd_token *token) |
{ |
{ |
|
const char *dst = token->cmd; |
|
|
/* Skip variable arguments. */ |
/* Skip variable arguments. */ |
if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) || | if (TERMINAL_RECORD (token->terminal)) |
CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst)) | |
return NULL; |
return NULL; |
|
|
/* In case of 'command \t', given src is NULL string. */ |
/* In case of 'command \t', given src is NULL string. */ |
Line 1474 cmd_entry_function (const char *src, const char *dst)
|
Line 1962 cmd_entry_function (const char *src, const char *dst)
|
/* This version will return the dst string always if it is |
/* This version will return the dst string always if it is |
CMD_VARIABLE for '?' key processing */ |
CMD_VARIABLE for '?' key processing */ |
static const char * |
static const char * |
cmd_entry_function_desc (const char *src, const char *dst) | cmd_entry_function_desc (const char *src, struct cmd_token *token) |
{ |
{ |
if (CMD_VARARG (dst)) | const char *dst = token->cmd; |
return dst; | |
|
|
if (CMD_RANGE (dst)) | switch (token->terminal) |
{ |
{ |
if (cmd_range_match (dst, src)) | case TERMINAL_VARARG: |
return dst; | return dst; |
else | |
return NULL; | |
} | |
|
|
#ifdef HAVE_IPV6 | case TERMINAL_RANGE: |
if (CMD_IPV6 (dst)) | if (cmd_range_match (dst, src)) |
{ | return dst; |
if (cmd_ipv6_match (src)) | else |
return dst; | return NULL; |
else | |
return NULL; | |
} | |
|
|
if (CMD_IPV6_PREFIX (dst)) | case TERMINAL_IPV6: |
{ | if (cmd_ipv6_match (src)) |
if (cmd_ipv6_prefix_match (src)) | return dst; |
return dst; | else |
else | return NULL; |
return NULL; | |
} | |
#endif /* HAVE_IPV6 */ | |
|
|
if (CMD_IPV4 (dst)) | case TERMINAL_IPV6_PREFIX: |
{ | if (cmd_ipv6_prefix_match (src)) |
if (cmd_ipv4_match (src)) | return dst; |
return dst; | else |
else | return NULL; |
return NULL; | |
} | |
|
|
if (CMD_IPV4_PREFIX (dst)) | case TERMINAL_IPV4: |
{ | if (cmd_ipv4_match (src)) |
if (cmd_ipv4_prefix_match (src)) | return dst; |
return dst; | else |
else | return NULL; |
return NULL; | |
} | |
|
|
/* Optional or variable commands always match on '?' */ | case TERMINAL_IPV4_PREFIX: |
if (CMD_OPTION (dst) || CMD_VARIABLE (dst)) | if (cmd_ipv4_prefix_match (src)) |
return dst; | return dst; |
| else |
| return NULL; |
|
|
/* In case of 'command \t', given src is NULL string. */ | /* Optional or variable commands always match on '?' */ |
if (src == NULL) | case TERMINAL_OPTION: |
return dst; | case TERMINAL_VARIABLE: |
| return dst; |
|
|
if (strncmp (src, dst, strlen (src)) == 0) | case TERMINAL_LITERAL: |
return dst; | /* In case of 'command \t', given src is NULL string. */ |
else | if (src == NULL) |
return NULL; | return dst; |
| |
| if (strncmp (src, dst, strlen (src)) == 0) |
| return dst; |
| else |
| return NULL; |
| |
| default: |
| assert(0); |
| 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 |
static int |
cmd_unique_string (vector v, const char *str) |
cmd_unique_string (vector v, const char *str) |
{ |
{ |
Line 1550 cmd_unique_string (vector v, const char *str)
|
Line 2041 cmd_unique_string (vector v, const char *str)
|
return 1; |
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 |
static int |
desc_unique_string (vector v, const char *str) |
desc_unique_string (vector v, const char *str) |
{ |
{ |
unsigned int i; |
unsigned int i; |
struct desc *desc; | struct cmd_token *token; |
|
|
for (i = 0; i < vector_active (v); i++) |
for (i = 0; i < vector_active (v); i++) |
if ((desc = vector_slot (v, i)) != NULL) | if ((token = vector_slot (v, i)) != NULL) |
if (strcmp (desc->cmd, str) == 0) | if (strcmp (token->cmd, str) == 0) |
return 1; | return 0; |
return 0; | return 1; |
} |
} |
|
|
static int |
static int |
Line 1578 cmd_try_do_shortcut (enum node_type node, char* first_
|
Line 2075 cmd_try_do_shortcut (enum node_type node, char* first_
|
return 0; |
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. */ |
/* '?' describe command support. */ |
static vector |
static vector |
cmd_describe_command_real (vector vline, struct vty *vty, int *status) |
cmd_describe_command_real (vector vline, struct vty *vty, int *status) |
Line 1591 cmd_describe_command_real (vector vline, struct vty *v
|
Line 2117 cmd_describe_command_real (vector vline, struct vty *v
|
int ret; |
int ret; |
enum match_type match; |
enum match_type match; |
char *command; |
char *command; |
|
vector matches = NULL; |
|
vector match_vector; |
|
uint32_t command_found = 0; |
|
const char *last_word; |
|
|
/* Set index. */ |
/* Set index. */ |
if (vector_active (vline) == 0) |
if (vector_active (vline) == 0) |
Line 1598 cmd_describe_command_real (vector vline, struct vty *v
|
Line 2128 cmd_describe_command_real (vector vline, struct vty *v
|
*status = CMD_ERR_NO_MATCH; |
*status = CMD_ERR_NO_MATCH; |
return NULL; |
return NULL; |
} |
} |
else | |
index = vector_active (vline) - 1; | index = vector_active (vline) - 1; |
| |
/* Make copy vector of current node's command vector. */ |
/* Make copy vector of current node's command vector. */ |
cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); |
cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); |
|
|
/* Prepare match vector */ |
/* Prepare match vector */ |
matchvec = vector_init (INIT_MATCHVEC_SIZE); |
matchvec = vector_init (INIT_MATCHVEC_SIZE); |
|
|
/* Filter commands. */ | /* Filter commands and build a list how they could possibly continue. */ |
/* Only words precedes current word will be checked in this loop. */ | for (i = 0; i <= index; i++) |
for (i = 0; i < index; i++) | { |
if ((command = vector_slot (vline, i))) | 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; | |
|
|
for (j = 0; j < vector_active (cmd_vector); j++) | if (matches) |
if ((cmd_element = vector_slot (cmd_vector, j)) != NULL | cmd_matches_free(&matches); |
&& (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); | |
|
|
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) | if (ret != CMD_SUCCESS) |
{ | { |
vector_free (cmd_vector); | vector_free (cmd_vector); |
vector_free (matchvec); | vector_free (matchvec); |
*status = CMD_ERR_AMBIGUOUS; | cmd_matches_free(&matches); |
return NULL; | *status = ret; |
} | return NULL; |
else if (ret == 2) | } |
{ | |
vector_free (cmd_vector); | |
vector_free (matchvec); | |
*status = CMD_ERR_NO_MATCH; | |
return NULL; | |
} | |
} | |
|
|
/* Prepare match vector */ | /* The last match may well be ambigious, so break here */ |
/* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ | if (i == index) |
| break; |
|
|
/* Make sure that cmd_vector is filtered based on current word */ | if (match == vararg_match) |
command = vector_slot (vline, index); | { |
if (command) | /* We found a vararg match - so we can throw out the current matches here |
match = cmd_filter_by_completion (command, cmd_vector, index); | * and don't need to continue checking the command input */ |
| unsigned int j, k; |
|
|
|
for (j = 0; j < vector_active (matches); j++) |
|
if ((match_vector = vector_slot (matches, j)) != NULL) |
|
for (k = 0; k < vector_active (match_vector); k++) |
|
{ |
|
struct cmd_token *token = vector_slot (match_vector, k); |
|
vector_set (matchvec, token); |
|
} |
|
|
|
*status = CMD_SUCCESS; |
|
vector_set(matchvec, &token_cr); |
|
vector_free (cmd_vector); |
|
cmd_matches_free(&matches); |
|
cmd_describe_sort(matchvec); |
|
return matchvec; |
|
} |
|
|
|
ret = is_cmd_ambiguous(cmd_vector, command, matches, match); |
|
if (ret == 1) |
|
{ |
|
vector_free (cmd_vector); |
|
vector_free (matchvec); |
|
cmd_matches_free(&matches); |
|
*status = CMD_ERR_AMBIGUOUS; |
|
return NULL; |
|
} |
|
else if (ret == 2) |
|
{ |
|
vector_free (cmd_vector); |
|
vector_free (matchvec); |
|
cmd_matches_free(&matches); |
|
*status = CMD_ERR_NO_MATCH; |
|
return NULL; |
|
} |
|
} |
|
|
/* Make description vector. */ |
/* 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) | { |
{ | if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) |
vector strvec = cmd_element->strvec; | { |
| unsigned int j; |
| vector vline_trimmed; |
|
|
/* if command is NULL, index may be equal to vector_active */ | command_found++; |
if (command && index >= vector_active (strvec)) | last_word = vector_slot(vline, vector_active(vline) - 1); |
vector_slot (cmd_vector, i) = NULL; | if (last_word == NULL || !strlen(last_word)) |
else | { |
{ | vline_trimmed = vector_copy(vline); |
/* Check if command is completed. */ | vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); |
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 (cmd_is_complete(cmd_element, vline_trimmed) |
if ((desc = vector_slot (descvec, j))) | && desc_unique_string(matchvec, command_cr)) |
{ | { |
const char *string; | if (match != vararg_match) |
| vector_set(matchvec, &token_cr); |
| } |
|
|
string = cmd_entry_function_desc (command, desc->cmd); | vector_free(vline_trimmed); |
if (string) | } |
{ | |
/* Uniqueness check */ | match_vector = vector_slot (matches, i); |
if (!desc_unique_string (matchvec, string)) | if (match_vector) |
vector_set (matchvec, desc); | { |
} | for (j = 0; j < vector_active(match_vector); j++) |
} | { |
} | struct cmd_token *token = vector_slot(match_vector, j); |
} | const char *string; |
} | |
| string = cmd_entry_function_desc(command, token); |
| if (string && desc_unique_string(matchvec, string)) |
| vector_set(matchvec, token); |
| } |
| } |
| } |
| } |
| |
| /* |
| * We can get into this situation when the command is complete |
| * but the last part of the command is an optional piece of |
| * the cli. |
| */ |
| last_word = vector_slot(vline, vector_active(vline) - 1); |
| if (command_found == 0 && (last_word == NULL || !strlen(last_word))) |
| vector_set(matchvec, &token_cr); |
| |
vector_free (cmd_vector); |
vector_free (cmd_vector); |
|
cmd_matches_free(&matches); |
|
|
if (vector_slot (matchvec, 0) == NULL) |
if (vector_slot (matchvec, 0) == NULL) |
{ |
{ |
Line 1712 cmd_describe_command_real (vector vline, struct vty *v
|
Line 2266 cmd_describe_command_real (vector vline, struct vty *v
|
} |
} |
|
|
*status = CMD_SUCCESS; |
*status = CMD_SUCCESS; |
|
cmd_describe_sort(matchvec); |
return matchvec; |
return matchvec; |
} |
} |
|
|
Line 1782 cmd_lcd (char **matched)
|
Line 2337 cmd_lcd (char **matched)
|
return lcd; |
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. */ |
/* Command line completion support. */ |
static char ** |
static char ** |
cmd_complete_command_real (vector vline, struct vty *vty, int *status) | cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib) |
{ |
{ |
unsigned int i; |
unsigned int i; |
vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); |
vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); |
#define INIT_MATCHVEC_SIZE 10 |
#define INIT_MATCHVEC_SIZE 10 |
vector matchvec; |
vector matchvec; |
struct cmd_element *cmd_element; |
|
unsigned int index; |
unsigned int index; |
char **match_str; |
char **match_str; |
struct desc *desc; | struct cmd_token *token; |
vector descvec; | |
char *command; |
char *command; |
int lcd; |
int lcd; |
|
vector matches = NULL; |
|
vector match_vector; |
|
|
if (vector_active (vline) == 0) |
if (vector_active (vline) == 0) |
{ |
{ |
Line 1807 cmd_complete_command_real (vector vline, struct vty *v
|
Line 2387 cmd_complete_command_real (vector vline, struct vty *v
|
else |
else |
index = vector_active (vline) - 1; |
index = vector_active (vline) - 1; |
|
|
/* First, filter by preceeding command string */ | /* First, filter by command string */ |
for (i = 0; i < index; i++) | for (i = 0; i <= index; i++) |
if ((command = vector_slot (vline, i))) | { |
{ | command = vector_slot (vline, i); |
enum match_type match; | enum match_type match; |
int ret; | int ret; |
|
|
/* First try completion match, if there is exactly match return 1 */ | if (matches) |
match = cmd_filter_by_completion (command, cmd_vector, i); | cmd_matches_free(&matches); |
|
|
/* If there is exact match then filter ambiguous match else check | /* First try completion match, if there is exactly match return 1 */ |
ambiguousness. */ | ret = cmd_vector_filter(cmd_vector, |
if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) | FILTER_RELAXED, |
{ | vline, i, |
vector_free (cmd_vector); | &match, |
*status = CMD_ERR_AMBIGUOUS; | &matches); |
return NULL; | |
} | 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) |
else if (ret == 2) |
{ |
{ |
vector_free (cmd_vector); |
vector_free (cmd_vector); |
|
cmd_matches_free(&matches); |
*status = CMD_ERR_NO_MATCH; |
*status = CMD_ERR_NO_MATCH; |
return NULL; |
return NULL; |
} |
} |
*/ |
*/ |
} | } |
|
|
/* Prepare match vector. */ |
/* Prepare match vector. */ |
matchvec = vector_init (INIT_MATCHVEC_SIZE); |
matchvec = vector_init (INIT_MATCHVEC_SIZE); |
|
|
/* Now we got into completion */ | /* Build the possible list of continuations into a list of completions */ |
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))) | if ((match_vector = vector_slot (matches, i))) |
{ |
{ |
const char *string; |
const char *string; |
vector strvec = cmd_element->strvec; | unsigned int j; |
|
|
/* Check field length */ | for (j = 0; j < vector_active (match_vector); j++) |
if (index >= vector_active (strvec)) | if ((token = vector_slot (match_vector, j))) |
vector_slot (cmd_vector, i) = NULL; | { |
else | string = cmd_entry_function (vector_slot (vline, index), token); |
{ | if (string && cmd_unique_string (matchvec, string)) |
unsigned int j; | vector_set (matchvec, (islib != 0 ? |
| XSTRDUP (MTYPE_TMP, string) : |
descvec = vector_slot (strvec, index); | strdup (string) /* rl freed */)); |
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)); | |
} | |
} | |
} |
} |
|
|
/* We don't need cmd_vector any more. */ |
/* We don't need cmd_vector any more. */ |
vector_free (cmd_vector); |
vector_free (cmd_vector); |
|
cmd_matches_free(&matches); |
|
|
/* No matched command */ |
/* No matched command */ |
if (vector_slot (matchvec, 0) == NULL) |
if (vector_slot (matchvec, 0) == NULL) |
Line 1906 cmd_complete_command_real (vector vline, struct vty *v
|
Line 2500 cmd_complete_command_real (vector vline, struct vty *v
|
{ |
{ |
char *lcdstr; |
char *lcdstr; |
|
|
lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1); | lcdstr = (islib != 0 ? |
| XMALLOC (MTYPE_TMP, lcd + 1) : |
| malloc(lcd + 1)); |
memcpy (lcdstr, matchvec->index[0], lcd); |
memcpy (lcdstr, matchvec->index[0], lcd); |
lcdstr[lcd] = '\0'; |
lcdstr[lcd] = '\0'; |
|
|
Line 1914 cmd_complete_command_real (vector vline, struct vty *v
|
Line 2510 cmd_complete_command_real (vector vline, struct vty *v
|
|
|
/* Free matchvec. */ |
/* Free matchvec. */ |
for (i = 0; i < vector_active (matchvec); i++) |
for (i = 0; i < vector_active (matchvec); i++) |
{ | { |
if (vector_slot (matchvec, i)) | if (vector_slot (matchvec, i)) |
XFREE (MTYPE_STRVEC, vector_slot (matchvec, i)); | { |
} | if (islib != 0) |
| XFREE (MTYPE_TMP, vector_slot (matchvec, i)); |
| else |
| free (vector_slot (matchvec, i)); |
| } |
| } |
vector_free (matchvec); |
vector_free (matchvec); |
|
|
/* Make new matchvec. */ |
/* Make new matchvec. */ |
Line 1933 cmd_complete_command_real (vector vline, struct vty *v
|
Line 2534 cmd_complete_command_real (vector vline, struct vty *v
|
} |
} |
|
|
match_str = (char **) matchvec->index; |
match_str = (char **) matchvec->index; |
|
cmd_complete_sort(matchvec); |
vector_only_wrapper_free (matchvec); |
vector_only_wrapper_free (matchvec); |
*status = CMD_COMPLETE_LIST_MATCH; |
*status = CMD_COMPLETE_LIST_MATCH; |
return match_str; |
return match_str; |
} |
} |
|
|
char ** |
char ** |
cmd_complete_command (vector vline, struct vty *vty, int *status) | cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) |
{ |
{ |
char **ret; |
char **ret; |
|
|
Line 1960 cmd_complete_command (vector vline, struct vty *vty, i
|
Line 2562 cmd_complete_command (vector vline, struct vty *vty, i
|
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); |
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); |
} |
} |
|
|
ret = cmd_complete_command_real (shifted_vline, vty, status); | ret = cmd_complete_command_real (shifted_vline, vty, status, islib); |
|
|
vector_free(shifted_vline); |
vector_free(shifted_vline); |
vty->node = onode; |
vty->node = onode; |
return ret; |
return ret; |
} |
} |
|
|
|
return cmd_complete_command_real (vline, vty, status, islib); |
|
} |
|
|
return cmd_complete_command_real (vline, vty, status); | char ** |
| cmd_complete_command (vector vline, struct vty *vty, int *status) |
| { |
| return cmd_complete_command_lib (vline, vty, status, 0); |
} |
} |
|
|
/* return parent node */ |
/* return parent node */ |
Line 1983 node_parent ( enum node_type node )
|
Line 2590 node_parent ( enum node_type node )
|
switch (node) |
switch (node) |
{ |
{ |
case BGP_VPNV4_NODE: |
case BGP_VPNV4_NODE: |
|
case BGP_VPNV6_NODE: |
|
case BGP_ENCAP_NODE: |
|
case BGP_ENCAPV6_NODE: |
case BGP_IPV4_NODE: |
case BGP_IPV4_NODE: |
case BGP_IPV4M_NODE: |
case BGP_IPV4M_NODE: |
case BGP_IPV6_NODE: |
case BGP_IPV6_NODE: |
Line 2001 node_parent ( enum node_type node )
|
Line 2611 node_parent ( enum node_type node )
|
|
|
/* Execute command by argument vline vector. */ |
/* Execute command by argument vline vector. */ |
static int |
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) |
struct cmd_element **cmd) |
{ |
{ |
unsigned int i; |
unsigned int i; |
Line 2013 cmd_execute_command_real (vector vline, struct vty *vt
|
Line 2625 cmd_execute_command_real (vector vline, struct vty *vt
|
int argc; |
int argc; |
const char *argv[CMD_ARGC_MAX]; |
const char *argv[CMD_ARGC_MAX]; |
enum match_type match = 0; |
enum match_type match = 0; |
int varflag; |
|
char *command; |
char *command; |
|
int ret; |
|
vector matches; |
|
|
/* Make copy of command elements. */ |
/* Make copy of command elements. */ |
cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); |
cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); |
|
|
for (index = 0; index < vector_active (vline); index++) |
for (index = 0; index < vector_active (vline); index++) |
if ((command = vector_slot (vline, index))) | { |
{ | command = vector_slot (vline, index); |
int ret; | ret = cmd_vector_filter(cmd_vector, |
| filter, |
| vline, index, |
| &match, |
| &matches); |
|
|
match = cmd_filter_by_completion (command, cmd_vector, index); | if (ret != CMD_SUCCESS) |
| { |
| cmd_matches_free(&matches); |
| return ret; |
| } |
|
|
if (match == vararg_match) | if (match == vararg_match) |
| { |
| cmd_matches_free(&matches); |
break; |
break; |
| } |
ret = is_cmd_ambiguous (command, cmd_vector, index, match); | |
|
|
if (ret == 1) | ret = is_cmd_ambiguous (cmd_vector, command, matches, match); |
{ | cmd_matches_free(&matches); |
vector_free (cmd_vector); | |
return CMD_ERR_AMBIGUOUS; | |
} | |
else if (ret == 2) | |
{ | |
vector_free (cmd_vector); | |
return CMD_ERR_NO_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; |
|
} |
|
} |
|
|
/* Check matched count. */ |
/* Check matched count. */ |
matched_element = NULL; |
matched_element = NULL; |
matched_count = 0; |
matched_count = 0; |
Line 2051 cmd_execute_command_real (vector vline, struct vty *vt
|
Line 2676 cmd_execute_command_real (vector vline, struct vty *vt
|
for (i = 0; i < vector_active (cmd_vector); i++) |
for (i = 0; i < vector_active (cmd_vector); i++) |
if ((cmd_element = vector_slot (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; |
matched_element = cmd_element; |
#if 0 |
|
printf ("DEBUG: %s\n", cmd_element->string); |
|
#endif |
|
matched_count++; |
matched_count++; |
} |
} |
else |
else |
Line 2080 cmd_execute_command_real (vector vline, struct vty *vt
|
Line 2702 cmd_execute_command_real (vector vline, struct vty *vt
|
if (matched_count > 1) |
if (matched_count > 1) |
return CMD_ERR_AMBIGUOUS; |
return CMD_ERR_AMBIGUOUS; |
|
|
/* Argument treatment */ | ret = cmd_parse(matched_element, vline, &argc, argv); |
varflag = 0; | if (ret != CMD_SUCCESS) |
argc = 0; | return ret; |
|
|
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. */ |
/* For vtysh execution. */ |
if (cmd) |
if (cmd) |
*cmd = matched_element; |
*cmd = matched_element; |
Line 2121 cmd_execute_command_real (vector vline, struct vty *vt
|
Line 2717 cmd_execute_command_real (vector vline, struct vty *vt
|
return (*matched_element->func) (matched_element, vty, argc, argv); |
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 |
int |
cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, |
cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, |
int vtysh) { |
int vtysh) { |
Line 2144 cmd_execute_command (vector vline, struct vty *vty, st
|
Line 2755 cmd_execute_command (vector vline, struct vty *vty, st
|
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); |
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); |
vector_free(shifted_vline); |
vty->node = onode; |
vty->node = onode; |
Line 2152 cmd_execute_command (vector vline, struct vty *vty, st
|
Line 2763 cmd_execute_command (vector vline, struct vty *vty, st
|
} |
} |
|
|
|
|
saved_ret = ret = cmd_execute_command_real (vline, vty, cmd); | saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); |
|
|
if (vtysh) |
if (vtysh) |
return saved_ret; |
return saved_ret; |
Line 2163 cmd_execute_command (vector vline, struct vty *vty, st
|
Line 2774 cmd_execute_command (vector vline, struct vty *vty, st
|
{ |
{ |
try_node = node_parent(try_node); |
try_node = node_parent(try_node); |
vty->node = 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; |
tried = 1; |
if (ret == CMD_SUCCESS || ret == CMD_WARNING) |
if (ret == CMD_SUCCESS || ret == CMD_WARNING) |
{ |
{ |
Line 2178 cmd_execute_command (vector vline, struct vty *vty, st
|
Line 2789 cmd_execute_command (vector vline, struct vty *vty, st
|
return saved_ret; |
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 |
int |
cmd_execute_command_strict (vector vline, struct vty *vty, |
cmd_execute_command_strict (vector vline, struct vty *vty, |
struct cmd_element **cmd) |
struct cmd_element **cmd) |
{ |
{ |
unsigned int i; | return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); |
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)); | * Parse one line of config, walking up the parse tree attempting to find a match |
| * |
| * @param vty The vty context in which the command should be executed. |
| * @param cmd Pointer where the struct cmd_element* of the match command |
| * will be stored, if any. May be set to NULL if this info is |
| * not needed. |
| * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON |
| * or not. |
| * @return The status of the command that has been executed or an error code |
| * as to why no command could be executed. |
| */ |
| int |
| command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon) |
| { |
| vector vline; |
| int saved_node; |
| int ret; |
|
|
for (index = 0; index < vector_active (vline); index++) | vline = cmd_make_strvec (vty->buf); |
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. */ | /* In case of comment line */ |
if (match == vararg_match) | if (vline == NULL) |
break; | return CMD_SUCCESS; |
| |
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. */ | /* Execute configuration command : this is strict match */ |
matched_element = NULL; | ret = cmd_execute_command_strict (vline, vty, cmd); |
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) | saved_node = vty->node; |
{ | |
matched_element = cmd_element; | |
matched_count++; | |
} | |
else | |
incomplete_count++; | |
} | |
|
|
/* Finish of using cmd_vector. */ | while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && |
vector_free (cmd_vector); | ret != CMD_SUCCESS && ret != CMD_WARNING && |
| ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) { |
| vty->node = node_parent(vty->node); |
| ret = cmd_execute_command_strict (vline, vty, NULL); |
| } |
|
|
/* To execute command, matched_count must be 1. */ | // If climbing the tree did not work then ignore the command and |
if (matched_count == 0) | // stay at the same node |
| if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && |
| ret != CMD_SUCCESS && ret != CMD_WARNING && |
| ret != CMD_ERR_NOTHING_TODO) |
{ |
{ |
if (incomplete_count) | vty->node = saved_node; |
return CMD_ERR_INCOMPLETE; | |
else | |
return CMD_ERR_NO_MATCH; | |
} |
} |
|
|
if (matched_count > 1) | cmd_free_strvec (vline); |
return CMD_ERR_AMBIGUOUS; | |
|
|
/* Argument treatment */ | return ret; |
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); | |
} |
} |
|
|
/* Configration make from file. */ |
/* Configration make from file. */ |
int |
int |
config_from_file (struct vty *vty, FILE *fp) | config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num) |
{ |
{ |
int ret; |
int ret; |
vector vline; | *line_num = 0; |
|
|
while (fgets (vty->buf, VTY_BUFSIZ, fp)) |
while (fgets (vty->buf, VTY_BUFSIZ, fp)) |
{ |
{ |
vline = cmd_make_strvec (vty->buf); | ++(*line_num); |
|
|
/* In case of comment line */ | ret = command_config_read_one_line (vty, NULL, 0); |
if (vline == NULL) | |
continue; | |
/* Execute configuration command : this is strict match */ | |
ret = cmd_execute_command_strict (vline, vty, NULL); | |
|
|
/* Try again with setting node to CONFIG_NODE */ |
|
while (ret != CMD_SUCCESS && ret != CMD_WARNING |
|
&& ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) |
|
{ |
|
vty->node = node_parent(vty->node); |
|
ret = cmd_execute_command_strict (vline, vty, NULL); |
|
} |
|
|
|
cmd_free_strvec (vline); |
|
|
|
if (ret != CMD_SUCCESS && ret != CMD_WARNING |
if (ret != CMD_SUCCESS && ret != CMD_WARNING |
&& ret != CMD_ERR_NOTHING_TODO) |
&& ret != CMD_ERR_NOTHING_TODO) |
return ret; |
return ret; |
Line 2400 DEFUN (config_exit,
|
Line 2949 DEFUN (config_exit,
|
case BGP_NODE: |
case BGP_NODE: |
case RIP_NODE: |
case RIP_NODE: |
case RIPNG_NODE: |
case RIPNG_NODE: |
|
case BABEL_NODE: |
case OSPF_NODE: |
case OSPF_NODE: |
case OSPF6_NODE: |
case OSPF6_NODE: |
case ISIS_NODE: |
case ISIS_NODE: |
case KEYCHAIN_NODE: |
case KEYCHAIN_NODE: |
case MASC_NODE: |
case MASC_NODE: |
case RMAP_NODE: |
case RMAP_NODE: |
|
case PIM_NODE: |
case VTY_NODE: |
case VTY_NODE: |
vty->node = CONFIG_NODE; |
vty->node = CONFIG_NODE; |
break; |
break; |
case BGP_VPNV4_NODE: |
|
case BGP_IPV4_NODE: |
case BGP_IPV4_NODE: |
case BGP_IPV4M_NODE: |
case BGP_IPV4M_NODE: |
|
case BGP_VPNV4_NODE: |
|
case BGP_VPNV6_NODE: |
|
case BGP_ENCAP_NODE: |
|
case BGP_ENCAPV6_NODE: |
case BGP_IPV6_NODE: |
case BGP_IPV6_NODE: |
case BGP_IPV6M_NODE: |
case BGP_IPV6M_NODE: |
vty->node = BGP_NODE; |
vty->node = BGP_NODE; |
Line 2449 DEFUN (config_end,
|
Line 3003 DEFUN (config_end,
|
case ZEBRA_NODE: |
case ZEBRA_NODE: |
case RIP_NODE: |
case RIP_NODE: |
case RIPNG_NODE: |
case RIPNG_NODE: |
|
case BABEL_NODE: |
case BGP_NODE: |
case BGP_NODE: |
|
case BGP_ENCAP_NODE: |
|
case BGP_ENCAPV6_NODE: |
case BGP_VPNV4_NODE: |
case BGP_VPNV4_NODE: |
|
case BGP_VPNV6_NODE: |
case BGP_IPV4_NODE: |
case BGP_IPV4_NODE: |
case BGP_IPV4M_NODE: |
case BGP_IPV4M_NODE: |
case BGP_IPV6_NODE: |
case BGP_IPV6_NODE: |
Line 2462 DEFUN (config_end,
|
Line 3020 DEFUN (config_end,
|
case KEYCHAIN_NODE: |
case KEYCHAIN_NODE: |
case KEYCHAIN_KEY_NODE: |
case KEYCHAIN_KEY_NODE: |
case MASC_NODE: |
case MASC_NODE: |
|
case PIM_NODE: |
case VTY_NODE: |
case VTY_NODE: |
vty_config_unlock (vty); |
vty_config_unlock (vty); |
vty->node = ENABLE_NODE; |
vty->node = ENABLE_NODE; |
Line 2481 DEFUN (show_version,
|
Line 3040 DEFUN (show_version,
|
{ |
{ |
vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", |
vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", |
VTY_NEWLINE); |
VTY_NEWLINE); |
vty_out (vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE); | vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE); |
| vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE, |
| QUAGGA_CONFIG_ARGS, VTY_NEWLINE); |
|
|
return CMD_SUCCESS; |
return CMD_SUCCESS; |
} |
} |
Line 2576 DEFUN (config_write_file,
|
Line 3137 DEFUN (config_write_file,
|
|
|
/* Make vty for configuration file. */ |
/* Make vty for configuration file. */ |
file_vty = vty_new (); |
file_vty = vty_new (); |
file_vty->fd = fd; | file_vty->wfd = fd; |
file_vty->type = VTY_FILE; |
file_vty->type = VTY_FILE; |
|
|
/* Config file header print. */ |
/* Config file header print. */ |
Line 3504 DEFUN (no_banner_motd,
|
Line 4065 DEFUN (no_banner_motd,
|
return CMD_SUCCESS; |
return CMD_SUCCESS; |
} |
} |
|
|
|
DEFUN (show_commandtree, |
|
show_commandtree_cmd, |
|
"show commandtree", |
|
NO_STR |
|
"Show command tree\n") |
|
{ |
|
/* TBD */ |
|
vector cmd_vector; |
|
unsigned int i; |
|
|
|
vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE); |
|
|
|
/* vector of all commands installed at this node */ |
|
cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); |
|
|
|
/* loop over all commands at this node */ |
|
for (i = 0; i < vector_active(cmd_vector); ++i) |
|
{ |
|
struct cmd_element *cmd_element; |
|
|
|
/* A cmd_element (seems to be) is an individual command */ |
|
if ((cmd_element = vector_slot (cmd_vector, i)) == NULL) |
|
continue; |
|
|
|
vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE); |
|
} |
|
|
|
vector_free (cmd_vector); |
|
return CMD_SUCCESS; |
|
} |
|
|
/* Set config filename. Called from vty.c */ |
/* Set config filename. Called from vty.c */ |
void |
void |
host_config_set (char *filename) |
host_config_set (char *filename) |
Line 3533 install_default (enum node_type node)
|
Line 4125 install_default (enum node_type node)
|
void |
void |
cmd_init (int terminal) |
cmd_init (int terminal) |
{ |
{ |
command_cr = XSTRDUP(MTYPE_STRVEC, "<cr>"); | command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>"); |
desc_cr.cmd = command_cr; | token_cr.type = TOKEN_TERMINAL; |
desc_cr.str = XSTRDUP(MTYPE_STRVEC, ""); | token_cr.terminal = TERMINAL_LITERAL; |
| token_cr.cmd = command_cr; |
| token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); |
|
|
/* Allocate initial top vector of commands. */ |
/* Allocate initial top vector of commands. */ |
cmdvec = vector_init (VECTOR_MIN_SIZE); |
cmdvec = vector_init (VECTOR_MIN_SIZE); |
Line 3570 cmd_init (int terminal)
|
Line 4164 cmd_init (int terminal)
|
install_element (VIEW_NODE, &config_terminal_length_cmd); |
install_element (VIEW_NODE, &config_terminal_length_cmd); |
install_element (VIEW_NODE, &config_terminal_no_length_cmd); |
install_element (VIEW_NODE, &config_terminal_no_length_cmd); |
install_element (VIEW_NODE, &show_logging_cmd); |
install_element (VIEW_NODE, &show_logging_cmd); |
|
install_element (VIEW_NODE, &show_commandtree_cmd); |
install_element (VIEW_NODE, &echo_cmd); |
install_element (VIEW_NODE, &echo_cmd); |
|
|
install_element (RESTRICTED_NODE, &config_list_cmd); |
install_element (RESTRICTED_NODE, &config_list_cmd); |
Line 3579 cmd_init (int terminal)
|
Line 4174 cmd_init (int terminal)
|
install_element (RESTRICTED_NODE, &config_enable_cmd); |
install_element (RESTRICTED_NODE, &config_enable_cmd); |
install_element (RESTRICTED_NODE, &config_terminal_length_cmd); |
install_element (RESTRICTED_NODE, &config_terminal_length_cmd); |
install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd); |
install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd); |
|
install_element (RESTRICTED_NODE, &show_commandtree_cmd); |
install_element (RESTRICTED_NODE, &echo_cmd); |
install_element (RESTRICTED_NODE, &echo_cmd); |
} |
} |
|
|
Line 3591 cmd_init (int terminal)
|
Line 4187 cmd_init (int terminal)
|
} |
} |
install_element (ENABLE_NODE, &show_startup_config_cmd); |
install_element (ENABLE_NODE, &show_startup_config_cmd); |
install_element (ENABLE_NODE, &show_version_cmd); |
install_element (ENABLE_NODE, &show_version_cmd); |
|
install_element (ENABLE_NODE, &show_commandtree_cmd); |
|
|
if (terminal) |
if (terminal) |
{ |
{ |
Line 3653 cmd_init (int terminal)
|
Line 4250 cmd_init (int terminal)
|
install_element (VIEW_NODE, &show_work_queues_cmd); |
install_element (VIEW_NODE, &show_work_queues_cmd); |
install_element (ENABLE_NODE, &show_work_queues_cmd); |
install_element (ENABLE_NODE, &show_work_queues_cmd); |
} |
} |
srand(time(NULL)); | install_element (CONFIG_NODE, &show_commandtree_cmd); |
| srandom(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 |
void |
cmd_terminate () |
cmd_terminate () |
{ |
{ |
unsigned int i, j, k, l; | unsigned int i, j; |
struct cmd_node *cmd_node; |
struct cmd_node *cmd_node; |
struct cmd_element *cmd_element; |
struct cmd_element *cmd_element; |
struct desc *desc; | vector cmd_node_v; |
vector cmd_node_v, cmd_element_v, desc_v; | |
|
|
if (cmdvec) |
if (cmdvec) |
{ |
{ |
Line 3673 cmd_terminate ()
|
Line 4318 cmd_terminate ()
|
cmd_node_v = cmd_node->cmd_vector; |
cmd_node_v = cmd_node->cmd_vector; |
|
|
for (j = 0; j < vector_active (cmd_node_v); j++) |
for (j = 0; j < vector_active (cmd_node_v); j++) |
if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL && | if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) |
cmd_element->strvec != NULL) | cmd_terminate_element(cmd_element); |
{ | |
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); |
|
} |
|
|
|
vector_free (cmd_node_v); |
vector_free (cmd_node_v); |
} |
} |
|
|
Line 3706 cmd_terminate ()
|
Line 4329 cmd_terminate ()
|
} |
} |
|
|
if (command_cr) |
if (command_cr) |
XFREE(MTYPE_STRVEC, command_cr); | XFREE(MTYPE_CMD_TOKENS, command_cr); |
if (desc_cr.str) | if (token_cr.desc) |
XFREE(MTYPE_STRVEC, desc_cr.str); | XFREE(MTYPE_CMD_TOKENS, token_cr.desc); |
if (host.name) |
if (host.name) |
XFREE (MTYPE_HOST, host.name); |
XFREE (MTYPE_HOST, host.name); |
if (host.password) |
if (host.password) |