summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWormHeamer2025-09-29 00:44:28 -0400
committerWormHeamer2025-09-29 00:44:28 -0400
commit39f42c4f391af09dcfb3fb0272534caec26495f5 (patch)
tree645552ef3271fcecef8d79d698bffb384c17fa2f
parent7fadbf2f752135731d3cd50a68dc34f34bda7e06 (diff)
add opt.hHEADmaster
-rw-r--r--opt.h86
1 files changed, 86 insertions, 0 deletions
diff --git a/opt.h b/opt.h
new file mode 100644
index 0000000..0971112
--- /dev/null
+++ b/opt.h
@@ -0,0 +1,86 @@
+#ifndef OPT_H
+#define OPT_H
+
+#define OPT_ARG 1
+#define OPT_AT_END 0
+#define OPT_ERR_NO_PARAM -1
+#define OPT_ERR_BAD_OPT -2
+
+#define OPTF_MID_SHORT 1
+#define OPTF_IGNORE 2
+#define OPTF_NO_PRINT_ERR 4
+
+/* fmt is a set of short option chars, preceded by : if they accept an
+ * argument, followed by [foo] for a corresponding long option name. */
+
+int opt_next(int *optf, int *argc, const char ***argv, const char *fmt);
+
+#ifdef OPT_IMPL
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+int opt_next(int *optf, int *argc, const char ***argv, const char *fmt) {
+ if (~*optf & OPTF_MID_SHORT) {
+ *argv += 1, *argc -= 1;
+ if (*argc < 1) return OPT_AT_END;
+ }
+ if (*optf & OPTF_IGNORE) return OPT_ARG;
+
+ int is_long = 0;
+ if (~*optf & OPTF_MID_SHORT) {
+ if ((**argv)[0] != '-') return OPT_ARG;
+ is_long = (**argv)[1] == '-';
+ if (is_long && !(**argv)[2]) {
+ *optf |= OPTF_IGNORE;
+ return opt_next(optf, argc, argv, fmt);
+ }
+ }
+
+ const char *opt = **argv + is_long + !(*optf & OPTF_MID_SHORT);
+ const char *opt_end = is_long ? strchr(opt, '=') : opt + 1;
+ if (!opt_end) opt_end = opt + strlen(opt);
+ unsigned opt_len = opt_end - opt;
+
+ int opt_arg = 0, match = 0;
+ char opt_char = '\0';
+ while (*fmt && !match) {
+ opt_arg = (*fmt == ':');
+ if (opt_arg) fmt++;
+ opt_char = *fmt++;
+ assert(opt_char);
+ match = !is_long && *opt == opt_char;
+ if (*fmt == '[') {
+ const char *rt = strchr(++fmt, ']');
+ assert(rt);
+ match |= is_long && rt-fmt == opt_len && !memcmp(opt, fmt, opt_len);
+ fmt = rt + 1;
+ }
+ }
+
+ *optf &= ~OPTF_MID_SHORT;
+ if (match && opt_arg) {
+ if (*opt_end) {
+ **argv = opt_end + is_long;
+ } else {
+ *argv += 1, *argc -= 1;
+ if (*argc > 0) return opt_char;
+ if (~*optf & OPTF_NO_PRINT_ERR) {
+ fprintf(stderr, "option '%.*s' expected parameter, but none given\n", opt_len, opt);
+ }
+ return OPT_ERR_NO_PARAM;
+ }
+ } else if (!is_long && *opt_end) {
+ *optf |= OPTF_MID_SHORT;
+ **argv = opt_end;
+ }
+
+ if (!match && ~*optf & OPTF_NO_PRINT_ERR) {
+ fprintf(stderr, "unrecognized option '%.*s'\n", opt_len, opt);
+ }
+ return match ? opt_char : OPT_ERR_BAD_OPT;
+}
+
+#endif
+#endif