48 #ifndef MISC_OPTPARSE_H
49 #define MISC_OPTPARSE_H
65 enum optparse_argtype {
74 enum optparse_argtype argtype;
81 void optparse_init(
struct optparse *options,
char **argv);
116 char *optparse_arg(
struct optparse *options);
119 #ifdef OPTPARSE_IMPLEMENTATION
121 #define OPTPARSE_MSG_INVALID "invalid option"
122 #define OPTPARSE_MSG_MISSING "option requires an argument"
123 #define OPTPARSE_MSG_TOOMANY "option takes no arguments"
124 #define OPTPARSE_MSG_UNKNOWN "unknown error"
127 optparse_error(
struct optparse *options,
const char *msg,
const char *data)
130 const char *sep =
" -- '";
133 options->errmsg[p++] = *msg++;
136 options->errmsg[p++] = *sep++;
138 while (p <
sizeof(options->errmsg) - 2 && *data)
139 options->errmsg[p++] = *data++;
141 options->errmsg[p++] =
'\'';
142 options->errmsg[p++] =
'\0';
148 optparse_init(
struct optparse *options,
char **argv)
150 options->argv = argv;
151 options->permute = 1;
152 options->optind = argv[0] != 0;
155 options->errmsg[0] =
'\0';
159 optparse_is_dashdash(
const char *arg)
161 return arg != 0 && arg[0] ==
'-' && arg[1] ==
'-' && arg[2] ==
'\0';
165 optparse_is_shortopt(
const char *arg)
167 return arg != 0 && arg[0] ==
'-' && arg[1] !=
'-' && arg[1] !=
'\0';
171 optparse_is_longopt(
const char *arg)
173 return arg != 0 && arg[0] ==
'-' && arg[1] ==
'-' && arg[2] !=
'\0';
177 optparse_permute(
const struct optparse *
const options,
int index)
179 char *nonoption = options->argv[index];
182 for (i = index; i < options->optind - 1; i++)
183 options->argv[i] = options->argv[i + 1];
185 options->argv[options->optind - 1] = nonoption;
189 optparse_argtype(
const char *optstring,
char c)
191 int count = OPTPARSE_NONE;
196 for (; *optstring && c != *optstring; optstring++)
202 if (optstring[1] ==
':')
203 count += optstring[2] ==
':' ? 2 : 1;
214 char *option = options->argv[options->optind];
220 if (optparse_is_dashdash(option)) {
225 if (!optparse_is_shortopt(option)) {
226 if (options->permute) {
227 int index = options->optind;
231 int r =
optparse(options, optstring);
233 optparse_permute(options, index);
241 options->errmsg[0] =
'\0';
244 option += options->subopt + 1;
245 options->optopt = option[0];
246 type = optparse_argtype(optstring, option[0]);
247 next = options->argv[options->optind + 1];
249 char str[2] = {0, 0};
251 str[0] = options->optopt;
256 return optparse_error(options, OPTPARSE_MSG_INVALID, str);
265 case OPTPARSE_REQUIRED:
269 options->optarg = option + 1;
270 }
else if (next != 0) {
271 options->optarg = next;
275 return optparse_error(options, OPTPARSE_MSG_MISSING, str);
278 case OPTPARSE_OPTIONAL:
282 options->optarg = option + 1;
287 return optparse_error(options, OPTPARSE_MSG_UNKNOWN, str);
293 optparse_arg(
struct optparse *options)
295 char *option = options->argv[options->optind];
305 optparse_longopts_end(
const struct optparse_long *longopts,
int i)
307 return !longopts[i].longname && !longopts[i].shortname;
311 optparse_from_long(
const struct optparse_long *longopts,
char *optstring)
316 for (i = 0; !optparse_longopts_end(longopts, i); i++) {
317 if (longopts[i].shortname && longopts[i].shortname < 127) {
320 *p++ = longopts[i].shortname;
321 for (a = 0; a < (int)longopts[i].argtype; a++)
331 optparse_longopts_match(
const char *longname,
const char *option)
333 const char *a = option;
334 const char *n = longname;
339 for (; *a && *n && *a !=
'='; a++, n++) {
344 return *n ==
'\0' && (*a ==
'\0' || *a ==
'=');
349 optparse_longopts_arg(
char *option)
351 for (; *option && *option !=
'='; option++)
361 optparse_long_fallback(
struct optparse *options,
366 char optstring[96 * 3 + 1];
368 optparse_from_long(longopts, optstring);
369 result =
optparse(options, optstring);
379 for (
int i = 0; !optparse_longopts_end(longopts, i); i++) {
380 if (longopts[i].shortname == options->optopt)
395 char *option = options->argv[options->optind];
400 if (optparse_is_dashdash(option)) {
405 if (optparse_is_shortopt(option))
406 return optparse_long_fallback(options, longopts, longindex);
408 if (!optparse_is_longopt(option)) {
409 if (options->permute) {
410 int index = options->optind;
416 optparse_permute(options, index);
425 options->errmsg[0] =
'\0';
431 for (i = 0; !optparse_longopts_end(longopts, i); i++) {
432 const char *name = longopts[i].longname;
434 if (optparse_longopts_match(name, option)) {
440 options->optopt = longopts[i].shortname;
441 arg = optparse_longopts_arg(option);
443 if (longopts[i].argtype == OPTPARSE_NONE && arg != 0)
444 return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
447 options->optarg = arg;
448 return options->optopt;
452 if (longopts[i].argtype == OPTPARSE_REQUIRED) {
453 options->optarg = options->argv[options->optind];
455 if (options->optarg == 0)
456 return optparse_error(options, OPTPARSE_MSG_MISSING, name);
461 return options->optopt;
465 return optparse_error(options, OPTPARSE_MSG_INVALID, option);