summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2011-12-01 16:44:51 +1030
committerRusty Russell <rusty@rustcorp.com.au>2011-12-01 16:44:51 +1030
commit50212d0d271f967d9f5d14c116ec54c0050882a8 (patch)
treeffb0f929e626182bc06fbc7e14b26481b43af94f
parentbb2a75f445f408e43730fc55a1a978581ebe9b96 (diff)
opt: add OPT_EARLY and opt_early_parse.
Parsing options like --verbose and --debug can be a pain. You need to have everything set up before invoking parse_args(), but that may be a significant amount of work, for which you may want verbose or debugging enabled. Thus the concept of "early" args: you can nominate arguments to be parse before anything else, using opt_early_parse().
-rw-r--r--ccan/opt/opt.c27
-rw-r--r--ccan/opt/opt.h121
-rw-r--r--ccan/opt/parse.c13
-rw-r--r--ccan/opt/private.h2
-rw-r--r--ccan/opt/test/run-early.c77
-rw-r--r--ccan/opt/test/utils.c23
-rw-r--r--ccan/opt/test/utils.h1
7 files changed, 244 insertions, 20 deletions
diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c
index db7686be..094b15c7 100644
--- a/ccan/opt/opt.c
+++ b/ccan/opt/opt.c
@@ -107,7 +107,9 @@ static void check_opt(const struct opt_table *entry)
const char *p;
unsigned len;
- if (entry->type != OPT_HASARG && entry->type != OPT_NOARG)
+ if (entry->type != OPT_HASARG && entry->type != OPT_NOARG
+ && entry->type != (OPT_EARLY|OPT_HASARG)
+ && entry->type != (OPT_EARLY|OPT_NOARG))
errx(1, "Option %s: unknown entry type %u",
entry->names, entry->type);
@@ -196,7 +198,28 @@ bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
/* This helps opt_usage. */
opt_argv0 = argv[0];
- while ((ret = parse_one(argc, argv, &offset, errlog)) == 1);
+ while ((ret = parse_one(argc, argv, 0, &offset, errlog)) == 1);
+
+ /* parse_one returns 0 on finish, -1 on error */
+ return (ret == 0);
+}
+
+bool opt_early_parse(int argc, char *argv[],
+ void (*errlog)(const char *fmt, ...))
+{
+ int ret;
+ unsigned off = 0;
+ char **tmpargv = malloc(sizeof(argv[0]) * (argc + 1));
+
+ /* We could avoid a copy and skip instead, but this is simple. */
+ memcpy(tmpargv, argv, sizeof(argv[0]) * (argc + 1));
+
+ /* This helps opt_usage. */
+ opt_argv0 = argv[0];
+
+ while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog)) == 1);
+
+ free(tmpargv);
/* parse_one returns 0 on finish, -1 on error */
return (ret == 0);
diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h
index e7896340..8bbc8f51 100644
--- a/ccan/opt/opt.h
+++ b/ccan/opt/opt.h
@@ -31,7 +31,7 @@ struct opt_table;
* OPT_WITH_ARG()
*/
#define OPT_WITHOUT_ARG(names, cb, arg, desc) \
- { (names), OPT_CB_NOARG((cb), (arg)), { (arg) }, (desc) }
+ { (names), OPT_CB_NOARG((cb), 0, (arg)), { (arg) }, (desc) }
/**
* OPT_WITH_ARG() - macro for initializing an opt_table entry (with arg)
@@ -66,7 +66,7 @@ struct opt_table;
* OPT_WITHOUT_ARG()
*/
#define OPT_WITH_ARG(name, cb, show, arg, desc) \
- { (name), OPT_CB_ARG((cb), (show), (arg)), { (arg) }, (desc) }
+ { (name), OPT_CB_ARG((cb), 0, (show), (arg)), { (arg) }, (desc) }
/**
* OPT_SUBTABLE() - macro for including another table inside a table.
@@ -79,6 +79,39 @@ struct opt_table;
{ NULL }, (desc) }
/**
+ * OPT_EARLY_WITHOUT_ARG() - macro for a early opt_table entry (without arg)
+ * @names: the names of the option eg. "--foo", "-f" or "--foo|-f|--foobar".
+ * @cb: the callback when the option is found.
+ * @arg: the argument to hand to @cb.
+ * @desc: the description for opt_usage(), or opt_hidden.
+ *
+ * This is the same as OPT_WITHOUT_ARG, but for opt_early_parse() instead of
+ * opt_parse().
+ *
+ * See Also:
+ * OPT_EARLY_WITH_ARG(), opt_early_parse()
+ */
+#define OPT_EARLY_WITHOUT_ARG(names, cb, arg, desc) \
+ { (names), OPT_CB_NOARG((cb), OPT_EARLY, (arg)), { (arg) }, (desc) }
+
+/**
+ * OPT_EARLY_WITH_ARG() - macro for an early opt_table entry (with arg)
+ * @names: the option names eg. "--foo=<arg>", "-f" or "-f|--foo <arg>".
+ * @cb: the callback when the option is found (along with <arg>).
+ * @show: the callback to print the value in get_usage (or NULL)
+ * @arg: the argument to hand to @cb and @show
+ * @desc: the description for opt_usage(), or opt_hidden.
+ *
+ * This is the same as OPT_WITH_ARG, but for opt_early_parse() instead of
+ * opt_parse().
+ *
+ * See Also:
+ * OPT_EARLY_WITHOUT_ARG(), opt_early_parse()
+ */
+#define OPT_EARLY_WITH_ARG(name, cb, show, arg, desc) \
+ { (name), OPT_CB_ARG((cb), OPT_EARLY, (show), (arg)), { (arg) }, (desc) }
+
+/**
* OPT_ENDTABLE - macro to create final entry in table.
*
* This must be the final element in the opt_table array.
@@ -129,7 +162,7 @@ void opt_register_table(const struct opt_table *table, const char *desc);
* string and return false.
*/
#define opt_register_noarg(names, cb, arg, desc) \
- _opt_register((names), OPT_CB_NOARG((cb), (arg)), (arg), (desc))
+ _opt_register((names), OPT_CB_NOARG((cb), 0, (arg)), (arg), (desc))
/**
* opt_register_arg - register an option with an arguments
@@ -160,7 +193,40 @@ void opt_register_table(const struct opt_table *table, const char *desc);
* opt_register_arg("--explode|--boom", explode, NULL, NULL, opt_hidden);
*/
#define opt_register_arg(names, cb, show, arg, desc) \
- _opt_register((names), OPT_CB_ARG((cb), (show), (arg)), (arg), (desc))
+ _opt_register((names), OPT_CB_ARG((cb),0,(show), (arg)), (arg), (desc))
+
+/**
+ * opt_register_early_noarg - register an early option with no arguments
+ * @names: the names of the option eg. "--foo", "-f" or "--foo|-f|--foobar".
+ * @cb: the callback when the option is found.
+ * @arg: the argument to hand to @cb.
+ * @desc: the verbose description of the option (for opt_usage()), or NULL.
+ *
+ * This is the same as opt_register_noarg(), but for opt_early_parse().
+ *
+ * See Also:
+ * opt_register_early_arg(), opt_early_parse()
+ */
+#define opt_register_early_noarg(names, cb, arg, desc) \
+ _opt_register((names), OPT_CB_NOARG((cb), OPT_EARLY, (arg)), \
+ (arg), (desc))
+
+/**
+ * opt_register_early_arg - register an early option with an arguments
+ * @names: the names of the option eg. "--foo", "-f" or "--foo|-f|--foobar".
+ * @cb: the callback when the option is found.
+ * @show: the callback to print the value in get_usage (or NULL)
+ * @arg: the argument to hand to @cb.
+ * @desc: the verbose description of the option (for opt_usage()), or NULL.
+ *
+ * This is the same as opt_register_arg(), but for opt_early_parse().
+ *
+ * See Also:
+ * opt_register_early_noarg(), opt_early_parse()
+ */
+#define opt_register_early_arg(names, cb, show, arg, desc) \
+ _opt_register((names), OPT_CB_ARG((cb), OPT_EARLY, (show),(arg)), \
+ (arg), (desc))
/**
* opt_parse - parse arguments.
@@ -169,8 +235,9 @@ void opt_register_table(const struct opt_table *table, const char *desc);
* @errlog: the function to print errors
*
* This iterates through the command line and calls callbacks registered with
- * opt_register_table()/opt_register_arg()/opt_register_noarg(). As this
- * occurs successfully each option is removed from argc and argv.
+ * opt_register_arg()/opt_register_noarg() or OPT_WITHOUT_ARG/OPT_WITH_ARG
+ * entries in tables registered with opt_register_table(). As this occurs
+ * each option is removed from argc and argv.
*
* If there are unknown options, missing arguments or a callback
* returns false, then an error message is printed and false is
@@ -186,11 +253,40 @@ void opt_register_table(const struct opt_table *table, const char *desc);
* }
*
* See Also:
- * opt_log_stderr, opt_log_stderr_exit
+ * opt_log_stderr, opt_log_stderr_exit, opt_early_parse()
*/
bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...));
/**
+ * opt_early_parse - parse early arguments.
+ * @argc: argc
+ * @argv: argv array.
+ * @errlog: the function to print errors
+ *
+ * There are times when you want to parse some arguments before any other
+ * arguments; this is especially important for debugging flags (eg. --verbose)
+ * when you have complicated callbacks in option processing.
+ *
+ * You can use opt_early_parse() to only parse options registered with
+ * opt_register_earlyarg()/opt_register_early_noarg() or
+ * OPT_EARLY_WITHOUT_ARG/OPT_EARLY_WITH_ARG entries in tables registered with
+ * opt_register_table().
+ *
+ * Note that unlike opt_parse(), argc and argv are not altered.
+ *
+ * Example:
+ * if (!opt_early_parse(argc, argv, opt_log_stderr)) {
+ * printf("You screwed up, aborting!\n");
+ * exit(1);
+ * }
+ *
+ * See Also:
+ * opt_parse()
+ */
+bool opt_early_parse(int argc, char *argv[],
+ void (*errlog)(const char *fmt, ...));
+
+/**
* opt_free_table - reset the opt library.
*
* This frees the internal memory and returns counters to zero. Call
@@ -337,7 +433,8 @@ enum opt_type {
OPT_NOARG = 1, /* -f|--foo */
OPT_HASARG = 2, /* -f arg|--foo=arg|--foo arg */
OPT_SUBTABLE = 4, /* Actually, longopt points to a subtable... */
- OPT_END = 8, /* End of the table. */
+ OPT_EARLY = 8, /* Parse this from opt_early_parse() only. */
+ OPT_END = 16, /* End of the table. */
};
struct opt_table {
@@ -355,8 +452,8 @@ struct opt_table {
};
/* Resolves to the four parameters for non-arg callbacks. */
-#define OPT_CB_NOARG(cb, arg) \
- OPT_NOARG, \
+#define OPT_CB_NOARG(cb, pre, arg) \
+ OPT_NOARG|(pre), \
typesafe_cb_cast3(char *(*)(void *), \
char *(*)(typeof(*(arg))*), \
char *(*)(const typeof(*(arg))*), \
@@ -364,8 +461,8 @@ struct opt_table {
NULL, NULL
/* Resolves to the four parameters for arg callbacks. */
-#define OPT_CB_ARG(cb, show, arg) \
- OPT_HASARG, NULL, \
+#define OPT_CB_ARG(cb, pre, show, arg) \
+ OPT_HASARG|(pre), NULL, \
typesafe_cb_cast3(char *(*)(const char *,void *), \
char *(*)(const char *, typeof(*(arg))*), \
char *(*)(const char *, const typeof(*(arg))*), \
diff --git a/ccan/opt/parse.c b/ccan/opt/parse.c
index b92bcb14..5b2cec8b 100644
--- a/ccan/opt/parse.c
+++ b/ccan/opt/parse.c
@@ -29,12 +29,12 @@ static void consume_option(int *argc, char *argv[], unsigned optnum)
}
/* Returns 1 if argument consumed, 0 if all done, -1 on error. */
-int parse_one(int *argc, char *argv[], unsigned *offset,
+int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
void (*errlog)(const char *fmt, ...))
{
unsigned i, arg, len;
const char *o, *optarg = NULL;
- char *problem;
+ char *problem = NULL;
if (getenv("POSIXLY_CORRECT")) {
/* Don't find options after non-options. */
@@ -91,11 +91,12 @@ int parse_one(int *argc, char *argv[], unsigned *offset,
len = 2;
}
- if (opt_table[i].type == OPT_NOARG) {
+ if ((opt_table[i].type & ~OPT_EARLY) == OPT_NOARG) {
if (optarg)
return parse_err(errlog, argv[0], o, len,
"doesn't allow an argument");
- problem = opt_table[i].cb(opt_table[i].u.arg);
+ if ((opt_table[i].type & OPT_EARLY) == is_early)
+ problem = opt_table[i].cb(opt_table[i].u.arg);
} else {
if (!optarg) {
/* Swallow any short options as optarg, eg -afile */
@@ -108,7 +109,9 @@ int parse_one(int *argc, char *argv[], unsigned *offset,
if (!optarg)
return parse_err(errlog, argv[0], o, len,
"requires an argument");
- problem = opt_table[i].cb_arg(optarg, opt_table[i].u.arg);
+ if ((opt_table[i].type & OPT_EARLY) == is_early)
+ problem = opt_table[i].cb_arg(optarg,
+ opt_table[i].u.arg);
}
if (problem) {
diff --git a/ccan/opt/private.h b/ccan/opt/private.h
index f2199e48..e4ad24a2 100644
--- a/ccan/opt/private.h
+++ b/ccan/opt/private.h
@@ -14,7 +14,7 @@ const char *next_sopt(const char *names, unsigned *i);
const char *first_lopt(unsigned *i, unsigned *len);
const char *next_lopt(const char *p, unsigned *i, unsigned *len);
-int parse_one(int *argc, char *argv[], unsigned *offset,
+int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
void (*errlog)(const char *fmt, ...));
#endif /* CCAN_OPT_PRIVATE_H */
diff --git a/ccan/opt/test/run-early.c b/ccan/opt/test/run-early.c
new file mode 100644
index 00000000..439ccabb
--- /dev/null
+++ b/ccan/opt/test/run-early.c
@@ -0,0 +1,77 @@
+/* With errlog == NULL, we never get a "failure". */
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+struct opt_table some_early_table[] = {
+ OPT_EARLY_WITHOUT_ARG("--verbose|-v", test_noarg,
+ "vvv", "Description of verbose"),
+ OPT_EARLY_WITH_ARG("--debug|-d", test_arg, show_arg,
+ "ddd", "Description of debug"),
+ OPT_WITHOUT_ARG("-h|--hhh", test_noarg, "hhh", "Description of hhh"),
+ OPT_ENDTABLE
+};
+
+int main(int argc, char *argv[])
+{
+ const char *myname = argv[0];
+
+ plan_tests(37);
+
+ /* Simple short arg.*/
+ opt_register_noarg("-a", test_noarg, NULL, "All");
+ opt_register_early_noarg("-b", test_noarg, NULL, "All");
+
+ /* Early parsing doesn't mangle. */
+ ok1(parse_early_args(&argc, &argv, "-a", NULL));
+ ok1(argc == 2);
+ ok1(argv[0] == myname);
+ ok1(strcmp(argv[1], "-a") == 0);
+ ok1(argv[2] == NULL);
+ ok1(test_cb_called == 0);
+
+ /* ... even if it processes arg. */
+ ok1(parse_early_args(&argc, &argv, "-b", NULL));
+ ok1(argc == 2);
+ ok1(argv[0] == myname);
+ ok1(strcmp(argv[1], "-b") == 0);
+ ok1(argv[2] == NULL);
+ ok1(test_cb_called == 1);
+
+ ok1(parse_early_args(&argc, &argv, "-ab", NULL));
+ ok1(argc == 2);
+ ok1(argv[0] == myname);
+ ok1(strcmp(argv[1], "-ab") == 0);
+ ok1(argv[2] == NULL);
+ ok1(test_cb_called == 2);
+
+ ok1(parse_args(&argc, &argv, "-ab", NULL));
+ ok1(argc == 1);
+ ok1(argv[0] == myname);
+ ok1(argv[1] == NULL);
+ ok1(test_cb_called == 3);
+
+ opt_register_table(some_early_table, "Some early args");
+ ok1(parse_early_args(&argc, &argv, "--verbose", "-dddd", "-h", NULL));
+ ok1(argc == 4);
+ ok1(argv[0] == myname);
+ ok1(strcmp(argv[1], "--verbose") == 0);
+ ok1(strcmp(argv[2], "-dddd") == 0);
+ ok1(strcmp(argv[3], "-h") == 0);
+ ok1(argv[4] == NULL);
+ ok1(test_cb_called == 5);
+
+ ok1(parse_args(&argc, &argv, "--verbose", "-d", "ddd", "-h", NULL));
+ ok1(argc == 1);
+ ok1(argv[0] == myname);
+ ok1(argv[1] == NULL);
+ ok1(test_cb_called == 6);
+
+ /* parse_args allocates argv */
+ free(argv);
+ return exit_status();
+}
diff --git a/ccan/opt/test/utils.c b/ccan/opt/test/utils.c
index 7378d844..c2967fca 100644
--- a/ccan/opt/test/utils.c
+++ b/ccan/opt/test/utils.c
@@ -80,6 +80,29 @@ bool parse_args(int *argc, char ***argv, ...)
return opt_parse(argc, *argv, save_err_output);
}
+bool parse_early_args(int *argc, char ***argv, ...)
+{
+ char **a;
+ va_list ap;
+
+ va_start(ap, argv);
+ *argc = 1;
+ a = malloc(sizeof(*a) * (*argc + 1));
+ a[0] = (*argv)[0];
+ while ((a[*argc] = va_arg(ap, char *)) != NULL) {
+ (*argc)++;
+ a = realloc(a, sizeof(*a) * (*argc + 1));
+ }
+
+ if (allocated)
+ free(*argv);
+
+ *argv = a;
+ allocated = true;
+
+ return opt_early_parse(*argc, *argv, save_err_output);
+}
+
struct opt_table short_table[] = {
/* Short opts, different args. */
OPT_WITHOUT_ARG("-a", test_noarg, "a", "Description of a"),
diff --git a/ccan/opt/test/utils.h b/ccan/opt/test/utils.h
index 33a2dbdc..1c3658d7 100644
--- a/ccan/opt/test/utils.h
+++ b/ccan/opt/test/utils.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
bool parse_args(int *argc, char ***argv, ...);
+bool parse_early_args(int *argc, char ***argv, ...);
extern char *err_output;
void save_err_output(const char *fmt, ...);
void reset_options(void);