1#include "argparse/libargparse.h"
2
3#if __MOS_MINIMAL_LIBC__
4#include <mos_stdio.h>
5#else
6#include <stdio.h>
7#endif
8
9#define ARGPARSE_MSG_INVALID "invalid option"
10#define ARGPARSE_MSG_MISSING "option requires an argument"
11#define ARGPARSE_MSG_TOOMANY "option takes no arguments"
12
13static int argparse_error(argparse_state_t *state, const char *msg, const char *data)
14{
15 unsigned p = 0;
16 const char *sep = " -- '";
17 while (*msg)
18 state->errmsg[p++] = *msg++;
19 while (*sep)
20 state->errmsg[p++] = *sep++;
21 while (p < sizeof(state->errmsg) - 2 && *data)
22 state->errmsg[p++] = *data++;
23 state->errmsg[p++] = '\'';
24 state->errmsg[p++] = '\0';
25 return '?';
26}
27
28void argparse_init(argparse_state_t *state, const char *argv[])
29{
30 state->argv = argv;
31 state->permute = 1;
32 state->optind = argv[0] != 0;
33 state->subopt = 0;
34 state->optarg = 0;
35 state->errmsg[0] = '\0';
36}
37
38static int argparse_is_dashdash(const char *arg)
39{
40 return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
41}
42
43static int argparse_is_shortopt(const char *arg)
44{
45 return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
46}
47
48static int argparse_is_longopt(const char *arg)
49{
50 return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
51}
52
53static void argparse_permute(argparse_state_t *state, int index)
54{
55 const char *nonoption = state->argv[index];
56 int i;
57 for (i = index; i < state->optind - 1; i++)
58 state->argv[i] = state->argv[i + 1];
59 state->argv[state->optind - 1] = nonoption;
60}
61
62static int argparse_get_argtype(const char *optstring, char c)
63{
64 int count = ARGPARSE_NONE;
65 if (c == ':')
66 return -1;
67 for (; *optstring && c != *optstring; optstring++)
68 ;
69 if (!*optstring)
70 return -1;
71 if (optstring[1] == ':')
72 count += optstring[2] == ':' ? 2 : 1;
73 return count;
74}
75
76int argparse(argparse_state_t *state, const char *optstring)
77{
78 int type;
79 const char *next;
80 const char *option = state->argv[state->optind];
81 state->errmsg[0] = '\0';
82 state->optopt = 0;
83 state->optarg = 0;
84 if (option == 0)
85 {
86 return -1;
87 }
88 else if (argparse_is_dashdash(arg: option))
89 {
90 state->optind++; /* consume "--" */
91 return -1;
92 }
93 else if (!argparse_is_shortopt(arg: option))
94 {
95 if (state->permute)
96 {
97 int index = state->optind++;
98 int r = argparse(state, optstring);
99 argparse_permute(state, index);
100 state->optind--;
101 return r;
102 }
103 else
104 {
105 return -1;
106 }
107 }
108 option += state->subopt + 1;
109 state->optopt = option[0];
110 type = argparse_get_argtype(optstring, c: option[0]);
111 next = state->argv[state->optind + 1];
112 switch (type)
113 {
114 case -1:
115 {
116 char str[2] = { 0, 0 };
117 str[0] = option[0];
118 state->optind++;
119 return argparse_error(state, ARGPARSE_MSG_INVALID, data: str);
120 }
121 case ARGPARSE_NONE:
122 if (option[1])
123 {
124 state->subopt++;
125 }
126 else
127 {
128 state->subopt = 0;
129 state->optind++;
130 }
131 return option[0];
132 case ARGPARSE_REQUIRED:
133 state->subopt = 0;
134 state->optind++;
135 if (option[1])
136 {
137 state->optarg = option + 1;
138 }
139 else if (next != 0)
140 {
141 state->optarg = next;
142 state->optind++;
143 }
144 else
145 {
146 char str[2] = { 0, 0 };
147 str[0] = option[0];
148 state->optarg = 0;
149 return argparse_error(state, ARGPARSE_MSG_MISSING, data: str);
150 }
151 return option[0];
152 case ARGPARSE_OPTIONAL:
153 state->subopt = 0;
154 state->optind++;
155 if (option[1])
156 state->optarg = option + 1;
157 else
158 state->optarg = 0;
159 return option[0];
160 }
161 return 0;
162}
163
164const char *argparse_arg(argparse_state_t *options)
165{
166 const char *option = options->argv[options->optind];
167 options->subopt = 0;
168 if (option != 0)
169 options->optind++;
170 return option;
171}
172
173static int argparse_longopts_end(const argparse_arg_t *longopts, int i)
174{
175 return !longopts[i].full && !longopts[i].abbr;
176}
177
178static void argparse_from_long(const argparse_arg_t *longopts, char *optstring)
179{
180 char *p = optstring;
181 int i;
182 for (i = 0; !argparse_longopts_end(longopts, i); i++)
183 {
184 if (longopts[i].abbr && longopts[i].abbr < 127)
185 {
186 int a;
187 *p++ = longopts[i].abbr;
188 for (a = 0; a < (int) longopts[i].argtype; a++)
189 *p++ = ':';
190 }
191 }
192 *p = '\0';
193}
194
195/* Unlike strcmp(), handles options containing "=". */
196static int argparse_longopts_match(const char *longname, const char *option)
197{
198 const char *a = option, *n = longname;
199 if (longname == 0)
200 return 0;
201 for (; *a && *n && *a != '='; a++, n++)
202 if (*a != *n)
203 return 0;
204 return *n == '\0' && (*a == '\0' || *a == '=');
205}
206
207/* Return the part after "=", or NULL. */
208static const char *argparse_longopts_arg(const char *option)
209{
210 for (; *option && *option != '='; option++)
211 ;
212 if (*option == '=')
213 return option + 1;
214 else
215 return 0;
216}
217
218static int argparse_long_fallback(argparse_state_t *options, const argparse_arg_t *longopts, int *longindex)
219{
220 int result;
221 char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
222 argparse_from_long(longopts, optstring);
223 result = argparse(state: options, optstring);
224 if (longindex != 0)
225 {
226 *longindex = -1;
227 if (result != -1)
228 {
229 int i;
230 for (i = 0; !argparse_longopts_end(longopts, i); i++)
231 if (longopts[i].abbr == options->optopt)
232 *longindex = i;
233 }
234 }
235 return result;
236}
237
238int argparse_long(argparse_state_t *options, const argparse_arg_t *longopts, int *longindex)
239{
240 int i;
241 const char *option = options->argv[options->optind];
242 if (option == 0)
243 {
244 return -1;
245 }
246 else if (argparse_is_dashdash(arg: option))
247 {
248 options->optind++; /* consume "--" */
249 return -1;
250 }
251 else if (argparse_is_shortopt(arg: option))
252 {
253 return argparse_long_fallback(options, longopts, longindex);
254 }
255 else if (!argparse_is_longopt(arg: option))
256 {
257 if (options->permute)
258 {
259 int index = options->optind++;
260 int r = argparse_long(options, longopts, longindex);
261 argparse_permute(state: options, index);
262 options->optind--;
263 return r;
264 }
265 else
266 {
267 return -1;
268 }
269 }
270
271 /* Parse as long option. */
272 options->errmsg[0] = '\0';
273 options->optopt = 0;
274 options->optarg = 0;
275 option += 2; /* skip "--" */
276 options->optind++;
277 for (i = 0; !argparse_longopts_end(longopts, i); i++)
278 {
279 const char *name = longopts[i].full;
280 if (argparse_longopts_match(longname: name, option))
281 {
282 const char *arg;
283 if (longindex)
284 *longindex = i;
285 options->optopt = longopts[i].abbr;
286 arg = argparse_longopts_arg(option);
287 if (longopts[i].argtype == ARGPARSE_NONE && arg != 0)
288 {
289 return argparse_error(state: options, ARGPARSE_MSG_TOOMANY, data: name);
290 }
291 if (arg != 0)
292 {
293 options->optarg = arg;
294 }
295 else if (longopts[i].argtype == ARGPARSE_REQUIRED)
296 {
297 options->optarg = options->argv[options->optind];
298 if (options->optarg == 0)
299 return argparse_error(state: options, ARGPARSE_MSG_MISSING, data: name);
300 else
301 options->optind++;
302 }
303 return options->optopt;
304 }
305 }
306 return argparse_error(state: options, ARGPARSE_MSG_INVALID, data: option);
307}
308
309void argparse_usage(argparse_state_t *options, const argparse_arg_t *args, const char *usage)
310{
311 fprintf(stream: stderr, format: "Usage: %s, %s\n", options->argv[0], usage);
312 static const int help_indent = 24;
313
314 for (int i = 0; !argparse_longopts_end(longopts: args, i); i++)
315 {
316 const char *name = args[i].full;
317 const char abbr = args[i].abbr;
318 const char *help = args[i].help ? args[i].help : "";
319 const char *arg = args[i].argtype == ARGPARSE_NONE ? "" : args[i].argtype == ARGPARSE_REQUIRED ? " ARG" : " [ARG]";
320
321 int printed = 0;
322 if (abbr)
323 printed += fprintf(stream: stderr, format: " -%c", abbr);
324
325 if (name)
326 printed += fprintf(stream: stderr, format: ", --%s%s", name, arg);
327 else
328 printed++;
329
330 if (printed < help_indent)
331 fprintf(stream: stderr, format: "%*s", help_indent - printed, "");
332 else
333 fprintf(stream: stderr, format: "\n%*s", help_indent, "");
334
335 fprintf(stream: stderr, format: "%s\n", help);
336 }
337
338 fprintf(stream: stderr, format: "\n");
339}
340