#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