[PATCH v4 05/14] command: Support a getopt-based command signature

Simon Glass sjg at chromium.org
Wed Jul 1 13:42:25 CEST 2026


U-Boot commands take a (cmdtp, flag, argc, argv) argument list, so a
command which parses options must declare a struct getopt_state, call
getopt_init_state() and thread argc/argv through getopt(). This is the
same duplication in every such command.

Add a second command signature which takes a single struct getopt_state
pointer. A command marked with CMDF_GETOPT stores a function of this
type in its ->cmd slot; cmd_invoke() sets up the getopt_state and calls
it. Commands without the flag are called as before, so the two
signatures coexist and commands can be moved over one at a time.

cmd_invoke() builds the state with getopt_init_state(), which already
records argc/argv (and a working copy of argv) in the state, so the
command can reach them through the same pointer. Add U_BOOT_CMD_GETOPT()
for declaring a top-level command and U_BOOT_CMD_MKENT_GETOPT() for a
command-table entry that uses the new signature, along with _COMPLETE
variants that also take an auto-complete function so a getopt command
can still offer tab completion.

The new CMDF_GETOPT flag goes in a cmd_flags byte, which fits in the
padding freed by narrowing maxargs to a short. The two command entries
built by hand rather than through a U_BOOT_CMD_MKENT*() macro (the '['
alias in test and the '?' alias in help) gain an explicit cmd_flags
slot.

Guard the getopt path in cmd_invoke() with CONFIG_IS_ENABLED(GETOPT) so
the getopt_init_state() call compiles out when GETOPT is disabled.
getopt_init_state() lives in lib/getopt.c (built only for
CONFIG_GETOPT), and cmd_invoke() is always-compiled core code, so
without the guard every board that has commands but not GETOPT fails to
link with an undefined reference. Only commands that select GETOPT set
CMDF_GETOPT, so the guard never disables a reachable path.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

Changes in v4:
- Guard cmd_invoke() with CONFIG_IS_ENABLED(GETOPT) so boards without
  GETOPT still link

 cmd/help.c        |  2 +-
 cmd/test.c        |  2 +-
 common/command.c  | 20 +++++++++--
 include/command.h | 84 ++++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 100 insertions(+), 8 deletions(-)

diff --git a/cmd/help.c b/cmd/help.c
index 1be83ba607d..b87c19003d4 100644
--- a/cmd/help.c
+++ b/cmd/help.c
@@ -28,7 +28,7 @@ U_BOOT_CMD(
  * nor can we rely on the CONFIG_SYS_LONGHELP helper macro
  */
 ll_entry_declare(struct cmd_tbl, question_mark, cmd) = {
-	"?",	CONFIG_SYS_MAXARGS, cmd_always_repeatable,	do_help,
+	"?",	CONFIG_SYS_MAXARGS, 0, cmd_always_repeatable,	do_help,
 	"alias for 'help'",
 #ifdef  CONFIG_SYS_LONGHELP
 	""
diff --git a/cmd/test.c b/cmd/test.c
index c76ebf800ee..f0b89aa0af0 100644
--- a/cmd/test.c
+++ b/cmd/test.c
@@ -244,7 +244,7 @@ U_BOOT_CMD(
  * This does not use the U_BOOT_CMD macro as [ can't be used in symbol names
  */
 ll_entry_declare(struct cmd_tbl, lbracket, cmd) = {
-	"[",	CONFIG_SYS_MAXARGS, cmd_always_repeatable, do_test,
+	"[",	CONFIG_SYS_MAXARGS, 0, cmd_always_repeatable, do_test,
 	"alias for 'test'",
 #ifdef CONFIG_SYS_LONGHELP
 	" <test expression> ]"
diff --git a/common/command.c b/common/command.c
index eb2c2123534..c747bef7883 100644
--- a/common/command.c
+++ b/common/command.c
@@ -13,6 +13,7 @@
 #include <command.h>
 #include <console.h>
 #include <env.h>
+#include <getopt.h>
 #include <image.h>
 #include <log.h>
 #include <mapmem.h>
@@ -542,12 +543,27 @@ void fixup_cmdtable(struct cmd_tbl *cmdtp, int size)
 	}
 }
 
+int cmd_invoke(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	if (CONFIG_IS_ENABLED(GETOPT) && (cmdtp->cmd_flags & CMDF_GETOPT)) {
+		int (*func)(struct getopt_state *gs);
+		struct getopt_state gs;
+
+		func = (int (*)(struct getopt_state *))cmdtp->cmd;
+		getopt_init_state(&gs, argc, argv);
+
+		return func(&gs);
+	}
+
+	return cmdtp->cmd(cmdtp, flag, argc, argv);
+}
+
 int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
 			  char *const argv[], int *repeatable)
 {
 	*repeatable = 1;
 
-	return cmdtp->cmd(cmdtp, flag, argc, argv);
+	return cmd_invoke(cmdtp, flag, argc, argv);
 }
 
 int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
@@ -555,7 +571,7 @@ int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
 {
 	*repeatable = 0;
 
-	return cmdtp->cmd(cmdtp, flag, argc, argv);
+	return cmd_invoke(cmdtp, flag, argc, argv);
 }
 
 int cmd_discard_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
diff --git a/include/command.h b/include/command.h
index 2e9646c97fb..495d6a07d75 100644
--- a/include/command.h
+++ b/include/command.h
@@ -13,7 +13,9 @@
 #include <linker_lists.h>
 #include <limits.h>
 
+#include <linux/bitops.h>
 #include <linux/compiler_attributes.h>
+#include <linux/types.h>
 
 #ifndef NULL
 #define NULL	0
@@ -36,6 +38,7 @@
 struct cmd_tbl {
 	char		*name;		/* Command Name			*/
 	short		maxargs;	/* maximum number of arguments	*/
+	u8		cmd_flags;	/* CMDF_... flags		*/
 					/*
 					 * Same as ->cmd() except the command
 					 * tells us if it can be repeated.
@@ -66,6 +69,15 @@ struct cmd_tbl {
  */
 #define CMD_MAXARGS	SHRT_MAX
 
+/* Values for cmd_tbl->cmd_flags */
+enum {
+	/*
+	 * ->cmd holds a function with the getopt_state signature
+	 * (int (*)(struct getopt_state *)) instead of the classic one
+	 */
+	CMDF_GETOPT	= BIT(0),
+};
+
 /**
  * cmd_arg_get() - Get a particular argument
  *
@@ -116,6 +128,20 @@ int complete_subcmdv(struct cmd_tbl *cmdtp, int count, int argc,
 
 int cmd_usage(const struct cmd_tbl *cmdtp);
 
+/**
+ * cmd_invoke() - Call a command's implementation function
+ * @cmdtp: Command to call
+ * @flag: Command flags (CMD_FLAG_...)
+ * @argc: Number of arguments
+ * @argv: Argument list
+ *
+ * This calls @cmdtp->cmd, handling both the classic command signature
+ * and the &struct getopt_state signature (CMDF_GETOPT).
+ *
+ * Return: result of the command (0 if OK, else CMD_RET_...)
+ */
+int cmd_invoke(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+
 /* Dummy ->cmd and ->cmd_rep wrappers. */
 int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
 			  char *const argv[], int *repeatable);
@@ -417,20 +443,43 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 #if CONFIG_IS_ENABLED(CMDLINE)
 #define U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep,		\
 				     _usage, _help, _comp)		\
-		{ #_name, _maxargs, _cmd_rep, cmd_discard_repeatable,	\
+		{ #_name, _maxargs, 0, _cmd_rep, cmd_discard_repeatable,	\
 		  _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
 #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,		\
 				_usage, _help, _comp)			\
-		{ #_name, _maxargs,					\
+		{ #_name, _maxargs, 0,					\
 		 _rep ? cmd_always_repeatable : cmd_never_repeatable,	\
 		 _cmd, _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
+#define U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
+					 _usage, _help, _comp)		\
+		{ #_name, _maxargs, CMDF_GETOPT,			\
+		 _rep ? cmd_always_repeatable : cmd_never_repeatable,	\
+		 (int (*)(struct cmd_tbl *, int, int,			\
+			  char *const []))(_cmd),			\
+		 _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
+
+#define U_BOOT_CMD_MKENT_GETOPT(_name, _maxargs, _rep, _cmd,		\
+				_usage, _help)				\
+	U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
+					 _usage, _help, NULL)
+
 #define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
 	ll_entry_declare(struct cmd_tbl, _name, cmd) =			\
 		U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
 						_usage, _help, _comp)
 
+#define U_BOOT_CMD_GETOPT(_name, _maxargs, _rep, _cmd, _usage, _help)	\
+	U_BOOT_CMD_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,		\
+				   _usage, _help, NULL)
+
+#define U_BOOT_CMD_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd, _usage,	\
+				   _help, _comp)			\
+	ll_entry_declare(struct cmd_tbl, _name, cmd) =			\
+		U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep,	\
+						 _cmd, _usage, _help, _comp)
+
 #define U_BOOT_CMDREP_COMPLETE(_name, _maxargs, _cmd_rep, _usage,	\
 			       _help, _comp)				\
 	ll_entry_declare(struct cmd_tbl, _name, cmd) =			\
@@ -457,20 +506,47 @@ int cmd_source_script(ulong addr, const char *fit_uname, const char *confname);
 		return 0;						\
 	}
 
+#define _CMD_REMOVE_GETOPT(_name, _cmd)					\
+	int __remove_ ## _name(void)					\
+	{								\
+		if (0)							\
+			_cmd(NULL);					\
+		return 0;						\
+	}
+
 #define U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep,		\
 				     _usage, _help, _comp)		\
-		{ #_name, _maxargs, 0 ? _cmd_rep : NULL, NULL, _usage,	\
+		{ #_name, _maxargs, 0, 0 ? _cmd_rep : NULL, NULL, _usage, \
 			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
 #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, _usage,	\
 				  _help, _comp)				\
-		{ #_name, _maxargs, NULL, 0 ? _cmd : NULL, _usage,	\
+		{ #_name, _maxargs, 0, NULL, 0 ? _cmd : NULL, _usage,	\
+			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
+
+#define U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
+					 _usage, _help, _comp)		\
+		{ #_name, _maxargs, CMDF_GETOPT, NULL,			\
+		 0 ? (int (*)(struct cmd_tbl *, int, int,		\
+			      char *const []))(_cmd) : NULL, _usage,	\
 			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
+#define U_BOOT_CMD_MKENT_GETOPT(_name, _maxargs, _rep, _cmd,		\
+				_usage, _help)				\
+	U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
+					 _usage, _help, NULL)
+
 #define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help,	\
 			    _comp)				\
 	_CMD_REMOVE(sub_ ## _name, _cmd)
 
+#define U_BOOT_CMD_GETOPT(_name, _maxargs, _rep, _cmd, _usage, _help)	\
+	_CMD_REMOVE_GETOPT(sub_ ## _name, _cmd)
+
+#define U_BOOT_CMD_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd, _usage,	\
+				   _help, _comp)			\
+	_CMD_REMOVE_GETOPT(sub_ ## _name, _cmd)
+
 #define U_BOOT_CMDREP_COMPLETE(_name, _maxargs, _cmd_rep, _usage,	\
 			       _help, _comp)				\
 	_CMD_REMOVE_REP(sub_ ## _name, _cmd_rep)
-- 
2.43.0



More information about the U-Boot mailing list