From: Sean Anderson <seanga2@gmail.com>
To: Simon Glass <sjg@chromium.org>, u-boot@lists.denx.de
Cc: Andrew Goodbody <andrew.goodbody@linaro.org>,
Heiko Schocher <hs@nabladev.com>,
Heinrich Schuchardt <xypron.glpk@gmx.de>,
Ilias Apalodimas <ilias.apalodimas@linaro.org>,
Jerome Forissier <jerome.forissier@arm.com>,
"Kory Maincent (TI.com)" <kory.maincent@bootlin.com>,
Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>,
Patrice Chotard <patrice.chotard@foss.st.com>,
Peng Fan <peng.fan@nxp.com>,
Quentin Schulz <quentin.schulz@cherry.de>,
Tom Rini <trini@konsulko.com>, Yao Zi <me@ziyao.cc>
Subject: Re: [RFC PATCH 07/11] cmd: echo: Use getopt() with '+' prefix for option parsing
Date: Fri, 15 May 2026 17:58:15 -0400 [thread overview]
Message-ID: <23a059c1-e3ea-4351-bdfb-fba4462539ad@gmail.com> (raw)
In-Reply-To: <20260515203311.2555651-8-sjg@chromium.org>
On 5/15/26 16:32, Simon Glass wrote:
> The 'echo' command's option parser is a single strcmp against argv[1]
> that decides whether to suppress the trailing newline. Convert it to
> getopt() so echo follows the same shape as the other commands in this
> series and exercises the '+' prefix that POSIX-style 'stop at first
> non-option' callers need.
>
> The optstring uses the '+' prefix to preserve bash echo behaviour: -n is
> honoured only as the very first argument, so 'echo hello -n' still
> prints 'hello -n\n' verbatim.
>
> Two minor differences from the bash builtin remain, both of which
> the user can work around with quoting or --:
>
> * -x (an unknown short option) returns CMD_RET_USAGE rather than
> printing literally; use 'echo -- -x' to print it.
> * -nfoo (joined form) parses -n and then errors on the trailing
> characters.
Why? This introduces incompatibility with echo as it exists today as
well as unix-style echo. We can have an (almost) completely-compatible
echo with
getopt_init_state(&gs, argc, argv);
while (getopt_silent(&gs, "+n") == 'n')
newline = false;
for (i = gs.index; i < argc; ++i) {
<snip>
The only difference is that something like "echo -na" will result in
"-na" and not "-na\n". IMO echo is not a good candidate for getopt
due to its unusual argument handling. Even coreutils echo does not
use getopt. So I think we should really leave echo as-is.
> Add three test cases that pin down the new behaviour: trailing -n stays
> literal, -- ends option parsing, and -- is consumed even after a
> recognised flag. The positional loop uses getopt_pop() as an iterator.
> CMD_ECHO selects GETOPT so the parser is linked in on boards that don't
> already enable it.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
> cmd/Kconfig | 1 +
> cmd/echo.c | 23 ++++++++++++++---------
> test/cmd/test_echo.c | 10 ++++++++++
> 3 files changed, 25 insertions(+), 9 deletions(-)
>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index c71c6824a19..709696c3c41 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1916,6 +1916,7 @@ config CMD_CAT
> config CMD_ECHO
> bool "echo"
> default y
> + select GETOPT
> help
> Echo args to console
>
> diff --git a/cmd/echo.c b/cmd/echo.c
> index d1346504cfb..63422c75cc6 100644
> --- a/cmd/echo.c
> +++ b/cmd/echo.c
> @@ -5,27 +5,32 @@
> */
>
> #include <command.h>
> -#include <linux/string.h>
> +#include <getopt.h>
>
> static int do_echo(struct cmd_tbl *cmdtp, int flag, int argc,
> char *const argv[])
> {
> - int i = 1;
> + struct getopt_state gs;
> bool space = false;
> bool newline = true;
> + char *arg;
> + int opt;
>
> - if (argc > 1) {
> - if (!strcmp(argv[1], "-n")) {
> + getopt_init_state(&gs, argc, argv);
> + while ((opt = getopt(&gs, "+n")) > 0) {
> + switch (opt) {
> + case 'n':
> newline = false;
> - ++i;
> + break;
> + default:
> + return CMD_RET_USAGE;
> }
> }
>
> - for (; i < argc; ++i) {
> - if (space) {
> + while ((arg = getopt_pop(&gs))) {
> + if (space)
> putc(' ');
> - }
> - puts(argv[i]);
> + puts(arg);
> space = true;
> }
>
> diff --git a/test/cmd/test_echo.c b/test/cmd/test_echo.c
> index 7ed534742f7..5fc139fbe68 100644
> --- a/test/cmd/test_echo.c
> +++ b/test/cmd/test_echo.c
> @@ -35,6 +35,16 @@ static struct test_data echo_data[] = {
> /* Test handling of shell variables. */
> {"setenv jQx; for jQx in 1 2 3; do echo -n \"${jQx}, \"; done; echo;",
> "1, 2, 3, "},
> + /* -n only suppresses the newline when it comes before any
> + * positional argument; a trailing -n is just another argument.
> + */
> + {"echo hello -n", "hello -n"},
> + /* "--" ends option parsing, so a following dash-prefixed token
> + * is printed verbatim instead of being rejected.
> + */
> + {"echo -- -x", "-x"},
> + /* "--" is consumed even when it follows a recognised flag. */
> + {"echo -n -- foo; echo done", "foodone"},
> };
>
> static int lib_test_hush_echo(struct unit_test_state *uts)
next prev parent reply other threads:[~2026-05-15 21:58 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-15 20:32 [RFC PATCH 00/11] Tidy command option parsing and use it a bit Simon Glass
2026-05-15 20:32 ` [RFC PATCH 01/11] lib: string: Add strlower() Simon Glass
2026-05-15 20:32 ` [RFC PATCH 02/11] cmd: ini: Use strlower() to normalise case Simon Glass
2026-05-15 20:32 ` [RFC PATCH 03/11] fs: fat: " Simon Glass
2026-05-15 20:32 ` [RFC PATCH 04/11] boot: pxe_utils: Use strlower() in get_string() Simon Glass
2026-05-15 20:32 ` [RFC PATCH 05/11] lib: getopt: Permute by default with inline reorder Simon Glass
2026-05-15 21:37 ` Sean Anderson
2026-05-20 20:42 ` Simon Glass
2026-05-15 20:32 ` [RFC PATCH 06/11] lib: getopt: Add getopt_pop() helper Simon Glass
2026-05-15 21:40 ` Sean Anderson
2026-05-20 20:41 ` Simon Glass
2026-05-15 20:32 ` [RFC PATCH 07/11] cmd: echo: Use getopt() with '+' prefix for option parsing Simon Glass
2026-05-15 21:58 ` Sean Anderson [this message]
2026-05-20 20:41 ` Simon Glass
2026-05-15 20:32 ` [RFC PATCH 08/11] cmd: hash: Use getopt() " Simon Glass
2026-05-15 20:33 ` [RFC PATCH 09/11] cmd: nvedit: Use getopt() in env grep Simon Glass
2026-05-15 20:33 ` [RFC PATCH 10/11] cmd: nvedit: Use getopt() in env export and env import Simon Glass
2026-05-15 20:33 ` [RFC PATCH 11/11] doc: commands: Recommend getopt() for option parsing Simon Glass
2026-05-15 21:43 ` [RFC PATCH 00/11] Tidy command option parsing and use it a bit Tom Rini
2026-05-15 21:59 ` Sean Anderson
2026-05-15 22:06 ` Sean Anderson
2026-05-15 22:21 ` Tom Rini
2026-05-15 22:27 ` Sean Anderson
2026-05-20 20:40 ` Simon Glass
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=23a059c1-e3ea-4351-bdfb-fba4462539ad@gmail.com \
--to=seanga2@gmail.com \
--cc=andrew.goodbody@linaro.org \
--cc=hs@nabladev.com \
--cc=ilias.apalodimas@linaro.org \
--cc=jerome.forissier@arm.com \
--cc=kory.maincent@bootlin.com \
--cc=me@ziyao.cc \
--cc=mikhail.kshevetskiy@iopsys.eu \
--cc=patrice.chotard@foss.st.com \
--cc=peng.fan@nxp.com \
--cc=quentin.schulz@cherry.de \
--cc=sjg@chromium.org \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
--cc=xypron.glpk@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox