X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ucw%2Fopt.c;h=1a94f7c0bd691042eef5f05d3e279653cd7cea3e;hb=84a25b8a8b5c99ef48dd63ca1078e22aa18ecab8;hp=59bcb26088ce8aeda7aab0fbd2ac481eea695e40;hpb=4a2604f821344eaa3fc9bde1c8380577d93d7b1c;p=libucw.git diff --git a/ucw/opt.c b/ucw/opt.c index 59bcb260..1a94f7c0 100644 --- a/ucw/opt.c +++ b/ucw/opt.c @@ -1,5 +1,5 @@ /* - * UCW Library -- Parsing of command line options + * UCW Library -- Parsing of command-line options * * (c) 2013 Jan Moskyto Matejka * (c) 2014 Martin Mares @@ -10,26 +10,13 @@ #include #include -#include -#include -#include +#include #include #include -#include -#include #include #include -/*** - * Value flags defaults - * ~~~~~~~~~~~~~~~~~~~~ - * - * OPT_NO_VALUE for OPT_BOOL, OPT_SWITCH and OPT_INC - * OPT_MAYBE_VALUE for OPT_STRING, OPT_UNS, OPT_INT - * Some of the value flags (OPT_NO_VALUE, OPT_MAYBE_VALUE, OPT_REQUIRED_VALUE) - * must be specified for OPT_CALL and OPT_USER. - ***/ static uns opt_default_value_flags[] = { [OPT_CL_BOOL] = OPT_NO_VALUE, [OPT_CL_STATIC] = OPT_MAYBE_VALUE, @@ -41,34 +28,11 @@ static uns opt_default_value_flags[] = { [OPT_CL_HELP] = 0 }; -struct opt_precomputed { - struct opt_item * item; - const char * name; - short flags; - short count; -}; - -struct opt_context { - struct opt_precomputed * opts; - struct opt_precomputed ** shortopt; - struct opt_item ** hooks_before_arg; - struct opt_item ** hooks_before_value; - struct opt_item ** hooks_after_value; - short opt_count; - short hooks_before_arg_count; - short hooks_before_value_count; - short hooks_after_value_count; - int positional_max; - int positional_count; -}; - -static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET; -static void opt_failure(const char * mesg, ...) { +void opt_failure(const char * mesg, ...) { va_list args; va_start(args, mesg); vfprintf(stderr, mesg, args); - fprintf(stderr, "\n"); - opt_usage(); + fprintf(stderr, "\nRun with --help for more information.\n"); exit(OPT_EXIT_BAD_ARGS); } @@ -87,7 +51,7 @@ static char *opt_name(struct opt_context *oc, struct opt_precomputed *opt) #define THIS_OPT opt_name(oc, opt) -static void opt_precompute(struct opt_precomputed *opt, struct opt_item *item) +void opt_precompute(struct opt_precomputed *opt, struct opt_item *item) { opt->item = item; opt->count = 0; @@ -106,146 +70,15 @@ static void opt_precompute(struct opt_precomputed *opt, struct opt_item *item) opt->flags = flags; } -#define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1) - -struct help { - struct mempool *pool; - struct help_line *lines; // A growing array of lines -}; - -struct help_line { - const char *extra; - char *fields[3]; -}; - -static void opt_help_scan_item(struct help *h, struct opt_precomputed *opt) -{ - struct opt_item *item = opt->item; - - if (opt->flags & OPT_NO_HELP) - return; - - if (item->cls == OPT_CL_HELP) { - struct help_line *l = GARY_PUSH(h->lines, 1); - l->extra = item->help ? : ""; - return; - } - - if (item->letter >= OPT_POSITIONAL_TAIL) - return; - - struct help_line *first = GARY_PUSH(h->lines, 1); - if (item->help) { - char *text = mp_strdup(h->pool, item->help); - struct help_line *l = first; - while (text) { - char *eol = strchr(text, '\n'); - if (eol) - *eol++ = 0; - - int field = (l == first ? 1 : 0); - char *f = text; - while (f) { - char *tab = strchr(f, '\t'); - if (tab) - *tab++ = 0; - if (field < 3) - l->fields[field++] = f; - f = tab; - } - - text = eol; - if (text) - l = GARY_PUSH(h->lines, 1); - } - } - - if (item->name) { - char *val = first->fields[1] ? : ""; - if (opt->flags & OPT_REQUIRED_VALUE) - val = mp_printf(h->pool, "=%s", val); - else if (!(opt->flags & OPT_NO_VALUE)) - val = mp_printf(h->pool, "[=%s]", val); - first->fields[1] = mp_printf(h->pool, "--%s%s", item->name, val); - } - - if (item->letter) { - if (item->name) - first->fields[0] = mp_printf(h->pool, "-%c, ", item->letter); - else { - char *val = first->fields[1] ? : ""; - if (!(opt->flags & OPT_REQUIRED_VALUE) && !(opt->flags & OPT_NO_VALUE)) - val = mp_printf(h->pool, "[%s]", val); - first->fields[0] = mp_printf(h->pool, "-%c%s", item->letter, val); - first->fields[1] = NULL; - } - } -} - -static void opt_help_scan(struct help *h, const struct opt_section *sec) +static void opt_invoke_hooks(struct opt_context *oc, uns event, struct opt_item *item, char *value) { - for (struct opt_item * item = sec->opt; item->cls != OPT_CL_END; item++) { - if (item->cls == OPT_CL_SECTION) - opt_help_scan(h, item->u.section); - else { - struct opt_precomputed opt; - opt_precompute(&opt, item); - opt_help_scan_item(h, &opt); - } - } -} - -void opt_help(const struct opt_section * sec) { - // Prepare help text - struct help h; - h.pool = mp_new(4096); - GARY_INIT_ZERO(h.lines, 0); - opt_help_scan(&h, sec); - - // Calculate natural width of each column - uns n = GARY_SIZE(h.lines); - uns widths[3] = { 0, 0, 0 }; - for (uns i=0; ifields[f]) - l->fields[f] = ""; - uns w = strlen(l->fields[f]); - widths[f] = MAX(widths[f], w); - } - } - if (widths[0] > 4) { - /* - * This is tricky: if there are short options, which have an argument, - * but no long variant, we are willing to let column 0 overflow to column 1. - */ - widths[1] = MAX(widths[1], widths[0] - 4); - widths[0] = 4; - } - widths[1] += 4; - - // Print columns - for (uns i=0; iextra) - puts(l->extra); - else { - int t = 0; - for (uns f=0; f<3; f++) { - t += widths[f]; - t -= printf("%s", l->fields[f]); - while (t > 0) { - putchar(' '); - t--; - } - } - putchar('\n'); + for (int i = 0; i < oc->hook_count; i++) { + struct opt_item *hook = oc->hooks[i]; + if (hook->flags & event) { + void *data = (hook->flags & OPT_HOOK_INTERNAL) ? oc : hook->ptr; + hook->u.hook(item, event, value, data); } } - - // Clean up - GARY_FREE(h.lines); - mp_delete(h.pool); } static struct opt_precomputed * opt_find_item_longopt(struct opt_context * oc, char * str) { @@ -298,8 +131,10 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op if (opt->count++ && (opt->flags & OPT_SINGLE)) opt_failure("Option %s must be specified at most once.", THIS_OPT); - for (int i = 0; i < oc->hooks_before_value_count; i++) - oc->hooks_before_value[i]->u.call(item, value, oc->hooks_before_value[i]->ptr); + if (opt->flags & OPT_LAST_ARG) + oc->stop_parsing = 1; + + opt_invoke_hooks(oc, OPT_HOOK_BEFORE_VALUE, item, value); switch (item->cls) { case OPT_CL_BOOL: @@ -370,8 +205,11 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op (*((int *)item->ptr))++; break; case OPT_CL_CALL: - item->u.call(item, value, item->ptr); - break; + { + void *data = (opt->flags & OPT_INTERNAL) ? oc : item->ptr; + item->u.call(item, value, data); + break; + } case OPT_CL_USER: { char * e = NULL; @@ -384,8 +222,7 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op ASSERT(0); } - for (int i = 0;i < oc->hooks_after_value_count; i++) - oc->hooks_after_value[i]->u.call(item, value, oc->hooks_after_value[i]->ptr); + opt_invoke_hooks(oc, OPT_HOOK_AFTER_VALUE, item, value); } static int opt_longopt(struct opt_context * oc, char ** argv, int index) { @@ -479,16 +316,9 @@ static void opt_count_items(struct opt_context *oc, const struct opt_section *se for (const struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) { if (item->cls == OPT_CL_SECTION) opt_count_items(oc, item->u.section); - else if (item->cls == OPT_CL_HOOK) { - if (item->flags & OPT_HOOK_BEFORE_ARG) - oc->hooks_before_arg_count++; - else if (item->flags & OPT_HOOK_BEFORE_VALUE) - oc->hooks_before_value_count++; - else if (item->flags & OPT_HOOK_AFTER_VALUE) - oc->hooks_after_value_count++; - else - ASSERT(0); - } else if (item->letter || item->name) { + else if (item->cls == OPT_CL_HOOK) + oc->hook_count++; + else if (item->letter || item->name) { oc->opt_count++; if (item->letter > OPT_POSITIONAL_TAIL) oc->positional_max++; @@ -501,16 +331,9 @@ static void opt_prepare_items(struct opt_context *oc, const struct opt_section * for (struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) { if (item->cls == OPT_CL_SECTION) opt_prepare_items(oc, item->u.section); - else if (item->cls == OPT_CL_HOOK) { - if (item->flags & OPT_HOOK_BEFORE_ARG) - oc->hooks_before_arg[oc->hooks_before_arg_count++] = item; - else if (item->flags & OPT_HOOK_BEFORE_VALUE) - oc->hooks_before_value[oc->hooks_before_value_count++] = item; - else if (item->flags & OPT_HOOK_AFTER_VALUE) - oc->hooks_after_value[oc->hooks_after_value_count++] = item; - else - ASSERT(0); - } else if (item->letter || item->name) { + else if (item->cls == OPT_CL_HOOK) + oc->hooks[oc->hook_count++] = item; + else if (item->letter || item->name) { struct opt_precomputed * opt = &oc->opts[oc->opt_count++]; opt_precompute(opt, item); if (item->letter) @@ -539,29 +362,26 @@ static void opt_check_required(struct opt_context *oc) } } -void opt_parse(const struct opt_section * options, char ** argv) { +int opt_parse(const struct opt_section * options, char ** argv) { struct opt_context * oc = alloca(sizeof(*oc)); memset(oc, 0, sizeof (*oc)); + oc->options = options; opt_count_items(oc, options); oc->opts = alloca(sizeof(*oc->opts) * oc->opt_count); oc->shortopt = alloca(sizeof(*oc->shortopt) * (oc->positional_max + 257)); memset(oc->shortopt, 0, sizeof(*oc->shortopt) * (oc->positional_max + 257)); - oc->hooks_before_arg = alloca(sizeof (*oc->hooks_before_arg) * oc->hooks_before_arg_count); - oc->hooks_before_value = alloca(sizeof (*oc->hooks_before_value) * oc->hooks_before_value_count); - oc->hooks_after_value = alloca(sizeof (*oc->hooks_after_value) * oc->hooks_after_value_count); + oc->hooks = alloca(sizeof (*oc->hooks) * oc->hook_count); oc->opt_count = 0; - oc->hooks_before_arg_count = 0; - oc->hooks_before_value_count = 0; - oc->hooks_after_value_count = 0; + oc->hook_count = 0; opt_prepare_items(oc, options); int force_positional = 0; - for (int i = 0; argv[i]; i++) { + int i; + for (i=0; argv[i] && !oc->stop_parsing; i++) { char *arg = argv[i]; - for (int j = 0; j < oc->hooks_before_arg_count; j++) - oc->hooks_before_arg[j]->u.call(NULL, NULL, oc->hooks_before_arg[j]->ptr); + opt_invoke_hooks(oc, OPT_HOOK_BEFORE_ARG, NULL, NULL); if (arg[0] != '-' || force_positional) opt_positional(oc, arg); else { @@ -578,70 +398,6 @@ void opt_parse(const struct opt_section * options, char ** argv) { } opt_check_required(oc); -} - -static void opt_conf_end_of_options(struct cf_context *cc) { - cf_load_default(cc); - if (cc->postpone_commit && cf_close_group()) - opt_failure("Loading of configuration failed"); -} - -void opt_conf_internal(struct opt_item * opt, const char * value, void * data UNUSED) { - struct cf_context *cc = cf_get_context(); - switch (opt->letter) { - case 'S': - cf_load_default(cc); - if (cf_set(value)) - opt_failure("Cannot set %s", value); - break; - case 'C': - if (cf_load(value)) - opt_failure("Cannot load config file %s", value); - break; -#ifdef CONFIG_UCW_DEBUG - case '0': - opt_conf_end_of_options(cc); - struct fastbuf *b = bfdopen(1, 4096); - cf_dump_sections(b); - bclose(b); - exit(0); - break; -#endif - } -} - -void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) { - static enum { - OPT_CONF_HOOK_BEGIN, - OPT_CONF_HOOK_CONFIG, - OPT_CONF_HOOK_OTHERS - } state = OPT_CONF_HOOK_BEGIN; - - int confopt = 0; - - if (opt->letter == 'S' || opt->letter == 'C' || (opt->name && !strcmp(opt->name, "dumpconfig"))) - confopt = 1; - - switch (state) { - case OPT_CONF_HOOK_BEGIN: - if (confopt) - state = OPT_CONF_HOOK_CONFIG; - else { - opt_conf_end_of_options(cf_get_context()); - state = OPT_CONF_HOOK_OTHERS; - } - break; - case OPT_CONF_HOOK_CONFIG: - if (!confopt) { - opt_conf_end_of_options(cf_get_context()); - state = OPT_CONF_HOOK_OTHERS; - } - break; - case OPT_CONF_HOOK_OTHERS: - if (confopt) - opt_failure("Config options (-C, -S) must stand before other options."); - break; - default: - ASSERT(0); - } + opt_invoke_hooks(oc, OPT_HOOK_FINAL, NULL, NULL); + return i; }