Commit c656dceb authored by Rusty Russell's avatar Rusty Russell

opt: add new parse_early_args_incomplete.

If we have plugins, and those can register args, we have a problem finding
the plugin dir!  So, do a best-effort incomplete parse.

Note that this can screw up in theory if we have "--unknown --foo" since we
don't know if unknown takes an argument (in which case, ignore --foo) or
not.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 97026ced
......@@ -207,14 +207,15 @@ 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, 0, &offset, errlog)) == 1);
while ((ret = parse_one(argc, argv, 0, &offset, errlog, false)) == 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, ...))
static bool early_parse(int argc, char *argv[],
void (*errlog)(const char *fmt, ...),
bool ignore_unknown)
{
int ret;
unsigned off = 0;
......@@ -226,7 +227,7 @@ bool opt_early_parse(int argc, char *argv[],
/* This helps opt_usage. */
opt_argv0 = argv[0];
while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog)) == 1);
while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog, ignore_unknown)) == 1);
opt_alloc.free(tmpargv);
......@@ -234,6 +235,18 @@ bool opt_early_parse(int argc, char *argv[],
return (ret == 0);
}
bool opt_early_parse(int argc, char *argv[],
void (*errlog)(const char *fmt, ...))
{
return early_parse(argc, argv, errlog, false);
}
bool opt_early_parse_incomplete(int argc, char *argv[],
void (*errlog)(const char *fmt, ...))
{
return early_parse(argc, argv, errlog, true);
}
void opt_free_table(void)
{
opt_alloc.free(opt_table);
......
......@@ -286,6 +286,30 @@ bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...));
bool opt_early_parse(int argc, char *argv[],
void (*errlog)(const char *fmt, ...));
/**
* opt_early_parse_incomplete - parse early arguments, ignoring unknown ones.
* @argc: argc
* @argv: argv array.
* @errlog: the function to print errors
*
* If you have plugins, you might need to do early parsing (eg. to find the
* plugin directory) but you don't know what options the plugins will want.
*
* Thus, this function is just like opt_early_parse, but ignores unknown options.
*
* Example:
* if (!opt_early_parse_incomplete(argc, argv, opt_log_stderr)) {
* printf("You screwed up, aborting!\n");
* exit(1);
* }
*
* See Also:
* opt_early_parse()
*/
bool opt_early_parse_incomplete(int argc, char *argv[],
void (*errlog)(const char *fmt, ...));
/**
* opt_free_table - reset the opt library.
*
......
......@@ -30,7 +30,7 @@ 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[], enum opt_type is_early, unsigned *offset,
void (*errlog)(const char *fmt, ...))
void (*errlog)(const char *fmt, ...), bool unknown_ok)
{
unsigned i, arg, len;
const char *o, *optarg = NULL;
......@@ -67,10 +67,13 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
continue;
break;
}
if (!o)
if (!o) {
if (unknown_ok)
goto ok;
return parse_err(errlog, argv[0],
argv[arg], strlen(argv[arg]),
"unrecognized option");
}
/* For error messages, we include the leading '--' */
o -= 2;
len += 2;
......@@ -82,10 +85,15 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
(*offset)++;
break;
}
if (!o)
if (!o) {
if (unknown_ok) {
(*offset)++;
goto ok;
}
return parse_err(errlog, argv[0],
argv[arg], strlen(argv[arg]),
"unrecognized option");
}
/* For error messages, we include the leading '-' */
o--;
len = 2;
......@@ -120,6 +128,7 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
return -1;
}
ok:
/* If no more letters in that short opt, reset offset. */
if (*offset && !argv[arg][*offset + 1])
*offset = 0;
......
......@@ -22,6 +22,6 @@ struct opt_alloc {
extern struct opt_alloc opt_alloc;
int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
void (*errlog)(const char *fmt, ...));
void (*errlog)(const char *fmt, ...), bool unknown_ok);
#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"
int main(int argc, char *argv[])
{
plan_tests(8);
/* Simple short args.*/
opt_register_noarg("-a", test_noarg, NULL, "All");
opt_register_early_noarg("-b|--blong", test_noarg, NULL, "All");
/* This is OK. */
ok1(parse_early_args_incomplete(&argc, &argv, "-c", NULL));
ok1(test_cb_called == 0);
/* Skips letters correctly */
ok1(parse_early_args_incomplete(&argc, &argv, "-ca", NULL));
ok1(test_cb_called == 0); /* a is not an early arg! */
test_cb_called = 0;
ok1(parse_early_args_incomplete(&argc, &argv, "-bca", NULL));
ok1(test_cb_called == 1);
test_cb_called = 0;
ok1(parse_early_args_incomplete(&argc, &argv, "--unknown", "--also-unknown", "--blong", NULL));
ok1(test_cb_called == 1);
/* parse_args allocates argv */
free(argv);
return exit_status();
}
......@@ -103,6 +103,29 @@ bool parse_early_args(int *argc, char ***argv, ...)
return opt_early_parse(*argc, *argv, save_err_output);
}
bool parse_early_args_incomplete(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_incomplete(*argc, *argv, save_err_output);
}
struct opt_table short_table[] = {
/* Short opts, different args. */
OPT_WITHOUT_ARG("-a", test_noarg, "a", "Description of a"),
......
......@@ -5,6 +5,7 @@
bool parse_args(int *argc, char ***argv, ...);
bool parse_early_args(int *argc, char ***argv, ...);
bool parse_early_args_incomplete(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