|
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) |