From 04044a567b6b177eb24944343d9e9e61a89edd53 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:23 +0200 Subject: [PATCH 01/12] cmd: test: add support for =~ operator Currently, the only way to make use of regex matching in the shell is by using "setexpr [g]sub" command. That's rather awkward for asking whether a string matches a regex. At the very least, it requires providing setexpr with a dummy target variable, but also, the return value of setexpr doesn't say whether any substitutions were done, so one would have to do some roundabout thing like env set dummy "${string_to_test}" setexpr sub dummy '' '' if test "${dummy}" != "${string_to_test}" ; then ... When CONFIG_REGEX is set, teach the test command a new operator, =~, which will allow one to more naturally write if test "${string_to_test}" =~ '' ; then ... The =~ operator with similar functionality is also supported in bash when using its "extended" test operator [[ ]]. Reviewed-by: Simon Glass Reviewed-by: Tom Rini Signed-off-by: Rasmus Villemoes --- cmd/test.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cmd/test.c b/cmd/test.c index b4c3eabf9f6..a42a523d33d 100644 --- a/cmd/test.c +++ b/cmd/test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #define OP_INVALID 0 @@ -26,6 +27,7 @@ #define OP_INT_GT 14 #define OP_INT_GE 15 #define OP_FILE_EXISTS 16 +#define OP_REGEX 17 const struct { int arg; @@ -49,6 +51,9 @@ const struct { {0, "-z", OP_STR_EMPTY, 2}, {0, "-n", OP_STR_NEMPTY, 2}, {0, "-e", OP_FILE_EXISTS, 4}, +#ifdef CONFIG_REGEX + {1, "=~", OP_REGEX, 3}, +#endif }; static int do_test(struct cmd_tbl *cmdtp, int flag, int argc, @@ -141,6 +146,20 @@ static int do_test(struct cmd_tbl *cmdtp, int flag, int argc, case OP_FILE_EXISTS: expr = file_exists(ap[1], ap[2], ap[3], FS_TYPE_ANY); break; +#ifdef CONFIG_REGEX + case OP_REGEX: { + struct slre slre; + + if (slre_compile(&slre, ap[2]) == 0) { + printf("Error compiling regex: %s\n", slre.err_str); + expr = 0; + break; + } + + expr = slre_match(&slre, ap[0], strlen(ap[0]), NULL); + break; + } +#endif } switch (op) { From a5af8f9ad2779e2a18bc9d723c687ee2213086f0 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:24 +0200 Subject: [PATCH 02/12] slre: add myself as maintainer I guess that touching these files means "tag, you're it". That's fine with me. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d62dd35a385..94c62daf834 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1628,6 +1628,12 @@ F: drivers/gpio/sl28cpld-gpio.c F: drivers/misc/sl28cpld.c F: drivers/watchdog/sl28cpld-wdt.c +SLRE +M: Rasmus Villemoes +S: Maintained +F: include/slre.h +F: lib/slre.c + SMCCC TRNG M: Etienne Carriere S: Maintained From ced883d92c0568cdb15b5b67106c29a4623b19d8 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:25 +0200 Subject: [PATCH 03/12] test: slre: add tests for regex library Inspecting the slre.c code reveals a few bugs; those are easy to demonstrate with the new '=~' test operator. Before fixing them, let's add a place to add test cases. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- MAINTAINERS | 1 + test/lib/Makefile | 1 + test/lib/slre.c | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 test/lib/slre.c diff --git a/MAINTAINERS b/MAINTAINERS index 94c62daf834..111e2767917 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1633,6 +1633,7 @@ M: Rasmus Villemoes S: Maintained F: include/slre.h F: lib/slre.c +F: test/lib/slre.c SMCCC TRNG M: Etienne Carriere diff --git a/test/lib/Makefile b/test/lib/Makefile index d620510f998..ff4ff63270d 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_SHA256) += test_sha256_hmac.o obj-$(CONFIG_HKDF_MBEDTLS) += test_sha256_hkdf.o obj-$(CONFIG_GETOPT) += getopt.o obj-$(CONFIG_CRC8) += test_crc8.o +obj-$(CONFIG_REGEX) += slre.o obj-$(CONFIG_UT_LIB_CRYPT) += test_crypt.o obj-$(CONFIG_UT_TIME) += time.o obj-$(CONFIG_$(PHASE_)UT_UNICODE) += unicode.o diff --git a/test/lib/slre.c b/test/lib/slre.c new file mode 100644 index 00000000000..51a50b269aa --- /dev/null +++ b/test/lib/slre.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +#include +#include +#include + +struct re_test { + const char *str; + const char *re; + int match; +}; + +static const struct re_test re_test[] = { + { "123", "^\\d+$", 1}, + { "x23", "^\\d+$", 0}, + { "banana", "^([bn]a)*$", 1}, + { "panama", "^([bn]a)*$", 0}, + {} +}; + +static int lib_slre(struct unit_test_state *uts) +{ + const struct re_test *t; + + for (t = re_test; t->str; t++) { + struct slre slre; + + ut_assert(slre_compile(&slre, t->re)); + ut_assertf(!!slre_match(&slre, t->str, strlen(t->str), NULL) == t->match, + "'%s' unexpectedly %s '%s'\n", t->str, + t->match ? "didn't match" : "matched", t->re); + } + + return 0; +} +LIB_TEST(lib_slre, 0); From 19b3e24083eb0b1b5299e689d0bc5f1a6c4ebdcd Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:26 +0200 Subject: [PATCH 04/12] slre: drop wrong "anchored" optimization The regex '^a|b' means "does the string start with a, or does it have a b anywhere", not "does the string start with a or b" (the latter should be spelled '^[ab]' or '^(a|b)'). It should match exactly the same strings as 'b|^a'. But the current implementation hard-codes an assumption that when the regex starts with a ^, the whole regex must match from the beginning, i.e. it only attempts at offset 0. It really should be completely symmetrical to 'b|c$' ("does it have a b anywhere or end with c?"), which is treated correctly. Another quirk is that currently the regex 'x*$', which should match all strings (because it just means "does the string end with 0 or more x'es"), does not, because in the unanchored case we never attempt to match at ofs==len. In the anchored case, '^x*$', this works correctly and matches exactly strings (including the empty string) consisting entirely of x'es. Fix both of these issues by dropping all use of the slre->anchored member and always test at all possible offsets. If the regex does have a ^ somewhere (including after a | branch character), that is correctly handled by the match engine by only matching when *ofs is 0. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- include/slre.h | 1 - lib/slre.c | 13 +++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/include/slre.h b/include/slre.h index 4b41a4b276f..af5b1302d9c 100644 --- a/include/slre.h +++ b/include/slre.h @@ -63,7 +63,6 @@ struct slre { int code_size; int data_size; int num_caps; /* Number of bracket pairs */ - int anchored; /* Must match from string start */ const char *err_str; /* Error string */ }; diff --git a/lib/slre.c b/lib/slre.c index 277a59a03a7..4f455400d3a 100644 --- a/lib/slre.c +++ b/lib/slre.c @@ -413,10 +413,7 @@ int slre_compile(struct slre *r, const char *re) { r->err_str = NULL; - r->code_size = r->data_size = r->num_caps = r->anchored = 0; - - if (*re == '^') - r->anchored++; + r->code_size = r->data_size = r->num_caps = 0; emit(r, OPEN); /* This will capture what matches full RE */ emit(r, 0); @@ -650,13 +647,9 @@ slre_match(const struct slre *r, const char *buf, int len, { int i, ofs = 0, res = 0; - if (r->anchored) { + for (i = 0; i <= len && res == 0; i++) { + ofs = i; res = match(r, 0, buf, len, &ofs, caps); - } else { - for (i = 0; i < len && res == 0; i++) { - ofs = i; - res = match(r, 0, buf, len, &ofs, caps); - } } return res; From 09b48305d3b6492553982df75e3a9f99d1f856d0 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:27 +0200 Subject: [PATCH 05/12] test: slre: add more test cases Add some tests for the "drop wrong anchored optimization". Without the previous commit, the first, fifth and seventh of these would fail, i.e. those: { "xby", "^a|b", 1}, { "", "x*$", 1}, { "yy", "x*$", 1}, Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- test/lib/slre.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/lib/slre.c b/test/lib/slre.c index 51a50b269aa..b76d33475dd 100644 --- a/test/lib/slre.c +++ b/test/lib/slre.c @@ -15,6 +15,14 @@ static const struct re_test re_test[] = { { "x23", "^\\d+$", 0}, { "banana", "^([bn]a)*$", 1}, { "panama", "^([bn]a)*$", 0}, + { "xby", "^a|b", 1}, + { "xby", "b|^a", 1}, + { "xby", "b|c$", 1}, + { "xby", "c$|b", 1}, + { "", "x*$", 1}, + { "", "^x*$", 1}, + { "yy", "x*$", 1}, + { "yy", "^x*$", 0}, {} }; From ebdd78c487f6599f693e53e707606d7295f20f6e Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:28 +0200 Subject: [PATCH 06/12] test: slre: add some (negative) character class tests Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- test/lib/slre.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/lib/slre.c b/test/lib/slre.c index b76d33475dd..9b41ea92f38 100644 --- a/test/lib/slre.c +++ b/test/lib/slre.c @@ -23,6 +23,9 @@ static const struct re_test re_test[] = { { "", "^x*$", 1}, { "yy", "x*$", 1}, { "yy", "^x*$", 0}, + { "Gadsby", "^[^eE]*$", 1}, + { "Ernest", "^[^eE]*$", 0}, + { "6d41f0a39d6", "^[0123456789abcdef]*$", 1 }, {} }; From 457f19815c8f8b74c36d8cbef454a0b575ea7068 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:29 +0200 Subject: [PATCH 07/12] slre: refactor is_any_but() As preparation for fixing the handling of backslash-escapes used inside a character class, refactor is_any_but() to be defined in terms of is_any_of() so we don't have to repeat the same logic in two places. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- lib/slre.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/slre.c b/lib/slre.c index 4f455400d3a..5cb0d3ec7fa 100644 --- a/lib/slre.c +++ b/lib/slre.c @@ -484,17 +484,14 @@ is_any_of(const unsigned char *p, int len, const char *s, int *ofs) static int is_any_but(const unsigned char *p, int len, const char *s, int *ofs) { - int i, ch; + int dummy = *ofs; - ch = s[*ofs]; - - for (i = 0; i < len; i++) { - if (p[i] == ch) - return 0; + if (is_any_of(p, len, s, &dummy)) { + return 0; + } else { + (*ofs)++; + return 1; } - - (*ofs)++; - return 1; } static int From 5d3f91d6a82386e94c3178721641608563560871 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:30 +0200 Subject: [PATCH 08/12] slre: fix matching of escape sequence used inside character class At the compile stage, the anyof() function clearly intends to handle escape sequences like \d (for digits) inside square brackets, since the logic emits a 0 byte followed by the code representing the character class (NONSPACE, SPACE or DIGIT). However, this is not handled in the corresponding match helper is_any_of(); it just naively loops over all the bytes in the ->data array emitted by anyof() and compares those directly to the current character. For example, this means that the string "\x11" (containing the single character with value 17) is matched by the regex "[#%\d]", because DIGIT happens to be 17. Fix that by recognizing a zero byte as indicating something special and act accordingly. In order not to repeat the "increment *ofs and return 1" in all places, put those two lines after a new match: label. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- lib/slre.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/slre.c b/lib/slre.c index 5cb0d3ec7fa..87dfde720e9 100644 --- a/lib/slre.c +++ b/lib/slre.c @@ -472,13 +472,33 @@ is_any_of(const unsigned char *p, int len, const char *s, int *ofs) ch = s[*ofs]; - for (i = 0; i < len; i++) - if (p[i] == ch) { - (*ofs)++; - return 1; + for (i = 0; i < len; i++) { + if (p[i] == '\0') { + switch (p[++i]) { + case NONSPACE: + if (!isspace(ch)) + goto match; + break; + case SPACE: + if (isspace(ch)) + goto match; + break; + case DIGIT: + if (isdigit(ch)) + goto match; + break; + } + continue; } + if (p[i] == ch) + goto match; + } return 0; + +match: + (*ofs)++; + return 1; } static int From 4d08883556b588bc1e6ef392349c51eceb550829 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:31 +0200 Subject: [PATCH 09/12] test: slre: add test cases for escape char in character class Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- test/lib/slre.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/lib/slre.c b/test/lib/slre.c index 9b41ea92f38..053d046075e 100644 --- a/test/lib/slre.c +++ b/test/lib/slre.c @@ -26,6 +26,9 @@ static const struct re_test re_test[] = { { "Gadsby", "^[^eE]*$", 1}, { "Ernest", "^[^eE]*$", 0}, { "6d41f0a39d6", "^[0123456789abcdef]*$", 1 }, + /* DIGIT is 17 */ + { "##\x11%%\x11", "^[#%\\d]*$", 0 }, + { "##23%%45", "^[#%\\d]*$", 1 }, {} }; From fe4f21185050ef3f2cc847ba04701d1e714522e3 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:32 +0200 Subject: [PATCH 10/12] slre: implement support for ranges in character classes When trying to use U-Boot's regex facility, it is a rather large gotcha that [a-z] range syntax is not supported. It doesn't require a lot of extra code to implement that; we just let the regular parsing emit the start and end literal symbols as usual, and add a new "escape" code RANGE. At match time, this means the code will first just see an 'a' and try to match that, and only then recognize that it's actually part of a range and then do the 'a' <= ch <= 'z' test. Of course, this means that a - in the middle of a [] pair no longer matches a literal dash, but I highly doubt anybody relies on that. Putting it first or last, or escaping it with \, as in most other RE engines, continues to work. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- lib/slre.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/slre.c b/lib/slre.c index 87dfde720e9..117815a6d60 100644 --- a/lib/slre.c +++ b/lib/slre.c @@ -30,7 +30,7 @@ #include enum {END, BRANCH, ANY, EXACT, ANYOF, ANYBUT, OPEN, CLOSE, BOL, EOL, - STAR, PLUS, STARQ, PLUSQ, QUEST, SPACE, NONSPACE, DIGIT}; + STAR, PLUS, STARQ, PLUSQ, QUEST, SPACE, NONSPACE, DIGIT, RANGE}; #ifdef SLRE_TEST static struct { @@ -55,7 +55,8 @@ static struct { {"QUEST", 1, "o"}, /* Match zero or one time, "?" */ {"SPACE", 0, ""}, /* Match whitespace, "\s" */ {"NONSPACE", 0, ""}, /* Match non-space, "\S" */ - {"DIGIT", 0, ""} /* Match digit, "\d" */ + {"DIGIT", 0, ""}, /* Match digit, "\d" */ + {"RANGE", 0, ""}, /* Range separator - */ }; #endif /* SLRE_TEST */ @@ -260,6 +261,15 @@ anyof(struct slre *r, const char **re) return; /* NOTREACHED */ break; + case '-': + if (r->data_size == old_data_size || **re == ']') { + /* First or last character, just match - itself. */ + store_char_in_data(r, '-'); + break; + } + store_char_in_data(r, 0); + store_char_in_data(r, RANGE); + break; case '\\': esc = get_escape_char(re); if ((esc & 0xff) == 0) { @@ -487,6 +497,14 @@ is_any_of(const unsigned char *p, int len, const char *s, int *ofs) if (isdigit(ch)) goto match; break; + case RANGE: + /* + * a-z is represented in the data array as {'a', \0, RANGE, 'z'} + */ + ++i; + if (p[i - 3] <= (unsigned char)ch && (unsigned char)ch <= p[i]) + goto match; + break; } continue; } From de6e54d74dec9da5d7b8572d5a0711c7280eae2c Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:33 +0200 Subject: [PATCH 11/12] test: slre: add tests for character ranges The first of these, { "U-Boot", "^[B-Uo-t]*$", 0 }, would match previously when the - and the letters were all interpreted literally. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- test/lib/slre.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/lib/slre.c b/test/lib/slre.c index 053d046075e..ff2386d614a 100644 --- a/test/lib/slre.c +++ b/test/lib/slre.c @@ -29,6 +29,14 @@ static const struct re_test re_test[] = { /* DIGIT is 17 */ { "##\x11%%\x11", "^[#%\\d]*$", 0 }, { "##23%%45", "^[#%\\d]*$", 1 }, + { "U-Boot", "^[B-Uo-t]*$", 0 }, + { "U-Boot", "^[A-Zm-v-]*$", 1 }, + { "U-Boot", "^[-A-Za-z]*$", 1 }, + /* The range --C covers both - and B. */ + { "U-Boot", "^[--CUot]*$", 1 }, + { "U-Boot", "^[^0-9]*$", 1 }, + { "U-Boot", "^[^0-9<->]*$", 1 }, + { "U-Boot", "^[^0-9<\\->]*$", 0 }, {} }; From 6990cc5257283a631a286a4321a3515c7537f636 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 13 May 2025 10:40:34 +0200 Subject: [PATCH 12/12] doc: document test command Add documentation for the test command, including the newly added =~ operator and some gotchas wrt. the numeric comparisons. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- doc/usage/cmd/setexpr.rst | 5 +- doc/usage/cmd/test.rst | 102 ++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 doc/usage/cmd/test.rst diff --git a/doc/usage/cmd/setexpr.rst b/doc/usage/cmd/setexpr.rst index 593a0ea91e1..5bc37ae50fc 100644 --- a/doc/usage/cmd/setexpr.rst +++ b/doc/usage/cmd/setexpr.rst @@ -144,8 +144,9 @@ Configuration * The *setexpr* command is only available if CMD_SETEXPR=y. * The *setexpr fmt* sub-command is only available if CMD_SETEXPR_FMT=y. -* The *setexpr gsub* and *setexpr sub* sub-commands are only available if - CONFIG_REGEX=y. +* The *setexpr gsub* and *setexpr sub* sub-commands are only available + if CONFIG_REGEX=y. For an overview of the supported regex syntax, + see :doc:`test`. Return value ------------ diff --git a/doc/usage/cmd/test.rst b/doc/usage/cmd/test.rst new file mode 100644 index 00000000000..d1379117fca --- /dev/null +++ b/doc/usage/cmd/test.rst @@ -0,0 +1,102 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. index:: + single: test (command) + +test command +============ + +Synopsis +-------- + +:: + + test + test + test + test ! + test -o + test -a + test -e + test =~ + +Description +----------- + +The ``test`` command is similar to the ordinary shell built-in by the +same name. Unlike in ordinary shells, it cannot be spelled ``[``. + +Strings +~~~~~~~ + +The string tests ``-n`` and ``-z``, and string comparison operators +``=``, ``!=``, ``<`` and ``>``, work exactly as in ordinary shells. + +Numbers +~~~~~~~ + +The number comparison operators ``-lt``, ``-le``, ``-gt``, ``-gt``, +``-eq`` and ``-ne`` work as in ordinary shells. + +.. note:: + Numbers are parsed with ``simple_strtol(, 0)``, meaning that they + are treated as decimal unless there is a `0x` prefix, any errors in + parsing are ignored, and parsing stops as soon as a non-digit (for + the selected base) is encountered. And most U-Boot commands that + generate "numeric" environment variables store them as hexadecimal + *without* a `0x` prefix. + +For example, this is not a correct way of testing whether a given file +has a size less than 4KiB:: + + # Assuming readme.txt exists, sets 'filesize' environment variable + $ size mmc 0:1 readme.txt + $ if test "$filesize" -lt 4096 ; then ... + +If the file size is actually 8000 (decimal), its hexadecimal +representation, and thus the value of ``$filesize``, is ``1f40``, so +the comparison that is done ends up being "1 < 4096". + +Logic +~~~~~ + +The ``!`` operator negates the sense of the test of the expression +````. + +The ``-o`` and ``-a`` operators perform logical OR and logical AND, +respectively, of the two expressions. + +File existence +~~~~~~~~~~~~~~ + +Like ordinary shells, the ``-e`` operator can be used to test for +existence of a file. However, the U-Boot version takes three +arguments: + +- The interface (e.g. ``mmc``). +- The device number, possibly including a partition specification. +- The usual path argument, which is interpreted relative to the root + of the filesystem. + +Regular expressions +~~~~~~~~~~~~~~~~~~~ + +When ``CONFIG_REGEX`` is enabled, an additional operator ``=~`` is +available. This is similar to the same operator available with bash's +extended test command ``[[ ]]``. The left operand is a string which is +matched against the regular expression described by the right operand. + +The regular expression engine supports these features: + +- Anchoring ``^`` and ``$``, matching at the beginning/end of the + string. +- Matching any single character (including whitespace) using ``.``. +- Character classes ``[ ]``, including ranges ``[0-9]`` and negation + ``[^ /.]``. +- Grouping ``( )``. +- Alternation ``|``. +- Postfix qualifiers ``*``, ``+`` and ``?`` and their non-greedy + variants ``*?``, ``+?`` and ``??`` + +For extracting the parts matching a capture group and/or performing +substitutions, including back references, see :doc:`setexpr`. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 372ef56c967..c5b45fd9290 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -123,6 +123,7 @@ Shell commands cmd/source cmd/tcpm cmd/temperature + cmd/test cmd/tftpput cmd/trace cmd/true