From bdccfb6b49a17a2ac0e1fb3063df3766cd4abd5c Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Mon, 27 Jan 2014 20:23:18 +0100 Subject: [PATCH] Opt: Generalization of hooks Hooks can specify a subset of events they wish to receive. Helps to get rid of lots of special-casing. In particular, the end of parsing is also an event, so that we can load configuration if it has not been loaded yet. (This was a bug in the original implementation, which never turned up, because opt-test has mandatory arguments.) --- ucw/opt-conf.c | 14 ++++++++--- ucw/opt-help.c | 2 ++ ucw/opt-test.c | 2 +- ucw/opt.c | 64 ++++++++++++++++++++------------------------------ ucw/opt.h | 18 ++++++++------ 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/ucw/opt-conf.c b/ucw/opt-conf.c index d306db98..f34b8b82 100644 --- a/ucw/opt-conf.c +++ b/ucw/opt-conf.c @@ -49,12 +49,20 @@ void opt_handle_dumpconfig(struct opt_item * opt UNUSED, const char * value UNUS exit(0); } -void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) { +void opt_conf_hook_internal(struct opt_item * opt, uns event, 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; + struct cf_context *cc = cf_get_context(); + + if (event == OPT_HOOK_FINAL) { + opt_conf_end_of_options(cc); + return; + } + + ASSERT(event == OPT_HOOK_BEFORE_VALUE); bool confopt = opt->flags & OPT_BEFORE_CONFIG; @@ -63,13 +71,13 @@ void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, vo if (confopt) state = OPT_CONF_HOOK_CONFIG; else { - opt_conf_end_of_options(cf_get_context()); + opt_conf_end_of_options(cc); state = OPT_CONF_HOOK_OTHERS; } break; case OPT_CONF_HOOK_CONFIG: if (!confopt) { - opt_conf_end_of_options(cf_get_context()); + opt_conf_end_of_options(cc); state = OPT_CONF_HOOK_OTHERS; } break; diff --git a/ucw/opt-help.c b/ucw/opt-help.c index 5c4b44e8..c69c22d3 100644 --- a/ucw/opt-help.c +++ b/ucw/opt-help.c @@ -95,6 +95,8 @@ static void opt_help_scan(struct help *h, const struct opt_section *sec) 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 if (item->cls == OPT_CL_HOOK) + ; else { struct opt_precomputed opt; opt_precompute(&opt, item); diff --git a/ucw/opt-test.c b/ucw/opt-test.c index a7117460..66f95ea1 100644 --- a/ucw/opt-test.c +++ b/ucw/opt-test.c @@ -97,7 +97,7 @@ static struct cf_user_type teapot_temperature_t = { .dumper = (cf_dumper1*) teapot_temperature_dumper }; -static void opt_test_hook(struct opt_item * opt, const char * value, void * data) { +static void opt_test_hook(struct opt_item * opt, uns event UNUSED, const char * value, void * data) { if (!show_hooks) return; if (opt) diff --git a/ucw/opt.c b/ucw/opt.c index 2552efce..f7bae0d8 100644 --- a/ucw/opt.c +++ b/ucw/opt.c @@ -41,18 +41,15 @@ static uns opt_default_value_flags[] = { 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; + struct opt_item ** hooks; + int opt_count; + int hook_count; int positional_max; int positional_count; bool stop_parsing; }; +// FIXME: Make public? void opt_failure(const char * mesg, ...) { va_list args; va_start(args, mesg); @@ -96,6 +93,15 @@ void opt_precompute(struct opt_precomputed *opt, struct opt_item *item) opt->flags = flags; } +static void opt_invoke_hooks(struct opt_context *oc, uns event, struct opt_item *item, char *value) +{ + for (int i = 0; i < oc->hook_count; i++) { + struct opt_item *hook = oc->hooks[i]; + if (hook->flags & event) + hook->u.hook(item, event, value, hook->ptr); + } +} + static struct opt_precomputed * opt_find_item_longopt(struct opt_context * oc, char * str) { uns len = strlen(str); struct opt_precomputed * candidate = NULL; @@ -149,8 +155,7 @@ static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * op if (opt->flags & OPT_LAST_ARG) oc->stop_parsing = 1; - 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); + opt_invoke_hooks(oc, OPT_HOOK_BEFORE_VALUE, item, value); switch (item->cls) { case OPT_CL_BOOL: @@ -235,8 +240,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) { @@ -330,16 +334,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++; @@ -352,16 +349,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) @@ -398,22 +388,17 @@ int opt_parse(const struct opt_section * options, char ** argv) { 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; 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 { @@ -430,5 +415,6 @@ int opt_parse(const struct opt_section * options, char ** argv) { } opt_check_required(oc); + opt_invoke_hooks(oc, OPT_HOOK_FINAL, NULL, NULL); return i; } diff --git a/ucw/opt.h b/ucw/opt.h index f9332b86..25db7d45 100644 --- a/ucw/opt.h +++ b/ucw/opt.h @@ -58,6 +58,7 @@ struct opt_item { struct opt_section * section; // subsection for OPT_SECTION int value; // value for OPT_SWITCH void (* call)(struct opt_item * opt, const char * value, void * data); // function to call for OPT_CALL + void (* hook)(struct opt_item * opt, uns event, const char * value, void * data); // function to call for OPT_CL_HOOK struct cf_user_type * utype; // specification of the user-defined type } u; u16 flags; @@ -93,7 +94,7 @@ struct opt_section { * ***/ -#define OPT_HELP_OPTION(help) OPT_CALL(0, "help", opt_handle_help, &help, OPT_NO_VALUE, "\tShow this help") +#define OPT_HELP_OPTION(help) OPT_CALL(0, "help", opt_handle_help, &help, OPT_BEFORE_CONFIG | OPT_NO_VALUE, "\tShow this help") #define OPT_HELP(line) { .help = line, .cls = OPT_CL_HELP } #define OPT_BOOL(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_BOOL, .type = CT_INT } #define OPT_STRING(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_STRING } @@ -111,7 +112,7 @@ struct opt_section { // FIXME: Check that the target is of the right type (likewise in other statically typed options) #define OPT_INC(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .flags = fl, .help = desc, .cls = OPT_CL_INC, .type = CT_INT } #define OPT_SECTION(sec) { .cls = OPT_CL_SECTION, .u.section = &sec } -#define OPT_HOOK(fn, data, fl) { .cls = OPT_CL_HOOK, .u.call = fn, .flags = OPT_NO_HELP | fl, .ptr = data } +#define OPT_HOOK(fn, data, events) { .cls = OPT_CL_HOOK, .u.hook = fn, .flags = events, .ptr = data } #define OPT_END { .cls = OPT_CL_END } /*** @@ -130,12 +131,12 @@ struct opt_section { #define OPT_CONF_CONFIG OPT_CALL('C', "config", opt_handle_config, NULL, OPT_BEFORE_CONFIG | OPT_REQUIRED_VALUE, "\tOverride the default configuration file") #define OPT_CONF_SET OPT_CALL('S', "set", opt_handle_set, NULL, OPT_BEFORE_CONFIG | OPT_REQUIRED_VALUE, "\tManual setting of a configuration item") #define OPT_CONF_DUMPCONFIG OPT_CALL(0, "dumpconfig", opt_handle_dumpconfig, NULL, OPT_NO_VALUE, "\tDump program configuration") -#define OPT_CONF_HOOK OPT_HOOK(opt_conf_hook_internal, NULL, OPT_HOOK_BEFORE_VALUE) +#define OPT_CONF_HOOK OPT_HOOK(opt_conf_hook_internal, NULL, OPT_HOOK_BEFORE_VALUE | OPT_HOOK_FINAL) void opt_handle_config(struct opt_item * opt, const char * value, void * data); void opt_handle_set(struct opt_item * opt, const char * value, void * data); void opt_handle_dumpconfig(struct opt_item * opt, const char * value, void * data); -void opt_conf_hook_internal(struct opt_item * opt, const char * value, void * data); +void opt_conf_hook_internal(struct opt_item * opt, uns event, const char * value, void * data); // XXX: This is duplicated with , but that one will hopefully go away one day. /** @@ -185,9 +186,12 @@ extern char *cf_env_file; #define OPT_MULTIPLE 0x200 /** Argument may appear any time; will save all the values into a simple list **/ #define OPT_SEEN_AS_LONG 0x400 // Used internally #define OPT_BEFORE_CONFIG 0x800 /** Argument may appear before config file is loaded **/ -#define OPT_HOOK_BEFORE_ARG 0x1000 /** Call before option parsing **/ -#define OPT_HOOK_BEFORE_VALUE 0x2000 /** Call before value parsing **/ -#define OPT_HOOK_AFTER_VALUE 0x4000 /** Call after value parsing **/ + +// For hooks, the flags contain a combination of events. +#define OPT_HOOK_BEFORE_ARG 0x1 /** Call before option parsing **/ +#define OPT_HOOK_BEFORE_VALUE 0x2 /** Call before value parsing **/ +#define OPT_HOOK_AFTER_VALUE 0x4 /** Call after value parsing **/ +#define OPT_HOOK_FINAL 0x8 /** Call just before opt_parse() returns **/ void opt_help(const struct opt_section * sec); void opt_handle_help(struct opt_item * opt, const char * value, void * data); -- 2.39.2