[RFC PATCH v2 16/17] lib: getopt: Add a Kconfig to drop the permutation code
Simon Glass
sjg at chromium.org
Wed May 20 01:31:42 CEST 2026
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 <sjg at chromium.org>
---
(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
More information about the U-Boot
mailing list