Commit 50212d0d authored by Rusty Russell's avatar Rusty Russell

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().
parent bb2a75f4
......@@ -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);
......
......@@ -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.
......@@ -78,6 +78,39 @@ struct opt_table;
sizeof(_check_is_entry(table)) ? NULL : NULL, NULL, NULL, \
{ 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.
*
......@@ -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,10 +253,39 @@ 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.
*
......@@ -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))*), \
......
......@@ -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) {
......
......@@ -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 */
/* 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();
}
......@@ -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"),
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment