docs: improve unformat documentation 26/21926/2
authorDave Barach <dave@barachs.net>
Mon, 9 Sep 2019 15:02:45 +0000 (11:02 -0400)
committerFlorin Coras <florin.coras@gmail.com>
Mon, 9 Sep 2019 18:37:35 +0000 (18:37 +0000)
Specifically: document the unformat specifiers "%=", "%|", and correct
usage of unformat_line_input.

Signed-off-by: Dave Barach <dave@barachs.net>
Change-Id: I254f9c4a61c5c8d3edbf76f521e5f38fb89150f1

docs/gettingstarted/developers/infrastructure.md

index 9dde982..95465f0 100644 (file)
@@ -49,7 +49,7 @@ macro! It's smart about NULL pointers.\]
 Typically, the user header is not present. User headers allow for other
 data structures to be built atop vppinfra vectors. Users may specify the
 alignment for first data element of a vector via the \[vec\]()\*\_aligned
 Typically, the user header is not present. User headers allow for other
 data structures to be built atop vppinfra vectors. Users may specify the
 alignment for first data element of a vector via the \[vec\]()\*\_aligned
-macros. 
+macros.
 
 Vector elements can be any C type e.g. (int, double, struct bar). This
 is also true for data types built atop vectors (e.g. heap, pool, etc.).
 
 Vector elements can be any C type e.g. (int, double, struct bar). This
 is also true for data types built atop vectors (e.g. heap, pool, etc.).
@@ -138,8 +138,8 @@ schemes may be used:
 
 ```c
     vec_add1 (result, 0)
 
 ```c
     vec_add1 (result, 0)
-    or 
-    result = format (result, "<whatever>%c", 0); 
+    or
+    result = format (result, "<whatever>%c", 0);
 ```
 
 Remember to vec\_free() the result if appropriate. Be careful not to
 ```
 
 Remember to vec\_free() the result if appropriate. Be careful not to
@@ -161,8 +161,8 @@ format specification. For example:
 
 format\_junk() can invoke other user-format functions if desired. The
 programmer shoulders responsibility for argument type-checking. It is
 
 format\_junk() can invoke other user-format functions if desired. The
 programmer shoulders responsibility for argument type-checking. It is
-typical for user format functions to blow up if the va\_arg(va,
-type) macros don't match the caller's idea of reality.
+typical for user format functions to blow up spectaculary if the
+va\_arg(va, type) macros don't match the caller's idea of reality.
 
 Unformat
 --------
 
 Unformat
 --------
@@ -185,20 +185,132 @@ follows:
 Then loop parsing individual elements:
 
 ```c
 Then loop parsing individual elements:
 
 ```c
-    while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT) 
+    while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
     {
       if (unformat (&input, "value1 %d", &value1))
         ;/* unformat sets value1 */
       else if (unformat (&input, "value2 %d", &value2)
         ;/* unformat sets value2 */
       else
     {
       if (unformat (&input, "value1 %d", &value1))
         ;/* unformat sets value1 */
       else if (unformat (&input, "value2 %d", &value2)
         ;/* unformat sets value2 */
       else
-        return clib_error_return (0, "unknown input '%U'", 
+        return clib_error_return (0, "unknown input '%U'",
                                   format_unformat_error, input);
     }
 ```
 
 As with format, unformat implements a user-unformat function capability
                                   format_unformat_error, input);
     }
 ```
 
 As with format, unformat implements a user-unformat function capability
-via a "%U" user unformat function scheme.
+via a "%U" user unformat function scheme. Generally, one can trivially
+transform "format (s, "foo %d", foo) -> "unformat (input, "foo %d", &foo)".
+
+Unformat implements a couple of handy non-scanf-like format specifiers:
+
+```c
+    unformat (input, "enable %=", &enable, 1 /* defaults to 1 */);
+    unformat (input, "bitzero %|", &mask, (1<<0));
+    unformat (input, "bitone %|", &mask, (1<<1));
+    <etc>
+```
+
+The phrase "enable %=" means "set the supplied variable to the default
+value" if unformat parses the "enable" keyword all by itself. If
+unformat parses "enable 123" set the supplied variable to 123.
+
+We could clean up a number of hand-rolled "verbose" + "verbose %d"
+argument parsing codes using "%=".
+
+The phrase "bitzero %|" means "set the specified bit in the supplied
+bitmask" if unformat parses "bitzero". Although it looks like it could
+be fairly handy, it's very lightly used in the code base.
+
+### How to parse a single input line
+
+Debug CLI command functions MUST NOT accidentally consume input
+belonging to other debug CLI commands. Otherwise, it's impossible to
+script a set of debug CLI commands which "work fine" when issued one
+at a time.
+
+This bit of code is NOT correct:
+
+```c
+  /* Eats script input NOT beloging to it, and chokes! */
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, ...))
+       ;
+      else if (unformat (input, ...))
+       ;
+      else
+        return clib_error_return (0, "parse error: '%U'",
+                                    format_unformat_error, input);
+       }
+    }
+```
+
+When executed as part of a script, such a function will return "parse
+error: '<next-command-text>'" every time, unless it happens to be the
+last command in the script.
+
+Instead, use "unformat_line_input" to consume the rest of a line's
+worth of input - everything past the path specified in the
+VLIB_CLI_COMMAND declaration.
+
+For example, unformat_line_input with "my_command" set up as shown
+below and user input "my path is clear" will produce an
+unformat_input_t that contains "is clear".
+
+```c
+    VLIB_CLI_COMMAND (...) = {
+        .path = "my path",
+    };
+```
+
+Here's a bit of code which shows the required mechanics, in full:
+
+```c
+    static clib_error_t *
+    my_command_fn (vlib_main_t * vm,
+                   unformat_input_t * input,
+                   vlib_cli_command_t * cmd)
+    {
+      unformat_input_t _line_input, *line_input = &_line_input;
+      u32 this, that;
+      clib_error_t *error = 0;
+
+      if (!unformat_user (input, unformat_line_input, line_input))
+        return 0;
+
+      /*
+       * Here, UNFORMAT_END_OF_INPUT is at the end of the line we consumed,
+       * not at the end of the script...
+       */
+      while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+        {
+           if (unformat (line_input, "this %u", &this))
+             ;
+           else if (unformat (line_input, "that %u", &that))
+             ;
+           else
+             {
+               error = clib_error_return (0, "parse error: '%U'",
+                                    format_unformat_error, line_input);
+               goto done;
+             }
+          }
+
+    <do something based on "this" and "that", etc>
+
+    done:
+      unformat_free (line_input);
+      return error;
+    }
+   /* *INDENT-OFF* */
+   VLIB_CLI_COMMAND (my_command, static) = {
+     .path = "my path",
+     .function = my_command_fn",
+   };
+   /* *INDENT-ON* */
+
+```
+
 
 Vppinfra errors and warnings
 ----------------------------
 
 Vppinfra errors and warnings
 ----------------------------