opt: add opt_log_stderr_exit helper, and opt_usage NULL option.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 7 Oct 2010 23:41:11 +0000 (10:11 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 7 Oct 2010 23:41:11 +0000 (10:11 +1030)
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
ccan/opt/opt.h
ccan/opt/test/run-correct-reporting.c
ccan/opt/test/run-helpers.c
ccan/opt/test/run-iter.c
ccan/opt/test/run-no-options.c
ccan/opt/test/run-usage.c
ccan/opt/test/run.c
ccan/opt/usage.c

index bae3c665c93e7594bb4c5e7e32880f5e73b13952..de56299cc34a6259bd45650867e5b856d11116fc 100644 (file)
@@ -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));
index 879833652d320cd1f4e0fd67d0dea3521f78a5fc..7d7bc571fc2363f40241497297bcdc32ff5f6716 100644 (file)
@@ -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);
index bbac3402dde35a70d12ff58dd1961377a791e5b3..2dc62656ff7c9fc7253d85c2081dafd8bf86946f 100644 (file)
@@ -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[])
index 91e66374bbaf42e7e688521165cc93c343426ed5..34ffbab834cb11923d2ba7719fd1091850860b5e 100644 (file)
@@ -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();
 }
index 147470fffd6c276ff22a41a26ae728a98b577721..652cd316cfca4cdf0065b0d88466f0a766c2afee 100644 (file)
@@ -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[])
index 98ab8d903d3c79c44b2d98a68b7bcde14795fb30..7b05cbae87b79c9761ec0de8f094098f1aef9dd7 100644 (file)
@@ -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[])
index 943fcf6da38e6efb5e3097af46f997a3d228a486..60df988c97bdcb6f2163aad172723740dbabf094 100644 (file)
@@ -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();
index 82c0398c92c43d5761df1ee29f63fa8d9338e187..2dd7a8d9a734e5becdd20c644f5f4857e64d4fac 100644 (file)
@@ -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)
index b7381bc7378c6673c742d6dc23b0c42f211e8265..eedaae7eb821d0dab8114de49222b14496887aa0 100644 (file)
@@ -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