aboutsummaryrefslogtreecommitdiff
path: root/tools/getopt.h
diff options
context:
space:
mode:
authorOskari Timperi <oskari.timperi@iki.fi>2017-02-12 17:12:52 +0200
committerOskari Timperi <oskari.timperi@iki.fi>2017-02-12 17:12:52 +0200
commitcd2faf64b2e995dee42e7e3911325d372c6604a1 (patch)
treee0ab608cdd7005e759b570a5a4ec2031191bafb5 /tools/getopt.h
parent54f78d73ec378fe1d62a696edeb2ddc5a59e2669 (diff)
downloadmqtt-cd2faf64b2e995dee42e7e3911325d372c6604a1.tar.gz
mqtt-cd2faf64b2e995dee42e7e3911325d372c6604a1.zip
Add all the stuff
Diffstat (limited to 'tools/getopt.h')
-rw-r--r--tools/getopt.h175
1 files changed, 175 insertions, 0 deletions
diff --git a/tools/getopt.h b/tools/getopt.h
new file mode 100644
index 0000000..5ed3145
--- /dev/null
+++ b/tools/getopt.h
@@ -0,0 +1,175 @@
+#ifndef _GETOPT_H_
+#define _GETOPT_H_
+
+#include <setjmp.h>
+#include <stddef.h>
+
+/**
+ * This getopt implementation parses options of the following forms:
+ * -a -b -c foo (single-character options)
+ * -abc foo (packed single-character options)
+ * -abcfoo (packed single-character options and an argument)
+ * --foo bar (long option)
+ * --foo=bar (long option and argument separated by '=')
+ *
+ * It does not support abbreviated options (e.g., interpreting --foo as
+ * --foobar when there are no other --foo* options) since that misfeature
+ * results in breakage when new options are added. It also does not support
+ * options appearing after non-options (e.g., "cp foo bar -R") since that is
+ * a horrible GNU perversion.
+ */
+
+/* Work around LLVM bug. */
+#ifdef __clang__
+#warning Working around bug in LLVM optimizer
+#warning For more details see https://llvm.org/bugs/show_bug.cgi?id=27190
+#define DO_SETJMP _DO_SETJMP(__LINE__)
+#define _DO_SETJMP(x) __DO_SETJMP(x)
+#define __DO_SETJMP(x) \
+ void * getopt_initloop = && getopt_initloop_ ## x; \
+ getopt_initloop_ ## x:
+#define DO_LONGJMP \
+ goto *getopt_initloop
+#else
+#define DO_SETJMP \
+ sigjmp_buf getopt_initloop; \
+ if (!getopt_initialized) \
+ sigsetjmp(getopt_initloop, 0)
+#define DO_LONGJMP \
+ siglongjmp(getopt_initloop, 1)
+#endif
+
+/* Avoid namespace collisions with libc getopt. */
+#define getopt libcperciva_getopt
+#define optarg libcperciva_optarg
+#define optind libcperciva_optind
+#define opterr libcperciva_opterr
+#define optreset libcperciva_optreset
+
+/* Standard getopt global variables. */
+extern const char * optarg;
+extern int optind, opterr, optreset;
+
+/* Dummy option string, equal to "(dummy)". */
+#define GETOPT_DUMMY getopt_dummy
+
+/**
+ * GETOPT(argc, argv):
+ * When called for the first time (or the first time after optreset is set to
+ * a nonzero value), return GETOPT_DUMMY, aka. "(dummy)". Thereafter, return
+ * the next option string and set optarg / optind appropriately; abort if not
+ * properly initialized when not being called for the first time.
+ */
+#define GETOPT(argc, argv) getopt(argc, argv)
+
+/**
+ * GETOPT_SWITCH(ch):
+ * Jump to the appropriate GETOPT_OPT, GETOPT_OPTARG, GETOPT_MISSING_ARG, or
+ * GETOPT_DEFAULT based on the option string ${ch}. When called for the first
+ * time, perform magic to index the options.
+ *
+ * GETOPT_SWITCH(ch) is equivalent to "switch (ch)" in a standard getopt loop.
+ */
+#define GETOPT_SWITCH(ch) \
+ volatile size_t getopt_ln_min = __LINE__; \
+ volatile size_t getopt_ln = getopt_ln_min - 1; \
+ volatile int getopt_default_missing = 0; \
+ DO_SETJMP; \
+ switch (getopt_initialized ? getopt_lookup(ch) + getopt_ln_min : getopt_ln++)
+
+/**
+ * GETOPT_OPT(os):
+ * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH.
+ *
+ * GETOPT_OPT("-x") is equivalent to "case 'x'" in a standard getopt loop
+ * which has an optstring containing "x".
+ */
+#define GETOPT_OPT(os) _GETOPT_OPT(os, __LINE__)
+#define _GETOPT_OPT(os, ln) __GETOPT_OPT(os, ln)
+#define __GETOPT_OPT(os, ln) \
+ case ln: \
+ if (getopt_initialized) \
+ goto getopt_skip_ ## ln; \
+ getopt_register_opt(os, ln - getopt_ln_min, 0); \
+ DO_LONGJMP; \
+ getopt_skip_ ## ln
+
+/**
+ * GETOPT_OPTARG(os):
+ * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH,
+ * unless no argument is available, in which case jump to GETOPT_MISSING_ARG
+ * (if present) or GETOPT_DEFAULT (if not).
+ *
+ * GETOPT_OPTARG("-x") is equivalent to "case 'x'" in a standard getopt loop
+ * which has an optstring containing "x:".
+ */
+#define GETOPT_OPTARG(os) _GETOPT_OPTARG(os, __LINE__)
+#define _GETOPT_OPTARG(os, ln) __GETOPT_OPTARG(os, ln)
+#define __GETOPT_OPTARG(os, ln) \
+ case ln: \
+ if (getopt_initialized) \
+ goto getopt_skip_ ## ln; \
+ getopt_register_opt(os, ln - getopt_ln_min, 1); \
+ DO_LONGJMP; \
+ getopt_skip_ ## ln
+
+/**
+ * GETOPT_MISSING_ARG:
+ * Jump to this point if an option string specified in GETOPT_OPTARG is seen
+ * but no argument is available.
+ *
+ * GETOPT_MISSING_ARG is equivalent to "case ':'" in a standard getopt loop
+ * which has an optstring starting with ":". As such, it also has the effect
+ * of disabling warnings about invalid options, as if opterr had been zeroed.
+ */
+#define GETOPT_MISSING_ARG _GETOPT_MISSING_ARG(__LINE__)
+#define _GETOPT_MISSING_ARG(ln) __GETOPT_MISSING_ARG(ln)
+#define __GETOPT_MISSING_ARG(ln) \
+ case ln: \
+ if (getopt_initialized) \
+ goto getopt_skip_ ## ln; \
+ getopt_register_missing(ln - getopt_ln_min); \
+ DO_LONGJMP; \
+ getopt_skip_ ## ln
+
+/**
+ * GETOPT_DEFAULT:
+ * Jump to this point if an unrecognized option is seen or if an option
+ * specified in GETOPT_OPTARG is seen, no argument is available, and there is
+ * no GETOPT_MISSING_ARG label.
+ *
+ * GETOPT_DEFAULT is equivalent to "case '?'" in a standard getopt loop.
+ *
+ * NOTE: This MUST be present in the GETOPT_SWITCH statement, and MUST occur
+ * after all other GETOPT_* labels.
+ */
+#define GETOPT_DEFAULT _GETOPT_DEFAULT(__LINE__)
+#define _GETOPT_DEFAULT(ln) __GETOPT_DEFAULT(ln)
+#define __GETOPT_DEFAULT(ln) \
+ goto getopt_skip_ ## ln; \
+ case ln: \
+ getopt_initialized = 1; \
+ break; \
+ default: \
+ if (getopt_initialized) \
+ goto getopt_skip_ ## ln; \
+ if (!getopt_default_missing) { \
+ getopt_setrange(ln - getopt_ln_min); \
+ getopt_default_missing = 1; \
+ } \
+ DO_LONGJMP; \
+ getopt_skip_ ## ln
+
+/*
+ * The back-end implementation. These should be considered internal
+ * interfaces and not used directly.
+ */
+const char * getopt(int, char * const []);
+size_t getopt_lookup(const char *);
+void getopt_register_opt(const char *, size_t, int);
+void getopt_register_missing(size_t);
+void getopt_setrange(size_t);
+extern const char * getopt_dummy;
+extern int getopt_initialized;
+
+#endif /* !_GETOPT_H_ */