opt: add new parse_early_args_incomplete.
authorRusty Russell <rusty@rustcorp.com.au>
Fri, 2 Nov 2018 01:27:57 +0000 (11:57 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Fri, 2 Nov 2018 01:29:52 +0000 (11:59 +1030)
If we have plugins, and those can register args, we have a problem finding
the plugin dir!  So, do a best-effort incomplete parse.

Note that this can screw up in theory if we have "--unknown --foo" since we
don't know if unknown takes an argument (in which case, ignore --foo) or
not.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/opt/opt.c
ccan/opt/opt.h
ccan/opt/parse.c
ccan/opt/private.h
ccan/opt/test/run-early_incomplete.c [new file with mode: 0644]
ccan/opt/test/utils.c
ccan/opt/test/utils.h

index 09b29bcaf669e0768cb48866b8dc696f3ffc1c72..0514dc8702dfc8ef2327d892f7cbd7606dec2b06 100644 (file)
@@ -207,14 +207,15 @@ bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
        /* This helps opt_usage. */
        opt_argv0 = argv[0];
 
-       while ((ret = parse_one(argc, argv, 0, &offset, errlog)) == 1);
+       while ((ret = parse_one(argc, argv, 0, &offset, errlog, false)) == 1);
 
        /* parse_one returns 0 on finish, -1 on error */
        return (ret == 0);
 }
 
-bool opt_early_parse(int argc, char *argv[],
-                    void (*errlog)(const char *fmt, ...))
+static bool early_parse(int argc, char *argv[],
+                       void (*errlog)(const char *fmt, ...),
+                       bool ignore_unknown)
 {
        int ret;
        unsigned off = 0;
@@ -226,7 +227,7 @@ bool opt_early_parse(int argc, char *argv[],
        /* This helps opt_usage. */
        opt_argv0 = argv[0];
 
-       while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog)) == 1);
+       while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog, ignore_unknown)) == 1);
 
        opt_alloc.free(tmpargv);
 
@@ -234,6 +235,18 @@ bool opt_early_parse(int argc, char *argv[],
        return (ret == 0);
 }
 
+bool opt_early_parse(int argc, char *argv[],
+                    void (*errlog)(const char *fmt, ...))
+{
+       return early_parse(argc, argv, errlog, false);
+}
+
+bool opt_early_parse_incomplete(int argc, char *argv[],
+                               void (*errlog)(const char *fmt, ...))
+{
+       return early_parse(argc, argv, errlog, true);
+}
+
 void opt_free_table(void)
 {
        opt_alloc.free(opt_table);
index 0d508cf6fd173d1578bf26d1a703c3b12bbf3643..c642ec6fafc85d1b87377458d69fa9e3d909101c 100644 (file)
@@ -286,6 +286,30 @@ bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...));
 bool opt_early_parse(int argc, char *argv[],
                     void (*errlog)(const char *fmt, ...));
 
+/**
+ * opt_early_parse_incomplete - parse early arguments, ignoring unknown ones.
+ * @argc: argc
+ * @argv: argv array.
+ * @errlog: the function to print errors
+ *
+ * If you have plugins, you might need to do early parsing (eg. to find the
+ * plugin directory) but you don't know what options the plugins will want.
+ *
+ * Thus, this function is just like opt_early_parse, but ignores unknown options.
+ *
+ * Example:
+ *     if (!opt_early_parse_incomplete(argc, argv, opt_log_stderr)) {
+ *             printf("You screwed up, aborting!\n");
+ *             exit(1);
+ *     }
+ *
+ * See Also:
+ *     opt_early_parse()
+ */
+bool opt_early_parse_incomplete(int argc, char *argv[],
+                               void (*errlog)(const char *fmt, ...));
+
+
 /**
  * opt_free_table - reset the opt library.
  *
index 94d75ad1fbe02ee77cd522f8615b7dff36998056..d227f7bca2ab679033dd920fe426983dc0ea9314 100644 (file)
@@ -30,7 +30,7 @@ static void consume_option(int *argc, char *argv[], unsigned optnum)
 
 /* Returns 1 if argument consumed, 0 if all done, -1 on error. */
 int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
-             void (*errlog)(const char *fmt, ...))
+             void (*errlog)(const char *fmt, ...), bool unknown_ok)
 {
        unsigned i, arg, len;
        const char *o, *optarg = NULL;
@@ -67,10 +67,13 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
                                continue;
                        break;
                }
-               if (!o)
+               if (!o) {
+                       if (unknown_ok)
+                               goto ok;
                        return parse_err(errlog, argv[0],
                                         argv[arg], strlen(argv[arg]),
                                         "unrecognized option");
+               }
                /* For error messages, we include the leading '--' */
                o -= 2;
                len += 2;
@@ -82,10 +85,15 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
                        (*offset)++;
                        break;
                }
-               if (!o)
+               if (!o) {
+                       if (unknown_ok) {
+                               (*offset)++;
+                               goto ok;
+                       }
                        return parse_err(errlog, argv[0],
                                         argv[arg], strlen(argv[arg]),
                                         "unrecognized option");
+               }
                /* For error messages, we include the leading '-' */
                o--;
                len = 2;
@@ -120,6 +128,7 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
                return -1;
        }
 
+ok:
        /* If no more letters in that short opt, reset offset. */
        if (*offset && !argv[arg][*offset + 1])
                *offset = 0;
index 47b8c4e056eead80956103965c1324033da9b05a..0621c82eb6bcc693c3d2b6fcee0d30e25f720bf8 100644 (file)
@@ -22,6 +22,6 @@ struct opt_alloc {
 extern struct opt_alloc opt_alloc;
 
 int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
-             void (*errlog)(const char *fmt, ...));
+             void (*errlog)(const char *fmt, ...), bool unknown_ok);
 
 #endif /* CCAN_OPT_PRIVATE_H */
diff --git a/ccan/opt/test/run-early_incomplete.c b/ccan/opt/test/run-early_incomplete.c
new file mode 100644 (file)
index 0000000..4720cb7
--- /dev/null
@@ -0,0 +1,37 @@
+/* With errlog == NULL, we never get a "failure". */
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+#include <ccan/opt/opt.c>
+#include <ccan/opt/usage.c>
+#include <ccan/opt/helpers.c>
+#include <ccan/opt/parse.c>
+#include "utils.h"
+
+int main(int argc, char *argv[])
+{
+       plan_tests(8);
+
+       /* Simple short args.*/
+       opt_register_noarg("-a", test_noarg, NULL, "All");
+       opt_register_early_noarg("-b|--blong", test_noarg, NULL, "All");
+
+       /* This is OK. */
+       ok1(parse_early_args_incomplete(&argc, &argv, "-c", NULL));
+       ok1(test_cb_called == 0);
+
+       /* Skips letters correctly */
+       ok1(parse_early_args_incomplete(&argc, &argv, "-ca", NULL));
+       ok1(test_cb_called == 0); /* a is not an early arg! */
+
+       test_cb_called = 0;
+       ok1(parse_early_args_incomplete(&argc, &argv, "-bca", NULL));
+       ok1(test_cb_called == 1);
+
+       test_cb_called = 0;
+       ok1(parse_early_args_incomplete(&argc, &argv, "--unknown", "--also-unknown", "--blong", NULL));
+       ok1(test_cb_called == 1);
+
+       /* parse_args allocates argv */
+       free(argv);
+       return exit_status();
+}
index a9bedf2e0063333060a597a3bd591ff76744ab00..2ff04884ebdc0efe6dc4d9dd8fe4ae6af4b9175b 100644 (file)
@@ -103,6 +103,29 @@ bool parse_early_args(int *argc, char ***argv, ...)
        return opt_early_parse(*argc, *argv, save_err_output);
 }
 
+bool parse_early_args_incomplete(int *argc, char ***argv, ...)
+{
+       char **a;
+       va_list ap;
+
+       va_start(ap, argv);
+       *argc = 1;
+       a = malloc(sizeof(*a) * (*argc + 1));
+       a[0] = (*argv)[0];
+       while ((a[*argc] = va_arg(ap, char *)) != NULL) {
+               (*argc)++;
+               a = realloc(a, sizeof(*a) * (*argc + 1));
+       }
+
+       if (allocated)
+               free(*argv);
+
+       *argv = a;
+       allocated = true;
+
+       return opt_early_parse_incomplete(*argc, *argv, save_err_output);
+}
+
 struct opt_table short_table[] = {
        /* Short opts, different args. */
        OPT_WITHOUT_ARG("-a", test_noarg, "a", "Description of a"),
index 1c3658d7c7f04ce495b2e80dfd77c926110a8a96..12cf0b753e99be441ead57f73b1dc1c3e2d79999 100644 (file)
@@ -5,6 +5,7 @@
 
 bool parse_args(int *argc, char ***argv, ...);
 bool parse_early_args(int *argc, char ***argv, ...);
+bool parse_early_args_incomplete(int *argc, char ***argv, ...);
 extern char *err_output;
 void save_err_output(const char *fmt, ...);
 void reset_options(void);