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