docs: improve unformat documentation

Specifically: document the unformat specifiers "%=", "%|", and correct
usage of unformat_line_input.

Signed-off-by: Dave Barach <dave@barachs.net>
Change-Id: I254f9c4a61c5c8d3edbf76f521e5f38fb89150f1
This commit is contained in:
Dave Barach
2019-09-09 11:02:45 -04:00
committed by Florin Coras
parent 546f955b3d
commit 5b786fb894

View 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
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.).
@ -138,8 +138,8 @@ schemes may be used:
```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
@ -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
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
--------
@ -185,20 +185,132 @@ follows:
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
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
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
----------------------------