On Sat, Mar 3, 2012 at 11:38 PM, Mike Frysinger <vap...@gentoo.org> wrote: > From: Simon Glass <s...@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 <s...@chromium.org> > Signed-off-by: Mike Frysinger <vap...@gentoo.org>
Acked-by: Simon Glass <s...@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 > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot