]> git.ozlabs.org Git - ccan/blobdiff - ccan/opt/opt.c
base64: fix for unsigned chars (e.g. ARM).
[ccan] / ccan / opt / opt.c
index 8f67740036e7e5c5ac53bb3c2abe8d5a9ccc7fce..9149374cb001e6152eb46e03de6cc092ef6e4297 100644 (file)
@@ -1,9 +1,9 @@
+/* Licensed under GPLv2+ - see LICENSE file for details */
 #include <ccan/opt/opt.h>
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <err.h>
 #include <assert.h>
 #include <stdarg.h>
 #include <stdint.h>
@@ -12,6 +12,9 @@
 struct opt_table *opt_table;
 unsigned int opt_count, opt_num_short, opt_num_short_arg, opt_num_long;
 const char *opt_argv0;
+struct opt_alloc opt_alloc = {
+       malloc, realloc, free
+};
 
 /* Returns string after first '-'. */
 static const char *first_name(const char *names, unsigned *len)
@@ -31,7 +34,7 @@ static const char *next_name(const char *names, unsigned *len)
 static const char *first_opt(unsigned *i, unsigned *len)
 {
        for (*i = 0; *i < opt_count; (*i)++) {
-               if (opt_table[*i].type == OPT_SUBTABLE)
+               if (opt_table[*i].type & OPT_SUBTABLE)
                        continue;
                return first_name(opt_table[*i].names, len);
        }
@@ -41,7 +44,7 @@ static const char *first_opt(unsigned *i, unsigned *len)
 static const char *next_opt(const char *p, unsigned *i, unsigned *len)
 {
        for (; *i < opt_count; (*i)++) {
-               if (opt_table[*i].type == OPT_SUBTABLE)
+               if (opt_table[*i].type & OPT_SUBTABLE)
                        continue;
                if (!p)
                        return first_name(opt_table[*i].names, len);
@@ -101,32 +104,41 @@ const char *next_sopt(const char *p, unsigned *i)
        return p;
 }
 
+/* Avoids dependency on err.h or ccan/err */
+#ifndef failmsg
+#define failmsg(fmt, ...) \
+       do { fprintf(stderr, fmt, __VA_ARGS__); exit(1); } while(0)
+#endif
+
 static void check_opt(const struct opt_table *entry)
 {
        const char *p;
        unsigned len;
+       enum opt_type type = entry->type & (OPT_USER_MIN-1);
 
-       if (entry->type != OPT_HASARG && entry->type != OPT_NOARG)
-               errx(1, "Option %s: unknown entry type %u",
-                    entry->names, entry->type);
+       if (type != OPT_HASARG && type != OPT_NOARG
+           && type != (OPT_EARLY|OPT_HASARG)
+           && type != (OPT_EARLY|OPT_NOARG))
+               failmsg("Option %s: unknown entry type %u",
+                       entry->names, entry->type);
 
        if (!entry->desc)
-               errx(1, "Option %s: description cannot be NULL", entry->names);
+               failmsg("Option %s: description cannot be NULL", entry->names);
 
 
        if (entry->names[0] != '-')
-               errx(1, "Option %s: does not begin with '-'", entry->names);
+               failmsg("Option %s: does not begin with '-'", entry->names);
 
        for (p = first_name(entry->names, &len); p; p = next_name(p, &len)) {
                if (*p == '-') {
                        if (len == 1)
-                               errx(1, "Option %s: invalid long option '--'",
-                                    entry->names);
+                               failmsg("Option %s: invalid long option '--'",
+                                       entry->names);
                        opt_num_long++;
                } else {
                        if (len != 1)
-                               errx(1, "Option %s: invalid short option"
-                                    " '%.*s'", entry->names, len+1, p-1);
+                               failmsg("Option %s: invalid short option"
+                                       " '%.*s'", entry->names, len+1, p-1);
                        opt_num_short++;
                        if (entry->type == OPT_HASARG)
                                opt_num_short_arg++;
@@ -134,23 +146,24 @@ static void check_opt(const struct opt_table *entry)
                /* Don't document args unless there are some. */
                if (entry->type == OPT_NOARG) {
                        if (p[len] == ' ' || p[len] == '=')
-                               errx(1, "Option %s: does not take arguments"
-                                    " '%s'", entry->names, p+len+1);
+                               failmsg("Option %s: does not take arguments"
+                                       " '%s'", entry->names, p+len+1);
                }
        }
 }
 
 static void add_opt(const struct opt_table *entry)
 {
-       opt_table = realloc(opt_table, sizeof(opt_table[0]) * (opt_count+1));
+       opt_table = opt_alloc.realloc(opt_table,
+                                     sizeof(opt_table[0]) * (opt_count+1));
        opt_table[opt_count++] = *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[OPT_SHOW_LEN], const void *arg),
-                  void *arg, const char *desc)
+                  bool (*show)(char *buf, size_t len, const void *arg),
+                  const void *arg, const char *desc)
 {
        struct opt_table opt;
        opt.names = names;
@@ -158,12 +171,30 @@ void _opt_register(const char *names, enum opt_type type,
        opt.cb = cb;
        opt.cb_arg = cb_arg;
        opt.show = show;
-       opt.arg = arg;
+       opt.u.carg = arg;
        opt.desc = desc;
        check_opt(&opt);
        add_opt(&opt);
 }
 
+bool opt_unregister(const char *names)
+{
+       int found = -1, i;
+
+       for (i = 0; i < opt_count; i++) {
+               if (opt_table[i].type & OPT_SUBTABLE)
+                       continue;
+               if (strcmp(opt_table[i].names, names) == 0)
+                       found = i;
+       }
+       if (found == -1)
+               return false;
+       opt_count--;
+       memmove(&opt_table[found], &opt_table[found+1],
+               (opt_count - found) * sizeof(opt_table[found]));
+       return true;
+}
+
 void opt_register_table(const struct opt_table entry[], const char *desc)
 {
        unsigned int i, start = opt_count;
@@ -173,7 +204,7 @@ void opt_register_table(const struct opt_table entry[], const char *desc)
                add_opt(&heading);
        }
        for (i = 0; entry[i].type != OPT_END; i++) {
-               if (entry[i].type == OPT_SUBTABLE)
+               if (entry[i].type & OPT_SUBTABLE)
                        opt_register_table(subtable_of(&entry[i]),
                                           entry[i].desc);
                else {
@@ -183,7 +214,7 @@ void opt_register_table(const struct opt_table entry[], const char *desc)
        }
        /* We store the table length in arg ptr. */
        if (desc)
-               opt_table[start].arg = (void *)(intptr_t)(opt_count - start);
+               opt_table[start].u.tlen = (opt_count - start);
 }
 
 /* Parse your arguments. */
@@ -195,12 +226,53 @@ 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, &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);
+}
+
+static bool early_parse(int argc, char *argv[],
+                       void (*errlog)(const char *fmt, ...),
+                       bool ignore_unknown)
+{
+       int ret;
+       unsigned off = 0;
+       char **tmpargv = opt_alloc.alloc(sizeof(argv[0]) * (argc + 1));
+
+       /* We could avoid a copy and skip instead, but this is simple. */
+       memcpy(tmpargv, argv, sizeof(argv[0]) * (argc + 1));
+
+       /* This helps opt_usage. */
+       opt_argv0 = argv[0];
+
+       while ((ret = parse_one(&argc, tmpargv, OPT_EARLY, &off, errlog, ignore_unknown)) == 1);
+
+       opt_alloc.free(tmpargv);
 
        /* 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, ...))
+{
+       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);
+       opt_table = NULL;
+       opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
+}
+
 void opt_log_stderr(const char *fmt, ...)
 {
        va_list ap;
@@ -224,7 +296,16 @@ void opt_log_stderr_exit(const char *fmt, ...)
 
 char *opt_invalid_argument(const char *arg)
 {
-       char *str = malloc(sizeof("Invalid argument '%s'") + strlen(arg));
+       char *str = opt_alloc.alloc(sizeof("Invalid argument '%s'") + strlen(arg));
        sprintf(str, "Invalid argument '%s'", arg);
        return str;
 }
+
+void opt_set_alloc(void *(*allocfn)(size_t size),
+                  void *(*reallocfn)(void *ptr, size_t size),
+                  void (*freefn)(void *ptr))
+{
+       opt_alloc.alloc = allocfn;
+       opt_alloc.realloc = reallocfn;
+       opt_alloc.free = freefn;
+}