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

