[U-Boot] [PATCH 3/3 v2] sandbox: add getopt support
Simon Glass
sjg at chromium.org
Thu Mar 8 07:15:28 CET 2012
On Sat, Mar 3, 2012 at 11:38 PM, Mike Frysinger <vapier at gentoo.org> wrote:
> From: Simon Glass <sjg at chromium.org>
>
> This adds simple command-line parsing to sandbox. The idea is that it
> sets up the state with options provided, and this state can then be
> queried later, as needed.
>
> New flags are declared with the SB_CMDLINE_OPT_SHORT helper macro,
> pointers are automatically gathered up in a special section, and
> then the core code takes care of gathering them up and processing
> at runtime. This way there is no central place where we have to
> store a list of flags with ifdefs.
>
> Signed-off-by: Simon Glass <sjg at chromium.org>
> Signed-off-by: Mike Frysinger <vapier at gentoo.org>
Acked-by: Simon Glass <sjg at chromium.org>
> ---
> v2
> - rework error detection to handle edge cases
> - add comments all over
> - do some renaming
> - split into dedicated headers where applicable
> - should be ready for merging now if people are happy
>
> arch/sandbox/cpu/os.c | 98 +++++++++++++++++++++++++++++
> arch/sandbox/cpu/start.c | 88 ++++++++++++++++++++++++++
> arch/sandbox/cpu/u-boot.lds | 4 +
> arch/sandbox/include/asm/getopt.h | 71 +++++++++++++++++++++
> arch/sandbox/include/asm/sections.h | 22 +++++++
> arch/sandbox/include/asm/state.h | 5 ++
> arch/sandbox/include/asm/u-boot-sandbox.h | 1 +
> arch/sandbox/lib/board.c | 1 +
> include/os.h | 14 ++++
> 9 files changed, 304 insertions(+), 0 deletions(-)
> create mode 100644 arch/sandbox/include/asm/getopt.h
> create mode 100644 arch/sandbox/include/asm/sections.h
>
> diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c
> index cb469e0..36637af 100644
> --- a/arch/sandbox/cpu/os.c
> +++ b/arch/sandbox/cpu/os.c
> @@ -21,6 +21,7 @@
>
> #include <errno.h>
> #include <fcntl.h>
> +#include <getopt.h>
> #include <stdlib.h>
> #include <termios.h>
> #include <time.h>
> @@ -31,6 +32,9 @@
> #include <sys/types.h>
> #include <linux/types.h>
>
> +#include <asm/getopt.h>
> +#include <asm/sections.h>
> +#include <asm/state.h>
> #include <os.h>
>
> /* Operating System Interface */
> @@ -155,3 +159,97 @@ u64 os_get_nsec(void)
> return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
> #endif
> }
> +
> +static char *short_opts;
> +static struct option *long_opts;
> +
> +int os_parse_args(struct sandbox_state *state, int argc, char *argv[])
> +{
> + struct sb_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
> + size_t num_options = __u_boot_sandbox_option_count();
> + size_t i;
> +
> + int hidden_short_opt;
> + size_t si;
> +
> + int c;
> +
> + if (short_opts || long_opts)
> + return 1;
> +
> + state->argc = argc;
> + state->argv = argv;
> +
> + /* dynamically construct the arguments to the system getopt_long */
> + short_opts = os_malloc(sizeof(*short_opts) * num_options * 2 + 1);
> + long_opts = os_malloc(sizeof(*long_opts) * num_options);
> + if (!short_opts || !long_opts)
> + return 1;
> +
> + /*
> + * getopt_long requires "val" to be unique (since that is what the
> + * func returns), so generate unique values automatically for flags
> + * that don't have a short option. pick 0x100 as that is above the
> + * single byte range (where ASCII/ISO-XXXX-X charsets live).
> + */
> + hidden_short_opt = 0x100;
> + si = 0;
> + for (i = 0; i < num_options; ++i) {
> + long_opts[i].name = sb_opt[i]->flag;
> + long_opts[i].has_arg = sb_opt[i]->has_arg ?
> + required_argument : no_argument;
> + long_opts[i].flag = NULL;
> +
> + if (sb_opt[i]->flag_short) {
> + short_opts[si++] = long_opts[i].val = sb_opt[i]->flag_short;
> + if (long_opts[i].has_arg == required_argument)
> + short_opts[si++] = ':';
> + } else
> + long_opts[i].val = sb_opt[i]->flag_short = hidden_short_opt++;
> + }
> + short_opts[si] = '\0';
> +
> + /* we need to handle output ourselves since u-boot provides printf */
> + opterr = 0;
> +
> + /*
> + * walk all of the options the user gave us on the command line,
> + * figure out what u-boot option structure they belong to (via
> + * the unique short val key), and call the appropriate callback.
> + */
> + while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
> + for (i = 0; i < num_options; ++i) {
> + if (sb_opt[i]->flag_short == c) {
> + if (sb_opt[i]->callback(state, optarg)) {
> + state->parse_err = sb_opt[i]->flag;
> + return 0;
> + }
> + break;
> + }
> + }
> + if (i == num_options) {
> + /*
> + * store the faulting flag for later display. we have to
> + * store the flag itself as the getopt parsing itself is
> + * tricky: need to handle the following flags (assume all
> + * of the below are unknown):
> + * -a optopt='a' optind=<next>
> + * -abbbb optopt='a' optind=<this>
> + * -aaaaa optopt='a' optind=<this>
> + * --a optopt=0 optind=<this>
> + * as you can see, it is impossible to determine the exact
> + * faulting flag without doing the parsing ourselves, so
> + * we just report the specific flag that failed.
> + */
> + if (optopt) {
> + static char parse_err[3] = { '-', 0, '\0', };
> + parse_err[1] = optopt;
> + state->parse_err = parse_err;
> + } else
> + state->parse_err = argv[optind - 1];
> + break;
> + }
> + }
> +
> + return 0;
> +}
> diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c
> index 4a84486..6c3e8eb 100644
> --- a/arch/sandbox/cpu/start.c
> +++ b/arch/sandbox/cpu/start.c
> @@ -20,21 +20,109 @@
> */
>
> #include <common.h>
> +#include <asm/getopt.h>
> +#include <asm/sections.h>
> #include <asm/state.h>
>
> +#include <os.h>
> +
> +int sandbox_early_getopt_check(void)
> +{
> + struct sandbox_state *state = state_get_current();
> + struct sb_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
> + size_t num_options = __u_boot_sandbox_option_count();
> + size_t i;
> + int max_arg_len, max_noarg_len;
> +
> + /* parse_err will be a string of the faulting option */
> + if (!state->parse_err)
> + return 0;
> +
> + if (strcmp(state->parse_err, "help")) {
> + printf("u-boot: error: failed while parsing option: %s\n"
> + "\ttry running with --help for more information.\n",
> + state->parse_err);
> + os_exit(1);
> + }
> +
> + printf(
> + "u-boot, a command line test interface to U-Boot\n\n"
> + "Usage: u-boot [options]\n"
> + "Options:\n");
> +
> + max_arg_len = 0;
> + for (i = 0; i < num_options; ++i)
> + max_arg_len = max(strlen(sb_opt[i]->flag), max_arg_len);
> + max_noarg_len = max_arg_len + 7;
> +
> + for (i = 0; i < num_options; ++i) {
> + struct sb_cmdline_option *opt = sb_opt[i];
> +
> + /* first output the short flag if it has one */
> + if (opt->flag_short >= 0x100)
> + printf(" ");
> + else
> + printf(" -%c, ", opt->flag_short);
> +
> + /* then the long flag */
> + if (opt->has_arg)
> + printf("--%-*s", max_noarg_len, opt->flag);
> + else
> + printf("--%-*s <arg> ", max_arg_len, opt->flag);
> +
> + /* finally the help text */
> + printf(" %s\n", opt->help);
> + }
> +
> + os_exit(0);
> +}
> +
> +static int sb_cmdline_cb_help(struct sandbox_state *state, const char *arg)
> +{
> + /* just flag to sandbox_early_getopt_check to show usage */
> + return 1;
> +}
> +SB_CMDLINE_OPT_SHORT(help, 'h', 0, "Display help");
> +
> int sandbox_main_loop_init(void)
> {
> + struct sandbox_state *state = state_get_current();
> +
> + /* Execute command if required */
> + if (state->cmd) {
> + /* TODO: redo this when cmd tidy-up series lands */
> +#ifdef CONFIG_SYS_HUSH_PARSER
> + run_command(state->cmd, 0);
> +#else
> + parse_string_outer(state->cmd, FLAG_PARSE_SEMICOLON |
> + FLAG_EXIT_FROM_LOOP);
> +#endif
> + os_exit(state->exit_type);
> + }
> +
> return 0;
> }
>
> +static int sb_cmdline_cb_command(struct sandbox_state *state, const char *arg)
> +{
> + state->cmd = arg;
> + return 0;
> +}
> +SB_CMDLINE_OPT_SHORT(command, 'c', 1, "Execute U-Boot command");
> +
> int main(int argc, char *argv[])
> {
> + struct sandbox_state *state;
> int err;
>
> err = state_init();
> if (err)
> return err;
>
> + state = state_get_current();
> + if (os_parse_args(state, argc, argv))
> + return 1;
> +
> /*
> * Do pre- and post-relocation init, then start up U-Boot. This will
> * never return.
> diff --git a/arch/sandbox/cpu/u-boot.lds b/arch/sandbox/cpu/u-boot.lds
> index 0c56aa7..9960138 100644
> --- a/arch/sandbox/cpu/u-boot.lds
> +++ b/arch/sandbox/cpu/u-boot.lds
> @@ -28,6 +28,10 @@ SECTIONS
> _u_boot_cmd : { *(.u_boot_cmd) }
> __u_boot_cmd_end = .;
>
> + __u_boot_sandbox_option_start = .;
> + _u_boot_sandbox_getopt : { *(.u_boot_sandbox_getopt) }
> + __u_boot_sandbox_option_end = .;
> +
> __bss_start = .;
> }
>
> diff --git a/arch/sandbox/include/asm/getopt.h b/arch/sandbox/include/asm/getopt.h
> new file mode 100644
> index 0000000..685883c
> --- /dev/null
> +++ b/arch/sandbox/include/asm/getopt.h
> @@ -0,0 +1,71 @@
> +/*
> + * Code for setting up command line flags like `./u-boot --help`
> + *
> + * Copyright (c) 2011 The Chromium OS Authors.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#ifndef __SANDBOX_GETOPT_H
> +#define __SANDBOX_GETOPT_H
> +
> +struct sandbox_state;
> +
> +/*
> + * Internal structure for storing details about the flag.
> + * Most people should not have to dig around in this as
> + * it only gets parsed by the core sandbox code. End
> + * consumer code should focus on the macros below and
> + * the callback function.
> + */
> +struct sb_cmdline_option {
> + /* The long flag name: "help" for "--help" */
> + const char *flag;
> + /* The (optional) short flag name: "h" for "-h" */
> + int flag_short;
> + /* The help string shown to the user when processing --help */
> + const char *help;
> + /* Whether this flag takes an argument */
> + int has_arg;
> + /* Callback into the end consumer code with the option */
> + int (*callback)(struct sandbox_state *state, const char *opt);
> +};
> +
> +/*
> + * Internal macro to expand the lower macros into the necessary
> + * magic junk that makes this all work.
> + */
> +#define _SB_CMDLINE_OPT(f, s, ha, h) \
> + static struct sb_cmdline_option sb_cmdline_option_##f = { \
> + .flag = #f, \
> + .flag_short = s, \
> + .help = h, \
> + .has_arg = ha, \
> + .callback = sb_cmdline_cb_##f, \
> + }; \
> + /* Ppointer to the struct in a special section for the linker script */ \
> + static __attribute__((section(".u_boot_sandbox_getopt"), used)) \
> + struct sb_cmdline_option *sb_cmdline_option_##f##_ptr = \
> + &sb_cmdline_option_##f
> +
> +/**
> + * Macros for end code to declare new command line flags.
> + *
> + * @param f The long flag name e.g. help
> + * @param ha Does the flag have an argument e.g. 0/1
> + * @param h The help string displayed when showing --help
> + *
> + * This invocation:
> + * SB_CMDLINE_OPT(foo, 0, "The foo arg");
> + * Will create a new flag named "--foo" (no short option) that takes
> + * no argument. If the user specifies "--foo", then the callback func
> + * sb_cmdline_cb_foo() will automatically be called.
> + */
> +#define SB_CMDLINE_OPT(f, ha, h) _SB_CMDLINE_OPT(f, 0, ha, h)
> +/*
> + * Same as above, but @s is used to specify a short flag e.g.
> + * SB_CMDLINE_OPT(foo, 'f', 0, "The foo arg");
> + */
> +#define SB_CMDLINE_OPT_SHORT(f, s, ha, h) _SB_CMDLINE_OPT(f, s, ha, h)
> +
> +#endif
> diff --git a/arch/sandbox/include/asm/sections.h b/arch/sandbox/include/asm/sections.h
> new file mode 100644
> index 0000000..eafce7d
> --- /dev/null
> +++ b/arch/sandbox/include/asm/sections.h
> @@ -0,0 +1,22 @@
> +/*
> + * decls for symbols defined in the linker script
> + *
> + * Copyright (c) 2012 The Chromium OS Authors.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#ifndef __SANDBOX_SECTIONS_H
> +#define __SANDBOX_SECTIONS_H
> +
> +struct sb_cmdline_option;
> +
> +extern struct sb_cmdline_option *__u_boot_sandbox_option_start[],
> + *__u_boot_sandbox_option_end[];
> +
> +static inline size_t __u_boot_sandbox_option_count(void)
> +{
> + return __u_boot_sandbox_option_end - __u_boot_sandbox_option_start;
> +}
> +
> +#endif
> diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h
> index 5b34e94..2b62b46 100644
> --- a/arch/sandbox/include/asm/state.h
> +++ b/arch/sandbox/include/asm/state.h
> @@ -22,6 +22,8 @@
> #ifndef __SANDBOX_STATE_H
> #define __SANDBOX_STATE_H
>
> +#include <config.h>
> +
> /* How we exited U-Boot */
> enum exit_type_id {
> STATE_EXIT_NORMAL,
> @@ -33,6 +35,9 @@ enum exit_type_id {
> struct sandbox_state {
> const char *cmd; /* Command to execute */
> enum exit_type_id exit_type; /* How we exited U-Boot */
> + const char *parse_err; /* Error to report from parsing */
> + int argc; /* Program arguments */
> + char **argv;
> };
>
> /**
> diff --git a/arch/sandbox/include/asm/u-boot-sandbox.h b/arch/sandbox/include/asm/u-boot-sandbox.h
> index 99e950b..50bf8c6 100644
> --- a/arch/sandbox/include/asm/u-boot-sandbox.h
> +++ b/arch/sandbox/include/asm/u-boot-sandbox.h
> @@ -36,6 +36,7 @@ int board_init(void);
> int dram_init(void);
>
> /* start.c */
> +int sandbox_early_getopt_check(void);
> int sandbox_main_loop_init(void);
>
> #endif /* _U_BOOT_SANDBOX_H_ */
> diff --git a/arch/sandbox/lib/board.c b/arch/sandbox/lib/board.c
> index 25a8d02..306d1ec 100644
> --- a/arch/sandbox/lib/board.c
> +++ b/arch/sandbox/lib/board.c
> @@ -134,6 +134,7 @@ init_fnc_t *init_sequence[] = {
> env_init, /* initialize environment */
> serial_init, /* serial communications setup */
> console_init_f, /* stage 1 init of console */
> + sandbox_early_getopt_check, /* process command line flags (err/help) */
> display_banner, /* say that we are here */
> #if defined(CONFIG_DISPLAY_CPUINFO)
> print_cpuinfo, /* display cpu info (and speed) */
> diff --git a/include/os.h b/include/os.h
> index 6b7ee47..45729c1 100644
> --- a/include/os.h
> +++ b/include/os.h
> @@ -27,6 +27,8 @@
> #ifndef __OS_H__
> #define __OS_H__
>
> +struct sandbox_state;
> +
> /**
> * Access to the OS read() system call
> *
> @@ -122,4 +124,16 @@ void os_usleep(unsigned long usec);
> */
> u64 os_get_nsec(void);
>
> +/**
> + * Parse arguments and update sandbox state.
> + *
> + * @param state Sandbox state to update
> + * @param argc Argument count
> + * @param argv Argument vector
> + * @return 0 if ok, and program should continue;
> + * 1 if ok, but program should stop;
> + * -1 on error: program should terminate.
> + */
> +int os_parse_args(struct sandbox_state *state, int argc, char *argv[]);
> +
> #endif
> --
> 1.7.8.4
>
More information about the U-Boot
mailing list