]> mj.ucw.cz Git - libucw.git/commitdiff
Opt: Generalization of hooks
authorMartin Mares <mj@ucw.cz>
Mon, 27 Jan 2014 19:23:18 +0000 (20:23 +0100)
committerMartin Mares <mj@ucw.cz>
Mon, 27 Jan 2014 19:23:18 +0000 (20:23 +0100)
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
ucw/opt-help.c
ucw/opt-test.c
ucw/opt.c
ucw/opt.h

index d306db98dc4dab6322e7e96b5200c58ef7b39d04..f34b8b82ae3f3fcd658bb9a97e0c702f4c1c1d16 100644 (file)
@@ -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;
index 5c4b44e850f7d94a4902130122ecc9de178a3220..c69c22d360e6f55b498acf0f396360ad01fd36c2 100644 (file)
@@ -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);
index a71174602ac18ba5ca6ba48265c65f88586231f0..66f95ea15e7fbbabdf415871ed413095036f298f 100644 (file)
@@ -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)
index 2552efceb1a80fb1597ab52fe041ad7e3355eb3f..f7bae0d847c314c0ae0ebc1b3cfc948ea0068fb6 100644 (file)
--- 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;
 }
index f9332b8648c144cb72fde5936be538ac3b612076..25db7d45cd8b7190be391840d8b92674881ef18e 100644 (file)
--- 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, "<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.
 /**
@@ -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);