Commit 0191b7a7 authored by Rusty Russell's avatar Rusty Russell

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).
parent 3e8aea05
......@@ -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));
......
......@@ -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], "<args>..."));
* 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);
......
......@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include "utils.h"
int main(int argc, char *argv[])
......
......@@ -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 <ccan/opt/helpers.c>
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
......@@ -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();
}
......@@ -7,6 +7,7 @@
#include "utils.h"
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
/* Test iterators. */
int main(int argc, char *argv[])
......
......@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include "utils.h"
int main(int argc, char *argv[])
......
......@@ -7,6 +7,7 @@
#include "utils.h"
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
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, "<MyArgs>...",
"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 <arg>"));
ok1(strstr(output, "<MyArgs>..."));
ok1(strstr(output, "-a "));
ok1(strstr(output, " Description of a\n"));
ok1(strstr(output, "-b <arg>"));
ok1(strstr(output, " Description of b (default: b)\n"));
ok1(strstr(output, "--ddd "));
ok1(strstr(output, " Description of ddd\n"));
ok1(strstr(output, "--eee <filename> "));
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 <arg>"));
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();
......
......@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include "utils.h"
static void reset_options(void)
......
......@@ -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
......
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