The open-coded check for -n only matches it as the first argument, so 'echo foo -n' treats -n as a literal argument while still relying on a hand-rolled scan that exists in every option-parsing command. Convert do_echo() to the getopt-state command signature and parse -n with getopt().
Use a '+' prefix on the optstring so getopt() works in POSIX mode and stops at the first non-option, matching the documented behaviour that -n is only an option when it precedes the arguments. The positional arguments are then printed from gs->argv[gs->index] onwards, so no manual argc/argv juggling is needed. CMD_ECHO selects GETOPT. This drops the cmd/echo.o text size from 334 to 320 bytes on sandbox. Add a test case covering -n after an argument, which is now printed literally with the trailing newline preserved. Signed-off-by: Simon Glass <[email protected]> --- (no changes since v1) cmd/Kconfig | 1 + cmd/echo.c | 26 ++++++++++++++------------ test/cmd/test_echo.c | 7 +++++++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index e100a08d271..8c73b136fa8 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1927,6 +1927,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..ace6b71233b 100644 --- a/cmd/echo.c +++ b/cmd/echo.c @@ -5,27 +5,29 @@ */ #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[]) +static int do_echo(struct getopt_state *gs) { - int i = 1; bool space = false; bool newline = true; + const char *arg; + int opt; - if (argc > 1) { - if (!strcmp(argv[1], "-n")) { + 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; } @@ -35,7 +37,7 @@ static int do_echo(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } -U_BOOT_CMD( +U_BOOT_CMD_GETOPT( echo, CONFIG_SYS_MAXARGS, 1, do_echo, "echo args to console", "[-n] [args..]\n" diff --git a/test/cmd/test_echo.c b/test/cmd/test_echo.c index 7ed534742f7..64589e3656f 100644 --- a/test/cmd/test_echo.c +++ b/test/cmd/test_echo.c @@ -22,6 +22,13 @@ static struct test_data echo_data[] = { /* Test new line handling */ {"echo -n 1 2 3; echo a b c", "1 2 3a b c"}, + /* + * Option parsing stops at the first non-option, so a -n which + * follows an argument is printed literally and the trailing + * newline is still emitted. + */ + {"echo 1 -n 2", + "1 -n 2"}, /* * Test handling of environment variables. * -- 2.43.0

