From 3beff01ae4dfb8b843bf5e78905fb8bc434cb270 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 May 2023 11:00:31 +0930 Subject: [PATCH] opt: allow show callbacks to return false. Sometimes, arguments are optional, so it's useful to decide at runtime whether to print a default in --help. Signed-off-by: Rusty Russell --- ccan/opt/helpers.c | 70 +++++++++++++++++++++++------------- ccan/opt/opt.c | 2 +- ccan/opt/opt.h | 58 +++++++++++++++--------------- ccan/opt/test/run-add_desc.c | 23 ++++++++++-- ccan/opt/test/utils.c | 3 +- ccan/opt/test/utils.h | 2 +- ccan/opt/usage.c | 24 ++++++------- 7 files changed, 111 insertions(+), 71 deletions(-) diff --git a/ccan/opt/helpers.c b/ccan/opt/helpers.c index 0a6cb345..df7ee6bb 100644 --- a/ccan/opt/helpers.c +++ b/ccan/opt/helpers.c @@ -138,10 +138,11 @@ char *opt_set_floatval(const char *arg, float *f) return NULL; } -void opt_show_floatval(char *buf, size_t len, const float *f) +bool opt_show_floatval(char *buf, size_t len, const float *f) { double d = *f; opt_show_doubleval(buf, len, &d); + return true; } char *opt_set_doubleval(const char *arg, double *d) @@ -160,9 +161,10 @@ char *opt_set_doubleval(const char *arg, double *d) return NULL; } -void opt_show_doubleval(char *buf, size_t len, const double *d) +bool opt_show_doubleval(char *buf, size_t len, const double *d) { snprintf(buf, len, "%f", *d); + return true; } char *opt_inc_intval(int *i) @@ -196,22 +198,24 @@ char *opt_usage_and_exit(const char *extra) exit(0); } -void opt_show_bool(char *buf, size_t len, const bool *b) +bool opt_show_bool(char *buf, size_t len, const bool *b) { strncpy(buf, *b ? "true" : "false", len); + return true; } -void opt_show_invbool(char *buf, size_t len, const bool *b) +bool opt_show_invbool(char *buf, size_t len, const bool *b) { strncpy(buf, *b ? "false" : "true", len); + return true; } -void opt_show_charp(char *buf, size_t len, char *const *p) +bool opt_show_charp(char *buf, size_t len, char *const *p) { if (*p) { size_t plen = strlen(*p); if (len < 2) - return; + return false; buf[0] = '"'; if (plen > len - 2) plen = len - 2; @@ -219,31 +223,35 @@ void opt_show_charp(char *buf, size_t len, char *const *p) buf[1+plen] = '"'; if (plen < len - 2) buf[2+plen] = '\0'; - } - else { - strncpy(buf, "(nil)", len); + return true; + } else { + return false; } } /* Show an integer value, various forms. */ -void opt_show_intval(char *buf, size_t len, const int *i) +bool opt_show_intval(char *buf, size_t len, const int *i) { snprintf(buf, len, "%i", *i); + return true; } -void opt_show_uintval(char *buf, size_t len, const unsigned int *ui) +bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui) { snprintf(buf, len, "%u", *ui); + return true; } -void opt_show_longval(char *buf, size_t len, const long *l) +bool opt_show_longval(char *buf, size_t len, const long *l) { snprintf(buf, len, "%li", *l); + return true; } -void opt_show_ulongval(char *buf, size_t len, const unsigned long *ul) +bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul) { snprintf(buf, len, "%lu", *ul); + return true; } /* a helper function that multiplies out an argument's kMGTPE suffix in the @@ -495,66 +503,78 @@ static void show_ullong_with_suffix(char *buf, size_t len, } /* _bi, signed */ -void opt_show_intval_bi(char *buf, size_t len, const int *x) +bool opt_show_intval_bi(char *buf, size_t len, const int *x) { show_llong_with_suffix(buf, len, *x, 1024); + return true; } -void opt_show_longval_bi(char *buf, size_t len, const long *x) +bool opt_show_longval_bi(char *buf, size_t len, const long *x) { show_llong_with_suffix(buf, len, *x, 1024); + return true; } -void opt_show_longlongval_bi(char *buf, size_t len, const long long *x) +bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x) { show_llong_with_suffix(buf, len, *x, 1024); + return true; } /* _bi, unsigned */ -void opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x) +bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024); + return true; } -void opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x) +bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024); + return true; } -void opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x) +bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024); + return true; } /* _si, signed */ -void opt_show_intval_si(char *buf, size_t len, const int *x) +bool opt_show_intval_si(char *buf, size_t len, const int *x) { show_llong_with_suffix(buf, len, (long long) *x, 1000); + return true; } -void opt_show_longval_si(char *buf, size_t len, const long *x) +bool opt_show_longval_si(char *buf, size_t len, const long *x) { show_llong_with_suffix(buf, len, (long long) *x, 1000); + return true; } -void opt_show_longlongval_si(char *buf, size_t len, const long long *x) +bool opt_show_longlongval_si(char *buf, size_t len, const long long *x) { show_llong_with_suffix(buf, len, *x, 1000); + return true; } /* _si, unsigned */ -void opt_show_uintval_si(char *buf, size_t len, const unsigned int *x) +bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000); + return true; } -void opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x) +bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000); + return true; } -void opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x) +bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x) { show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000); + return true; } diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index 09c85081..9149374c 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -162,7 +162,7 @@ static void add_opt(const struct opt_table *entry) void _opt_register(const char *names, enum opt_type type, char *(*cb)(void *arg), char *(*cb_arg)(const char *optarg, void *arg), - void (*show)(char *buf, size_t len, const void *arg), + bool (*show)(char *buf, size_t len, const void *arg), const void *arg, const char *desc) { struct opt_table opt; diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 9c9f337d..e0331be2 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -47,9 +47,10 @@ struct opt_table; * 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 *, - * size_t len, const type *)". It should write up to len bytes into the first - * argument; unless it uses the entire len bytes it should nul-terminate that + * Similarly, if @show is not NULL, it should be of type "bool show(char *, + * size_t len, const type *)". If there is no default, it should return false, + * otherwise it should write up to len bytes into the first argument and + * return true; unless it uses the entire len bytes it should nul-terminate that * buffer. * * Any number of equivalent short or long options can be listed in @names, @@ -434,32 +435,33 @@ extern const char opt_hidden[]; 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, size_t len, const bool *b); +bool opt_show_bool(char *buf, size_t len, const bool *b); /* The inverse */ char *opt_set_invbool(bool *b); -void opt_show_invbool(char *buf, size_t len, const bool *b); +bool opt_show_invbool(char *buf, size_t 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, size_t len, char *const *p); +/* If *p is NULL, this returns false (i.e. doesn't show a default) */ +bool opt_show_charp(char *buf, size_t 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, size_t len, const int *i); +bool opt_show_intval(char *buf, size_t len, const int *i); char *opt_set_uintval(const char *arg, unsigned int *ui); -void opt_show_uintval(char *buf, size_t len, const unsigned int *ui); +bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui); char *opt_set_longval(const char *arg, long *l); -void opt_show_longval(char *buf, size_t len, const long *l); +bool opt_show_longval(char *buf, size_t len, const long *l); char *opt_set_ulongval(const char *arg, unsigned long *ul); -void opt_show_ulongval(char *buf, size_t len, const unsigned long *ul); +bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul); /* Set an floating point value, various forms. */ char *opt_set_floatval(const char *arg, float *f); -void opt_show_floatval(char *buf, size_t len, const float *f); +bool opt_show_floatval(char *buf, size_t len, const float *f); char *opt_set_doubleval(const char *arg, double *d); -void opt_show_doubleval(char *buf, size_t len, const double *d); +bool opt_show_doubleval(char *buf, size_t len, const double *d); /* the following setting functions accept k, M, G, T, P, or E suffixes, which multiplies the numeric value by the corresponding power of 1000 or 1024 @@ -479,19 +481,19 @@ char *opt_set_ulonglongval_bi(const char *arg, unsigned long long *ll); char *opt_set_ulonglongval_si(const char *arg, unsigned long long *ll); -void opt_show_intval_bi(char *buf, size_t len, const int *x); -void opt_show_longval_bi(char *buf, size_t len, const long *x); -void opt_show_longlongval_bi(char *buf, size_t len, const long long *x); -void opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x); -void opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x); -void opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x); +bool opt_show_intval_bi(char *buf, size_t len, const int *x); +bool opt_show_longval_bi(char *buf, size_t len, const long *x); +bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x); +bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x); +bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x); +bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x); -void opt_show_intval_si(char *buf, size_t len, const int *x); -void opt_show_longval_si(char *buf, size_t len, const long *x); -void opt_show_longlongval_si(char *buf, size_t len, const long long *x); -void opt_show_uintval_si(char *buf, size_t len, const unsigned int *x); -void opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x); -void opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x); +bool opt_show_intval_si(char *buf, size_t len, const int *x); +bool opt_show_longval_si(char *buf, size_t len, const long *x); +bool opt_show_longlongval_si(char *buf, size_t len, const long long *x); +bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x); +bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x); +bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x); @@ -551,7 +553,7 @@ struct opt_table { enum opt_type type; char *(*cb)(void *arg); /* OPT_NOARG */ char *(*cb_arg)(const char *optarg, void *arg); /* OPT_HASARG */ - void (*show)(char *buf, size_t len, const void *arg); + bool (*show)(char *buf, size_t len, const void *arg); union { const void *carg; void *arg; @@ -577,14 +579,14 @@ struct opt_table { char *(*)(const char *, const typeof(*(arg))*), \ char *(*)(const char *, const void *), \ (cb)), \ - typesafe_cb_cast(void (*)(char *buf, size_t, const void *), \ - void (*)(char *buf, size_t, const typeof(*(arg))*), (show)) + typesafe_cb_cast(bool (*)(char *buf, size_t, const void *), \ + bool (*)(char *buf, size_t, const typeof(*(arg))*), (show)) /* Non-typesafe register function. */ void _opt_register(const char *names, enum opt_type type, char *(*cb)(void *arg), char *(*cb_arg)(const char *optarg, void *arg), - void (*show)(char *buf, size_t len, const void *arg), + bool (*show)(char *buf, size_t len, const void *arg), const void *arg, const char *desc); /* We use this to get typechecking for OPT_SUBTABLE */ diff --git a/ccan/opt/test/run-add_desc.c b/ccan/opt/test/run-add_desc.c index ddec6f13..03e6986d 100644 --- a/ccan/opt/test/run-add_desc.c +++ b/ccan/opt/test/run-add_desc.c @@ -4,15 +4,24 @@ #include #include -static void show_10(char *buf, size_t len, const void *arg UNNEEDED) +static bool show_10(char *buf, size_t len, const void *arg UNNEEDED) { memset(buf, 'X', 10); buf[10] = '\0'; + return true; } -static void show_max(char *buf, size_t len, const void *arg UNNEEDED) +static bool show_10_false(char *buf, size_t len, const void *arg UNNEEDED) +{ + memset(buf, 'X', 10); + buf[10] = '\0'; + return false; +} + +static bool show_max(char *buf, size_t len, const void *arg UNNEEDED) { memset(buf, 'X', OPT_SHOW_LEN); + return true; } /* Test add_desc helper. */ @@ -22,7 +31,7 @@ int main(void) char *ret; size_t len, max; - plan_tests(30); + plan_tests(32); opt.show = NULL; opt.names = "01234"; @@ -113,6 +122,14 @@ int main(void) " (default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...)\n") == 0); free(ret); len = max = 0; + /* With show function which fails doesn't print. */ + opt.show = show_10_false; + ret = add_desc(NULL, &len, &max, 7, 41, &opt); + ok1(len < max); + ret[len] = '\0'; + ok1(strcmp(ret, "01234 0123456789 0\n") == 0); + free(ret); len = max = 0; + /* With added " ". Fits, just. */ opt.show = NULL; opt.type = OPT_HASARG; diff --git a/ccan/opt/test/utils.c b/ccan/opt/test/utils.c index 700748be..61199fb4 100644 --- a/ccan/opt/test/utils.c +++ b/ccan/opt/test/utils.c @@ -21,9 +21,10 @@ char *test_arg(const char *optarg, const char *arg) return NULL; } -void show_arg(char *buf, size_t len, const char *arg) +bool show_arg(char *buf, size_t len, const char *arg) { strncpy(buf, arg, len); + return true; } char *err_output = NULL; diff --git a/ccan/opt/test/utils.h b/ccan/opt/test/utils.h index 64641ec4..3ada62d1 100644 --- a/ccan/opt/test/utils.h +++ b/ccan/opt/test/utils.h @@ -13,7 +13,7 @@ void reset_options(void); extern unsigned int test_cb_called; char *test_noarg(void *arg); char *test_arg(const char *optarg, const char *arg); -void show_arg(char *buf, size_t len, const char *arg); +bool show_arg(char *buf, size_t 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 e9629838..568e4661 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -151,20 +151,20 @@ static char *add_desc(char *base, size_t *len, size_t *max, if (opt->show) { char buf[OPT_SHOW_LEN + sizeof("...")]; strcpy(buf + OPT_SHOW_LEN, "..."); - opt->show(buf, OPT_SHOW_LEN, opt->u.arg); + if (opt->show(buf, OPT_SHOW_LEN, opt->u.arg)) { + /* If it doesn't fit on this line, indent. */ + if (off + strlen(" (default: ") + strlen(buf) + strlen(")") + > width) { + base = add_indent(base, len, max, indent); + } else { + /* Remove \n. */ + (*len)--; + } - /* If it doesn't fit on this line, indent. */ - if (off + strlen(" (default: ") + strlen(buf) + strlen(")") - > width) { - base = add_indent(base, len, max, indent); - } else { - /* Remove \n. */ - (*len)--; + base = add_str(base, len, max, " (default: "); + base = add_str(base, len, max, buf); + base = add_str(base, len, max, ")\n"); } - - base = add_str(base, len, max, " (default: "); - base = add_str(base, len, max, buf); - base = add_str(base, len, max, ")\n"); } return base; } -- 2.39.2