From 39f42c4f391af09dcfb3fb0272534caec26495f5 Mon Sep 17 00:00:00 2001 From: WormHeamer Date: Mon, 29 Sep 2025 00:44:28 -0400 Subject: add opt.h --- opt.h | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 opt.h 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 +#include +#include + +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 -- cgit v1.2.3