+
+/*========== 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();
+}
+static void badusage(const char *fmt, ...) NORET_PRINTF(1,2);
+static void badusage(const char *fmt, ...) {
+ va_list al;
+ va_start(al,fmt);
+ vbadusage(fmt,al);
+}
+
+enum OptFlags {
+ of_seconds= 001000u,
+ of_boolean= 002000u,
+};
+
+typedef struct Option Option;
+typedef void OptionParser(const Option*, const char *val);
+
+struct Option {
+ int shrt;
+ const char *lng;
+ void *store;
+ OptionParser *fn;
+ int noarg, intval;
+};
+
+/*---------- specific option types ----------*/
+
+static void op_integer(const Option *o, const char *val) {
+ char *ep;
+ errno= 0;
+ unsigned long ul= strtoul(val,&ep,10);
+ if (*ep || ep==val || errno || ul>INT_MAX)
+ badusage("bad integer value for %s",o->lng);
+ int *store= o->store;
+ *store= ul;
+}
+
+static void op_double(const Option *o, const char *val) {
+ int *store= o->store;
+ char *ep;
+ errno= 0;
+ *store= strtod(val, &ep);
+ if (*ep || ep==val || errno)
+ badusage("bad floating point value for %s",o->lng);
+}
+
+static void op_string(const Option *o, const char *val) {
+ const char **store= o->store;
+ *store= val;
+}
+
+static void op_seconds(const Option *o, const char *val) {
+ int *store= o->store;
+ char *ep;
+ int unit;
+
+ double v= strtod(val,&ep);
+ if (ep==val) badusage("bad time/duration value for %s",o->lng);
+
+ if (!*ep || !strcmp(ep,"s")) unit= 1;
+ else if (!strcmp(ep,"m")) unit= 60;
+ else if (!strcmp(ep,"h")) unit= 3600;
+ else if (!strcmp(ep,"d")) unit= 86400;
+ else badusage("bad units %s for time/duration value for %s",ep,o->lng);
+
+ v *= unit;
+ v= ceil(v);
+ if (v > INT_MAX) badusage("time/duration value for %s out of range",o->lng);
+ *store= v;
+}
+
+static void op_periods_rndup(const Option *o, const char *val) {
+ int *store= o->store;
+ op_seconds(o,val);
+ *store += period_seconds-1;
+ *store /= period_seconds;
+}
+
+static void op_setint(const Option *o, const char *val) {
+ int *store= o->store;
+ *store= o->intval;
+}
+
+/*---------- specific options ----------*/
+
+static const Option options[]= {
+{'f',"feedfile", &feedfile, op_string },
+{'q',"quiet-multiple", &quiet_multiple, op_setint, 1,1 },
+
+{ 0, "max-connections", &max_connections, op_integer },
+{ 0, "max-queue-per-conn", &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, "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 },
+};
+
+int main(int argc, char **argv) {
+ const char *arg;
+
+ 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 */
+ }
+ }
+ }
+
+ if (!arg) badusage("need site name argument");
+ sitename= arg;
+
+ if ((arg= *++argv))
+ remote_host= arg;
+
+ if (*++argv) badusage("too many non-option arguments");
+
+ if (nocheck_thresh_pct < 0 || nocheck_thresh_pct > 100)
+ badusage("nocheck threshold percentage must be between 0..100");
+ nocheck_thresh= nocheck_thresh_pct * 0.01;
+
+ if (nocheck_decay_articles < 0.1)
+ badusage("nocheck decay articles must be at least 0.1");
+ nocheck_decay= 1 - 1/nocheck_decay_articles;
+
+ if (!pathoutgoing)
+ pathoutgoing= innconf->pathoutgoing;
+ innconf_read(inndconffile);
+
+ if (!feedfile)
+ feedfile= xasprintf("%s/%s",pathoutgoing,sitename);
+ else if (!feedfile[0])
+ badusage("feed filename must be nonempty");
+ else if (feedfile[strlen(feedfile)-1]=='/')
+ feedfile= xasprintf("%s%s",feedfile,sitename);
+
+ const char *feedfile_forbidden= "?*[~#";
+ int c;
+ while ((c= *feedfile_forbidden++))
+ if (strchr(feedfile, c))
+ badusage("feed filename may not contain metacharacter %c",c);
+
+ oop_source_sys *sysloop= oop_sys_new();
+ if (!sysloop) sysdie("could not create liboop event loop");
+ loop= (oop_source*)sysloop;
+