From 0191b7a74144ea8193961235f45715e715d4d8eb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Oct 2010 10:11:11 +1030 Subject: [PATCH] opt: add opt_log_stderr_exit helper, and opt_usage NULL option. The former encapsulates a common "just exit on error" case, the latter avoids having to repeat the extra usage string. (We actually would have crashed before if someone passed NULL there, even though the documentation said you could). --- ccan/opt/opt.c | 12 ++++ ccan/opt/opt.h | 29 +++++++++- ccan/opt/test/run-correct-reporting.c | 1 + ccan/opt/test/run-helpers.c | 80 ++++++++++++++++++++++++--- ccan/opt/test/run-iter.c | 1 + ccan/opt/test/run-no-options.c | 1 + ccan/opt/test/run-usage.c | 29 +++++++++- ccan/opt/test/run.c | 1 + ccan/opt/usage.c | 11 ++++ 9 files changed, 153 insertions(+), 12 deletions(-) diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index bae3c665..de56299c 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -347,9 +347,21 @@ void opt_log_stderr(const char *fmt, ...) va_start(ap, fmt); vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); va_end(ap); } +void opt_log_stderr_exit(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + char *opt_invalid_argument(const char *arg) { char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg)); diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 87983365..7d7bc571 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -181,7 +181,7 @@ void opt_register_table(const struct opt_table table[], const char *desc); * opt_parse - parse arguments. * @argc: pointer to argc * @argv: argv array. - * @errlog: the function to print errors (usually opt_log_stderr). + * @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(). If there @@ -193,9 +193,12 @@ void opt_register_table(const struct opt_table table[], const char *desc); * * Example: * if (!opt_parse(&argc, argv, opt_log_stderr)) { - * printf("%s", opt_usage(argv[0], "...")); + * printf("You screwed up, aborting!\n"); * exit(1); * } + * + * See Also: + * opt_log_stderr, opt_log_stderr_exit */ bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...)); @@ -203,10 +206,26 @@ bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...)); * opt_log_stderr - print message to stderr. * @fmt: printf-style format. * - * This is the standard helper for opt_parse, to print errors. + * This is a helper for opt_parse, to print errors to stderr. + * + * See Also: + * opt_log_stderr_exit */ void opt_log_stderr(const char *fmt, ...); +/** + * opt_log_stderr_exit - print message to stderr, then exit(1) + * @fmt: printf-style format. + * + * Just like opt_log_stderr, only then does exit(1). This means that + * when handed to opt_parse, opt_parse will never return false. + * + * Example: + * // This never returns false; just exits if there's an erorr. + * opt_parse(&argc, argv, opt_log_stderr_exit); + */ +void opt_log_stderr_exit(const char *fmt, ...); + /** * opt_invalid_argument - helper to allocate an "Invalid argument '%s'" string * @arg: the argument which was invalid. @@ -224,6 +243,10 @@ char *opt_invalid_argument(const char *arg); * and a table of all the options with their descriptions. If an option has * description opt_hidden, it is not shown here. * + * If "extra" is NULL, then the extra information is taken from any + * registered option which calls opt_usage_and_exit(). This avoids duplicating + * that string in the common case. + * * The result should be passed to free(). */ char *opt_usage(const char *argv0, const char *extra); diff --git a/ccan/opt/test/run-correct-reporting.c b/ccan/opt/test/run-correct-reporting.c index bbac3402..2dc62656 100644 --- a/ccan/opt/test/run-correct-reporting.c +++ b/ccan/opt/test/run-correct-reporting.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "utils.h" int main(int argc, char *argv[]) diff --git a/ccan/opt/test/run-helpers.c b/ccan/opt/test/run-helpers.c index 91e66374..34ffbab8 100644 --- a/ccan/opt/test/run-helpers.c +++ b/ccan/opt/test/run-helpers.c @@ -13,6 +13,12 @@ static jmp_buf exited; #define printf saved_printf static int saved_printf(const char *fmt, ...); +#define fprintf saved_fprintf +static int saved_fprintf(FILE *ignored, const char *fmt, ...); + +#define vfprintf(f, fmt, ap) saved_vprintf(fmt, ap) +static int saved_vprintf(const char *fmt, va_list ap); + #include #include #include @@ -26,15 +32,10 @@ static void reset_options(void) static char *output = NULL; -static int saved_printf(const char *fmt, ...) +static int saved_vprintf(const char *fmt, va_list ap) { - va_list ap; char *p; - int ret; - - va_start(ap, fmt); - ret = vasprintf(&p, fmt, ap); - va_end(ap); + int ret = vasprintf(&p, fmt, ap); if (output) { output = realloc(output, strlen(output) + strlen(p) + 1); @@ -42,14 +43,35 @@ static int saved_printf(const char *fmt, ...) free(p); } else output = p; + return ret; +} + +static int saved_printf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = saved_vprintf(fmt, ap); + va_end(ap); + return ret; +} + +static int saved_fprintf(FILE *ignored, const char *fmt, ...) +{ + va_list ap; + int ret; + va_start(ap, fmt); + ret = saved_vprintf(fmt, ap); + va_end(ap); return ret; } /* Test helpers. */ int main(int argc, char *argv[]) { - plan_tests(96); + plan_tests(100); /* opt_set_bool */ { @@ -339,5 +361,47 @@ int main(int argc, char *argv[]) ok1(buf[OPT_SHOW_LEN] == '!'); } + /* opt_log_stderr. */ + { + reset_options(); + opt_register_noarg("-a", + opt_usage_and_exit, "[args]", ""); + + argc = 2; + argv = malloc(sizeof(argv[0]) * 3); + argv[0] = "thisprog"; + argv[1] = "--garbage"; + argv[2] = NULL; + ok1(!opt_parse(&argc, argv, opt_log_stderr)); + ok1(!strcmp(output, + "thisprog: --garbage: unrecognized option\n")); + free(output); + output = NULL; + } + + /* opt_log_stderr_exit. */ + { + int exitval; + reset_options(); + opt_register_noarg("-a", + opt_usage_and_exit, "[args]", ""); + exitval = setjmp(exited); + if (exitval == 0) { + argc = 2; + argv = malloc(sizeof(argv[0]) * 3); + argv[0] = "thisprog"; + argv[1] = "--garbage"; + argv[2] = NULL; + opt_parse(&argc, argv, opt_log_stderr_exit); + fail("opt_log_stderr_exit returned?"); + } else { + ok1(exitval - 1 == 1); + } + ok1(!strcmp(output, + "thisprog: --garbage: unrecognized option\n")); + free(output); + output = NULL; + } + return exit_status(); } diff --git a/ccan/opt/test/run-iter.c b/ccan/opt/test/run-iter.c index 147470ff..652cd316 100644 --- a/ccan/opt/test/run-iter.c +++ b/ccan/opt/test/run-iter.c @@ -7,6 +7,7 @@ #include "utils.h" #include #include +#include /* Test iterators. */ int main(int argc, char *argv[]) diff --git a/ccan/opt/test/run-no-options.c b/ccan/opt/test/run-no-options.c index 98ab8d90..7b05cbae 100644 --- a/ccan/opt/test/run-no-options.c +++ b/ccan/opt/test/run-no-options.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "utils.h" int main(int argc, char *argv[]) diff --git a/ccan/opt/test/run-usage.c b/ccan/opt/test/run-usage.c index 943fcf6d..60df988c 100644 --- a/ccan/opt/test/run-usage.c +++ b/ccan/opt/test/run-usage.c @@ -7,6 +7,7 @@ #include "utils.h" #include #include +#include static char *my_cb(void *p) { @@ -18,9 +19,11 @@ int main(int argc, char *argv[]) { char *output; - plan_tests(19); + plan_tests(38); opt_register_table(subtables, NULL); opt_register_noarg("--kkk/-k", my_cb, NULL, "magic kkk option"); + opt_register_noarg("-?", opt_usage_and_exit, "...", + "This message"); output = opt_usage("my name", "ExTrA Args"); diag("%s", output); ok1(strstr(output, "Usage: my name")); @@ -43,7 +46,31 @@ int main(int argc, char *argv[]) ok1(strstr(output, "magic kkk option")); /* This entry is hidden. */ ok1(!strstr(output, "--mmm/-m")); + free(output); + /* NULL should use string from registered options. */ + output = opt_usage("my name", NULL); + diag("%s", output); + ok1(strstr(output, "Usage: my name")); + ok1(strstr(output, "--jjj/-j/--lll/-l ")); + ok1(strstr(output, "...")); + ok1(strstr(output, "-a ")); + ok1(strstr(output, " Description of a\n")); + ok1(strstr(output, "-b ")); + 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, " (default: eee)\n")); + ok1(strstr(output, "long table options:\n")); + ok1(strstr(output, "--ggg/-g ")); + ok1(strstr(output, " Description of ggg\n")); + ok1(strstr(output, "-h/--hhh ")); + ok1(strstr(output, " Description of hhh\n")); + ok1(strstr(output, "--kkk/-k")); + ok1(strstr(output, "magic kkk option")); + /* This entry is hidden. */ + ok1(!strstr(output, "--mmm/-m")); free(output); return exit_status(); diff --git a/ccan/opt/test/run.c b/ccan/opt/test/run.c index 82c0398c..2dd7a8d9 100644 --- a/ccan/opt/test/run.c +++ b/ccan/opt/test/run.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "utils.h" static void reset_options(void) diff --git a/ccan/opt/usage.c b/ccan/opt/usage.c index b7381bc7..eedaae7e 100644 --- a/ccan/opt/usage.c +++ b/ccan/opt/usage.c @@ -28,6 +28,17 @@ char *opt_usage(const char *argv0, const char *extra) unsigned int i, num, len; char *ret, *p; + if (!extra) { + extra = ""; + for (i = 0; i < opt_count; i++) { + if (opt_table[i].cb == (void *)opt_usage_and_exit + && opt_table[i].arg) { + extra = opt_table[i].arg; + break; + } + } + } + /* An overestimate of our length. */ len = strlen("Usage: %s ") + strlen(argv0) + strlen("[-%.*s]") + opt_num_short + 1 -- 2.39.2