From f8b1841d26dabd23c053f5fc61dbd1536cdad43c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 30 Sep 2010 11:06:10 +0930 Subject: [PATCH] opt: add support for showing default value. --- ccan/opt/helpers.c | 46 ++++- ccan/opt/opt.c | 2 + ccan/opt/opt.h | 58 +++++-- ccan/opt/test/run-helpers.c | 325 ++++++++++++++++++++++++++++++++++++ ccan/opt/test/run-usage.c | 6 +- ccan/opt/test/run.c | 2 +- ccan/opt/test/utils.c | 16 +- ccan/opt/test/utils.h | 3 +- ccan/opt/usage.c | 27 ++- 9 files changed, 451 insertions(+), 34 deletions(-) create mode 100644 ccan/opt/test/run-helpers.c diff --git a/ccan/opt/helpers.c b/ccan/opt/helpers.c index 7554c997..0f3671e5 100644 --- a/ccan/opt/helpers.c +++ b/ccan/opt/helpers.c @@ -5,6 +5,9 @@ #include #include "private.h" +/* Upper bound to sprintf this simple type? Each 3 bits < 1 digit. */ +#define CHAR_SIZE(type) (((sizeof(type)*CHAR_BIT + 2) / 3) + 1) + /* FIXME: asprintf module? */ static char *arg_bad(const char *fmt, const char *arg) { @@ -116,7 +119,7 @@ char *opt_inc_intval(int *i) } /* Display version string. */ -char *opt_show_version_and_exit(const char *version) +char *opt_version_and_exit(const char *version) { printf("%s\n", version); exit(0); @@ -127,3 +130,44 @@ char *opt_usage_and_exit(const char *extra) printf("%s", opt_usage(opt_argv0, extra)); exit(0); } + +void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b) +{ + strncpy(buf, *b ? "true" : "false", OPT_SHOW_LEN); +} + +void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b) +{ + strncpy(buf, *b ? "false" : "true", OPT_SHOW_LEN); +} + +void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p) +{ + size_t len = strlen(*p); + buf[0] = '"'; + if (len > OPT_SHOW_LEN - 2) + len = OPT_SHOW_LEN - 2; + strncpy(buf+1, *p, len); + buf[1+len] = '"'; +} + +/* Set an integer value, various forms. Sets to 1 on arg == NULL. */ +void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i) +{ + snprintf(buf, OPT_SHOW_LEN, "%i", *i); +} + +void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui) +{ + snprintf(buf, OPT_SHOW_LEN, "%u", *ui); +} + +void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l) +{ + snprintf(buf, OPT_SHOW_LEN, "%li", *l); +} + +void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul) +{ + snprintf(buf, OPT_SHOW_LEN, "%lu", *ul); +} diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index f601bb69..236d2af0 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -31,6 +31,7 @@ static void add_opt(const struct opt_table *entry) void _opt_register(const char *longopt, char shortopt, enum opt_flags flags, char *(*cb)(void *arg), char *(*cb_arg)(const char *optarg, void *arg), + void (*show)(char buf[OPT_SHOW_LEN], const void *arg), void *arg, const char *desc) { struct opt_table opt; @@ -39,6 +40,7 @@ void _opt_register(const char *longopt, char shortopt, enum opt_flags flags, opt.flags = flags; opt.cb = cb; opt.cb_arg = cb_arg; + opt.show = show; opt.arg = arg; opt.desc = desc; check_opt(&opt); diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index f47b1c88..74bda85b 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -12,12 +12,16 @@ enum opt_flags { OPT_END = 8, /* End of the table. */ }; +/* Maximum length of arg to show in opt_usage */ +#define OPT_SHOW_LEN 80 + struct opt_table { const char *longopt; /* --longopt, or NULL */ char shortopt; /* -s, or 0 */ enum opt_flags flags; char *(*cb)(void *arg); /* OPT_NOARG */ char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */ + void (*show)(char buf[OPT_SHOW_LEN], const void *arg); void *arg; const char *desc; }; @@ -48,22 +52,28 @@ struct opt_table { * @longopt: the name of the argument (eg. "foo" for "--foo "), or NULL. * @shortopt: the character of the argument (eg. 'f' for "-f "), or 0. * @cb: the callback when the option is found (along with ). - * @arg: the argument to hand to @cb. + * @show: the callback to print the value in get_usage (or NULL) + * @arg: the argument to hand to @cb and @show * * This is a typesafe wrapper for intializing a struct opt_table. The callback - * is of type "bool cb(const char *, type *)", - * "bool cb(const char *, const type *)" or "bool cb(const char *, void *)", + * is of type "char *cb(const char *, type *)", + * "char *cb(const char *, const type *)" or "char *cb(const char *, void *)", * where "type" is the type of the @arg argument. The first argument to the * @cb is the argument found on the commandline. * + * Similarly, if @show is not NULL, it should be of type "void *show(char *, + * const type *)". It should write up to OPT_SHOW_LEN bytes into the first + * argument; unless it uses the entire OPT_SHOW_LEN bytes it should + * nul-terminate that buffer. + * * At least one of @longopt and @shortopt must be non-zero. If the * @cb returns false, opt_parse() will stop parsing and return false. * * See Also: - * OPT_WITH_ARG() + * OPT_WITHOUT_ARG() */ -#define OPT_WITH_ARG(longopt, shortopt, cb, arg) \ - (longopt), (shortopt), OPT_CB_ARG((cb), (arg)) +#define OPT_WITH_ARG(longopt, shortopt, cb, show, arg) \ + (longopt), (shortopt), OPT_CB_ARG((cb), (show), (arg)) /** * OPT_SUBTABLE() - macro for including another table inside a table. @@ -74,7 +84,7 @@ struct opt_table { */ #define OPT_SUBTABLE(table, desc) \ { (const char *)(table), sizeof(_check_is_entry(table)), \ - OPT_SUBTABLE, NULL, NULL, NULL, (desc) } + OPT_SUBTABLE, NULL, NULL, NULL, NULL, (desc) } /** * opt_table_hidden - string for undocumented option tables. @@ -125,8 +135,8 @@ void opt_register_table(const struct opt_table table[], const char *desc); * This is used for registering a single commandline option which takes * no argument. * - * The callback is of type "bool cb(type *)", "bool cb(const type *)" - * or "bool cb(void *)", where "type" is the type of the @arg + * The callback is of type "char *cb(type *)", "char *cb(const type *)" + * or "char *cb(void *)", where "type" is the type of the @arg * argument. * * At least one of @longopt and @shortopt must be non-zero. If the @@ -140,14 +150,15 @@ void opt_register_table(const struct opt_table table[], const char *desc); * @longopt: the name of the argument (eg. "foo" for "--foo"), or NULL. * @shortopt: the character of the argument (eg. 'f' for "-f"), or 0. * @cb: the callback when the option is found. + * @show: the callback when the option is found. * @arg: the argument to hand to @cb. * @desc: the verbose desction of the option (for opt_usage()), or NULL. * * This is used for registering a single commandline option which takes * an argument. * - * The callback is of type "bool cb(const char *, type *)", - * "bool cb(const char *, const type *)" or "bool cb(const char *, void *)", + * The callback is of type "char *cb(const char *, type *)", + * "char *cb(const char *, const type *)" or "char *cb(const char *, void *)", * where "type" is the type of the @arg argument. The first argument to the * @cb is the argument found on the commandline. * @@ -158,8 +169,8 @@ void opt_register_table(const struct opt_table table[], const char *desc); * opt_register_arg("explode", 'e', explode_cb, NULL, * "Make the machine explode (developers only)"); */ -#define opt_register_arg(longopt, shortopt, cb, arg, desc) \ - _opt_register((longopt), (shortopt), OPT_CB_ARG((cb), (arg)), (desc)) +#define opt_register_arg(longopt, shortopt, cb, show, arg, desc) \ + _opt_register((longopt), (shortopt), OPT_CB_ARG((cb), (show), (arg)), (desc)) /** * opt_parse - parse arguments. @@ -216,24 +227,32 @@ char *opt_usage(const char *argv0, const char *extra); char *opt_set_bool(bool *b); /* Sets @b based on arg: (yes/no/true/false). */ char *opt_set_bool_arg(const char *arg, bool *b); +void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b); /* The inverse */ char *opt_set_invbool(bool *b); +void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b); +/* Sets @b based on !arg: (yes/no/true/false). */ char *opt_set_invbool_arg(const char *arg, bool *b); /* Set a char *. */ char *opt_set_charp(const char *arg, char **p); +void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p); /* Set an integer value, various forms. Sets to 1 on arg == NULL. */ char *opt_set_intval(const char *arg, int *i); +void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i); char *opt_set_uintval(const char *arg, unsigned int *ui); +void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui); char *opt_set_longval(const char *arg, long *l); +void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l); char *opt_set_ulongval(const char *arg, unsigned long *ul); +void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul); /* Increment. */ char *opt_inc_intval(int *i); /* Display version string to stdout, exit(0). */ -char *opt_show_version_and_exit(const char *version); +char *opt_version_and_exit(const char *version); /* Display usage string to stdout, exit(0). */ char *opt_usage_and_exit(const char *extra); @@ -245,22 +264,25 @@ char *opt_usage_and_exit(const char *extra); cast_if_any(char *(*)(void *), (cb), &*(cb), \ char *(*)(typeof(*(arg))*), \ char *(*)(const typeof(*(arg))*), \ - char *(*)(const typeof(*(arg))*)), \ - NULL, (arg) + char *(*)(const void *)), \ + NULL, NULL, (arg) /* Resolves to the four parameters for arg callbacks. */ -#define OPT_CB_ARG(cb, arg) \ +#define OPT_CB_ARG(cb, show, arg) \ OPT_HASARG, NULL, \ cast_if_any(char *(*)(const char *,void *), (cb), &*(cb), \ char *(*)(const char *, typeof(*(arg))*), \ char *(*)(const char *, const typeof(*(arg))*), \ - char *(*)(const char *, const typeof(*(arg))*)), \ + char *(*)(const char *, const void *)), \ + cast_if_type(void (*)(char buf[], const void *), (show), &*(show), \ + void (*)(char buf[], const typeof(*(arg))*)), \ (arg) /* Non-typesafe register function. */ void _opt_register(const char *longopt, char shortopt, enum opt_flags flags, char *(*cb)(void *arg), char *(*cb_arg)(const char *optarg, void *arg), + void (*show)(char buf[OPT_SHOW_LEN], const void *arg), void *arg, const char *desc); /* We use this to get typechecking for OPT_SUBTABLE */ diff --git a/ccan/opt/test/run-helpers.c b/ccan/opt/test/run-helpers.c new file mode 100644 index 00000000..8e1a751f --- /dev/null +++ b/ccan/opt/test/run-helpers.c @@ -0,0 +1,325 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include "utils.h" + +/* We don't actually want it to exit... */ +static jmp_buf exited; +#define exit(status) longjmp(exited, (status) + 1) + +#define printf saved_printf +static int saved_printf(const char *fmt, ...); + +#include +#include +#include + +static void reset_options(void) +{ + free(opt_table); + opt_table = NULL; + opt_count = 0; +} + +static char *output = NULL; + +static int saved_printf(const char *fmt, ...) +{ + va_list ap; + char *p; + int ret; + + va_start(ap, fmt); + ret = vasprintf(&p, fmt, ap); + va_end(ap); + + if (output) { + output = realloc(output, strlen(output) + strlen(p) + 1); + strcat(output, p); + free(p); + } else + output = p; + + return ret; +} + +/* Test helpers. */ +int main(int argc, char *argv[]) +{ + plan_tests(88); + + /* opt_set_bool */ + { + bool arg = false; + reset_options(); + opt_register_noarg(NULL, 'a', opt_set_bool, &arg, NULL); + ok1(parse_args(&argc, &argv, "-a", NULL)); + ok1(arg); + opt_register_arg(NULL, 'b', opt_set_bool_arg, NULL, &arg, NULL); + ok1(parse_args(&argc, &argv, "-b", "no", NULL)); + ok1(!arg); + ok1(parse_args(&argc, &argv, "-b", "yes", NULL)); + ok1(arg); + ok1(parse_args(&argc, &argv, "-b", "false", NULL)); + ok1(!arg); + ok1(parse_args(&argc, &argv, "-b", "true", NULL)); + ok1(arg); + } + /* opt_set_invbool */ + { + bool arg = true; + reset_options(); + opt_register_noarg(NULL, 'a', opt_set_invbool, &arg, NULL); + ok1(parse_args(&argc, &argv, "-a", NULL)); + ok1(!arg); + opt_register_arg(NULL, 'b', opt_set_invbool_arg, NULL, + &arg, NULL); + ok1(parse_args(&argc, &argv, "-b", "no", NULL)); + ok1(arg); + ok1(parse_args(&argc, &argv, "-b", "yes", NULL)); + ok1(!arg); + ok1(parse_args(&argc, &argv, "-b", "false", NULL)); + ok1(arg); + ok1(parse_args(&argc, &argv, "-b", "true", NULL)); + ok1(!arg); + } + /* opt_set_charp */ + { + char *arg = (char *)"wrong"; + reset_options(); + opt_register_arg(NULL, 'a', opt_set_charp, NULL, &arg, NULL); + ok1(parse_args(&argc, &argv, "-a", "string", NULL)); + ok1(strcmp(arg, "string") == 0); + } + /* opt_set_intval */ + { + int arg = 1000; + reset_options(); + opt_register_arg(NULL, 'a', opt_set_intval, NULL, &arg, NULL); + ok1(parse_args(&argc, &argv, "-a", "9999", NULL)); + ok1(arg == 9999); + ok1(parse_args(&argc, &argv, "-a", "-9999", NULL)); + ok1(arg == -9999); + ok1(parse_args(&argc, &argv, "-a", "0", NULL)); + ok1(arg == 0); + ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL)); + if (sizeof(int) == 4) + ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL)); + else + fail("Handle other int sizes"); + } + /* opt_set_uintval */ + { + unsigned int arg = 1000; + reset_options(); + opt_register_arg(NULL, 'a', opt_set_uintval, NULL, &arg, NULL); + ok1(parse_args(&argc, &argv, "-a", "9999", NULL)); + ok1(arg == 9999); + ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL)); + ok1(parse_args(&argc, &argv, "-a", "0", NULL)); + ok1(arg == 0); + ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL)); + ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL)); + } + /* opt_set_longval */ + { + long int arg = 1000; + reset_options(); + opt_register_arg(NULL, 'a', opt_set_longval, NULL, &arg, NULL); + ok1(parse_args(&argc, &argv, "-a", "9999", NULL)); + ok1(arg == 9999); + ok1(parse_args(&argc, &argv, "-a", "-9999", NULL)); + ok1(arg == -9999); + ok1(parse_args(&argc, &argv, "-a", "0", NULL)); + ok1(arg == 0); + ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL)); + if (sizeof(long) == 4) + ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL)); + else if (sizeof(long)== 8) + ok1(!parse_args(&argc, &argv, "-a", "18446744073709551616", NULL)); + else + fail("FIXME: Handle other long sizes"); + } + /* opt_set_ulongval */ + { + unsigned long int arg = 1000; + reset_options(); + opt_register_arg(NULL, 'a', opt_set_ulongval, NULL, &arg, NULL); + ok1(parse_args(&argc, &argv, "-a", "9999", NULL)); + ok1(arg == 9999); + ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL)); + ok1(parse_args(&argc, &argv, "-a", "0", NULL)); + ok1(arg == 0); + ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL)); + if (sizeof(long) == 4) + ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL)); + else if (sizeof(long)== 8) + ok1(!parse_args(&argc, &argv, "-a", "18446744073709551616", NULL)); + else + fail("FIXME: Handle other long sizes"); + } + /* opt_inc_intval */ + { + int arg = 1000; + reset_options(); + opt_register_noarg(NULL, 'a', opt_inc_intval, &arg, NULL); + ok1(parse_args(&argc, &argv, "-a", NULL)); + ok1(arg == 1001); + ok1(parse_args(&argc, &argv, "-a", "-a", NULL)); + ok1(arg == 1003); + ok1(parse_args(&argc, &argv, "-aa", NULL)); + ok1(arg == 1005); + } + + /* opt_show_version_and_exit. */ + { + int exitval; + reset_options(); + opt_register_noarg(NULL, 'a', + opt_version_and_exit, "1.2.3", NULL); + exitval = setjmp(exited); + if (exitval == 0) { + parse_args(&argc, &argv, "-a", NULL); + fail("opt_show_version_and_exit returned?"); + } else { + ok1(exitval - 1 == 0); + } + ok1(strcmp(output, "1.2.3\n") == 0); + free(output); + output = NULL; + } + + /* opt_usage_and_exit. */ + { + int exitval; + reset_options(); + opt_register_noarg(NULL, 'a', + opt_usage_and_exit, "[args]", NULL); + exitval = setjmp(exited); + if (exitval == 0) { + parse_args(&argc, &argv, "-a", NULL); + fail("opt_usage_and_exit returned?"); + } else { + ok1(exitval - 1 == 0); + } + ok1(strstr(output, "[args]")); + ok1(strstr(output, argv[0])); + ok1(strstr(output, "[-a]")); + free(output); + output = NULL; + } + + /* opt_show_bool */ + { + bool b; + char buf[OPT_SHOW_LEN+2] = { 0 }; + buf[OPT_SHOW_LEN] = '!'; + + b = true; + opt_show_bool(buf, &b); + ok1(strcmp(buf, "true") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + + b = false; + opt_show_bool(buf, &b); + ok1(strcmp(buf, "false") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + } + + /* opt_show_invbool */ + { + bool b; + char buf[OPT_SHOW_LEN+2] = { 0 }; + buf[OPT_SHOW_LEN] = '!'; + + b = true; + opt_show_invbool(buf, &b); + ok1(strcmp(buf, "false") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + + b = false; + opt_show_invbool(buf, &b); + ok1(strcmp(buf, "true") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + } + + /* opt_show_charp */ + { + char str[OPT_SHOW_LEN*2], *p; + char buf[OPT_SHOW_LEN+2] = { 0 }; + buf[OPT_SHOW_LEN] = '!'; + + /* Short test. */ + p = str; + strcpy(p, "short"); + opt_show_charp(buf, &p); + ok1(strcmp(buf, "\"short\"") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + + /* Truncate test. */ + memset(p, 'x', OPT_SHOW_LEN*2); + p[OPT_SHOW_LEN*2-1] = '\0'; + opt_show_charp(buf, &p); + ok1(buf[0] == '"'); + ok1(buf[OPT_SHOW_LEN-1] == '"'); + ok1(buf[OPT_SHOW_LEN] == '!'); + ok1(strspn(buf+1, "x") == OPT_SHOW_LEN-2); + } + + /* opt_show_intval */ + { + int i; + char buf[OPT_SHOW_LEN+2] = { 0 }; + buf[OPT_SHOW_LEN] = '!'; + + i = -77; + opt_show_intval(buf, &i); + ok1(strcmp(buf, "-77") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + + i = 77; + opt_show_intval(buf, &i); + ok1(strcmp(buf, "77") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + } + + /* opt_show_uintval */ + { + unsigned int ui; + char buf[OPT_SHOW_LEN+2] = { 0 }; + buf[OPT_SHOW_LEN] = '!'; + + ui = 4294967295U; + opt_show_uintval(buf, &ui); + ok1(strcmp(buf, "4294967295") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + } + + /* opt_show_longval */ + { + long l; + char buf[OPT_SHOW_LEN+2] = { 0 }; + buf[OPT_SHOW_LEN] = '!'; + + l = 1234567890L; + opt_show_longval(buf, &l); + ok1(strcmp(buf, "1234567890") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + } + + /* opt_show_ulongval */ + { + unsigned long ul; + char buf[OPT_SHOW_LEN+2] = { 0 }; + buf[OPT_SHOW_LEN] = '!'; + + ul = 4294967295UL; + opt_show_ulongval(buf, &ul); + ok1(strcmp(buf, "4294967295") == 0); + ok1(buf[OPT_SHOW_LEN] == '!'); + } + + return exit_status(); +} diff --git a/ccan/opt/test/run-usage.c b/ccan/opt/test/run-usage.c index 85b23b81..2e4ce400 100644 --- a/ccan/opt/test/run-usage.c +++ b/ccan/opt/test/run-usage.c @@ -19,7 +19,7 @@ int main(int argc, char *argv[]) char *output; plan_tests(18); opt_register_table(subtables, NULL); - opt_register_noarg("--kkk", 'k', my_cb, NULL, "magic kkk option"); + opt_register_noarg("kkk", 'k', my_cb, NULL, "magic kkk option"); output = opt_usage("my name", "ExTrA Args"); diag("%s", output); ok1(strstr(output, "Usage: my name")); @@ -28,11 +28,11 @@ int main(int argc, char *argv[]) ok1(strstr(output, "-a ")); ok1(strstr(output, " Description of a\n")); ok1(strstr(output, "-b ")); - ok1(strstr(output, " Description of b\n")); + ok1(strstr(output, " Description of b (default: b)\n")); ok1(strstr(output, "--ddd ")); ok1(strstr(output, " Description of ddd\n")); ok1(strstr(output, "--eee ")); - ok1(strstr(output, " Description of eee\n")); + ok1(strstr(output, " (default: eee)\n")); ok1(strstr(output, "long table options:\n")); /* This table is hidden. */ ok1(!strstr(output, "--ggg/-g ")); diff --git a/ccan/opt/test/run.c b/ccan/opt/test/run.c index a06e4b53..26c69dff 100644 --- a/ccan/opt/test/run.c +++ b/ccan/opt/test/run.c @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) /* Argument variants. */ reset_options(); test_cb_called = 0; - opt_register_arg("aaa", 'a', test_arg, "aaa", NULL); + opt_register_arg("aaa", 'a', test_arg, NULL, "aaa", NULL); ok1(parse_args(&argc, &argv, "--aaa", "aaa", NULL)); ok1(argc == 1); ok1(argv[0] == myname); diff --git a/ccan/opt/test/utils.c b/ccan/opt/test/utils.c index 899056ee..a012d40b 100644 --- a/ccan/opt/test/utils.c +++ b/ccan/opt/test/utils.c @@ -15,13 +15,18 @@ char *test_noarg(void *arg) return NULL; } -char *test_arg(const char *optarg, void *arg) +char *test_arg(const char *optarg, const char *arg) { test_cb_called++; ok1(strcmp(optarg, arg) == 0); return NULL; } +void show_arg(char buf[OPT_SHOW_LEN], const char *arg) +{ + strncpy(buf, arg, OPT_SHOW_LEN); +} + char *err_output = NULL; static void save_err_output(const char *fmt, ...) @@ -66,14 +71,14 @@ bool parse_args(int *argc, char ***argv, ...) struct opt_table short_table[] = { /* Short opts, different args. */ { OPT_WITHOUT_ARG(NULL, 'a', test_noarg, "a"), "Description of a" }, - { OPT_WITH_ARG(NULL, 'b', test_arg, "b"), "Description of b" }, + { OPT_WITH_ARG(NULL, 'b', test_arg, show_arg, "b"), "Description of b" }, OPT_ENDTABLE }; struct opt_table long_table[] = { /* Long opts, different args. */ { OPT_WITHOUT_ARG("ddd", 0, test_noarg, "ddd"), "Description of ddd" }, - { OPT_WITH_ARG("eee", 0, test_arg, "eee"), "Description of eee" }, + { OPT_WITH_ARG("eee", 0, test_arg, show_arg, "eee"), }, OPT_ENDTABLE }; @@ -81,14 +86,15 @@ struct opt_table long_and_short_table[] = { /* Short and long, different args. */ { OPT_WITHOUT_ARG("ggg", 'g', test_noarg, "ggg"), "Description of ggg" }, - { OPT_WITH_ARG("hhh", 'h', test_arg, "hhh"), "Description of hhh"}, + { OPT_WITH_ARG("hhh", 'h', test_arg, NULL, "hhh"), + "Description of hhh"}, OPT_ENDTABLE }; /* Sub-table test. */ struct opt_table subtables[] = { /* Short and long, no description */ - { OPT_WITH_ARG("jjj", 'j', test_arg, "jjj") }, + { OPT_WITH_ARG("jjj", 'j', test_arg, show_arg, "jjj") }, OPT_SUBTABLE(short_table, NULL), OPT_SUBTABLE(long_table, "long table options"), OPT_SUBTABLE(long_and_short_table, opt_table_hidden), diff --git a/ccan/opt/test/utils.h b/ccan/opt/test/utils.h index ea99eb09..b5192f7c 100644 --- a/ccan/opt/test/utils.h +++ b/ccan/opt/test/utils.h @@ -8,7 +8,8 @@ extern char *err_output; extern unsigned int test_cb_called; char *test_noarg(void *arg); -char *test_arg(const char *optarg, void *arg); +char *test_arg(const char *optarg, const char *arg); +void show_arg(char buf[OPT_SHOW_LEN], const char *arg); extern struct opt_table short_table[]; extern struct opt_table long_table[]; diff --git a/ccan/opt/usage.c b/ccan/opt/usage.c index 37ba3ed7..d76c3b03 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -25,6 +25,8 @@ static unsigned write_short_options(char *str) return num; } +#define OPT_SPACE_PAD " " + /* FIXME: Get all purdy. */ char *opt_usage(const char *argv0, const char *extra) { @@ -45,8 +47,14 @@ char *opt_usage(const char *argv0, const char *extra) len += strlen("--%s/-%c") + strlen(" "); if (opt_table[i].longopt) len += strlen(opt_table[i].longopt); - if (opt_table[i].desc) - len += 20 + strlen(opt_table[i].desc); + if (opt_table[i].desc) { + len += strlen(OPT_SPACE_PAD) + + strlen(opt_table[i].desc) + 1; + } + if (opt_table[i].show) { + len += strlen("(default: %s)") + + OPT_SHOW_LEN + sizeof("..."); + } len += strlen("\n"); } } @@ -89,11 +97,20 @@ char *opt_usage(const char *argv0, const char *extra) len = sprintf(p, "--%s", opt_table[i].longopt); if (opt_table[i].flags == OPT_HASARG) len += sprintf(p + len, " "); - if (opt_table[i].desc) { + if (opt_table[i].desc || opt_table[i].show) len += sprintf(p + len, "%.*s", - len < 20 ? 20 - len : 1, - " "); + len < strlen(OPT_SPACE_PAD) + ? strlen(OPT_SPACE_PAD) - len : 1, + OPT_SPACE_PAD); + + if (opt_table[i].desc) len += sprintf(p + len, "%s", opt_table[i].desc); + if (opt_table[i].show) { + char buf[OPT_SHOW_LEN + sizeof("...")]; + strcpy(buf + OPT_SHOW_LEN, "..."); + opt_table[i].show(buf, opt_table[i].arg); + len += sprintf(p + len, "%s(default: %s)", + opt_table[i].desc ? " " : "", buf); } p += len; p += sprintf(p, "\n"); -- 2.39.2