--- embedaddon/quagga/lib/command.c 2013/07/21 23:54:39 1.1.1.3 +++ embedaddon/quagga/lib/command.c 2016/11/02 10:09:11 1.1.1.4 @@ -1,6 +1,8 @@ /* Command interpreter routine for virtual terminal [aka TeletYpe] Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + Copyright (C) 2013 by Open Source Routing. + Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") This file is part of GNU Zebra. @@ -35,9 +37,32 @@ Boston, MA 02111-1307, USA. */ each daemon maintains each own cmdvec. */ vector cmdvec = NULL; -struct desc desc_cr; +struct cmd_token token_cr; char *command_cr = NULL; +enum filter_type +{ + FILTER_RELAXED, + FILTER_STRICT +}; + +enum matcher_rv +{ + MATCHER_OK, + MATCHER_COMPLETE, + MATCHER_INCOMPLETE, + MATCHER_NO_MATCH, + MATCHER_AMBIGUOUS, + MATCHER_EXCEED_ARGC_MAX +}; + +#define MATCHER_ERROR(matcher_rv) \ + ( (matcher_rv) == MATCHER_INCOMPLETE \ + || (matcher_rv) == MATCHER_NO_MATCH \ + || (matcher_rv) == MATCHER_AMBIGUOUS \ + || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ + ) + /* Host information structure. */ struct host host; @@ -156,9 +181,10 @@ print_version (const char *progname) { printf ("%s version %s\n", progname, QUAGGA_VERSION); printf ("%s\n", QUAGGA_COPYRIGHT); + printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS); } - + /* Utility function to concatenate argv argument into a single string with inserting ' ' character between each argument. */ char * @@ -196,53 +222,6 @@ install_node (struct cmd_node *node, node->cmd_vector = vector_init (VECTOR_MIN_SIZE); } -/* Compare two command's string. Used in sort_node (). */ -static int -cmp_node (const void *p, const void *q) -{ - const struct cmd_element *a = *(struct cmd_element * const *)p; - const struct cmd_element *b = *(struct cmd_element * const *)q; - - return strcmp (a->string, b->string); -} - -static int -cmp_desc (const void *p, const void *q) -{ - const struct desc *a = *(struct desc * const *)p; - const struct desc *b = *(struct desc * const *)q; - - return strcmp (a->cmd, b->cmd); -} - -/* Sort each node's command element according to command string. */ -void -sort_node () -{ - unsigned int i, j; - struct cmd_node *cnode; - vector descvec; - struct cmd_element *cmd_element; - - for (i = 0; i < vector_active (cmdvec); i++) - if ((cnode = vector_slot (cmdvec, i)) != NULL) - { - vector cmd_vector = cnode->cmd_vector; - qsort (cmd_vector->index, vector_active (cmd_vector), - sizeof (void *), cmp_node); - - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && vector_active (cmd_element->strvec)) - { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - qsort (descvec->index, vector_active (descvec), - sizeof (void *), cmp_desc); - } - } -} - /* Breaking up string into each command piece. I assume given character is separated by a space character. Return value is a vector which includes char ** data element. */ @@ -312,16 +291,47 @@ cmd_free_strvec (vector v) vector_free (v); } -/* Fetch next description. Used in cmd_make_descvec(). */ +struct format_parser_state +{ + vector topvect; /* Top level vector */ + vector intvect; /* Intermediate level vector, used when there's + * a multiple in a keyword. */ + vector curvect; /* current vector where read tokens should be + appended. */ + + const char *string; /* pointer to command string, not modified */ + const char *cp; /* pointer in command string, moved along while + parsing */ + const char *dp; /* pointer in description string, moved along while + parsing */ + + int in_keyword; /* flag to remember if we are in a keyword group */ + int in_multiple; /* flag to remember if we are in a multiple group */ + int just_read_word; /* flag to remember if the last thing we red was a + * real word and not some abstract token */ +}; + +static void +format_parser_error(struct format_parser_state *state, const char *message) +{ + int offset = state->cp - state->string + 1; + + fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string); + fprintf(stderr, " %*c\n", offset, '^'); + fprintf(stderr, "%s at offset %d.\n", message, offset); + fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n"); + exit(1); +} + static char * -cmd_desc_str (const char **string) +format_parser_desc_str(struct format_parser_state *state) { const char *cp, *start; char *token; int strlen; - - cp = *string; + cp = state->dp; + if (cp == NULL) return NULL; @@ -339,134 +349,247 @@ cmd_desc_str (const char **string) cp++; strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); + token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); memcpy (token, start, strlen); *(token + strlen) = '\0'; - *string = cp; + state->dp = cp; return token; } -/* New string vector. */ -static vector -cmd_make_descvec (const char *string, const char *descstr) +static void +format_parser_begin_keyword(struct format_parser_state *state) { - int multiple = 0; - const char *sp; - char *token; - int len; - const char *cp; - const char *dp; - vector allvec; - vector strvec = NULL; - struct desc *desc; + struct cmd_token *token; + vector keyword_vect; - cp = string; - dp = descstr; + if (state->in_keyword + || state->in_multiple) + format_parser_error(state, "Unexpected '{'"); - if (cp == NULL) - return NULL; + state->cp++; + state->in_keyword = 1; - allvec = vector_init (VECTOR_MIN_SIZE); + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_KEYWORD; + token->keyword = vector_init(VECTOR_MIN_SIZE); - while (1) - { - while (isspace ((int) *cp) && *cp != '\0') - cp++; + keyword_vect = vector_init(VECTOR_MIN_SIZE); + vector_set(token->keyword, keyword_vect); - if (*cp == '(') - { - multiple = 1; - cp++; - } - if (*cp == ')') - { - multiple = 0; - cp++; - } - if (*cp == '|') - { - if (! multiple) - { - fprintf (stderr, "Command parse error!: %s\n", string); - exit (1); - } - cp++; - } - - while (isspace ((int) *cp) && *cp != '\0') - cp++; + vector_set(state->curvect, token); + state->curvect = keyword_vect; +} - if (*cp == '(') - { - multiple = 1; - cp++; - } +static void +format_parser_begin_multiple(struct format_parser_state *state) +{ + struct cmd_token *token; - if (*cp == '\0') - return allvec; + if (state->in_keyword == 1) + format_parser_error(state, "Keyword starting with '('"); - sp = cp; + if (state->in_multiple) + format_parser_error(state, "Nested group"); - while (! (isspace ((int) *cp) || *cp == '\r' || *cp == '\n' || *cp == ')' || *cp == '|') && *cp != '\0') - cp++; + state->cp++; + state->in_multiple = 1; + state->just_read_word = 0; - len = cp - sp; + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_MULTIPLE; + token->multiple = vector_init(VECTOR_MIN_SIZE); - token = XMALLOC (MTYPE_STRVEC, len + 1); - memcpy (token, sp, len); - *(token + len) = '\0'; + vector_set(state->curvect, token); + if (state->curvect != state->topvect) + state->intvect = state->curvect; + state->curvect = token->multiple; +} - desc = XCALLOC (MTYPE_DESC, sizeof (struct desc)); - desc->cmd = token; - desc->str = cmd_desc_str (&dp); +static void +format_parser_end_keyword(struct format_parser_state *state) +{ + if (state->in_multiple + || !state->in_keyword) + format_parser_error(state, "Unexpected '}'"); - if (multiple) - { - if (multiple == 1) - { - strvec = vector_init (VECTOR_MIN_SIZE); - vector_set (allvec, strvec); - } - multiple++; - } - else - { - strvec = vector_init (VECTOR_MIN_SIZE); - vector_set (allvec, strvec); - } - vector_set (strvec, desc); + if (state->in_keyword == 1) + format_parser_error(state, "Empty keyword group"); + + state->cp++; + state->in_keyword = 0; + state->curvect = state->topvect; +} + +static void +format_parser_end_multiple(struct format_parser_state *state) +{ + char *dummy; + + if (!state->in_multiple) + format_parser_error(state, "Unepexted ')'"); + + if (vector_active(state->curvect) == 0) + format_parser_error(state, "Empty multiple section"); + + if (!state->just_read_word) + { + /* There are constructions like + * 'show ip ospf database ... (self-originate|)' + * in use. + * The old parser reads a description string for the + * word '' between |) which will never match. + * Simulate this behvaior by dropping the next desc + * string in such a case. */ + + dummy = format_parser_desc_str(state); + XFREE(MTYPE_CMD_TOKENS, dummy); } + + state->cp++; + state->in_multiple = 0; + + if (state->intvect) + state->curvect = state->intvect; + else + state->curvect = state->topvect; } -/* Count mandantory string vector size. This is to determine inputed - command has enough command length. */ -static int -cmd_cmdsize (vector strvec) +static void +format_parser_handle_pipe(struct format_parser_state *state) { - unsigned int i; - int size = 0; - vector descvec; - struct desc *desc; + struct cmd_token *keyword_token; + vector keyword_vect; - for (i = 0; i < vector_active (strvec); i++) - if ((descvec = vector_slot (strvec, i)) != NULL) + if (state->in_multiple) { - if ((vector_active (descvec)) == 1 - && (desc = vector_slot (descvec, 0)) != NULL) - { - if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) - return size; - else - size++; - } - else - size++; + state->just_read_word = 0; + state->cp++; } - 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. */ const char * cmd_prompt (enum node_type node) @@ -497,11 +620,8 @@ install_element (enum node_type ntype, struct cmd_elem } vector_set (cnode->cmd_vector, cmd); - - if (cmd->strvec == NULL) - cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); - - cmd->cmdsize = cmd_cmdsize (cmd->strvec); + if (cmd->tokens == NULL) + cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); } static const unsigned char itoa64[] = @@ -847,9 +967,6 @@ cmd_ipv4_prefix_match (const char *str) static enum match_type cmd_ipv6_match (const char *str) { - int state = STATE_START; - int colons = 0, nums = 0, double_colon = 0; - const char *sp = NULL; struct sockaddr_in6 sin6_dummy; int ret; @@ -1056,264 +1173,708 @@ cmd_range_match (const char *range, const char *str) return 1; } -/* Make completion match and return match type flag. */ static enum match_type -cmd_filter_by_completion (char *command, vector v, unsigned int index) +cmd_word_match(struct cmd_token *token, + enum filter_type filter, + const char *word) { - unsigned int i; const char *str; - struct cmd_element *cmd_element; enum match_type match_type; - vector descvec; - struct desc *desc; - match_type = no_match; + str = token->cmd; - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) - { - if (index >= vector_active (cmd_element->strvec)) - vector_slot (v, i) = NULL; - else - { - unsigned int j; - int matched = 0; + if (filter == FILTER_RELAXED) + if (!word || !strlen(word)) + return partly_match; - descvec = vector_slot (cmd_element->strvec, index); + if (!word) + return no_match; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; - - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; + switch (token->terminal) + { + case TERMINAL_VARARG: + return vararg_match; - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command)) - { - if (match_type < ipv6_match) - match_type = ipv6_match; + case TERMINAL_RANGE: + if (cmd_range_match(str, word)) + return range_match; + break; - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command)) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; + case TERMINAL_IPV6: + match_type = cmd_ipv6_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv6_match; + break; - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command)) - { - if (match_type < ipv4_match) - match_type = ipv4_match; + case TERMINAL_IPV6_PREFIX: + match_type = cmd_ipv6_prefix_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv6_prefix_match; + break; - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command)) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else - /* Check is this point's argument optional ? */ - if (CMD_OPTION (str) || CMD_VARIABLE (str)) - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else if (strncmp (command, str, strlen (command)) == 0) - { - if (strcmp (command, str) == 0) - match_type = exact_match; - else - { - if (match_type < partly_match) - match_type = partly_match; - } - matched++; - } - } - if (!matched) - vector_slot (v, i) = NULL; - } + case TERMINAL_IPV4: + match_type = cmd_ipv4_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_match; + break; + + case TERMINAL_IPV4_PREFIX: + match_type = cmd_ipv4_prefix_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_prefix_match; + break; + + case TERMINAL_OPTION: + case TERMINAL_VARIABLE: + return extend_match; + + case TERMINAL_LITERAL: + if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) + { + if (!strcmp(str, word)) + return exact_match; + return partly_match; + } + if (filter == FILTER_STRICT && !strcmp(str, word)) + return exact_match; + break; + + default: + assert (0); + } + + return no_match; +} + +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 match_type -cmd_filter_by_string (char *command, vector v, unsigned int index) +static enum matcher_rv +cmd_matcher_match_multiple(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv) { + enum match_type multiple_match; + unsigned int multiple_index; + const char *word; + const char *arg = 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; - 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; - enum match_type match_type; - vector descvec; - struct desc *desc; + enum match_type best_match; + enum match_type element_match; + enum matcher_rv matcher_rv; - match_type = no_match; + best_match = no_match; + *matches = vector_init(VECTOR_MIN_SIZE); - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) + for (i = 0; i < vector_active (commands); i++) + if ((cmd_element = vector_slot (commands, i)) != NULL) { - /* If given index is bigger than max string vector of command, - set NULL */ - if (index >= vector_active (cmd_element->strvec)) - vector_slot (v, i) = NULL; - else - { - unsigned int j; - int matched = 0; + vector_set_index(*matches, i, NULL); + matcher_rv = cmd_element_match(cmd_element, filter, + vline, index, + &element_match, + (vector*)&vector_slot(*matches, i), + NULL, NULL); + if (MATCHER_ERROR(matcher_rv)) + { + vector_slot(commands, i) = NULL; + if (matcher_rv == MATCHER_AMBIGUOUS) + return CMD_ERR_AMBIGUOUS; + if (matcher_rv == MATCHER_EXCEED_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + else if (element_match > best_match) + { + best_match = element_match; + } + } + *match_type = best_match; + return CMD_SUCCESS; +} - descvec = vector_slot (cmd_element->strvec, index); +/** + * Check whether a given commandline is complete if used for a specific + * cmd_element. + * + * @param cmd_element A cmd_element against which the commandline should be + * checked. + * @param vline The tokenized commandline. + * @return 1 if the given commandline is complete, 0 otherwise. + */ +static int +cmd_is_complete(struct cmd_element *cmd_element, + vector vline) +{ + enum matcher_rv rv; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; + rv = cmd_element_match(cmd_element, + FILTER_RELAXED, + vline, -1, + NULL, NULL, + NULL, NULL); + return (rv == MATCHER_COMPLETE); +} - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command) == exact_match) - { - if (match_type < ipv6_match) - match_type = ipv6_match; - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command) == exact_match) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command) == exact_match) - { - if (match_type < ipv4_match) - match_type = ipv4_match; - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command) == exact_match) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else if (CMD_OPTION (str) || CMD_VARIABLE (str)) - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else - { - if (strcmp (command, str) == 0) - { - match_type = exact_match; - matched++; - } - } - } - if (!matched) - vector_slot (v, i) = NULL; - } - } - return match_type; +/** + * Parse a given commandline and construct a list of arguments for the + * given command_element. + * + * @param cmd_element The cmd_element for which we want to construct arguments. + * @param vline The tokenized commandline. + * @param argc Where to store the argument count. + * @param argv Where to store the argument list. Should be at least + * CMD_ARGC_MAX elements long. + * @return CMD_SUCCESS if everything went alright, an error otherwise. + */ +static int +cmd_parse(struct cmd_element *cmd_element, + vector vline, + int *argc, const char **argv) +{ + enum matcher_rv rv = cmd_element_match(cmd_element, + FILTER_RELAXED, + vline, -1, + NULL, NULL, + argc, argv); + switch (rv) + { + case MATCHER_COMPLETE: + return CMD_SUCCESS; + + case MATCHER_NO_MATCH: + return CMD_ERR_NO_MATCH; + + case MATCHER_AMBIGUOUS: + return CMD_ERR_AMBIGUOUS; + + case MATCHER_EXCEED_ARGC_MAX: + return CMD_ERR_EXEED_ARGC_MAX; + + default: + return CMD_ERR_INCOMPLETE; + } } /* Check ambiguous match */ static int -is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) +is_cmd_ambiguous (vector cmd_vector, + const char *command, + vector matches, + enum match_type type) { unsigned int i; unsigned int j; const char *str = NULL; - struct cmd_element *cmd_element; const char *matched = NULL; - vector descvec; - struct desc *desc; + vector match_vector; + struct cmd_token *cmd_token; - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) + if (command == NULL) + command = ""; + + for (i = 0; i < vector_active (matches); i++) + if ((match_vector = vector_slot (matches, i)) != NULL) { int match = 0; - descvec = vector_slot (cmd_element->strvec, index); - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) + for (j = 0; j < vector_active (match_vector); j++) + if ((cmd_token = vector_slot (match_vector, j)) != NULL) { enum match_type ret; - - str = desc->cmd; + assert(cmd_token->type == TOKEN_TERMINAL); + if (cmd_token->type != TOKEN_TERMINAL) + continue; + + str = cmd_token->cmd; + switch (type) { case exact_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + if (!TERMINAL_RECORD (cmd_token->terminal) && strcmp (command, str) == 0) match++; break; case partly_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + if (!TERMINAL_RECORD (cmd_token->terminal) && strncmp (command, str, strlen (command)) == 0) { if (matched && strcmp (matched, str) != 0) @@ -1335,7 +1896,7 @@ is_cmd_ambiguous (char *command, vector v, int index, break; #ifdef HAVE_IPV6 case ipv6_match: - if (CMD_IPV6 (str)) + if (cmd_token->terminal == TERMINAL_IPV6) match++; break; case ipv6_prefix_match: @@ -1349,7 +1910,7 @@ is_cmd_ambiguous (char *command, vector v, int index, break; #endif /* HAVE_IPV6 */ case ipv4_match: - if (CMD_IPV4 (str)) + if (cmd_token->terminal == TERMINAL_IPV4) match++; break; case ipv4_prefix_match: @@ -1362,7 +1923,7 @@ is_cmd_ambiguous (char *command, vector v, int index, } break; case extend_match: - if (CMD_OPTION (str) || CMD_VARIABLE (str)) + if (TERMINAL_RECORD (cmd_token->terminal)) match++; break; case no_match: @@ -1371,18 +1932,19 @@ is_cmd_ambiguous (char *command, vector v, int index, } } if (!match) - vector_slot (v, i) = NULL; + vector_slot (cmd_vector, i) = NULL; } return 0; } /* If src matches dst return dst string, otherwise return NULL */ static const char * -cmd_entry_function (const char *src, const char *dst) +cmd_entry_function (const char *src, struct cmd_token *token) { + const char *dst = token->cmd; + /* Skip variable arguments. */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) || - CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst)) + if (TERMINAL_RECORD (token->terminal)) return NULL; /* In case of 'command \t', given src is NULL string. */ @@ -1400,69 +1962,72 @@ cmd_entry_function (const char *src, const char *dst) /* This version will return the dst string always if it is CMD_VARIABLE for '?' key processing */ static const char * -cmd_entry_function_desc (const char *src, const char *dst) +cmd_entry_function_desc (const char *src, struct cmd_token *token) { - if (CMD_VARARG (dst)) - return dst; + const char *dst = token->cmd; - if (CMD_RANGE (dst)) + switch (token->terminal) { - if (cmd_range_match (dst, src)) - return dst; - else - return NULL; - } + case TERMINAL_VARARG: + return dst; -#ifdef HAVE_IPV6 - if (CMD_IPV6 (dst)) - { - if (cmd_ipv6_match (src)) - return dst; - else - return NULL; - } + case TERMINAL_RANGE: + if (cmd_range_match (dst, src)) + return dst; + else + return NULL; - if (CMD_IPV6_PREFIX (dst)) - { - if (cmd_ipv6_prefix_match (src)) - return dst; - else - return NULL; - } -#endif /* HAVE_IPV6 */ + case TERMINAL_IPV6: + if (cmd_ipv6_match (src)) + return dst; + else + return NULL; - if (CMD_IPV4 (dst)) - { - if (cmd_ipv4_match (src)) - return dst; - else - return NULL; - } + case TERMINAL_IPV6_PREFIX: + if (cmd_ipv6_prefix_match (src)) + return dst; + else + return NULL; - if (CMD_IPV4_PREFIX (dst)) - { - if (cmd_ipv4_prefix_match (src)) - return dst; - else - return NULL; - } + case TERMINAL_IPV4: + if (cmd_ipv4_match (src)) + return dst; + else + return NULL; - /* Optional or variable commands always match on '?' */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst)) - return dst; + case TERMINAL_IPV4_PREFIX: + if (cmd_ipv4_prefix_match (src)) + return dst; + else + return NULL; - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; + /* Optional or variable commands always match on '?' */ + case TERMINAL_OPTION: + case TERMINAL_VARIABLE: + return dst; - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - else - return NULL; + case TERMINAL_LITERAL: + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + if (strncmp (src, dst, strlen (src)) == 0) + return dst; + else + return NULL; + + default: + assert(0); + return NULL; + } } -/* Check same string element existence. If it isn't there return - 1. */ +/** + * Check whether a string is already present in a vector of strings. + * @param v A vector of char*. + * @param str A char*. + * @return 0 if str is already present in the vector, 1 otherwise. + */ static int cmd_unique_string (vector v, const char *str) { @@ -1476,19 +2041,25 @@ cmd_unique_string (vector v, const char *str) return 1; } -/* Compare string to description vector. If there is same string - return 1 else return 0. */ +/** + * Check whether a struct cmd_token matching a given string is already + * present in a vector of struct cmd_token. + * @param v A vector of struct cmd_token*. + * @param str A char* which should be searched for. + * @return 0 if there is a struct cmd_token* with its cmd matching str, + * 1 otherwise. + */ static int desc_unique_string (vector v, const char *str) { unsigned int i; - struct desc *desc; + struct cmd_token *token; for (i = 0; i < vector_active (v); i++) - if ((desc = vector_slot (v, i)) != NULL) - if (strcmp (desc->cmd, str) == 0) - return 1; - return 0; + if ((token = vector_slot (v, i)) != NULL) + if (strcmp (token->cmd, str) == 0) + return 0; + return 1; } static int @@ -1504,6 +2075,35 @@ cmd_try_do_shortcut (enum node_type node, char* first_ return 0; } +static void +cmd_matches_free(vector *matches) +{ + unsigned int i; + vector cmd_matches; + + for (i = 0; i < vector_active(*matches); i++) + if ((cmd_matches = vector_slot(*matches, i)) != NULL) + vector_free(cmd_matches); + vector_free(*matches); + *matches = NULL; +} + +static int +cmd_describe_cmp(const void *a, const void *b) +{ + const struct cmd_token *first = *(struct cmd_token * const *)a; + const struct cmd_token *second = *(struct cmd_token * const *)b; + + return strcmp(first->cmd, second->cmd); +} + +static void +cmd_describe_sort(vector matchvec) +{ + qsort(matchvec->index, vector_active(matchvec), + sizeof(void*), cmd_describe_cmp); +} + /* '?' describe command support. */ static vector cmd_describe_command_real (vector vline, struct vty *vty, int *status) @@ -1517,6 +2117,10 @@ cmd_describe_command_real (vector vline, struct vty *v int ret; enum match_type match; char *command; + vector matches = NULL; + vector match_vector; + uint32_t command_found = 0; + const char *last_word; /* Set index. */ if (vector_active (vline) == 0) @@ -1524,111 +2128,135 @@ cmd_describe_command_real (vector vline, struct vty *v *status = CMD_ERR_NO_MATCH; return NULL; } - else - index = vector_active (vline) - 1; - + + index = vector_active (vline) - 1; + /* Make copy vector of current node's command vector. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); /* Prepare match vector */ matchvec = vector_init (INIT_MATCHVEC_SIZE); - /* Filter commands. */ - /* Only words precedes current word will be checked in this loop. */ - for (i = 0; i < index; i++) - if ((command = vector_slot (vline, i))) - { - match = cmd_filter_by_completion (command, cmd_vector, i); - - if (match == vararg_match) - { - struct cmd_element *cmd_element; - vector descvec; - unsigned int j, k; + /* Filter commands and build a list how they could possibly continue. */ + for (i = 0; i <= index; i++) + { + command = vector_slot (vline, i); - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && (vector_active (cmd_element->strvec))) - { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - for (k = 0; k < vector_active (descvec); k++) - { - struct desc *desc = vector_slot (descvec, k); - vector_set (matchvec, desc); - } - } - - vector_set (matchvec, &desc_cr); - vector_free (cmd_vector); + if (matches) + cmd_matches_free(&matches); - return matchvec; - } + ret = cmd_vector_filter(cmd_vector, + FILTER_RELAXED, + vline, i, + &match, + &matches); - if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) - { - vector_free (cmd_vector); - vector_free (matchvec); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - else if (ret == 2) - { - vector_free (cmd_vector); - vector_free (matchvec); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - } + if (ret != CMD_SUCCESS) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = ret; + return NULL; + } - /* Prepare match vector */ - /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ + /* The last match may well be ambigious, so break here */ + if (i == index) + break; - /* Make sure that cmd_vector is filtered based on current word */ - command = vector_slot (vline, index); - if (command) - match = cmd_filter_by_completion (command, cmd_vector, index); + if (match == vararg_match) + { + /* We found a vararg match - so we can throw out the current matches here + * and don't need to continue checking the command input */ + unsigned int j, k; + for (j = 0; j < vector_active (matches); j++) + if ((match_vector = vector_slot (matches, j)) != NULL) + for (k = 0; k < vector_active (match_vector); k++) + { + struct cmd_token *token = vector_slot (match_vector, k); + vector_set (matchvec, token); + } + + *status = CMD_SUCCESS; + vector_set(matchvec, &token_cr); + vector_free (cmd_vector); + cmd_matches_free(&matches); + cmd_describe_sort(matchvec); + return matchvec; + } + + ret = is_cmd_ambiguous(cmd_vector, command, matches, match); + if (ret == 1) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + else if (ret == 2) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + } + /* Make description vector. */ - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) - { - vector strvec = cmd_element->strvec; + for (i = 0; i < vector_active (matches); i++) + { + if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) + { + unsigned int j; + vector vline_trimmed; - /* if command is NULL, index may be equal to vector_active */ - if (command && index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - /* Check if command is completed. */ - if (command == NULL && index == vector_active (strvec)) - { - if (!desc_unique_string (matchvec, command_cr)) - vector_set (matchvec, &desc_cr); - } - else - { - unsigned int j; - vector descvec = vector_slot (strvec, index); - struct desc *desc; + command_found++; + last_word = vector_slot(vline, vector_active(vline) - 1); + if (last_word == NULL || !strlen(last_word)) + { + vline_trimmed = vector_copy(vline); + vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - const char *string; + if (cmd_is_complete(cmd_element, vline_trimmed) + && desc_unique_string(matchvec, command_cr)) + { + if (match != vararg_match) + vector_set(matchvec, &token_cr); + } - string = cmd_entry_function_desc (command, desc->cmd); - if (string) - { - /* Uniqueness check */ - if (!desc_unique_string (matchvec, string)) - vector_set (matchvec, desc); - } - } - } - } - } + vector_free(vline_trimmed); + } + + match_vector = vector_slot (matches, i); + if (match_vector) + { + for (j = 0; j < vector_active(match_vector); j++) + { + struct cmd_token *token = vector_slot(match_vector, j); + const char *string; + + string = cmd_entry_function_desc(command, token); + 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); + cmd_matches_free(&matches); if (vector_slot (matchvec, 0) == NULL) { @@ -1638,6 +2266,7 @@ cmd_describe_command_real (vector vline, struct vty *v } *status = CMD_SUCCESS; + cmd_describe_sort(matchvec); return matchvec; } @@ -1708,21 +2337,46 @@ cmd_lcd (char **matched) return lcd; } +static int +cmd_complete_cmp(const void *a, const void *b) +{ + const char *first = *(char * const *)a; + const char *second = *(char * const *)b; + + if (!first) + { + if (!second) + return 0; + return 1; + } + if (!second) + return -1; + + return strcmp(first, second); +} + +static void +cmd_complete_sort(vector matchvec) +{ + qsort(matchvec->index, vector_active(matchvec), + sizeof(void*), cmd_complete_cmp); +} + /* Command line completion support. */ static char ** -cmd_complete_command_real (vector vline, struct vty *vty, int *status) +cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib) { unsigned int i; vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); #define INIT_MATCHVEC_SIZE 10 vector matchvec; - struct cmd_element *cmd_element; unsigned int index; char **match_str; - struct desc *desc; - vector descvec; + struct cmd_token *token; char *command; int lcd; + vector matches = NULL; + vector match_vector; if (vector_active (vline) == 0) { @@ -1733,66 +2387,80 @@ cmd_complete_command_real (vector vline, struct vty *v else index = vector_active (vline) - 1; - /* First, filter by preceeding command string */ - for (i = 0; i < index; i++) - if ((command = vector_slot (vline, i))) - { - enum match_type match; - int ret; + /* First, filter by command string */ + for (i = 0; i <= index; i++) + { + command = vector_slot (vline, i); + enum match_type match; + int ret; - /* First try completion match, if there is exactly match return 1 */ - match = cmd_filter_by_completion (command, cmd_vector, i); + if (matches) + cmd_matches_free(&matches); - /* If there is exact match then filter ambiguous match else check - ambiguousness. */ - if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) - { - vector_free (cmd_vector); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - /* + /* First try completion match, if there is exactly match return 1 */ + ret = cmd_vector_filter(cmd_vector, + FILTER_RELAXED, + vline, i, + &match, + &matches); + + if (ret != CMD_SUCCESS) + { + vector_free(cmd_vector); + cmd_matches_free(&matches); + *status = ret; + return NULL; + } + + /* Break here - the completion mustn't be checked to be non-ambiguous */ + if (i == index) + break; + + /* If there is exact match then filter ambiguous match else check + ambiguousness. */ + ret = is_cmd_ambiguous (cmd_vector, command, matches, match); + if (ret == 1) + { + vector_free (cmd_vector); + cmd_matches_free(&matches); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + /* else if (ret == 2) { vector_free (cmd_vector); + cmd_matches_free(&matches); *status = CMD_ERR_NO_MATCH; return NULL; } */ - } + } /* Prepare match vector. */ matchvec = vector_init (INIT_MATCHVEC_SIZE); - /* Now we got into completion */ - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) + /* Build the possible list of continuations into a list of completions */ + for (i = 0; i < vector_active (matches); i++) + if ((match_vector = vector_slot (matches, i))) { const char *string; - vector strvec = cmd_element->strvec; + unsigned int j; - /* Check field length */ - if (index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - unsigned int j; - - descvec = vector_slot (strvec, index); - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - if ((string = - cmd_entry_function (vector_slot (vline, index), - desc->cmd))) - if (cmd_unique_string (matchvec, string)) - vector_set (matchvec, XSTRDUP (MTYPE_TMP, string)); - } - } + for (j = 0; j < vector_active (match_vector); j++) + if ((token = vector_slot (match_vector, j))) + { + string = cmd_entry_function (vector_slot (vline, index), token); + if (string && cmd_unique_string (matchvec, string)) + vector_set (matchvec, (islib != 0 ? + XSTRDUP (MTYPE_TMP, string) : + strdup (string) /* rl freed */)); + } } /* We don't need cmd_vector any more. */ vector_free (cmd_vector); + cmd_matches_free(&matches); /* No matched command */ if (vector_slot (matchvec, 0) == NULL) @@ -1832,7 +2500,9 @@ cmd_complete_command_real (vector vline, struct vty *v { 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); lcdstr[lcd] = '\0'; @@ -1840,10 +2510,15 @@ cmd_complete_command_real (vector vline, struct vty *v /* Free matchvec. */ for (i = 0; i < vector_active (matchvec); i++) - { - if (vector_slot (matchvec, i)) - XFREE (MTYPE_STRVEC, vector_slot (matchvec, i)); - } + { + if (vector_slot (matchvec, i)) + { + if (islib != 0) + XFREE (MTYPE_TMP, vector_slot (matchvec, i)); + else + free (vector_slot (matchvec, i)); + } + } vector_free (matchvec); /* Make new matchvec. */ @@ -1859,13 +2534,14 @@ cmd_complete_command_real (vector vline, struct vty *v } match_str = (char **) matchvec->index; + cmd_complete_sort(matchvec); vector_only_wrapper_free (matchvec); *status = CMD_COMPLETE_LIST_MATCH; return match_str; } 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; @@ -1886,15 +2562,20 @@ cmd_complete_command (vector vline, struct vty *vty, i vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } - ret = cmd_complete_command_real (shifted_vline, vty, status); + ret = cmd_complete_command_real (shifted_vline, vty, status, islib); vector_free(shifted_vline); vty->node = onode; return ret; } + return cmd_complete_command_real (vline, vty, status, islib); +} - return cmd_complete_command_real (vline, vty, status); +char ** +cmd_complete_command (vector vline, struct vty *vty, int *status) +{ + return cmd_complete_command_lib (vline, vty, status, 0); } /* return parent node */ @@ -1909,6 +2590,9 @@ node_parent ( enum node_type node ) switch (node) { case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -1927,7 +2611,9 @@ node_parent ( enum node_type node ) /* Execute command by argument vline vector. */ static int -cmd_execute_command_real (vector vline, struct vty *vty, +cmd_execute_command_real (vector vline, + enum filter_type filter, + struct vty *vty, struct cmd_element **cmd) { unsigned int i; @@ -1939,36 +2625,49 @@ cmd_execute_command_real (vector vline, struct vty *vt int argc; const char *argv[CMD_ARGC_MAX]; enum match_type match = 0; - int varflag; char *command; + int ret; + vector matches; /* Make copy of command elements. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) - { - int ret; + { + command = vector_slot (vline, index); + 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; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); + } - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - else if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } + ret = is_cmd_ambiguous (cmd_vector, command, matches, match); + cmd_matches_free(&matches); + if (ret == 1) + { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } + else if (ret == 2) + { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + /* Check matched count. */ matched_element = NULL; matched_count = 0; @@ -1977,12 +2676,9 @@ cmd_execute_command_real (vector vline, struct vty *vt for (i = 0; i < vector_active (cmd_vector); i++) if ((cmd_element = vector_slot (cmd_vector, i))) { - if (match == vararg_match || index >= cmd_element->cmdsize) + if (cmd_is_complete(cmd_element, vline)) { matched_element = cmd_element; -#if 0 - printf ("DEBUG: %s\n", cmd_element->string); -#endif matched_count++; } else @@ -2006,36 +2702,10 @@ cmd_execute_command_real (vector vline, struct vty *vt if (matched_count > 1) return CMD_ERR_AMBIGUOUS; - /* Argument treatment */ - varflag = 0; - argc = 0; + ret = cmd_parse(matched_element, vline, &argc, argv); + if (ret != CMD_SUCCESS) + 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. */ if (cmd) *cmd = matched_element; @@ -2047,6 +2717,21 @@ cmd_execute_command_real (vector vline, struct vty *vt return (*matched_element->func) (matched_element, vty, argc, argv); } +/** + * Execute a given command, handling things like "do ..." and checking + * whether the given command might apply at a parent node if doesn't + * apply for the current node. + * + * @param vline Command line input, vector of char* where each element is + * one input token. + * @param vty The vty context in which the command should be executed. + * @param cmd Pointer where the struct cmd_element of the matched command + * will be stored, if any. May be set to NULL if this info is + * not needed. + * @param vtysh If set != 0, don't lookup the command at parent nodes. + * @return The status of the command that has been executed or an error code + * as to why no command could be executed. + */ int cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, int vtysh) { @@ -2070,7 +2755,7 @@ cmd_execute_command (vector vline, struct vty *vty, st vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } - ret = cmd_execute_command_real (shifted_vline, vty, cmd); + ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); vector_free(shifted_vline); vty->node = onode; @@ -2078,7 +2763,7 @@ 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) return saved_ret; @@ -2089,7 +2774,7 @@ cmd_execute_command (vector vline, struct vty *vty, st { try_node = node_parent(try_node); vty->node = try_node; - ret = cmd_execute_command_real (vline, vty, cmd); + ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); tried = 1; if (ret == CMD_SUCCESS || ret == CMD_WARNING) { @@ -2104,152 +2789,90 @@ cmd_execute_command (vector vline, struct vty *vty, st return saved_ret; } -/* Execute command by argument readline. */ +/** + * Execute a given command, matching it strictly against the current node. + * This mode is used when reading config files. + * + * @param vline Command line input, vector of char* where each element is + * one input token. + * @param vty The vty context in which the command should be executed. + * @param cmd Pointer where the struct cmd_element* of the matched command + * will be stored, if any. May be set to NULL if this info is + * not needed. + * @return The status of the command that has been executed or an error code + * as to why no command could be executed. + */ int cmd_execute_command_strict (vector vline, struct vty *vty, struct cmd_element **cmd) { - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - int varflag; - enum match_type match = 0; - char *command; + return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); +} - /* 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++) - if ((command = vector_slot (vline, index))) - { - int ret; - - match = cmd_filter_by_string (vector_slot (vline, index), - cmd_vector, index); + vline = cmd_make_strvec (vty->buf); - /* If command meets '.VARARG' then finish matching. */ - if (match == vararg_match) - break; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } + /* In case of comment line */ + if (vline == NULL) + return CMD_SUCCESS; - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - for (i = 0; i < vector_active (cmd_vector); i++) - if (vector_slot (cmd_vector, i) != NULL) - { - cmd_element = vector_slot (cmd_vector, i); + /* Execute configuration command : this is strict match */ + ret = cmd_execute_command_strict (vline, vty, cmd); - if (match == vararg_match || index >= cmd_element->cmdsize) - { - matched_element = cmd_element; - matched_count++; - } - else - incomplete_count++; - } + saved_node = vty->node; - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); + while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && + 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 (matched_count == 0) + // If climbing the tree did not work then ignore the command and + // 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) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; + vty->node = saved_node; } - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; + cmd_free_strvec (vline); - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active (vline); i++) - { - if (varflag) - argv[argc++] = vector_slot (vline, i); - else - { - vector descvec = vector_slot (matched_element->strvec, i); - - if (vector_active (descvec) == 1) - { - struct desc *desc = vector_slot (descvec, 0); - - if (CMD_VARARG (desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) - argv[argc++] = vector_slot (vline, i); - } - else - argv[argc++] = vector_slot (vline, i); - } - - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } - - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; - - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; - - /* Now execute matched command */ - return (*matched_element->func) (matched_element, vty, argc, argv); + return ret; } /* Configration make from file. */ int -config_from_file (struct vty *vty, FILE *fp) +config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num) { int ret; - vector vline; + *line_num = 0; while (fgets (vty->buf, VTY_BUFSIZ, fp)) { - vline = cmd_make_strvec (vty->buf); + ++(*line_num); - /* In case of comment line */ - if (vline == NULL) - continue; - /* Execute configuration command : this is strict match */ - ret = cmd_execute_command_strict (vline, vty, NULL); + ret = command_config_read_one_line (vty, NULL, 0); - /* 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 && ret != CMD_ERR_NOTHING_TODO) return ret; @@ -2333,12 +2956,16 @@ DEFUN (config_exit, case KEYCHAIN_NODE: case MASC_NODE: case RMAP_NODE: + case PIM_NODE: case VTY_NODE: vty->node = CONFIG_NODE; break; - case BGP_VPNV4_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: + case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: vty->node = BGP_NODE; @@ -2378,7 +3005,10 @@ DEFUN (config_end, case RIPNG_NODE: case BABEL_NODE: case BGP_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -2390,6 +3020,7 @@ DEFUN (config_end, case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; @@ -2410,6 +3041,8 @@ DEFUN (show_version, vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", VTY_NEWLINE); vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE); + vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE, + QUAGGA_CONFIG_ARGS, VTY_NEWLINE); return CMD_SUCCESS; } @@ -2504,7 +3137,7 @@ DEFUN (config_write_file, /* Make vty for configuration file. */ file_vty = vty_new (); - file_vty->fd = fd; + file_vty->wfd = fd; file_vty->type = VTY_FILE; /* Config file header print. */ @@ -3432,6 +4065,37 @@ DEFUN (no_banner_motd, return CMD_SUCCESS; } +DEFUN (show_commandtree, + show_commandtree_cmd, + "show commandtree", + NO_STR + "Show command tree\n") +{ + /* TBD */ + vector cmd_vector; + unsigned int i; + + vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE); + + /* vector of all commands installed at this node */ + cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + + /* loop over all commands at this node */ + for (i = 0; i < vector_active(cmd_vector); ++i) + { + struct cmd_element *cmd_element; + + /* A cmd_element (seems to be) is an individual command */ + if ((cmd_element = vector_slot (cmd_vector, i)) == NULL) + continue; + + vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE); + } + + vector_free (cmd_vector); + return CMD_SUCCESS; +} + /* Set config filename. Called from vty.c */ void host_config_set (char *filename) @@ -3461,9 +4125,11 @@ install_default (enum node_type node) void cmd_init (int terminal) { - command_cr = XSTRDUP(MTYPE_STRVEC, ""); - desc_cr.cmd = command_cr; - desc_cr.str = XSTRDUP(MTYPE_STRVEC, ""); + command_cr = XSTRDUP(MTYPE_CMD_TOKENS, ""); + token_cr.type = TOKEN_TERMINAL; + token_cr.terminal = TERMINAL_LITERAL; + token_cr.cmd = command_cr; + token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); /* Allocate initial top vector of commands. */ cmdvec = vector_init (VECTOR_MIN_SIZE); @@ -3498,6 +4164,7 @@ cmd_init (int terminal) install_element (VIEW_NODE, &config_terminal_length_cmd); install_element (VIEW_NODE, &config_terminal_no_length_cmd); install_element (VIEW_NODE, &show_logging_cmd); + install_element (VIEW_NODE, &show_commandtree_cmd); install_element (VIEW_NODE, &echo_cmd); install_element (RESTRICTED_NODE, &config_list_cmd); @@ -3507,6 +4174,7 @@ cmd_init (int terminal) install_element (RESTRICTED_NODE, &config_enable_cmd); install_element (RESTRICTED_NODE, &config_terminal_length_cmd); install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd); + install_element (RESTRICTED_NODE, &show_commandtree_cmd); install_element (RESTRICTED_NODE, &echo_cmd); } @@ -3519,6 +4187,7 @@ cmd_init (int terminal) } install_element (ENABLE_NODE, &show_startup_config_cmd); install_element (ENABLE_NODE, &show_version_cmd); + install_element (ENABLE_NODE, &show_commandtree_cmd); if (terminal) { @@ -3581,17 +4250,65 @@ cmd_init (int terminal) install_element (VIEW_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 cmd_terminate () { - unsigned int i, j, k, l; + unsigned int i, j; struct cmd_node *cmd_node; struct cmd_element *cmd_element; - struct desc *desc; - vector cmd_node_v, cmd_element_v, desc_v; + vector cmd_node_v; if (cmdvec) { @@ -3601,31 +4318,9 @@ cmd_terminate () cmd_node_v = cmd_node->cmd_vector; for (j = 0; j < vector_active (cmd_node_v); j++) - if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL && - cmd_element->strvec != NULL) - { - cmd_element_v = cmd_element->strvec; + if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) + cmd_terminate_element(cmd_element); - 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); } @@ -3634,9 +4329,9 @@ cmd_terminate () } if (command_cr) - XFREE(MTYPE_STRVEC, command_cr); - if (desc_cr.str) - XFREE(MTYPE_STRVEC, desc_cr.str); + XFREE(MTYPE_CMD_TOKENS, command_cr); + if (token_cr.desc) + XFREE(MTYPE_CMD_TOKENS, token_cr.desc); if (host.name) XFREE (MTYPE_HOST, host.name); if (host.password)