From 4bfa8587c9b7c8450320232837c6481d07ce77f6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 May 2023 11:00:22 +0930 Subject: [PATCH] ccan: allow user to set some bits in opt_table.type. In particular, Core Lightning wants to use this to flag arguments which can be specified multiple times (without overriding). Signed-off-by: Rusty Russell --- ccan/opt/opt.c | 15 ++++----- ccan/opt/opt.h | 11 +++++++ ccan/opt/parse.c | 2 +- ccan/opt/test/run-userbits.c | 59 ++++++++++++++++++++++++++++++++++++ ccan/opt/usage.c | 6 ++-- 5 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 ccan/opt/test/run-userbits.c diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index d376a598..ef711d2c 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -34,7 +34,7 @@ static const char *next_name(const char *names, unsigned *len) static const char *first_opt(unsigned *i, unsigned *len) { for (*i = 0; *i < opt_count; (*i)++) { - if (opt_table[*i].type == OPT_SUBTABLE) + if (opt_table[*i].type & OPT_SUBTABLE) continue; return first_name(opt_table[*i].names, len); } @@ -44,7 +44,7 @@ static const char *first_opt(unsigned *i, unsigned *len) static const char *next_opt(const char *p, unsigned *i, unsigned *len) { for (; *i < opt_count; (*i)++) { - if (opt_table[*i].type == OPT_SUBTABLE) + if (opt_table[*i].type & OPT_SUBTABLE) continue; if (!p) return first_name(opt_table[*i].names, len); @@ -114,10 +114,11 @@ static void check_opt(const struct opt_table *entry) { const char *p; unsigned len; + enum opt_type type = entry->type & (OPT_USER_MIN-1); - if (entry->type != OPT_HASARG && entry->type != OPT_NOARG - && entry->type != (OPT_EARLY|OPT_HASARG) - && entry->type != (OPT_EARLY|OPT_NOARG)) + if (type != OPT_HASARG && type != OPT_NOARG + && type != (OPT_EARLY|OPT_HASARG) + && type != (OPT_EARLY|OPT_NOARG)) failmsg("Option %s: unknown entry type %u", entry->names, entry->type); @@ -181,7 +182,7 @@ bool opt_unregister(const char *names) int found = -1, i; for (i = 0; i < opt_count; i++) { - if (opt_table[i].type == OPT_SUBTABLE) + if (opt_table[i].type & OPT_SUBTABLE) continue; if (strcmp(opt_table[i].names, names) == 0) found = i; @@ -203,7 +204,7 @@ void opt_register_table(const struct opt_table entry[], const char *desc) add_opt(&heading); } for (i = 0; entry[i].type != OPT_END; i++) { - if (entry[i].type == OPT_SUBTABLE) + if (entry[i].type & OPT_SUBTABLE) opt_register_table(subtable_of(&entry[i]), entry[i].desc); else { diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 4b5a2c6c..9eec7385 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -527,6 +527,12 @@ struct opt_table *opt_find_long(const char *arg, const char **optarg); */ struct opt_table *opt_find_short(char arg); +/* opt_type bits reserved for users to play with (ignored!). + * You can set bits in type e.g. (1<type & ~OPT_EARLY) == OPT_NOARG) { + if (ot->type & OPT_NOARG) { if (optarg) return parse_err(errlog, argv[0], o, len, "doesn't allow an argument"); diff --git a/ccan/opt/test/run-userbits.c b/ccan/opt/test/run-userbits.c new file mode 100644 index 00000000..7f102f08 --- /dev/null +++ b/ccan/opt/test/run-userbits.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include "utils.h" + +int main(int argc, char *argv[]) +{ + const char *myname = argv[0]; + + plan_tests(28); + + opt_register_noarg("-a", test_noarg, NULL, "All"); + opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll"); + opt_register_arg("-b|--bbb", test_arg, NULL, "bbb", "AAAAAAll"); + + ok1(strcmp(opt_table[0].names, "-a") == 0); + ok1(opt_table[0].type == OPT_NOARG); + ok1(strcmp(opt_table[1].names, "--aaa") == 0); + ok1(opt_table[1].type == OPT_NOARG); + ok1(strcmp(opt_table[2].names, "-b|--bbb") == 0); + ok1(opt_table[2].type == OPT_HASARG); + + opt_table[0].type |= (1 << OPT_USER_START); + opt_table[1].type |= ((1 << OPT_USER_END)-1) - ((1 << OPT_USER_START)-1); + opt_table[2].type |= (1 << OPT_USER_END); + + /* Should all work fine! */ + ok1(parse_args(&argc, &argv, "-a", NULL)); + ok1(argc == 1); + ok1(argv[0] == myname); + ok1(test_cb_called == 1); + + ok1(parse_args(&argc, &argv, "--aaa", NULL)); + ok1(argc == 1); + ok1(argv[0] == myname); + ok1(test_cb_called == 2); + + /* This one needs an arg. */ + ok1(parse_args(&argc, &argv, "-b", NULL) == false); + ok1(test_cb_called == 2); + ok1(parse_args(&argc, &argv, "-b", "bbb", NULL)); + ok1(argc == 1); + ok1(argv[0] == myname); + ok1(argv[1] == NULL); + ok1(test_cb_called == 3); + + ok1(parse_args(&argc, &argv, "--bbb", "bbb", NULL)); + ok1(argc == 1); + ok1(argv[0] == myname); + ok1(argv[1] == NULL); + ok1(test_cb_called == 4); + + /* parse_args allocates argv */ + free(argv); + return exit_status(); +} diff --git a/ccan/opt/usage.c b/ccan/opt/usage.c index 8ee4ebd0..4ed27919 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -182,10 +182,10 @@ char *opt_usage(const char *argv0, const char *extra) size_t l; if (opt_table[i].desc == opt_hidden) continue; - if (opt_table[i].type == OPT_SUBTABLE) + if (opt_table[i].type & OPT_SUBTABLE) continue; l = strlen(opt_table[i].names); - if (opt_table[i].type == OPT_HASARG + if ((opt_table[i].type & OPT_HASARG) && !strchr(opt_table[i].names, ' ') && !strchr(opt_table[i].names, '=')) l += strlen(" "); @@ -221,7 +221,7 @@ char *opt_usage(const char *argv0, const char *extra) for (i = 0; i < opt_count; i++) { if (opt_table[i].desc == opt_hidden) continue; - if (opt_table[i].type == OPT_SUBTABLE) { + if (opt_table[i].type & OPT_SUBTABLE) { ret = add_str(ret, &len, &max, opt_table[i].desc); ret = add_str(ret, &len, &max, ":\n"); continue; -- 2.39.2