Permuting non-option arguments to the end (the in-loop bubble shift plus the nonopts bookkeeping) is the largest single feature in getopt() and the main reason it pulls in over 1KB on arm64 when echo is the first user to link it. Boards that are happy with POSIX semantics (options before positionals) do not need it.
Add CONFIG_GETOPT_PERMUTE, defaulting to n to reduce code size. When disabled the bubble loop is dead-code-eliminated and getopt() stops at the first non-option, as if every optstring were prefixed with '+'. Signed-off-by: Simon Glass <[email protected]> --- (no changes since v1) lib/Kconfig | 14 ++++++++++++++ lib/getopt.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/lib/Kconfig b/lib/Kconfig index 77ebc79e1db..182381d1458 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -1026,6 +1026,20 @@ config GETOPT help This enables functions for parsing command-line options. +config GETOPT_PERMUTE + bool "Permute non-option arguments to the end" + depends on GETOPT + help + When enabled, getopt() permutes non-option arguments to the end + of the argv list so options can appear anywhere on the command + line; this matches GNU getopt behaviour. + + When disabled, getopt() stops at the first non-option (POSIX + behaviour), which produces smaller code at the cost of forcing + callers to put all options before any positional arguments. The + '+' prefix on optstring becomes a no-op since that is already + the only mode. + config OF_LIBFDT bool "Enable the FDT library" default y if OF_CONTROL diff --git a/lib/getopt.c b/lib/getopt.c index d2ccefc616e..3fb41532d05 100644 --- a/lib/getopt.c +++ b/lib/getopt.c @@ -53,24 +53,30 @@ int getopt(struct getopt_state *gs, const char *optstring) return -1; } - /* - * Permute non-options to the end so we can keep - * scanning for options past them. In '+' mode - * (POSIX), stop at the first non-option instead. - */ - while (gs->index + gs->nonopts < argc) { - int i; - - if (*cur == '-') - break; - if (stop_nonopt) - return -1; - - gs->nonopts++; - for (i = gs->index; i + 1 < argc; i++) - argv[i] = argv[i + 1]; - argv[argc - 1] = cur; - cur = argv[gs->index]; + if (IS_ENABLED(CONFIG_GETOPT_PERMUTE)) { + /* + * Permute non-options to the end so we can + * keep scanning for options past them. In + * '+' mode (POSIX), stop at the first + * non-option instead. + */ + while (gs->index + gs->nonopts < argc) { + int i; + + if (*cur == '-') + break; + if (stop_nonopt) + return -1; + + gs->nonopts++; + for (i = gs->index; i + 1 < argc; i++) + argv[i] = argv[i + 1]; + argv[argc - 1] = cur; + cur = argv[gs->index]; + } + } else if (gs->index < argc && *cur != '-') { + /* POSIX-only mode: stop at first non-option */ + return -1; } if (gs->index + gs->nonopts >= argc) -- 2.43.0

