X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=ccan%2Fopt%2Fopt.c;h=9149374cb001e6152eb46e03de6cc092ef6e4297;hb=HEAD;hp=827109e34074f0edc33226f8157ab31bd27bd70c;hpb=e34192d580958aaffff3754a4e2bf1eccbb489f8;p=ccan diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index 827109e3..9149374c 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -1,9 +1,9 @@ +/* Licensed under GPLv2+ - see LICENSE file for details */ #include #include #include #include #include -#include #include #include #include @@ -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,22 +146,23 @@ 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), + bool (*show)(char *buf, size_t len, const void *arg), const void *arg, const char *desc) { struct opt_table opt; @@ -164,6 +177,24 @@ void _opt_register(const char *names, enum opt_type type, 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 { @@ -195,16 +226,51 @@ 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) { - free(opt_table); - opt_table=0; + 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, ...) @@ -230,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; +}