U-Boot Archive on lore.kernel.org
 help / color / mirror / Atom feed
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)


  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