[quagga-dev 11469] Re: DEFUN in 99.23

Paul Jakma paul at jakma.org
Tue Sep 9 13:52:38 BST 2014


On Wed, 20 Aug 2014, Daniel Walton wrote:

> Does the new DEFUN in 99.23 allow you to specify an optional keyword at the end of the command?  For instance I want to do:
>
>  "show ip bgp (A.B.C.D|A.B.C.D/M) (foo|)"
>
> which would allow the user to enter
> show ip bgp 1.1.1.1
> show ip bgp 1.1.1.1/32
> show ip bgp 1.1.1.1 foo
> show ip bgp 1.1.1.1/32 foo
>
> but the parser doesn't list <cr> as an option:
>
> cel-red-01# show ip bgp 1.1.1.1 ?
>  bestpath   Display only the bestpath
>  foo        Foo description here
>  multipath  Display only multipaths
> cel-red-01# show ip bgp 1.1.1.1
>
> Just need to clarify that what I have is the correct syntax and that 
> there isn't some other way to do this before I chase this down.

So, I havn't looked closely at your problem, and I havn't looked at this 
in a good while, but I have an old patch that sort of seems to be in the 
same area. I had a different issue in mind to yours though.

Don't know if it's relevant.

commit ebbf6b56fd1f5908d06bf186c543b894ce461743
Author: Paul Jakma <paul at quagga.net>
Date:   Wed Aug 19 13:22:46 2009 +0100

     lib: Do better filtering of arguments, optional args particularly

     * command.c: (cmd_deopt) helper to extract the inside argument string from
       an optional argument.

       (cmd_match) Consolidate matching code from cmd_filter_by_* to here.
       Use cmd_deopt to match on the /inner/ argument of an optional string,
       rather than treating optional arguments as matching pretty much any
       string.

       (cmd_filter) consolidate cmd_filter_by_* to here, and use cmd_match.
       Further, do a two-pass filter, to make use of the fact that we know
       the "strongest" match (e.g. range-match args beat variables) and can
       filter out weaker ones.

       Along with ability to match on inside of optional arguments, this makes
       our command definitions more powerful, and should allow us to avoid
       much redundancy in command defs.

       (is_cmd_ambiguous) look inside optional args, for better matching.

diff --git a/lib/command.c b/lib/command.c
index 31c067a..c3ea5c2 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -32,6 +32,7 @@ Boston, MA 02111-1307, USA.  */
  #include "vty.h"
  #include "command.h"
  #include "workqueue.h"
+#include <stdbool.h>

  /* Command vector which includes some level of command lists. Normally
     each daemon maintains each own cmdvec. */
@@ -686,7 +687,8 @@ cmd_filter_by_symbol (char *command, char *symbol)
  /* Completion match types. */
  enum match_type
  {
-  no_match,
+  no_match = 0,
+  any_match,
    extend_match,
    ipv4_prefix_match,
    ipv4_match,
@@ -695,7 +697,7 @@ enum match_type
    range_match,
    vararg_match,
    partly_match,
-  exact_match 
+  exact_match,
  };

  static enum match_type
@@ -1132,12 +1134,100 @@ 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 = XMALLOC(MTYPE_TMP, 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);
+ 
+      XFREE (MTYPE_TMP, 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;
@@ -1160,112 +1250,42 @@ 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;
+ 
+		  ret = cmd_match (desc->cmd, command, level, true);

-		  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;
-			}
-		      matched++;
-		    }
+		  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 command and cmd_element string does not match set NULL to vector */
+ 
+  if (match_type == no_match)
+    return no_match;
+ 
+  /* 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
@@ -1277,81 +1297,19 @@ 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;
-
-		  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++;
-			}
-		    }
+	        {
+	          enum match_type ret;
+ 
+		  ret = cmd_match (desc->cmd, command, any_match, true);
+ 
+		  if (ret >= match_type)
+		    matched++;
  		}
  	    if (!matched)
  	      vector_slot (v, i) = NULL;
  	  }
        }
+
    return match_type;
  }

@@ -1361,7 +1319,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;
@@ -1371,25 +1328,28 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
      if ((cmd_element = vector_slot (v, 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)))
  	    {
  	      enum match_type ret;
+	      char *str = desc->cmd;
+ 
+	      if (CMD_OPTION(str))
+	        if ((str = cmd_deopt (str)) == NULL)
+	          continue;

-	      str = desc->cmd;
-
  	      switch (type)
  		{
  		case exact_match:
-		  if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
+		  if (!(CMD_VARIABLE (str))
  		      && strcmp (command, str) == 0)
  		    match++;
  		  break;
  		case partly_match:
-		  if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
+		  if (!(CMD_VARIABLE (str))
  		      && strncmp (command, str, strlen (command)) == 0)
  		    {
  		      if (matched && strcmp (matched, str) != 0)
@@ -1438,13 +1398,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))
+                XFREE (MTYPE_TMP, str);
  	    }
  	if (!match)
  	  vector_slot (v, i) = NULL;
@@ -1614,7 +1577,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);
+	match = cmd_filter (command, cmd_vector, i, any_match);

  	if (match == vararg_match)
  	  {
@@ -1663,7 +1626,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++)
@@ -1817,7 +1780,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
  	int ret;

  	/* First try completion match, if there is exactly match return 1 */
-	match = cmd_filter_by_completion (command, cmd_vector, i);
+	match = cmd_filter (command, cmd_vector, i, any_match);

  	/* If there is exact match then filter ambiguous match else check
  	   ambiguousness. */
@@ -2026,7 +1989,7 @@ cmd_execute_command_real (vector vline, struct vty *vty,
        {
  	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;
@@ -2205,8 +2168,8 @@ cmd_execute_command_strict (vector vline, struct vty *vty,
        {
  	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)

regards,
-- 
Paul Jakma	paul at jakma.org	@pjakma	Key ID: 64A2FF6A
Fortune:
Of course you have a purpose -- to find a purpose.




More information about the Quagga-dev mailing list