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.)
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;
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;
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);
.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)
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);
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;
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:
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) {
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++;
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)
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 {
}
opt_check_required(oc);
+ opt_invoke_hooks(oc, OPT_HOOK_FINAL, NULL, NULL);
return i;
}
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;
*
***/
-#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 }
// 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 }
/***
#define OPT_CONF_CONFIG OPT_CALL('C', "config", opt_handle_config, NULL, OPT_BEFORE_CONFIG | OPT_REQUIRED_VALUE, "<file>\tOverride the default configuration file")
#define OPT_CONF_SET OPT_CALL('S', "set", opt_handle_set, NULL, OPT_BEFORE_CONFIG | OPT_REQUIRED_VALUE, "<item>\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 <ucw/getopt.h>, but that one will hopefully go away one day.
/**
#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);