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. 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 <[email protected]> --- 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) -- 2.43.0

