diff options
Diffstat (limited to 'lib/command.c')
-rw-r--r-- | lib/command.c | 1860 |
1 files changed, 1025 insertions, 835 deletions
diff --git a/lib/command.c b/lib/command.c index 251c8963..6e1726dc 100644 --- a/lib/command.c +++ b/lib/command.c @@ -33,6 +33,7 @@ Boston, MA 02111-1307, USA. */ #include "uty.h" #include "qstring.h" #include "command.h" +//#include "lib/route_types.h" #include "workqueue.h" #include "command_queue.h" @@ -172,7 +173,7 @@ print_version (const char *progname) /* Utility function to concatenate argv argument into a single string with inserting ' ' character between each argument. */ char * -argv_concat (const char **argv, int argc, int shift) +argv_concat (const char* const* argv, int argc, int shift) { int i; size_t len; @@ -196,6 +197,28 @@ argv_concat (const char **argv, int argc, int shift) return str; } +#if 0 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +/* 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); +} + +#endif //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + /* Install top node of command vector. */ void install_node (struct cmd_node *node, @@ -208,41 +231,57 @@ install_node (struct cmd_node *node, /* Compare two command's string. Used in sort_node (). */ static int -cmp_node (const struct cmd_element *a, const struct cmd_element *b) +cmp_node (const struct cmd_element **a, const struct cmd_element **b) { - return strcmp (a->string, b->string); + return strcmp ((*a)->string, (*b)->string); } static int -cmp_desc (const struct desc *a, const struct desc *b) +cmp_desc (const struct desc **a, const struct desc **b) { - return strcmp (a->cmd, b->cmd); + 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; - struct cmd_element *cmd_element; + unsigned int i ; - for (i = 0; i < vector_active (cmdvec); i++) - if ((cnode = vector_slot (cmdvec, i)) != NULL) - { - vector cmd_vector = cnode->cmd_vector; - vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ; + for (i = 0; i < vector_length(cmdvec); i++) + { + struct cmd_node *cnode; + vector cmd_vector ; + unsigned int j; - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && vector_active (cmd_element->strvec)) - { - vector descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - vector_sort(descvec, (vector_sort_cmp*)cmp_desc) ; - } - } -} + cnode = vector_get_item(cmdvec, i) ; + + if (cnode == NULL) + continue ; + + cmd_vector = cnode->cmd_vector; + if (cmd_vector == NULL) + continue ; + + vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ; + + for (j = 0; j < vector_length(cmd_vector); j++) + { + struct cmd_element *cmd_element ; + vector descvec ; + + cmd_element = vector_get_item (cmd_vector, j); + if (cmd_element == NULL) + continue ; + + descvec = vector_get_last_item(cmd_element->strvec) ; + if (descvec == NULL) + continue ; + + vector_sort(descvec, (vector_sort_cmp*)cmp_desc) ; + } ; + } ; +} ; /*------------------------------------------------------------------------------ * Take string and break it into tokens. @@ -260,57 +299,132 @@ sort_node () * Returns: NULL => empty line (after white-space trimming) or comment line. * otherwise: is vector containing one or more tokens. * + * .... + * + * * Note: all the tokens in the vector have at least one character, and no * entries are NULL. * * NB: it is the caller's responsibility to release the vector and its contents, * see cmd_free_strvec(). */ -extern vector -cmd_make_strvec (const char *string) +static vector +cmd_make_vline(vector vline, qstring qs, const char *string) { - const char *cp, *start; - char *token; - int strlen; - vector strvec; + char *token, *tp ; + const char *cp, *sp, *ep, *op ; - if (string == NULL) - return NULL; + /* Reset any existing vline, and empty the qstring if given. */ + if (vline != NULL) + vector_set_length(vline, 0) ; + qs_clear(qs) ; + + /* Strip leading and trailing white-space and deal with empty or effectively + * empty lines -- comment lines are treated as effectively empty. + */ cp = string; - /* Skip white spaces. */ + if (string == NULL) + return NULL; + while (isspace((int) *cp)) cp++; - /* Return if line is empty or effectively empty */ - if ((*cp == '\0') || (*cp == '!') || (*cp == '#')) + ep = cp + strlen(cp) ; + + while ((ep > cp) && (isspace((int)*(ep - 1)))) + --ep ; + + if ((cp == ep) || (*cp == '!') || (*cp == '#')) return NULL; /* Prepare return vector -- expect some reasonable number of tokens. */ - strvec = vector_init (10) ; + if (vline == NULL) + vline = vector_init(10) ; - /* Copy each command piece and set into vector. */ - while (1) + /* If writing the words to a qstring, copy the body of the original (less + * any leading/trailing whitespace) to the qstring and '\0' terminate. + */ + if (qs != NULL) { - start = cp; - while (!isspace((int) *cp) && (*cp != '\0')) - cp++ ; /* eat token characters */ - strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - vector_push_item(strvec, token); + qs_set_n(qs, cp, ep - cp) ; + tp = (char*)qs->body ; /* start at the beginning */ + } + else + tp = NULL ; /* not used, but not undefined */ + op = cp ; /* original start position */ + + /* Now have string cp..ep with no leading/trailing whitespace. + * + * If using a qstring, a copy of that exists at tp, complete with terminating + * '\0'. Writes '\0' terminators after each word found -- overwriting first + * separating white-space or the '\0' at the end. + * + * If not using a qstring, construct a new MTYPE_STRVEC for each word. + */ + while (cp < ep) + { while (isspace((int) *cp)) - cp++ ; /* skip white-space */ + cp++ ; /* skip white-space */ - if (*cp == '\0') - return strvec; - } + sp = cp ; + while ((cp < ep) && !isspace((int) *cp)) + cp++ ; /* eat token characters */ + + if (qs == NULL) + { + /* creating array of MTYPE_STRVEC */ + size_t len ; + + len = cp - sp ; + token = XMALLOC (MTYPE_STRVEC, len + 1); + memcpy (token, sp, len); + *(token + len) = '\0'; + } + else + { + /* using qstring */ + token = tp + (sp - op) ; /* token in qstring */ + *(tp + (cp - op)) = '\0' ; /* terminate */ + } ; + + vector_push_item(vline, token); + } ; + + return vline ; } /*------------------------------------------------------------------------------ + * Take string and break it into tokens. + * + * Discards leading and trailing white-space. + * + * Treats lines that start with '!' or '#' (after any leading white-space) + * as empty -- these are comment lines. + * + * Tokens are non-whitespace separated by one or more white-space. + * + * White-space is anything that isspace() thinks is a space. (Which in the + * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.) + * + * Returns: NULL => empty line (after white-space trimming) or comment line. + * otherwise: is vector containing one or more tokens. + * + * Note: all the tokens in the vector have at least one character, and no + * entries are NULL. + * + * NB: it is the caller's responsibility to release the vector and its contents, + * see cmd_free_strvec(). + */ +extern vector +cmd_make_strvec (const char *string) +{ + return cmd_make_vline(NULL, NULL, string) ; +} ; + +/*------------------------------------------------------------------------------ * Add given string to vector of strings. * * Create vector if required. @@ -478,23 +592,27 @@ cmd_cmdsize (vector strvec) { unsigned int i; int size = 0; - vector descvec; - struct desc *desc; - for (i = 0; i < vector_active (strvec); i++) - if ((descvec = vector_slot (strvec, i)) != NULL) + for (i = 0; i < vector_length(strvec); i++) { - 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++; - } + vector descvec; + + descvec = vector_get_item (strvec, i) ; + if (descvec == NULL) + continue ; + + if (vector_length(descvec) == 1) + { + struct desc *desc; + + desc = vector_get_item(descvec, 0) ; + if (desc != NULL) + if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) + break ; + } + size++; + } ; + return size; } @@ -509,7 +627,7 @@ cmd_prompt (enum node_type node) cnode = NULL ; if (node < cmdvec->limit) - cnode = vector_slot (cmdvec, node); + cnode = vector_get_item (cmdvec, node); if (cnode == NULL) { @@ -530,7 +648,7 @@ install_element (enum node_type ntype, struct cmd_element *cmd) if (!cmdvec) return; - cnode = vector_slot (cmdvec, ntype); + cnode = vector_get_item (cmdvec, ntype); if (cnode == NULL) { @@ -675,7 +793,7 @@ config_write_host (struct vty *vty) static vector cmd_node_vector (vector v, enum node_type ntype) { - struct cmd_node *cnode = vector_slot (v, ntype); + struct cmd_node *cnode = vector_get_item (v, ntype); return cnode->cmd_vector; } @@ -1290,12 +1408,14 @@ static enum match_type cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index) { unsigned int i; + unsigned int k; enum match_type match_type; match_type = no_match; - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (cmd_v); i++) + /* If command and cmd_element string does not match, remove from vector */ + k = 0 ; + for (i = 0; i < vector_length (cmd_v); i++) { const char *str; struct cmd_element *cmd_element; @@ -1304,110 +1424,113 @@ cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index) unsigned int j; int matched ; + cmd_element = vector_get_item(cmd_v, i) ; + /* Skip past cmd_v entries that have already been set NULL */ - if ((cmd_element = vector_slot (cmd_v, i)) == NULL) + if (cmd_element == NULL) continue ; /* Discard cmd_v entry that has no token at the current position */ - if (index >= vector_active (cmd_element->strvec)) - { - vector_slot (cmd_v, i) = NULL; - continue ; - } ; + descvec = vector_get_item(cmd_element->strvec, index) ; + if (descvec == NULL) + continue ; /* See if get any sort of match at current position */ matched = 0 ; - descvec = vector_slot (cmd_element->strvec, index); - - 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; + for (j = 0; j < vector_length (descvec); j++) + { + desc = vector_get_item(descvec, j) ; + if (desc == NULL) + continue ; + + str = desc->cmd; + + if (CMD_VARARG (str)) + { + if (match_type < vararg_match) + match_type = vararg_match; + matched++; + } + else if (CMD_RANGE (str)) + { + if (cmd_range_match (str, command)) + { + if (match_type < range_match) + match_type = range_match; - matched++; - } - } + matched++; + } + } #ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command)) - { - if (match_type < ipv6_match) - match_type = ipv6_match; + else if (CMD_IPV6 (str)) + { + if (cmd_ipv6_match (command)) + { + if (match_type < ipv6_match) + match_type = ipv6_match; - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command)) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; + matched++; + } + } + else if (CMD_IPV6_PREFIX (str)) + { + if (cmd_ipv6_prefix_match (command)) + { + if (match_type < ipv6_prefix_match) + match_type = ipv6_prefix_match; - matched++; - } - } + matched++; + } + } #endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command)) - { - if (match_type < ipv4_match) - match_type = ipv4_match; + else if (CMD_IPV4 (str)) + { + if (cmd_ipv4_match (command)) + { + if (match_type < ipv4_match) + match_type = ipv4_match; - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command)) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else if (CMD_OPTION (str) || CMD_VARIABLE (str)) - /* Check is this point's argument optional ? */ - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else if (strncmp (command, str, strlen (command)) == 0) - { - if (strcmp (command, str) == 0) - match_type = exact_match; - else - { - if (match_type < partly_match) - match_type = partly_match; - } - matched++; - } ; - } ; + matched++; + } + } + else if (CMD_IPV4_PREFIX (str)) + { + if (cmd_ipv4_prefix_match (command)) + { + if (match_type < ipv4_prefix_match) + match_type = ipv4_prefix_match; + matched++; + } + } + else if (CMD_OPTION (str) || CMD_VARIABLE (str)) + /* Check is this point's argument optional ? */ + { + if (match_type < extend_match) + match_type = extend_match; + matched++; + } + else if (strncmp (command, str, strlen (command)) == 0) + { + if (strcmp (command, str) == 0) + match_type = exact_match; + else + { + if (match_type < partly_match) + match_type = partly_match; + } + matched++; + } ; + } ; - /* Discard cmd_v entry that has no match at this position */ - if (!matched) - vector_slot (cmd_v, i) = NULL; - } + /* Keep cmd_v entry that has a match at this position */ + if (matched) + vector_set_item(cmd_v, k++, cmd_element) ; + } ; + + vector_set_length(cmd_v, k) ; /* discard what did not keep */ return match_type; -} +} ; /*------------------------------------------------------------------------------ * Filter vector by command character with index. @@ -1420,107 +1543,120 @@ cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index) static enum match_type cmd_filter_by_string (char *command, vector cmd_v, unsigned int index) { - unsigned int i; - const char *str; - struct cmd_element *cmd_element; + unsigned int i ; + unsigned int k ; enum match_type match_type; - vector descvec; - struct desc *desc; match_type = no_match; - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (cmd_v); i++) - if ((cmd_element = vector_slot (cmd_v, i)) != NULL) - { - /* If given index is bigger than max string vector of command, - set NULL */ - if (index >= vector_active (cmd_element->strvec)) - vector_slot (cmd_v, i) = NULL; - else - { - unsigned int j; - int matched = 0; + /* If command and cmd_element string do match, keep in vector */ + k = 0 ; + for (i = 0; i < vector_length(cmd_v); i++) + { + unsigned int j; + int matched ; + const char *str; + struct cmd_element *cmd_element; + vector descvec; + struct desc *desc; - descvec = vector_slot (cmd_element->strvec, index); + cmd_element = vector_get_item(cmd_v, i) ; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; + /* Skip past NULL cmd_v entries (just in case) */ + if (cmd_element == NULL) + continue ; - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; - matched++; - } - } + /* Discard cmd_v entry that has no token at the current position */ + descvec = vector_get_item (cmd_element->strvec, index) ; + if (descvec == NULL) + continue ; + + /* See if have a match against any of the current possibilities */ + matched = 0 ; + for (j = 0; j < vector_length(descvec); j++) + { + desc = vector_get_item (descvec, j) ; + if (desc == NULL) + continue ; + + str = desc->cmd; + + if (CMD_VARARG (str)) + { + if (match_type < vararg_match) + match_type = vararg_match; + matched++; + } + else if (CMD_RANGE (str)) + { + if (cmd_range_match (str, command)) + { + if (match_type < range_match) + match_type = range_match; + matched++; + } + } #ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command) == 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++; - } - } + 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 (cmd_v, i) = NULL; - } - } + 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++; + } ; + } ; + } ; + + /* Keep cmd_element if have a match */ + if (matched) + vector_set_item(cmd_v, k++, cmd_element) ; + } ; + + vector_set_length(cmd_v, k) ; /* discard what did not keep */ + return match_type; } @@ -1558,102 +1694,146 @@ cmd_filter_by_string (char *command, vector cmd_v, unsigned int index) * than one number range. * 2 => partial match for ipv4_prefix or ipv6_prefix * (missing '/' or no digits after '/'). + * + * NB: it is assumed that cannot find both 1 and 2 states. But in any case, + * returns 1 in preference. */ static int is_cmd_ambiguous (char *command, vector cmd_v, int index, 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; + unsigned int k; + int ret ; - for (i = 0; i < vector_active (cmd_v); i++) - if ((cmd_element = vector_slot (cmd_v, i)) != NULL) - { - int match = 0; + ret = 0 ; /* all's well so far */ + k = 0 ; /* nothing kept, yet */ - descvec = vector_slot (cmd_element->strvec, index); + for (i = 0; i < vector_length (cmd_v); i++) + { + unsigned int j; + struct cmd_element *cmd_element; + const char *str_matched ; + vector descvec; + struct desc *desc; + int matched ; + enum match_type mt ; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - enum match_type ret; + cmd_element = vector_get_item (cmd_v, i) ; - str = desc->cmd; + /* Skip past NULL cmd_v entries (just in case) */ + if (cmd_element == NULL) + continue ; + + /* The cmd_v entry MUST have a token at the current position */ + descvec = vector_get_item (cmd_element->strvec, index) ; + assert(descvec != NULL) ; + + /* See if have a match against any of the current possibilities + * + * str_matched is set the first time get a partial string match, + * or the first time get a number range match. + * + * If get a second partial string match or number range match, then + * unless + */ + str_matched = NULL ; + matched = 0; + for (j = 0; j < vector_length (descvec); j++) + { + enum match_type ret; + const char *str ; + + desc = vector_get_item (descvec, j) ; + if (desc == NULL) + continue ; + + str = desc->cmd; + + switch (type) + { + case exact_match: + if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + && strcmp (command, str) == 0) + matched++; + break; + + case partly_match: + if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + && strncmp (command, str, strlen (command)) == 0) + { + if (str_matched && (strcmp (str_matched, str) != 0)) + ret = 1; /* There is ambiguous match. */ + else + str_matched = str; + matched++; + } + break; + + case range_match: + if (cmd_range_match (str, command)) + { + if (str_matched && strcmp (str_matched, str) != 0) + ret = 1; + else + str_matched = str; + matched++; + } + break; - switch (type) - { - case exact_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) - && strcmp (command, str) == 0) - match++; - break; - case partly_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) - && strncmp (command, str, strlen (command)) == 0) - { - if (matched && strcmp (matched, str) != 0) - return 1; /* There is ambiguous match. */ - else - matched = str; - match++; - } - break; - case range_match: - if (cmd_range_match (str, command)) - { - if (matched && strcmp (matched, str) != 0) - return 1; - else - matched = str; - match++; - } - break; #ifdef HAVE_IPV6 - case ipv6_match: - if (CMD_IPV6 (str)) - match++; - break; - case ipv6_prefix_match: - if ((ret = cmd_ipv6_prefix_match (command)) != no_match) - { - if (ret == partly_match) - return 2; /* There is incomplete match. */ + case ipv6_match: + if (CMD_IPV6 (str)) + matched++; + break; - match++; - } - break; + case ipv6_prefix_match: + if ((mt = cmd_ipv6_prefix_match (command)) != no_match) + { + if (mt == partly_match) + if (ret != 1) + ret = 2; /* There is incomplete match. */ + + matched++; + } + break; #endif /* HAVE_IPV6 */ - case ipv4_match: - if (CMD_IPV4 (str)) - match++; - break; - case ipv4_prefix_match: - if ((ret = cmd_ipv4_prefix_match (command)) != no_match) - { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - match++; - } - break; - case extend_match: - if (CMD_OPTION (str) || CMD_VARIABLE (str)) - match++; - break; - case no_match: - default: - break; - } - } - if (!match) - vector_slot (cmd_v, i) = NULL; - } - return 0; -} + case ipv4_match: + if (CMD_IPV4 (str)) + matched++; + break; + + case ipv4_prefix_match: + if ((mt = cmd_ipv4_prefix_match (command)) != no_match) + { + if (mt == partly_match) + if (ret != 1) + ret = 2; /* There is incomplete match. */ + + matched++; + } + break; + + case extend_match: + if (CMD_OPTION (str) || CMD_VARIABLE (str)) + matched++; + break; + + case no_match: + default: + break; + } ; + } ; + + /* Keep cmd_element if have a match */ + if (matched) + vector_set_item(cmd_v, k++, cmd_element) ; + } ; + + vector_set_length(cmd_v, k) ; /* discard what did not keep */ + + return ret ; +} ; /*------------------------------------------------------------------------------ * If src matches dst return dst string, otherwise return NULL @@ -1748,14 +1928,14 @@ cmd_entry_function_desc (const char *src, const char *dst) * Returns: 0 => found same string in the vector * 1 => NOT found same string in the vector */ -static int +static bool cmd_unique_string (vector v, const char *str) { unsigned int i; char *match; - for (i = 0; i < vector_active (v); i++) - if ((match = vector_slot (v, i)) != NULL) + for (i = 0; i < vector_length (v); i++) + if ((match = vector_get_item (v, i)) != NULL) if (strcmp (match, str) == 0) return 0; return 1; @@ -1763,20 +1943,20 @@ cmd_unique_string (vector v, const char *str) /* Compare string to description vector. If there is same string return 1 else return 0. */ -static int +static bool desc_unique_string (vector v, const char *str) { unsigned int i; struct desc *desc; - for (i = 0; i < vector_active (v); i++) - if ((desc = vector_slot (v, i)) != NULL) + for (i = 0; i < vector_length (v); i++) + if ((desc = vector_get_item (v, i)) != NULL) if (strcmp (desc->cmd, str) == 0) return 1; return 0; } -static int +static bool cmd_try_do_shortcut (enum node_type node, char* first_word) { return (node >= MIN_DO_SHORTCUT_NODE) && (first_word != NULL) @@ -1798,13 +1978,13 @@ cmd_describe_command_real (vector vline, int node, int *status) char *command; /* Set index. */ - if (vector_active (vline) == 0) + if (vector_length (vline) == 0) { *status = CMD_ERR_NO_MATCH; return NULL; } else - index = vector_active (vline) - 1; + index = vector_length (vline) - 1; /* Make copy vector of current node's command vector. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, node)); @@ -1815,7 +1995,7 @@ cmd_describe_command_real (vector vline, int node, int *status) /* 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))) + if ((command = vector_get_item (vline, i))) { match = cmd_filter_by_completion (command, cmd_vector, i); @@ -1825,15 +2005,15 @@ cmd_describe_command_real (vector vline, int node, int *status) vector descvec; unsigned int j, k; - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && (vector_active (cmd_element->strvec))) + for (j = 0; j < vector_length (cmd_vector); j++) + if ((cmd_element = vector_get_item (cmd_vector, j)) != NULL + && (vector_length (cmd_element->strvec))) { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - for (k = 0; k < vector_active (descvec); k++) + descvec = vector_get_item (cmd_element->strvec, + vector_length (cmd_element->strvec) - 1); + for (k = 0; k < vector_length (descvec); k++) { - struct desc *desc = vector_slot (descvec, k); + struct desc *desc = vector_get_item (descvec, k); vector_set (matchvec, desc); } } @@ -1863,53 +2043,58 @@ cmd_describe_command_real (vector vline, int node, int *status) /* Prepare match vector */ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ - /* Make sure that cmd_vector is filtered based on current word */ - command = vector_slot (vline, index); + /* Make sure that cmd_vector is filtered based on current word */ + command = vector_get_item (vline, index); if (command) match = cmd_filter_by_completion (command, cmd_vector, index); /* 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_length (cmd_vector); i++) + { + vector strvec ; + + cmd_element = vector_get_item (cmd_vector, i) ; + if (cmd_element == NULL) + continue ; + + /* Ignore cmd_element if no tokens at index position. + * + * Deal with special case of possible <cr> completion. + */ + strvec = cmd_element->strvec; + if (index >= vector_length (strvec)) + { + if (command == NULL && index == vector_length (strvec)) + { + if (!desc_unique_string (matchvec, command_cr)) + vector_push_item(matchvec, &desc_cr); + } + continue ; + } ; + + /* Check if command is completed. */ + unsigned int j; + vector descvec = vector_get_item (strvec, index); + struct desc *desc; + + for (j = 0; j < vector_length (descvec); j++) + if ((desc = vector_get_item (descvec, j))) + { + const char *string; + + string = cmd_entry_function_desc (command, desc->cmd); + if (string) + { + /* Uniqueness check */ + if (!desc_unique_string (matchvec, string)) + vector_push_item(matchvec, desc); + } + } ; + } ; - /* if command is NULL, index may be equal to vector_active */ - if (command && index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - /* Check if command is completed. */ - if (command == NULL && index == vector_active (strvec)) - { - if (!desc_unique_string (matchvec, command_cr)) - vector_set (matchvec, &desc_cr); - } - else - { - unsigned int j; - vector descvec = vector_slot (strvec, index); - struct desc *desc; - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - const char *string; - - string = cmd_entry_function_desc (command, desc->cmd); - if (string) - { - /* Uniqueness check */ - if (!desc_unique_string (matchvec, string)) - vector_set (matchvec, desc); - } - } - } - } - } vector_free (cmd_vector); - if (vector_slot (matchvec, 0) == NULL) + if (vector_length(matchvec) == 0) { vector_free (matchvec); *status = CMD_ERR_NO_MATCH; @@ -1938,7 +2123,7 @@ cmd_describe_command (vector vline, int node, int *status) { vector ret; - if ( cmd_try_do_shortcut(node, vector_slot(vline, 0) ) ) + if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) ) { vector shifted_vline; unsigned int index; @@ -1947,7 +2132,7 @@ cmd_describe_command (vector vline, int node, int *status) shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) + for (index = 1; index < vector_length (vline); index++) { vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } @@ -2023,7 +2208,7 @@ cmd_complete_command_real (vector vline, int node, int *status) int n ; /* Stop immediately if the vline is empty. */ - if (vector_active (vline) == 0) + if (vector_length (vline) == 0) { *status = CMD_ERR_NO_MATCH; return NULL; @@ -2033,7 +2218,7 @@ cmd_complete_command_real (vector vline, int node, int *status) cmd_v = vector_copy (cmd_node_vector (cmdvec, node)); /* First, filter upto, but excluding last token */ - last_ivl = vector_active (vline) - 1; + last_ivl = vector_length (vline) - 1; for (ivl = 0; ivl < last_ivl; ivl++) { @@ -2041,7 +2226,7 @@ cmd_complete_command_real (vector vline, int node, int *status) int ret; /* TODO: does this test make any sense ? */ - if ((token = vector_slot (vline, ivl)) == NULL) + if ((token = vector_get_item (vline, ivl)) == NULL) continue ; /* First try completion match, return best kind of match */ @@ -2078,37 +2263,33 @@ cmd_complete_command_real (vector vline, int node, int *status) /* Now we got into completion */ index = last_ivl ; - token = vector_slot(vline, last_ivl) ; /* is now the last token */ + token = vector_get_item(vline, last_ivl) ; /* is now the last token */ - for (i = 0; i < vector_active (cmd_v); i++) + for (i = 0; i < vector_length (cmd_v); i++) { unsigned int j; const char *string; - if ((cmd_element = vector_slot (cmd_v, i)) == NULL) + if ((cmd_element = vector_get_item (cmd_v, i)) == NULL) continue ; - vector strvec = cmd_element->strvec; + descvec = vector_get_item (cmd_element->strvec, index); + if (descvec == NULL) + continue ; - /* Check field length */ - if (index >= vector_active (strvec)) + for (j = 0; j < vector_length (descvec); j++) { - vector_slot (cmd_v, i) = NULL; - continue ; - } - - descvec = vector_slot (strvec, index); + desc = vector_get_item (descvec, j) ; + if (desc == NULL) + continue ; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j)) != NULL) - { - string = cmd_entry_function(token, desc->cmd) ; - if ((string != NULL) && cmd_unique_string(matchvec, string)) - cmd_add_to_strvec (matchvec, string) ; - } + string = cmd_entry_function(token, desc->cmd) ; + if ((string != NULL) && cmd_unique_string(matchvec, string)) + cmd_add_to_strvec (matchvec, string) ; + } ; } ; - n = vector_end(matchvec) ; /* number of entries in the matchvec */ + n = vector_length(matchvec) ; /* number of entries in the matchvec */ /* We don't need cmd_v any more. */ vector_free (cmd_v); @@ -2121,13 +2302,12 @@ cmd_complete_command_real (vector vline, int node, int *status) /* In case of 'command \t' pattern. Do you need '?' command at the end of the line. */ if (*token == '\0') - *status = CMD_ERR_NOTHING_TODO; + *status = CMD_COMPLETE_ALREADY; else *status = CMD_ERR_NO_MATCH; return NULL; } -/* XXX: TODO: stop poking around inside vector */ /* Only one matched */ if (n == 1) { @@ -2152,7 +2332,7 @@ cmd_complete_command_real (vector vline, int node, int *status) cmd_free_strvec(matchvec) ; /* discard the match vector */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); + matchvec = vector_init (1); vector_push_item(matchvec, lcdstr) ; *status = CMD_COMPLETE_MATCH; @@ -2173,7 +2353,7 @@ cmd_complete_command (vector vline, int node, int *status) { vector ret; - if ( cmd_try_do_shortcut(node, vector_slot(vline, 0) ) ) + if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) ) { vector shifted_vline; unsigned int index; @@ -2182,7 +2362,7 @@ cmd_complete_command (vector vline, int node, int *status) shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) + for (index = 1; index < vector_length (vline); index++) { vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } @@ -2224,104 +2404,259 @@ node_parent ( enum node_type node ) } /*------------------------------------------------------------------------------ + * Initialise a new struct cmd_parsed, allocating if required + */ +extern cmd_parsed +cmd_parse_init_new(cmd_parsed parsed) +{ + if (parsed == NULL) + parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ; + else + memset(parsed, 0, sizeof(*parsed)) ; + + /* Zeroising the structure has set: + * + * cmd = NULL -- no command parsed, yet + * cnode -- no node set, yet + * + * do_shortcut -- false + * onode -- not material (do_shortcut is false) + * + * line = zeroised qstring -- empty + * words = zeroised qstring -- empty + * + * vline = zeroised vector -- empty + * + * so nothing else to do + */ + + return parsed ; +} ; + +/*------------------------------------------------------------------------------ + * Initialise a new struct cmd_parsed, allocating if required + */ +extern cmd_parsed +cmd_parse_reset(cmd_parsed parsed, bool free_structure) +{ + if (parsed != NULL) + { + qs_reset_keep(&parsed->words) ; + vector_reset_keep(&parsed->vline) ; + + if (free_structure) + XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */ + else + cmd_parse_init_new(parsed) ; + } ; + + return parsed ; +} ; + +/*------------------------------------------------------------------------------ * Parse a command in the given "node", if possible, ready for execution. * - * If "strict" use: cmd_filter_by_string() - * otherwise use: cmd_filter_by_completion() + * If 'strict': use cmd_filter_by_string() + * otherwise: use cmd_filter_by_completion() * - * Takes the node from parsed->cnode. + * If 'do': see if there is a 'do' at the front and proceed accordingly. + * + * If 'tree': move up the node tree to find command if not found in the + * current node. + */ + +static enum cmd_return_code +cmd_parse_this(struct cmd_parsed* parsed, bool strict) ; + +/*------------------------------------------------------------------------------ + * Parse a command in the given "node", or (if required) any of its ancestors. * * Returns: CMD_SUCCESS => successfully parsed command, and the result is * in the given parsed structure, ready for execution. * - * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a - * "daemon" value. + * NB: parsed->cnode may have changed. + * + * NB: parsed->cmd->daemon => daemon + * + * CMD_EMPTY => empty or comment line + * + * NB: parsed->cmd == NULL + * + * CMD_SUCCESS_DAEMON => parsed successfully. Something for vtysh ?? + * + * CMD_ERR_NO_MATCH ) + * CMD_ERR_AMBIGUOUS ) failed to parse + * CMD_ERR_INCOMPLETE ) * - * otherwise => some failure to parse + * NB: if has failed to parse in the current node + * and in any ancestor nodes, returns the error + * from the attempt to parse in the current node + * (parsed->cnode which is returned unchanged). * - * NB: the argv[] in the parsed structure contains *copies* of the char* - * pointers in the given vline. + * NB: the command line MUST be preserved until the parsed command is no + * longer required -- no copy is made. * - * The vline may not be released until the parsed structure is. + * NB: expects to have free run of everything in the vty structure (except + * the contents of the vty_io sub-structure) until the command completes. * - * The parsed structure may be released without worrying about the contents - * of the argv[]. + * See elsewhere for description of parsed structure. */ -enum cmd_parse_type +extern enum cmd_return_code +cmd_parse_command(struct vty* vty, enum cmd_parse_type type) { - cmd_parse_completion = 0, - cmd_parse_strict = 1, -}; + enum cmd_return_code ret ; + enum cmd_return_code first_ret ; + cmd_parsed parsed ; -static int -cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed, - enum cmd_parse_type type) + /* Initialise the parsed structure -- assuming no 'do' */ + if (vty->parsed == NULL) + vty->parsed = cmd_parse_init_new(NULL) ; + parsed = vty->parsed ; + + parsed->onode = parsed->cnode = vty->node ; + + parsed->cmd = NULL ; + parsed->do_shortcut = 0 ; + + /* Parse the line into words -- set up parsed->words and parsed->vline */ + cmd_make_vline(&parsed->vline, &parsed->words, vty->buf) ; + + if (vector_length(&parsed->vline) == 0) + return CMD_EMPTY ; /* NB: parsed->cmd == NULL */ + + /* If allowed to 'do', see if there. + * + * 'do' forces command to be parsed in ENABLE_NODE (if allowed) + */ + if ((type & cmd_parse_do) && + cmd_try_do_shortcut(parsed->cnode, vector_get_item(&parsed->vline, 0))) + { + parsed->cnode = ENABLE_NODE ; + parsed->do_shortcut = 1 ; + } ; + + /* Try in the current node */ + ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ; + + if (ret != CMD_SUCCESS) + { + if (((type & cmd_parse_tree) == 0) || parsed->do_shortcut) + return ret ; /* done if not allowed to walk tree + or just tried to parse a 'do' */ + + /* Try in parent node(s) */ + first_ret = ret ; + + while (ret != CMD_SUCCESS) + { + if (parsed->cnode <= CONFIG_NODE) + { + parsed->cnode = parsed->onode ; /* restore node state */ + return first_ret ; /* return original result */ + } ; + + parsed->cnode = node_parent(parsed->cnode) ; + ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ; + } ; + } ; + + return vty->parsed->cmd->daemon ? CMD_SUCCESS_DAEMON + : CMD_SUCCESS ; +} ; + +/*------------------------------------------------------------------------------ + * Work function for cmd_parse_command + * + * Takes a parsed structure, with the: + * + * cnode -- node to parse in + * vline -- the line broken into words + * do_shortcut -- true if first word is 'do' (to be ignored) + * + * and parses either strictly or with command completion. + * + * If successful, reduces the vline structure down to the variable portions, + * ie to the argv[] for the command function. + * + * Returns: CMD_SUCCESS -- parsed successfully + * CMD_ERR_NO_MATCH ) + * CMD_ERR_AMBIGUOUS ) failed to parse + * CMD_ERR_INCOMPLETE ) + */ +static enum cmd_return_code +cmd_parse_this(cmd_parsed parsed, bool strict) { unsigned int i ; unsigned int ivl ; unsigned index ; + unsigned first ; + unsigned argc ; vector cmd_v; struct cmd_element *cmd_element; struct cmd_element *matched_element; unsigned int matched_count, incomplete_count; - int argc; enum match_type match = 0; int varflag; char *command; - int strict = (type == cmd_parse_strict) ; + /* Need length of vline, discounting the first entry if required */ + first = parsed->do_shortcut ? 1 : 0 ; - parsed->cmd = NULL ; /* return this if parsing fails */ - parsed->argc = 0 ; + assert(vector_length(&parsed->vline) >= first) ; + ivl = vector_length(&parsed->vline) - first ; /* Make copy of command elements. */ cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode)); /* Look for an unambiguous result */ - index = 0 ; - for (ivl = first; ivl < vector_active (vline); ivl++) - if ((command = vector_slot (vline, ivl)) != NULL) - { - int ret ; + for (index = 0 ; index < ivl; index++) + { + int ret ; - index = ivl - first ; - match = strict ? cmd_filter_by_string(command, cmd_v, index) - : cmd_filter_by_completion(command, cmd_v, index) ; + command = vector_get_item(&parsed->vline, index + first) ; + if (command == NULL) + continue ; - if (match == vararg_match) - break; + match = strict ? cmd_filter_by_string(command, cmd_v, index) + : cmd_filter_by_completion(command, cmd_v, index) ; - ret = is_cmd_ambiguous (command, cmd_v, index, match); + if (match == vararg_match) + break; - if (ret != 0) - { - assert((ret == 1) || (ret == 2)) ; - vector_free (cmd_v); - return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ; - } - } + ret = is_cmd_ambiguous (command, cmd_v, index, match); + + if (ret != 0) + { + assert((ret == 1) || (ret == 2)) ; + vector_free (cmd_v); + return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ; + } + } ; /* Check matched count. */ matched_element = NULL; matched_count = 0; incomplete_count = 0; - for (i = 0; i < vector_active (cmd_v); i++) - if ((cmd_element = vector_slot (cmd_v, i)) != NULL) - { - if (match == vararg_match || index >= cmd_element->cmdsize) - { - matched_element = cmd_element; + for (i = 0; i < vector_length(cmd_v); i++) + { + cmd_element = vector_get_item(cmd_v, i) ; + if (cmd_element == NULL) + continue ; + + if (match == vararg_match || index >= cmd_element->cmdsize) + { + matched_element = cmd_element; #if 0 - printf ("DEBUG: %s\n", cmd_element->string); + printf ("DEBUG: %s\n", cmd_element->string); #endif - matched_count++; - } - else - { - incomplete_count++; - } - } + matched_count++; + } + else + { + incomplete_count++; + } + } ; /* Finished with cmd_v. */ vector_free (cmd_v); @@ -2339,18 +2674,17 @@ cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed, varflag = 0 ; argc = 0 ; - for (ivl = first; ivl < vector_active (vline); ivl++) + for (index = 0; index < ivl ; index++) { int take = varflag ; if (!varflag) { - int index = ivl - first ; - vector descvec = vector_slot (matched_element->strvec, index); + vector descvec = vector_get_item (matched_element->strvec, index); - if (vector_active (descvec) == 1) + if (vector_length (descvec) == 1) { - struct desc *desc = vector_slot (descvec, 0); + struct desc *desc = vector_get_item (descvec, 0); if (CMD_VARARG (desc->cmd)) take = varflag = 1 ; @@ -2362,109 +2696,53 @@ cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed, } if (take) - { - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXCEED_ARGC_MAX ; - parsed->argv[argc++] = vector_slot (vline, ivl); - } ; + vector_assign_item(&parsed->vline, argc++, index + first) ; } ; + vector_set_length(&parsed->vline, argc) ; /* set to new length */ + /* Everything checks out... ready to execute command */ parsed->cmd = matched_element ; - parsed->argc = argc ; - - if (parsed->cmd->daemon) - return CMD_SUCCESS_DAEMON; return CMD_SUCCESS ; } ; /*------------------------------------------------------------------------------ - * Parse a command in the given "node", or any of its ancestors. - * - * Takes the node to start in from parsed->cnode. - * - * Returns: CMD_SUCCESS => successfully parsed command, and the result is - * in the given parsed structure, ready for execution. - * - * NB: parsed->cnode may have changed. + * Dispatch a parsed command. * - * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a - * "daemon" value. + * Returns: command return code. NB: may be CMD_QUEUED (unless no_queue). * - * otherwise => some failure to parse - * - * NB: returns error from attempt to parse in the - * starting parsed->cnode (which is returned - * unchanged). - * - * See cmd_parse_command() for more detail. + * NB: expects to have free run of everything in the vty structure (except + * the contents of the vty_io sub-structure) until the command completes. */ -static int -cmd_parse_command_tree(vector vline, int first, struct cmd_parsed* parsed, - enum cmd_parse_type type) +extern enum cmd_return_code +cmd_dispatch(struct vty* vty, bool no_queue) { - int ret ; - int first_ret ; - enum node_type first_node ; - - /* Try in the current node */ - ret = cmd_parse_command(vline, first, parsed, type) ; + cmd_parsed parsed = vty->parsed ; + enum cmd_return_code ret ; - if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON)) - return ret ; /* done if found command */ + if (parsed->cmd == NULL) + return CMD_SUCCESS ; /* NULL commands are easy */ - /* Try in parent node(s) */ - first_node = parsed->cnode ; - first_ret = ret ; + vty->node = parsed->cnode ; - while (parsed->cnode <= CONFIG_NODE) + if (no_queue || !vty_cli_nexus) { - parsed->cnode = node_parent(parsed->cnode) ; - ret = cmd_parse_command(vline, first, parsed, type) ; - - if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON)) - return ret ; /* done if found command */ - } ; - - parsed->cnode = first_node ; /* restore node state */ - return first_ret ; /* return original result */ -} - -/*------------------------------------------------------------------------------ - * Prepare to parse command -- deal with possible "do" shortcut. - * - * Initialises parsed structure, setting cnode and onode to vty->node. - * - * Checks to see if this is a valid "do" shortcut, and adjusts cnode if so. - * - * Returns: 0 => not "do" -- first word of actual command is first word - * 1 => is "do" -- first word of actual command is second word - */ -static int -cmd_pre_command(struct vty* vty, struct cmd_parsed* parsed, vector vline) -{ - int first ; - - parsed->onode = parsed->cnode = vty_get_node(vty) ; - - parsed->cmd = NULL ; - parsed->argc = 0 ; - - /* 'do' forces command to be parsed in ENABLE_NODE (if allowed) */ - if (cmd_try_do_shortcut(parsed->cnode, vector_slot(vline, 0))) - { - parsed->cnode = ENABLE_NODE ; - parsed->do_shortcut = 1 ; - first = 1 ; + ret = cmd_dispatch_call(vty) ; + cmd_post_command(vty, ret) ; } else { - parsed->do_shortcut = 0 ; - first = 0 ; + /* Don't do it now, but send to bgp qpthread */ + if (parsed->cmd->attr & CMD_ATTR_CALL) + cq_enqueue(vty, vty_cli_nexus) ; + else + cq_enqueue(vty, vty_cmd_nexus) ; + + ret = CMD_QUEUED ; } ; - return first ; + return ret ; } ; /*------------------------------------------------------------------------------ @@ -2477,266 +2755,153 @@ cmd_pre_command(struct vty* vty, struct cmd_parsed* parsed, vector vline) * state. * * Arguments: ret = CMD_XXXX -- NB: CMD_QUEUED => command revoked - * action = true unless command return is being revoked */ extern void -cmd_post_command(struct vty* vty, struct cmd_parsed* parsed, int ret, - int action) +cmd_post_command(struct vty* vty, int ret) { - if (parsed->do_shortcut) + if (vty->parsed->do_shortcut) { - if (vty_get_node(vty) == ENABLE_NODE) - vty_set_node(vty, parsed->onode) ; + if (vty->node == ENABLE_NODE) + vty->node = vty->parsed->onode ; } ; } ; /*------------------------------------------------------------------------------ - * Execute command: + * Parse and execute a command. + * + * The command is given by vty->buf and vty->node. + * + * Uses vty->parsed. * - * -- use cmd_parse_completion type parsing. + * -- use strict/completion parsing, as required. * - * -- unless vtysh: if does not parse in current node, try ancestors + * -- parse in current node and in ancestors, as required * * If does not find in any ancestor, return error from current node. * - * -- implement the "do" shortcut + * -- implement the "do" shortcut, as required * * If qpthreads_enabled, then may queue the command rather than execute it * here. * * The vty->node may be changed during the execution of the command, and may * be returned changed once the command has completed. + * + * NB: expects to have free run of everything in the vty structure (except + * the contents of the vty_io sub-structure) until the command completes. */ -extern int -cmd_execute_command (vector vline, struct vty *vty, - struct cmd_element **cmd, qpn_nexus to_nexus, - qpn_nexus from_nexus, int vtysh) +extern enum cmd_return_code +cmd_execute_command(struct vty *vty, + enum cmd_parse_type type, struct cmd_element **cmd) { - int ret ; - int first ; - struct cmd_parsed parsed ; - - /* Set up parsed structure & deal with "do" shortcut prefix */ - first = cmd_pre_command(vty, &parsed, vline) ; - - /* Try to parse in parsed.cnode node or, if required, ancestors thereof. */ - if (vtysh) - ret = cmd_parse_command(vline, first, &parsed, cmd_parse_completion) ; - else - ret = cmd_parse_command_tree(vline, first, &parsed, cmd_parse_completion) ; - - if (cmd) - *cmd = parsed.cmd ; /* for vtysh */ + enum cmd_return_code ret ; - if (ret != CMD_SUCCESS) - return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */ - - /* Execute the parsed command */ - - vty_set_node(vty, parsed.cnode) ; + /* Try to parse in vty->node or, if required, ancestors thereof. */ + ret = cmd_parse_command(vty, type) ; - if (qpthreads_enabled && !(parsed.cmd->attr & CMD_ATTR_CALL)) - { - /* Don't do it now, but send to bgp qpthread */ - cq_enqueue(vty, &parsed, to_nexus, from_nexus) ; - ret = CMD_QUEUED ; - } - else - { - ret = (*(parsed.cmd->func))(parsed.cmd, vty, parsed.argc, parsed.argv) ; + if (cmd != NULL) + *cmd = vty->parsed->cmd ; /* for vtysh */ - cmd_post_command(vty, &parsed, ret, 1) ; - } ; + if (ret == CMD_SUCCESS) + ret = cmd_dispatch(vty, 0) ; + else if (ret == CMD_EMPTY) + ret = CMD_SUCCESS ; return ret ; } ; /*------------------------------------------------------------------------------ - * Execute command by argument readline. + * Read configuration from file. * - * -- use cmd_parse_strict type parsing. + * In the qpthreads world this assumes that it is running with the vty + * locked, and that all commands are to be executed directly. * - * -- unless vtysh: if does not parse in current node, try ancestors + * If the 'first_cmd' argument is not NULL it is the address of the first + * command that is expected to appear. If the first command is not this, then + * the 'first_cmd' is called with argv == NULL (and argc == 0) to signal the + * command is being invoked by default. * - * If does not find in any ancestor, return error from current node. + * Command processing continues while CMD_SUCCESS is returned by the command + * parser and command execution. + * + * If 'ignore_warning' is set, then any CMD_WARNING returned by command + * execution is converted to CMD_SUCCESS. Note that any CMD_WARNING returned + * by command parsing (or in execution of any default 'first_cmd'). + * + * Returns: cmd_return_code for last command + * vty->buf is last line processed + * vty->lineno is number of last line processed (1 is first) * - * -- does NOT implement the "do" shortcut + * If the file is empty, will return CMD_SUCCESS. * - * At all times executes the command immediately (no queueing, even if - * qpthreads_enabled). + * Never returns CMD_EMPTY -- that counts as CMD_SUCCESS. * - * The vty->node may be changed either when parsing or executing the command. + * If + * + * If return code is not CMD_SUCCESS, the the output buffering contains the + * output from the last command attempted. */ -extern int -cmd_execute_command_strict (vector vline, struct vty *vty, - struct cmd_element **cmd, int vtysh) +extern enum cmd_return_code +config_from_file (struct vty *vty, FILE *fp, struct cmd_element* first_cmd, + qstring buf, bool ignore_warning) { -#if 0 /* replaced by cmd_parse_command() */ - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - int varflag; - enum match_type match = 0; - char *command; - - /* Make copy of command element */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty_get_node(vty))); - - for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) - { - int ret; - - match = cmd_filter_by_string (vector_slot (vline, index), - cmd_vector, index); - - /* If command meets '.VARARG' then finish matching. */ - if (match == vararg_match) - break; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } - - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - for (i = 0; i < vector_active (cmd_vector); i++) - if (vector_slot (cmd_vector, i) != NULL) - { - cmd_element = vector_slot (cmd_vector, i); - - if (match == vararg_match || index >= cmd_element->cmdsize) - { - matched_element = cmd_element; - matched_count++; - } - else - incomplete_count++; - } - - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); - - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) - { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; - } - - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; + enum cmd_return_code ret; - /* Argument treatment */ - varflag = 0; - argc = 0; + vty->buf = buf->body ; + vty->lineno = 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_EXCEED_ARGC_MAX; - } -#endif - - struct cmd_parsed parsed ; - int ret ; - - /* Try to parse in parsed.cnode node or, if required, ancestors thereof. */ - if (vtysh) - ret = cmd_parse_command(vline, 0, &parsed, cmd_parse_strict) ; - else - ret = cmd_parse_command_tree(vline, 0, &parsed, cmd_parse_strict) ; - - if (cmd) - *cmd = parsed.cmd ; /* for vtysh */ - - if (ret != CMD_SUCCESS) - return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */ - - /* Now execute matched command */ - vty_set_node(vty, parsed.cnode) ; - - return (*(parsed.cmd->func)) (parsed.cmd, vty, parsed.argc, parsed.argv); -} - -/*------------------------------------------------------------------------------ - * Read configuration from file. - * - */ -int -config_from_file (struct vty *vty, FILE *fp, void (*after_first_cmd)(void), - qstring buf) -{ - int ret; - vector vline; - int first_cmd = 1; + ret = CMD_SUCCESS ; /* in case file is empty */ + vty_out_clear(vty) ; while (fgets (buf->body, buf->size, fp)) { - vline = cmd_make_strvec (buf->body); + ++vty->lineno ; + + /* Execute configuration command : this is strict match */ + ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ; - /* In case of comment line */ - if (vline == NULL) - continue; + if (ret == CMD_EMPTY) + continue ; /* skip empty/comment */ - /* Execute configuration command : this is strict match */ - ret = cmd_execute_command_strict (vline, vty, NULL, 0); + if (ret != CMD_SUCCESS) + break ; /* stop on *any* parsing issue */ - /* special handling for after the first command */ - if (first_cmd && after_first_cmd) + /* special handling before of first command */ + if (first_cmd != NULL) { - after_first_cmd(); - first_cmd = 0; + if (first_cmd != vty->parsed->cmd) + { + ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ; + if (ret != CMD_SUCCESS) + break ; /* stop on *any* issue with "default" */ + } ; + first_cmd = NULL ; } - cmd_free_strvec (vline); + /* Standard command handling */ + ret = cmd_dispatch(vty, cmd_no_queue) ; + + if (ret != CMD_SUCCESS) + { + /* Ignore CMD_WARNING if required + * + * Ignore CMD_CLOSE at all times. + */ + if ( ((ret == CMD_WARNING) && ignore_warning) + || (ret == CMD_CLOSE) ) + ret = CMD_SUCCESS ; /* in case at EOF */ + else + break ; /* stop */ + } ; - /* TODO: why does config file not stop on CMD_WARNING ?? */ - if (ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_ERR_NOTHING_TODO) - return ret; + vty_out_clear(vty) ; } ; - return CMD_SUCCESS; -} + if (ret == CMD_EMPTY) + ret = CMD_SUCCESS ; /* OK if end on empty line */ + + return ret ; +} ; /*----------------------------------------------------------------------------*/ @@ -2762,7 +2927,7 @@ DEFUN_CALL (enable, { /* If enable password is NULL, change to ENABLE_NODE */ if ((host.enable == NULL && host.enable_encrypt == NULL) || - vty_get_type(vty) == VTY_SHELL_SERV) + vty_shell_serv(vty)) vty_set_node(vty, ENABLE_NODE); else vty_set_node(vty, AUTH_ENABLE_NODE); @@ -2787,8 +2952,7 @@ DEFUN_CALL (config_exit, "exit", "Exit current mode and down to previous mode\n") { - vty_cmd_exit(vty) ; - return CMD_SUCCESS; + return vty_cmd_exit(vty) ; } /* quit is alias of exit. */ @@ -2803,8 +2967,7 @@ DEFUN_CALL (config_end, "end", "End current mode and change to enable mode.") { - vty_cmd_end(vty) ; - return CMD_SUCCESS; + return vty_cmd_end(vty) ; } /* Show version. */ @@ -2852,11 +3015,11 @@ DEFUN_CALL (config_list, "Print command list\n") { unsigned int i; - struct cmd_node *cnode = vector_slot (cmdvec, vty_get_node(vty)); + struct cmd_node *cnode = vector_get_item (cmdvec, vty_get_node(vty)); struct cmd_element *cmd; - for (i = 0; i < vector_active (cnode->cmd_vector); i++) - if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL + for (i = 0; i < vector_length (cnode->cmd_vector); i++) + if ((cmd = vector_get_item (cnode->cmd_vector, i)) != NULL && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))) vty_out (vty, " %s%s", cmd->string, VTY_NEWLINE); @@ -2872,12 +3035,12 @@ DEFUN (config_write_file, { unsigned int i; int fd; + int err; struct cmd_node *node; char *config_file; char *config_file_tmp = NULL; char *config_file_sav = NULL; int ret = CMD_WARNING; - struct vty *file_vty; /* Check and see if we are operating under vtysh configuration */ if (host.config == NULL) @@ -2909,47 +3072,61 @@ DEFUN (config_write_file, } /* Make vty for configuration file. */ - file_vty = vty_new (fd, VTY_FILE); + vty_open_config_write(vty, fd) ; /* Config file header print. */ - vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! "); - vty_time_print (file_vty, 1); - vty_out (file_vty, "!\n"); + vty_out (vty, "!\n! Zebra configuration saved from vty\n! "); + vty_time_print (vty, 1); + vty_out (vty, "!\n"); - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func) + for (i = 0; i < vector_length (cmdvec); i++) + if ((node = vector_get_item (cmdvec, i)) && node->func) { - if ((*node->func) (file_vty)) - vty_out (file_vty, "!\n"); + if ((*node->func) (vty)) + vty_out (vty, "!\n"); } - vty_close (file_vty); + + err = vty_close_config_write(vty) ; + close(fd) ; + + if (err != 0) + { + vty_out (vty, "Failed while writing configuration file %s.%s", + config_file_tmp, VTY_NEWLINE); + goto finished; + } if (unlink (config_file_sav) != 0) if (errno != ENOENT) { - vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, - VTY_NEWLINE); + vty_out (vty, "Can't unlink backup configuration file %s.%s", + config_file_sav, VTY_NEWLINE); goto finished; - } + } ; + if (link (config_file, config_file_sav) != 0) { - vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, - VTY_NEWLINE); + vty_out (vty, "Can't backup old configuration file %s.%s", + config_file_sav, VTY_NEWLINE); goto finished; - } - sync (); + } ; + + sync () ; + if (unlink (config_file) != 0) { - vty_out (vty, "Can't unlink configuration file %s.%s", config_file, - VTY_NEWLINE); + vty_out (vty, "Can't unlink configuration file %s.%s", + config_file, VTY_NEWLINE); goto finished; - } + } ; + if (link (config_file_tmp, config_file) != 0) { - vty_out (vty, "Can't save configuration file %s.%s", config_file, - VTY_NEWLINE); + vty_out (vty, "Can't save configuration file %s.%s", + config_file, VTY_NEWLINE); goto finished; - } + } ; + sync (); if (chmod (config_file, CONFIGFILE_MASK) != 0) @@ -2959,8 +3136,8 @@ DEFUN (config_write_file, goto finished; } - vty_out (vty, "Configuration saved to %s%s", config_file, - VTY_NEWLINE); + vty_out (vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE); + ret = CMD_SUCCESS; finished: @@ -2998,10 +3175,10 @@ DEFUN (config_write_terminal, unsigned int i; struct cmd_node *node; - if (vty_get_type(vty) == VTY_SHELL_SERV) + if (vty_shell_serv(vty)) { - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) + for (i = 0; i < vector_length (cmdvec); i++) + if ((node = vector_get_item (cmdvec, i)) && node->func && node->vtysh) { if ((*node->func) (vty)) vty_out (vty, "!%s", VTY_NEWLINE); @@ -3013,8 +3190,8 @@ DEFUN (config_write_terminal, VTY_NEWLINE); vty_out (vty, "!%s", VTY_NEWLINE); - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func) + for (i = 0; i < vector_length (cmdvec); i++) + if ((node = vector_get_item (cmdvec, i)) && node->func) { if ((*node->func) (vty)) vty_out (vty, "!%s", VTY_NEWLINE); @@ -4012,39 +4189,52 @@ cmd_terminate () if (cmdvec) { - for (i = 0; i < vector_active (cmdvec); i++) - if ((cmd_node = vector_slot (cmdvec, i)) != NULL) - { - cmd_node_v = cmd_node->cmd_vector; + for (i = 0; i < vector_length (cmdvec); i++) + { + cmd_node = vector_get_item (cmdvec, i) ; + if (cmd_node == NULL) + continue ; - for (j = 0; j < vector_active (cmd_node_v); j++) - if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL && - cmd_element->strvec != NULL) - { - cmd_element_v = cmd_element->strvec; - - for (k = 0; k < vector_active (cmd_element_v); k++) - if ((desc_v = vector_slot (cmd_element_v, k)) != NULL) - { - for (l = 0; l < vector_active (desc_v); l++) - if ((desc = vector_slot (desc_v, l)) != NULL) - { - if (desc->cmd) - XFREE (MTYPE_STRVEC, desc->cmd); - if (desc->str) - XFREE (MTYPE_STRVEC, desc->str); - - XFREE (MTYPE_DESC, desc); - } - vector_free (desc_v); - } - - cmd_element->strvec = NULL; - vector_free (cmd_element_v); - } + cmd_node_v = cmd_node->cmd_vector; - vector_free (cmd_node_v); - } + for (j = 0; j < vector_length (cmd_node_v); j++) + { + cmd_element = vector_get_item (cmd_node_v, j) ; + if (cmd_element == NULL) + continue ; + + cmd_element_v = cmd_element->strvec ; + if (cmd_element_v == NULL) + continue ; + + for (k = 0; k < vector_length (cmd_element_v); k++) + { + desc_v = vector_get_item (cmd_element_v, k) ; + if (desc_v == NULL) + continue ; + + for (l = 0; l < vector_length (desc_v); l++) + { + desc = vector_get_item (desc_v, l) ; + if (desc == NULL) + continue ; + + 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 (cmdvec); cmdvec = NULL; |