chiark / gitweb /
New option handling
[innduct.git] / backends / innduct.c
index 858fa0e3a736d08c75d61f6f50881864e71fab33..37241d4c9c892f6df484bd5de0dc723cf96c4a40 100644 (file)
@@ -2500,12 +2500,19 @@ EVERY(period, -1,0, {
 
 /*========== option parsing ==========*/
 
-/*---------- generic option parser and logging ----------*/
-
 static void vbadusage(const char *fmt, va_list al) NORET_PRINTF(1,0);
 static void vbadusage(const char *fmt, va_list al) {
-  abort();
+  char *m= xvasprintf(fmt,al);
+  fprintf(stderr, "bad usage: %s\n%s"
+         "say --help for help, or read the manpage\n",
+         m);
+  if (become_daemon)
+    syslog(LOG_CRIT,"innduct: invoked with bad usage: %s",m);
+  exit(8);
 }
+
+/*---------- generic option parser ----------*/
+
 static void badusage(const char *fmt, ...) NORET_PRINTF(1,2);
 static void badusage(const char *fmt, ...) {
   va_list al;
@@ -2523,12 +2530,80 @@ typedef void OptionParser(const Option*, const char *val);
 
 struct Option {
   int shrt;
-  const char *lng;
+  const char *lng, *formarg;
   void *store;
   OptionParser *fn;
-  int noarg, intval;
+  int intval;
 };
 
+static void parse_options(const Option *options, char ***argvp) {
+  /* on return *argvp is first non-option arg; argc is not updated */
+
+  for (;;) {
+    const char *arg= *++(*argvp);
+    if (!arg) break;
+    if (*arg != '-') break;
+    if (!strcmp(arg,"--")) { arg= *++(*argvp); break; }
+    int a;
+    while ((a= *++arg)) {
+      const Option *o;
+      if (a=='-') {
+       arg++;
+       char *equals= strchr(arg,'=');
+       int len= equals ? (equals - arg) : strlen(arg);
+       for (o=options; o->shrt || o->lng; o++)
+         if (strlen(o->lng) == len && !memcmp(o->lng,arg,len))
+           goto found_long;
+       badusage("unknown long option --%s",arg);
+      found_long:
+       if (!o->formarg) {
+         if (equals) badusage("option --%s does not take a value",o->lng);
+         arg= 0;
+       } else if (equals) {
+         arg= equals+1;
+       } else {
+         arg= *++(*argvp);
+         if (!arg) badusage("option --%s needs a value for %s",
+                            o->lng, o->formarg);
+       }
+       o->fn(o, arg);
+       break; /* eaten the whole argument now */
+      }
+      for (o=options; o->shrt || o->lng; o++)
+       if (a == o->shrt)
+         goto found_short;
+      badusage("unknown short option -%c",a);
+    found_short:
+      if (!o->formarg) {
+       o->fn(o,0);
+      } else {
+       if (!*++arg) {
+         arg= *++(*argvp);
+         if (!arg) badusage("option -%c needs a value for %s",
+                            o->shrt, o->formarg);
+       }
+       o->fn(o,arg);
+       break; /* eaten the whole argument now */
+      }
+    }
+  }
+}
+
+#define DELIMPERHAPS(delim,str)  (str) ? (delim) : "", (str) ? (str) : ""
+
+static void print_options(const Option *options, FILE *f) {
+  const Option *o;
+  for (o=options; o->shrt || o->lng; o++) {
+    char shrt[2] = { o->shrt, 0 };
+    char *optspec= xasprintf("%s%s%s%s%s",
+                            o->shrt ? "-" : "", o->shrt,
+                            o->shrt && o->lng ? "|" : "",
+                            DELIMPERHAPS("--", o->lng));
+    fprintf(f, "  %s%s%s\n", optspec, DELIMPERHAPS(" ", o->formarg));
+    free(optspec);
+  }
+}
+
 /*---------- specific option types ----------*/
 
 static void op_integer(const Option *o, const char *val) {
@@ -2589,86 +2664,65 @@ static void op_setint(const Option *o, const char *val) {
 
 /*---------- specific options ----------*/
 
-static const Option options[]= {
-{'f',"feedfile",              &feedfile,                 op_string           },
-{'q',"quiet-multiple",        &quiet_multiple,           op_setint,   1,1    },
+static void help(const Option *o, const char *val);
 
-{ 0, "max-connections",       &max_connections,          op_integer          },
-{ 0, "max-queue-per-conn",    &max_queue_per_conn,       op_integer          },
+static const Option innduct_options[]= {
+{'f',"feedfile",         "F",   &feedfile,             op_string           },
+{'q',"quiet-multiple",   0,     &quiet_multiple,       op_setint,   1    },
+{0,"help",               0,                            help             },
 
+{0,"max-connections",    "N",   &max_connections,      op_integer          },
+{0,"max-queue-per-conn", "N",   &max_queue_per_conn,   op_integer          },
 
-{ 0, "streaming",             &try_stream,               op_setint,  1,1     },
-{ 0, "no-streaming",          &try_stream,               op_setint,  1,0     },
-{'P',"port",                  &port,                     op_integer          },
-{ 0, "inndconf",              &inndconffile,             op_string           },
-{'d',"daemon",                &become_daemon,            op_setint,  1,1     },
-{ 0, "no-daemon",             &become_daemon,            op_setint,  1,0     },
 
-{ 0, "no-check-proportion",   &nocheck_thresh_pct,       op_double           },
-{ 0, "no-check-filter",       &nocheck_decay_articles,   op_double           },
+{0,"streaming",          0,     &try_stream,           op_setint,  1     },
+{0,"no-streaming",       0,     &try_stream,           op_setint,  0     },
+{'P',"port",             "PORT",&port,                 op_integer          },
+{0,"inndconf",           "F",   &inndconffile,         op_string           },
+{0,"no-daemon",          0,     &become_daemon,        op_setint,  0     },
 
-{ 0, "reconnect-interval",    &reconnect_delay_periods,  op_periods_rndup    },
-{ 0, "flush-retry-interval",  &flushfail_retry_periods,  op_periods_rndup    },
-{ 0, "inndcomm-timeout",      &inndcomm_flush_timeout,   op_seconds          },
+{0,"no-check-proportion","PERCENT", &nocheck_thresh_pct,      op_double },
+{0,"no-check-filter",    "ARTICLES", &nocheck_decay_articles, op_double },
+
+{0,"reconnect-interval",  "TIME", &reconnect_delay_periods, op_periods_rndup },
+{0,"flush-retry-interval","TIME", &flushfail_retry_periods, op_periods_rndup },
+{0,"inndcomm-timeout",    "TIME",  &inndcomm_flush_timeout,   op_seconds },
+{0,0}
 };
 
-int main(int argc, char **argv) {
-  const char *arg;
+static void printusage(FILE *f) {
+  fputs("usage: innduct [options] site [fqdn]\n"
+       "available options are:\n", f);
+  print_options(innduct_options, f);
+}
 
-  for (;;) {
-    arg= *++argv;
-    if (!arg) break;
-    if (*arg != '-') break;
-    if (!strcmp(arg,"--")) { arg= *++argv; break; }
-    int a;
-    while ((a= *++arg)) {
-      const Option *o;
-      if (a=='-') {
-       arg++;
-       char *equals= strchr(arg,'=');
-       int len= equals ? (equals - arg) : strlen(arg);
-       for (o=options; o->lng; o++)
-         if (strlen(o->lng) == len && !memcmp(o->lng,arg,len))
-           goto found_long;
-       badusage("unknown long option --%s",arg);
-      found_long:
-       if (o->noarg) {
-         if (equals) badusage("option --%s does not take a value",o->lng);
-         arg= 0;
-       } else if (equals) {
-         arg= equals+1;
-       } else {
-         arg= *++argv;
-         if (!arg) badusage("option --%s needs a value",o->lng);
-       }
-       o->fn(o, arg);
-       break; /* eaten the whole argument now */
-      }
-      for (o=options; o->lng; o++)
-       if (a == o->shrt)
-         goto found_short;
-      badusage("unknown short option -%c",a);
-    found_short:
-      if (o->noarg) {
-       o->fn(o,0);
-      } else {
-       if (!*++arg) {
-         arg= *++argv;
-         if (!arg) badusage("option -%c needs a value",o->shrt);
-       }
-       o->fn(o,arg);
-       break; /* eaten the whole argument now */
-      }
-    }
+static void help(const Option *o, const char *val) {
+  printusage(stdout);
+  if (ferror(stdout) || fflush(stdout)) {
+    perror("innduct: writing help");
+    exit(12);
   }
+  exit(0);
+}
 
-  if (!arg) badusage("need site name argument");
-  sitename= arg;
+int main(int argc, char **argv) {
+  if (!argv[1]) {
+    printusage(stderr);
+    exit(12);
+  }
 
-  if ((arg= *++argv))
-    remote_host= arg;
+  parse_options(innduct_options, &argv);
 
-  if (*++argv) badusage("too many non-option arguments");
+  /* arguments */
+
+  sitename= *argv++;
+  if (!sitename) badusage("need site name argument");
+  remote_host= *argv++;
+  if (*argv) badusage("too many non-option arguments");
+
+  /* defaults */
+
+  if (!remote_host) remote_host= sitename;
 
   if (nocheck_thresh_pct < 0 || nocheck_thresh_pct > 100)
     badusage("nocheck threshold percentage must be between 0..100");
@@ -2695,6 +2749,8 @@ int main(int argc, char **argv) {
     if (strchr(feedfile, c))
       badusage("feed filename may not contain metacharacter %c",c);
 
+  /* set things up */
+
   oop_source_sys *sysloop= oop_sys_new();
   if (!sysloop) sysdie("could not create liboop event loop");
   loop= (oop_source*)sysloop;
@@ -2741,6 +2797,8 @@ int main(int argc, char **argv) {
 
   statemc_init();
 
+  /* let's go */
+
   void *r= oop_sys_run(sysloop);
   assert(r == OOP_ERROR);
   sysdie("event loop failed");