sandbox: add getopt support
authorSimon Glass <sjg@chromium.org>
Wed, 15 Feb 2012 23:51:16 +0000 (15:51 -0800)
committerMike Frysinger <vapier@gentoo.org>
Mon, 12 Mar 2012 15:06:01 +0000 (11:06 -0400)
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@chromium.org>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
arch/sandbox/cpu/os.c
arch/sandbox/cpu/start.c
arch/sandbox/cpu/u-boot.lds
arch/sandbox/include/asm/getopt.h [new file with mode: 0644]
arch/sandbox/include/asm/sections.h [new file with mode: 0644]
arch/sandbox/include/asm/state.h
arch/sandbox/include/asm/u-boot-sandbox.h
arch/sandbox/lib/board.c
include/os.h

index cb469e05106ef561f22342e2dbacf7da1a1256f9..36637af6ce4c578a19e4133025822afe82ec6001 100644 (file)
@@ -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;
+}
index 4a84486c1eb39d8d69dd3fae7b2bb4b78996ae66..6c3e8eb379ea8c5f456ba56f913173b91b7c5c42 100644 (file)
  */
 
 #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.
index 0c56aa77937f4432b9e92ad4b1fc67aae99202f6..99601387cbec979f81820dfd44731beb82e98b97 100644 (file)
@@ -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 (file)
index 0000000..685883c
--- /dev/null
@@ -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 (file)
index 0000000..eafce7d
--- /dev/null
@@ -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
index 5b34e9448e1a917430155999a536b436f5b4902e..2b62b46ea2ccbbbc256f81d32436ddfbfb138fb5 100644 (file)
@@ -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;
 };
 
 /**
index 99e950b80579a0cf77f4f5fd6539112abba4b228..50bf8c605d4129bd2295745adda8462717f423e5 100644 (file)
@@ -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_ */
index 25a8d02fd7c76db778ba41c28bdd543a10395b66..306d1ec3320829f6da78858660bab93a650787a6 100644 (file)
@@ -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) */
index 6b7ee474f04ddee319694d03e1c73eca47460f6c..45729c1e44f67f5176dbc161dc2fca12feaa10f9 100644 (file)
@@ -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