[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