+/*
+ * Largely frivolous way to define all my command-line options. I
+ * present here a parametric macro which declares a series of
+ * _logical_ option identifiers, and for each one declares zero or
+ * more short option characters and zero or more long option
+ * words. Then I repeatedly invoke that macro with its arguments
+ * defined to be various other macros, which allows me to
+ * variously:
+ *
+ * - define an enum allocating a distinct integer value to each
+ * logical option id
+ * - define a string consisting of precisely all the short option
+ * characters
+ * - define a string array consisting of all the long option
+ * strings
+ * - define (with help from auxiliary enums) integer arrays
+ * parallel to both of the above giving the logical option id
+ * for each physical short and long option
+ * - define an array indexed by logical option id indicating
+ * whether the option in question takes a value.
+ *
+ * It's not at all clear to me that this trickery is actually
+ * particularly _efficient_ - it still, after all, requires going
+ * linearly through the option list at run time and doing a
+ * strcmp, whereas in an ideal world I'd have liked the lists of
+ * long and short options to be pre-sorted so that a binary search
+ * or some other more efficient lookup was possible. (Not that
+ * asymptotic algorithmic complexity is remotely vital in option
+ * parsing, but if I were doing this in, say, Lisp or something
+ * with an equivalently powerful preprocessor then once I'd had
+ * the idea of preparing the option-parsing data structures at
+ * compile time I would probably have made the effort to prepare
+ * them _properly_. I could have Perl generate me a source file
+ * from some sort of description, I suppose, but that would seem
+ * like overkill. And in any case, it's more of a challenge to
+ * achieve as much as possible by cunning use of cpp and enum than
+ * to just write some sensible and logical code in a Turing-
+ * complete language. I said it was largely frivolous :-)
+ *
+ * This approach does have the virtue that it brings together the
+ * option ids and option spellings into a single combined list and
+ * defines them all in exactly one place. If I want to add a new
+ * option, or a new spelling for an option, I only have to modify
+ * the main OPTIONS macro below and then add code to process the
+ * new logical id.
+ *
+ * (Though, really, even that isn't ideal, since it still involves
+ * modifying the source file in more than one place. In a
+ * _properly_ ideal world, I'd be able to interleave the option
+ * definitions with the code fragments that process them. And then
+ * not bother defining logical identifiers for them at all - those
+ * would be automatically generated, since I wouldn't have any
+ * need to specify them manually in another part of the code.)
+ */
+
+#define OPTIONS(NOVAL, VAL, SHORT, LONG) \
+ NOVAL(HELP) SHORT(h) LONG(help) \
+ NOVAL(VERSION) SHORT(V) LONG(version) \
+ NOVAL(LICENCE) LONG(licence) LONG(license) \
+ NOVAL(SCAN) SHORT(s) LONG(scan) \
+ NOVAL(DUMP) SHORT(d) LONG(dump) \
+ NOVAL(TEXT) SHORT(t) LONG(text) \
+ NOVAL(HTML) SHORT(H) LONG(html) \
+ NOVAL(HTTPD) SHORT(w) LONG(web) LONG(server) LONG(httpd) \
+ NOVAL(PROGRESS) LONG(progress) LONG(scan_progress) \
+ NOVAL(NOPROGRESS) LONG(no_progress) LONG(no_scan_progress) \
+ NOVAL(TTYPROGRESS) LONG(tty_progress) LONG(tty_scan_progress) \
+ LONG(progress_tty) LONG(scan_progress_tty) \
+ NOVAL(CROSSFS) LONG(cross_fs) \
+ NOVAL(NOCROSSFS) LONG(no_cross_fs) \
+ VAL(DATAFILE) SHORT(f) LONG(file) \
+ VAL(MINAGE) SHORT(a) LONG(age) LONG(min_age) LONG(minimum_age) \
+ VAL(AUTH) LONG(auth) LONG(http_auth) LONG(httpd_auth) \
+ LONG(server_auth) LONG(web_auth) \
+ VAL(INCLUDE) LONG(include) \
+ VAL(INCLUDEPATH) LONG(include_path) \
+ VAL(EXCLUDE) LONG(exclude) \
+ VAL(EXCLUDEPATH) LONG(exclude_path)
+
+#define IGNORE(x)
+#define DEFENUM(x) OPT_ ## x,
+#define ZERO(x) 0,
+#define ONE(x) 1,
+#define STRING(x) #x ,
+#define STRINGNOCOMMA(x) #x
+#define SHORTNEWOPT(x) SHORTtmp_ ## x = OPT_ ## x,
+#define SHORTTHISOPT(x) SHORTtmp2_ ## x, SHORTVAL_ ## x = SHORTtmp2_ ## x - 1,
+#define SHORTOPTVAL(x) SHORTVAL_ ## x,
+#define SHORTTMP(x) SHORTtmp3_ ## x,
+#define LONGNEWOPT(x) LONGtmp_ ## x = OPT_ ## x,
+#define LONGTHISOPT(x) LONGtmp2_ ## x, LONGVAL_ ## x = LONGtmp2_ ## x - 1,
+#define LONGOPTVAL(x) LONGVAL_ ## x,
+#define LONGTMP(x) SHORTtmp3_ ## x,
+
+enum { OPTIONS(DEFENUM,DEFENUM,IGNORE,IGNORE) NOPTIONS };
+enum { OPTIONS(IGNORE,IGNORE,SHORTTMP,IGNORE) NSHORTOPTS };
+enum { OPTIONS(IGNORE,IGNORE,IGNORE,LONGTMP) NLONGOPTS };
+static const int opthasval[NOPTIONS] = {OPTIONS(ZERO,ONE,IGNORE,IGNORE)};
+static const char shortopts[] = {OPTIONS(IGNORE,IGNORE,STRINGNOCOMMA,IGNORE)};
+static const char *const longopts[] = {OPTIONS(IGNORE,IGNORE,IGNORE,STRING)};
+enum { OPTIONS(SHORTNEWOPT,SHORTNEWOPT,SHORTTHISOPT,IGNORE) };
+enum { OPTIONS(LONGNEWOPT,LONGNEWOPT,IGNORE,LONGTHISOPT) };
+static const int shortvals[] = {OPTIONS(IGNORE,IGNORE,SHORTOPTVAL,IGNORE)};
+static const int longvals[] = {OPTIONS(IGNORE,IGNORE,IGNORE,LONGOPTVAL)};
+