From 4d8eea48f2357fb4de5795824165d32d1de3ae64 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 28 Dec 2012 11:58:23 +0100 Subject: vty: Do better filtering of arguments, optional args particularly This is essentially http://patchwork.diac24.net/patch/271/ forward ported to libosmovty Original-by: Paul Jakma Signed-off-by: Sylvain Munaut --- src/vty/command.c | 351 ++++++++++++++++++++++-------------------------------- 1 file changed, 143 insertions(+), 208 deletions(-) (limited to 'src') diff --git a/src/vty/command.c b/src/vty/command.c index 7f83a5e4..7a58503f 100644 --- a/src/vty/command.c +++ b/src/vty/command.c @@ -24,6 +24,7 @@ Boston, MA 02111-1307, USA. */ #include #include #include +#include #include #include #define _XOPEN_SOURCE @@ -652,7 +653,8 @@ static vector cmd_node_vector(vector v, enum node_type ntype) /* Completion match types. */ enum match_type { - no_match, + no_match = 0, + any_match, extend_match, ipv4_prefix_match, ipv4_match, @@ -661,7 +663,7 @@ enum match_type { range_match, vararg_match, partly_match, - exact_match + exact_match, }; static enum match_type cmd_ipv4_match(const char *str) @@ -1103,12 +1105,100 @@ static int cmd_range_match(const char *range, const char *str) return 1; } -/* Make completion match and return match type flag. */ +/* helper to retrieve the 'real' argument string from an optional argument */ +static char * +cmd_deopt(const char *str) +{ + /* we've got "[blah]". We want to strip off the []s and redo the + * match check for "blah" + */ + size_t len = strlen(str); + char *tmp; + + if (len < 3) + return NULL; + + /* tmp will hold a string of len-2 chars, so 'len' size is fine */ + tmp = talloc_size(NULL, len); + + memcpy(tmp, (str + 1), len - 2); + tmp[len - 2] = '\0'; + + return tmp; +} + static enum match_type -cmd_filter_by_completion(char *command, vector v, unsigned int index) +cmd_match(const char *str, const char *command, + enum match_type min, bool recur) +{ + + if (recur && CMD_OPTION(str)) + { + enum match_type ret; + char *tmp = cmd_deopt(str); + + /* this would be a bug in a command, however handle it gracefully + * as it we only discover it if a user tries to run it + */ + if (tmp == NULL) + return no_match; + + ret = cmd_match(tmp, command, min, false); + + talloc_free(tmp); + + return ret; + } + else if (CMD_VARARG(str)) + return vararg_match; + else if (CMD_RANGE(str)) + { + if (cmd_range_match(str, command)) + return range_match; + } +#ifdef HAVE_IPV6 + else if (CMD_IPV6(str)) + { + if (cmd_ipv6_match(command) >= min) + return ipv6_match; + } + else if (CMD_IPV6_PREFIX(str)) + { + if (cmd_ipv6_prefix_match(command) >= min) + return ipv6_prefix_match; + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) + { + if (cmd_ipv4_match(command) >= min) + return ipv4_match; + } + else if (CMD_IPV4_PREFIX(str)) + { + if (cmd_ipv4_prefix_match(command) >= min) + return ipv4_prefix_match; + } + else if (CMD_VARIABLE(str)) + return extend_match; + else if (strncmp(command, str, strlen(command)) == 0) + { + if (strcmp(command, str) == 0) + return exact_match; + else if (partly_match >= min) + return partly_match; + } + + return no_match; +} + +/* Filter vector at the specified index and by the given command string, to + * the desired matching level (thus allowing part matches), and return match + * type flag. + */ +static enum match_type +cmd_filter(char *command, vector v, unsigned int index, enum match_type level) { unsigned int i; - const char *str; struct cmd_element *cmd_element; enum match_type match_type; vector descvec; @@ -1130,124 +1220,40 @@ cmd_filter_by_completion(char *command, vector v, unsigned int index) for (j = 0; j < vector_active(descvec); j++) if ((desc = vector_slot(descvec, j))) { - str = desc->cmd; + enum match_type ret; - if (CMD_VARARG(str)) { - if (match_type < - vararg_match) - match_type = - vararg_match; - matched++; - } else if (CMD_RANGE(str)) { - if (cmd_range_match - (str, command)) { - if (match_type < - range_match) - match_type - = - range_match; - - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6(str)) { - if (cmd_ipv6_match - (command)) { - if (match_type < - ipv6_match) - match_type - = - ipv6_match; - - matched++; - } - } else if (CMD_IPV6_PREFIX(str)) { - if (cmd_ipv6_prefix_match(command)) { - if (match_type < - ipv6_prefix_match) - match_type - = - ipv6_prefix_match; - - matched++; - } - } -#endif /* HAVE_IPV6 */ - 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 - /* 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; - } + ret = cmd_match (desc->cmd, command, level, true); + + if (ret != no_match) matched++; - } + + if (match_type < ret) + match_type = ret; } if (!matched) vector_slot(v, i) = NULL; } } - return match_type; -} - -/* Filter vector by command character with index. */ -static enum match_type -cmd_filter_by_string(char *command, vector v, unsigned int index) -{ - unsigned int i; - const char *str; - struct cmd_element *cmd_element; - enum match_type match_type; - vector descvec; - struct desc *desc; - match_type = no_match; + if (match_type == no_match) + return no_match; - /* If command and cmd_element string does not match set NULL to vector */ + /* 2nd pass: We now know the 'strongest' match type for the index, so we + * go again and filter out commands whose argument (at this index) is + * 'weaker'. E.g., if we have 2 commands: + * + * foo bar <1-255> + * foo bar BLAH + * + * and the command string is 'foo bar 10', then we will get here with with + * 'range_match' being the strongest match. However, if 'BLAH' came + * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10"). + * + * If we don't do a 2nd pass and filter it out, the higher-layers will + * consider this to be ambiguous. + */ for (i = 0; i < vector_active(v); i++) if ((cmd_element = vector_slot(v, i)) != NULL) { - /* If given index is bigger than max string vector of command, - set NULL */ if (index >= vector_active(cmd_element->strvec)) vector_slot(v, i) = NULL; else { @@ -1259,89 +1265,18 @@ cmd_filter_by_string(char *command, vector v, unsigned int index) for (j = 0; j < vector_active(descvec); j++) if ((desc = vector_slot(descvec, j))) { - str = desc->cmd; + enum match_type ret; - 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; + ret = cmd_match(desc->cmd, command, any_match, true); + + if (ret >= match_type) matched++; - } else { - if (strcmp(command, str) - == 0) { - match_type = - exact_match; - matched++; - } - } } if (!matched) vector_slot(v, i) = NULL; } } + return match_type; } @@ -1351,7 +1286,6 @@ is_cmd_ambiguous(char *command, vector 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; @@ -1366,22 +1300,22 @@ is_cmd_ambiguous(char *command, vector v, int index, enum match_type type) for (j = 0; j < vector_active(descvec); j++) if ((desc = vector_slot(descvec, j))) { enum match_type ret; + const char *str = desc->cmd; - str = desc->cmd; + if (CMD_OPTION(str)) + if ((str = cmd_deopt(str)) == NULL) + continue; switch (type) { case exact_match: - if (! - (CMD_OPTION(str) - || CMD_VARIABLE(str)) -&& strcmp(command, str) == 0) + if (!(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 (!(CMD_VARIABLE (str)) + && strncmp(command, str, strlen (command)) == 0) + { if (matched && strcmp(matched, str) != 0) @@ -1434,14 +1368,16 @@ is_cmd_ambiguous(char *command, vector v, int index, enum match_type type) } break; case extend_match: - if (CMD_OPTION(str) - || CMD_VARIABLE(str)) + if (CMD_VARIABLE (str)) match++; break; case no_match: default: break; } + + if (CMD_OPTION(desc->cmd)) + talloc_free((void*)str); } if (!match) vector_slot(v, i) = NULL; @@ -1598,7 +1534,7 @@ cmd_describe_command_real(vector vline, struct vty *vty, int *status) for (i = 0; i < index; i++) if ((command = vector_slot(vline, i))) { match = - cmd_filter_by_completion(command, cmd_vector, i); + cmd_filter(command, cmd_vector, i, any_match); if (match == vararg_match) { struct cmd_element *cmd_element; @@ -1653,7 +1589,7 @@ cmd_describe_command_real(vector vline, struct vty *vty, int *status) /* 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); + match = cmd_filter(command, cmd_vector, index, any_match); /* Make description vector. */ for (i = 0; i < vector_active(cmd_vector); i++) @@ -1798,7 +1734,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty, /* First try completion match, if there is exactly match return 1 */ match = - cmd_filter_by_completion(command, cmd_vector, i); + cmd_filter(command, cmd_vector, i, any_match); /* If there is exact match then filter ambiguous match else check ambiguousness. */ @@ -1980,9 +1916,8 @@ cmd_execute_command_real(vector vline, struct vty *vty, if ((command = vector_slot(vline, index))) { int ret; - match = - cmd_filter_by_completion(command, cmd_vector, - index); + match = cmd_filter(command, cmd_vector, index, + any_match); if (match == vararg_match) break; @@ -2152,8 +2087,8 @@ cmd_execute_command_strict(vector vline, struct vty *vty, if ((command = vector_slot(vline, index))) { int ret; - match = cmd_filter_by_string(vector_slot(vline, index), - cmd_vector, index); + match = cmd_filter(vector_slot(vline, index), + cmd_vector, index, exact_match); /* If command meets '.VARARG' then finish matching. */ if (match == vararg_match) -- cgit v1.2.3