EM-ODP  3.7.0
Event Machine on ODP
optparse.h
1 /* Optparse --- portable, reentrant, embeddable, getopt-like option parser
2  *
3  * This is free and unencumbered software released into the public domain.
4  *
5  * To get the implementation, define OPTPARSE_IMPLEMENTATION.
6  * Optionally define OPTPARSE_API to control the API's visibility
7  * and/or linkage (static, __attribute__, __declspec).
8  *
9  * The POSIX getopt() option parser has three fatal flaws. These flaws
10  * are solved by Optparse.
11  *
12  * 1) Parser state is stored entirely in global variables, some of
13  * which are static and inaccessible. This means only one thread can
14  * use getopt(). It also means it's not possible to recursively parse
15  * nested sub-arguments while in the middle of argument parsing.
16  * Optparse fixes this by storing all state on a local struct.
17  *
18  * 2) The POSIX standard provides no way to properly reset the parser.
19  * This means for portable code that getopt() is only good for one
20  * run, over one argv with one option string. It also means subcommand
21  * options cannot be processed with getopt(). Most implementations
22  * provide a method to reset the parser, but it's not portable.
23  * Optparse provides an optparse_arg() function for stepping over
24  * subcommands and continuing parsing of options with another option
25  * string. The Optparse struct itself can be passed around to
26  * subcommand handlers for additional subcommand option parsing. A
27  * full reset can be achieved by with an additional optparse_init().
28  *
29  * 3) Error messages are printed to stderr. This can be disabled with
30  * opterr, but the messages themselves are still inaccessible.
31  * Optparse solves this by writing an error message in its errmsg
32  * field. The downside to Optparse is that this error message will
33  * always be in English rather than the current locale.
34  *
35  * Optparse should be familiar with anyone accustomed to getopt(), and
36  * it could be a nearly drop-in replacement. The option string is the
37  * same and the fields have the same names as the getopt() global
38  * variables (optarg, optind, optopt).
39  *
40  * Optparse also supports GNU-style long options with optparse_long().
41  * The interface is slightly different and simpler than getopt_long().
42  *
43  * By default, argv is permuted as it is parsed, moving non-option
44  * arguments to the end. This can be disabled by setting the `permute`
45  * field to 0 after initialization.
46  */
47 
48 #ifndef MISC_OPTPARSE_H
49 #define MISC_OPTPARSE_H
50 
51 #ifndef OPTPARSE_API
52 # define OPTPARSE_API
53 #endif
54 
55 struct optparse {
56  char **argv;
57  int permute;
58  int optind;
59  int optopt;
60  char *optarg;
61  char errmsg[64];
62  int subopt;
63 };
64 
65 enum optparse_argtype {
66  OPTPARSE_NONE,
67  OPTPARSE_REQUIRED,
68  OPTPARSE_OPTIONAL
69 };
70 
71 struct optparse_long {
72  const char *longname;
73  int shortname;
74  enum optparse_argtype argtype;
75 };
76 
77 /**
78  * Initializes the parser state.
79  */
80 OPTPARSE_API
81 void optparse_init(struct optparse *options, char **argv);
82 
83 /**
84  * Read the next option in the argv array.
85  * @param optstring a getopt()-formatted option string.
86  * @return the next option character, -1 for done, or '?' for error
87  *
88  * Just like getopt(), a character followed by no colons means no
89  * argument. One colon means the option has a required argument. Two
90  * colons means the option takes an optional argument.
91  */
92 OPTPARSE_API
93 int optparse(struct optparse *options, const char *optstring);
94 
95 /**
96  * Handles GNU-style long options in addition to getopt() options.
97  * This works a lot like GNU's getopt_long(). The last option in
98  * longopts must be all zeros, marking the end of the array. The
99  * longindex argument may be NULL.
100  */
101 OPTPARSE_API
102 int optparse_long(struct optparse *options,
103  const struct optparse_long *longopts,
104  int *longindex);
105 
106 /**
107  * Used for stepping over non-option arguments.
108  * @return the next non-option argument, or NULL for no more arguments
109  *
110  * Argument parsing can continue with optparse() after using this
111  * function. That would be used to parse the options for the
112  * subcommand returned by optparse_arg(). This function allows you to
113  * ignore the value of optind.
114  */
115 OPTPARSE_API
116 char *optparse_arg(struct optparse *options);
117 
118 /* Implementation */
119 #ifdef OPTPARSE_IMPLEMENTATION
120 
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"
125 
126 static int
127 optparse_error(struct optparse *options, const char *msg, const char *data)
128 {
129  unsigned int p = 0;
130  const char *sep = " -- '";
131 
132  while (*msg)
133  options->errmsg[p++] = *msg++;
134 
135  while (*sep)
136  options->errmsg[p++] = *sep++;
137 
138  while (p < sizeof(options->errmsg) - 2 && *data)
139  options->errmsg[p++] = *data++;
140 
141  options->errmsg[p++] = '\'';
142  options->errmsg[p++] = '\0';
143  return '?';
144 }
145 
146 OPTPARSE_API
147 void
148 optparse_init(struct optparse *options, char **argv)
149 {
150  options->argv = argv;
151  options->permute = 1;
152  options->optind = argv[0] != 0;
153  options->subopt = 0;
154  options->optarg = 0;
155  options->errmsg[0] = '\0';
156 }
157 
158 static int
159 optparse_is_dashdash(const char *arg)
160 {
161  return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
162 }
163 
164 static int
165 optparse_is_shortopt(const char *arg)
166 {
167  return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
168 }
169 
170 static int
171 optparse_is_longopt(const char *arg)
172 {
173  return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
174 }
175 
176 static void
177 optparse_permute(const struct optparse * const options, int index)
178 {
179  char *nonoption = options->argv[index];
180  int i;
181 
182  for (i = index; i < options->optind - 1; i++)
183  options->argv[i] = options->argv[i + 1];
184 
185  options->argv[options->optind - 1] = nonoption;
186 }
187 
188 static int
189 optparse_argtype(const char *optstring, char c)
190 {
191  int count = OPTPARSE_NONE;
192 
193  if (c == ':')
194  return -1;
195 
196  for (; *optstring && c != *optstring; optstring++)
197  ;
198 
199  if (!*optstring)
200  return -1;
201 
202  if (optstring[1] == ':')
203  count += optstring[2] == ':' ? 2 : 1;
204 
205  return count;
206 }
207 
208 OPTPARSE_API
209 int
210 optparse(struct optparse *options, const char *optstring)
211 {
212  int type;
213  char *next;
214  char *option = options->argv[options->optind];
215 
216  if (option == 0)
217  return -1;
218 
219  /* "--" forces an end of option-scanning */
220  if (optparse_is_dashdash(option)) {
221  options->optind++; /* consume "--" */
222  return -1;
223  }
224 
225  if (!optparse_is_shortopt(option)) {
226  if (options->permute) {
227  int index = options->optind;
228 
229  options->optind++;
230 
231  int r = optparse(options, optstring);
232 
233  optparse_permute(options, index);
234  options->optind--;
235  return r;
236  }
237 
238  return -1;
239  }
240 
241  options->errmsg[0] = '\0';
242  options->optopt = 0;
243  options->optarg = 0;
244  option += options->subopt + 1; /* skip "-" */
245  options->optopt = option[0];
246  type = optparse_argtype(optstring, option[0]);
247  next = options->argv[options->optind + 1];
248 
249  char str[2] = {0, 0};
250 
251  str[0] = options->optopt;
252 
253  switch (type) {
254  case -1:
255  options->optind++;
256  return optparse_error(options, OPTPARSE_MSG_INVALID, str);
257  case OPTPARSE_NONE:
258  if (option[1]) {
259  options->subopt++;
260  } else {
261  options->subopt = 0;
262  options->optind++;
263  }
264  return option[0];
265  case OPTPARSE_REQUIRED:
266  options->subopt = 0;
267  options->optind++;
268  if (option[1]) {
269  options->optarg = option + 1;
270  } else if (next != 0) {
271  options->optarg = next;
272  options->optind++;
273  } else {
274  options->optarg = 0;
275  return optparse_error(options, OPTPARSE_MSG_MISSING, str);
276  }
277  return option[0];
278  case OPTPARSE_OPTIONAL:
279  options->subopt = 0;
280  options->optind++;
281  if (option[1])
282  options->optarg = option + 1;
283  else
284  options->optarg = 0;
285  return option[0];
286  default:
287  return optparse_error(options, OPTPARSE_MSG_UNKNOWN, str);
288  }
289 }
290 
291 OPTPARSE_API
292 char *
293 optparse_arg(struct optparse *options)
294 {
295  char *option = options->argv[options->optind];
296 
297  options->subopt = 0;
298 
299  if (option != 0)
300  options->optind++;
301  return option;
302 }
303 
304 static int
305 optparse_longopts_end(const struct optparse_long *longopts, int i)
306 {
307  return !longopts[i].longname && !longopts[i].shortname;
308 }
309 
310 static void
311 optparse_from_long(const struct optparse_long *longopts, char *optstring)
312 {
313  char *p = optstring;
314  int i;
315 
316  for (i = 0; !optparse_longopts_end(longopts, i); i++) {
317  if (longopts[i].shortname && longopts[i].shortname < 127) {
318  int a;
319 
320  *p++ = longopts[i].shortname;
321  for (a = 0; a < (int)longopts[i].argtype; a++)
322  *p++ = ':';
323  }
324  }
325 
326  *p = '\0';
327 }
328 
329 /* Unlike strcmp(), handles options containing "=". */
330 static int
331 optparse_longopts_match(const char *longname, const char *option)
332 {
333  const char *a = option;
334  const char *n = longname;
335 
336  if (longname == 0)
337  return 0;
338 
339  for (; *a && *n && *a != '='; a++, n++) {
340  if (*a != *n)
341  return 0;
342  }
343 
344  return *n == '\0' && (*a == '\0' || *a == '=');
345 }
346 
347 /* Return the part after "=", or NULL. */
348 static char *
349 optparse_longopts_arg(char *option)
350 {
351  for (; *option && *option != '='; option++)
352  ;
353 
354  if (*option == '=')
355  return option + 1;
356  else
357  return 0;
358 }
359 
360 static int
361 optparse_long_fallback(struct optparse *options,
362  const struct optparse_long *longopts,
363  int *longindex)
364 {
365  int result;
366  char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
367 
368  optparse_from_long(longopts, optstring);
369  result = optparse(options, optstring);
370 
371  /* longindex is NULL*/
372  if (!longindex)
373  return result;
374 
375  /* longindex is not NULL*/
376  *longindex = -1; /* Default is -1 */
377 
378  if (result != -1) {
379  for (int i = 0; !optparse_longopts_end(longopts, i); i++) {
380  if (longopts[i].shortname == options->optopt)
381  *longindex = i;
382  }
383  }
384 
385  return result;
386 }
387 
388 OPTPARSE_API
389 int
390 optparse_long(struct optparse *options,
391  const struct optparse_long *longopts,
392  int *longindex)
393 {
394  int i;
395  char *option = options->argv[options->optind];
396 
397  if (option == 0)
398  return -1;
399 
400  if (optparse_is_dashdash(option)) {
401  options->optind++; /* consume "--" */
402  return -1;
403  }
404 
405  if (optparse_is_shortopt(option))
406  return optparse_long_fallback(options, longopts, longindex);
407 
408  if (!optparse_is_longopt(option)) {
409  if (options->permute) {
410  int index = options->optind;
411 
412  options->optind++;
413 
414  int r = optparse_long(options, longopts, longindex);
415 
416  optparse_permute(options, index);
417  options->optind--;
418  return r;
419  }
420 
421  return -1;
422  }
423 
424  /* Parse as long option. */
425  options->errmsg[0] = '\0';
426  options->optopt = 0;
427  options->optarg = 0;
428  option += 2; /* skip "--" */
429  options->optind++;
430 
431  for (i = 0; !optparse_longopts_end(longopts, i); i++) {
432  const char *name = longopts[i].longname;
433 
434  if (optparse_longopts_match(name, option)) {
435  char *arg;
436 
437  if (longindex)
438  *longindex = i;
439 
440  options->optopt = longopts[i].shortname;
441  arg = optparse_longopts_arg(option);
442 
443  if (longopts[i].argtype == OPTPARSE_NONE && arg != 0)
444  return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
445 
446  if (arg != 0) {
447  options->optarg = arg;
448  return options->optopt;
449  }
450 
451  /* arg is NULL */
452  if (longopts[i].argtype == OPTPARSE_REQUIRED) {
453  options->optarg = options->argv[options->optind];
454 
455  if (options->optarg == 0)
456  return optparse_error(options, OPTPARSE_MSG_MISSING, name);
457 
458  options->optind++;
459  }
460 
461  return options->optopt;
462  }
463  }
464 
465  return optparse_error(options, OPTPARSE_MSG_INVALID, option);
466 }
467 
468 #endif /* OPTPARSE_IMPLEMENTATION */
469 #endif /* MISC_OPTPARSE_H */
optparse
Definition: optparse.h:55
optparse_long
Definition: optparse.h:71