diff options
| author | Sylvain Munaut <tnt@246tNt.com> | 2012-12-28 11:58:23 +0100 | 
|---|---|---|
| committer | Sylvain Munaut <tnt@246tNt.com> | 2013-01-03 22:34:26 +0100 | 
| commit | 4d8eea48f2357fb4de5795824165d32d1de3ae64 (patch) | |
| tree | 5d5e6631702fa50b10f5e0d5c7c94ca4faaf3d4e /src/vty | |
| parent | 01e06046379350aa9090ef785a9b0fe2ca03ce23 (diff) | |
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 <paul@quagga.net>
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Diffstat (limited to 'src/vty')
| -rw-r--r-- | src/vty/command.c | 351 | 
1 files changed, 143 insertions, 208 deletions
| 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 <stdio.h>  #include <stdlib.h>  #include <string.h> +#include <stdbool.h>  #include <syslog.h>  #include <errno.h>  #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) | 
