1 /**
2 * Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
3 * All rights reserved.
4 *
5 * Use of this source code is governed by a MIT-style license that can be found
6 * in the LICENSE file.
7 *
8 * module: argarse, developped by cofyc.jackson
9 * project: https://github.com/cofyc/argparse
10 *
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <assert.h>
17 #include <errno.h>
18 #include "test_comm_argparse.h"
19
20 #define OPT_UNSET 1
21 #define OPT_LONG (1 << 1)
22
prefix_skip(const char * str,const char * prefix)23 static const char* prefix_skip(const char *str, const char *prefix) {
24 size_t len = strlen(prefix);
25 return strncmp(str, prefix, len) ? NULL : str + len;
26 }
27
prefix_cmp(const char * str,const char * prefix)28 static int prefix_cmp(const char *str, const char *prefix) {
29 for (;; str++, prefix++) {
30 if (!*prefix) {
31 return 0;
32 } else if (*str != *prefix) {
33 return (unsigned char)*prefix - (unsigned char)*str;
34 }
35 }
36 }
37
argparse_error(struct argparse * self,const struct argparse_option * opt,const char * reason,int flags)38 static void argparse_error(struct argparse *self, const struct argparse_option *opt,
39 const char *reason, int flags) {
40 (void)self;
41 if (flags & OPT_LONG) {
42 fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason);
43 } else {
44 fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason);
45 }
46 exit(1);
47 }
48
argparse_getvalue(struct argparse * self,const struct argparse_option * opt,int flags)49 static int argparse_getvalue(struct argparse *self, const struct argparse_option *opt, int flags) {
50 const char *s = NULL;
51 if (!opt->value)
52 goto skipped;
53 switch (opt->type) {
54 case ARGPARSE_OPT_BOOLEAN:
55 if (flags & OPT_UNSET) {
56 *(int *)opt->value = *(int *)opt->value - 1; // NOLINT
57 } else {
58 *(int *)opt->value = *(int *)opt->value + 1; // NOLINT
59 }
60 if (*(int *)opt->value < 0) { // NOLINT
61 *(int *)opt->value = 0; // NOLINT
62 }
63 break;
64 case ARGPARSE_OPT_BIT:
65 if (flags & OPT_UNSET) {
66 *(int *)opt->value &= ~opt->data; // NOLINT
67 } else {
68 *(int *)opt->value |= opt->data; // NOLINT
69 }
70 break;
71 case ARGPARSE_OPT_STRING:
72 if (self->optvalue) {
73 *(const char **)opt->value = self->optvalue;
74 self->optvalue = NULL;
75 } else if (self->argc > 1) {
76 self->argc--;
77 *(const char **)opt->value = *++self->argv;
78 } else {
79 argparse_error(self, opt, "requires a value", flags);
80 }
81 break;
82 case ARGPARSE_OPT_INTEGER:
83 errno = 0;
84 if (self->optvalue) {
85 *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); // NOLINT
86 self->optvalue = NULL;
87 } else if (self->argc > 1) {
88 self->argc--;
89 *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0); // NOLINT
90 } else {
91 argparse_error(self, opt, "requires a value", flags);
92 }
93 if (errno)
94 argparse_error(self, opt, strerror(errno), flags);
95 if (s[0] != '\0')
96 argparse_error(self, opt, "expects an integer value", flags);
97 break;
98 case ARGPARSE_OPT_FLOAT:
99 errno = 0;
100 if (self->optvalue) {
101 *(float *)opt->value = strtof(self->optvalue, (char **)&s); // NOLINT
102 self->optvalue = NULL;
103 } else if (self->argc > 1) {
104 self->argc--;
105 *(float *)opt->value = strtof(*++self->argv, (char **)&s); // NOLINT
106 } else {
107 argparse_error(self, opt, "requires a value", flags);
108 }
109 if (errno)
110 argparse_error(self, opt, strerror(errno), flags);
111 if (s[0] != '\0')
112 argparse_error(self, opt, "expects a numerical value", flags);
113 break;
114 default:
115 assert(0);
116 break;
117 }
118
119 skipped:
120 if (opt->callback) {
121 return opt->callback(self, opt);
122 }
123
124 return 0;
125 }
126
argparse_options_check(const struct argparse_option * options)127 static void argparse_options_check(const struct argparse_option *options) {
128 for (; options->type != ARGPARSE_OPT_END; options++) {
129 switch (options->type) {
130 case ARGPARSE_OPT_END:
131 case ARGPARSE_OPT_BOOLEAN:
132 case ARGPARSE_OPT_BIT:
133 case ARGPARSE_OPT_INTEGER:
134 case ARGPARSE_OPT_FLOAT:
135 case ARGPARSE_OPT_STRING:
136 case ARGPARSE_OPT_GROUP:
137 continue;
138 default:
139 fprintf(stderr, "wrong option type: %d", options->type);
140 break;
141 }
142 }
143 }
144
argparse_short_opt(struct argparse * self,const struct argparse_option * options)145 static int argparse_short_opt(struct argparse *self, const struct argparse_option *options) {
146 for (; options->type != ARGPARSE_OPT_END; options++) {
147 if (options->short_name == *self->optvalue) {
148 self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL;
149 return argparse_getvalue(self, options, 0);
150 }
151 }
152 return -2;
153 }
154
argparse_long_opt(struct argparse * self,const struct argparse_option * options)155 static int argparse_long_opt(struct argparse *self, const struct argparse_option *options) {
156 for (; options->type != ARGPARSE_OPT_END; options++) {
157 const char *rest;
158 int opt_flags = 0;
159 if (!options->long_name)
160 continue;
161
162 rest = prefix_skip(self->argv[0] + 2, options->long_name);
163 if (!rest) {
164 // negation disabled?
165 if (options->flags & OPT_NONEG) {
166 continue;
167 }
168 // only OPT_BOOLEAN/OPT_BIT supports negation
169 if (options->type != ARGPARSE_OPT_BOOLEAN && options->type !=
170 ARGPARSE_OPT_BIT) {
171 continue;
172 }
173
174 if (prefix_cmp(self->argv[0] + 2, "no-")) {
175 continue;
176 }
177 rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name);
178 if (!rest)
179 continue;
180 opt_flags |= OPT_UNSET;
181 }
182 if (*rest) {
183 if (*rest != '=')
184 continue;
185 self->optvalue = rest + 1;
186 }
187 return argparse_getvalue(self, options, opt_flags | OPT_LONG);
188 }
189 return -2;
190 }
191
argparse_init(struct argparse * self,struct argparse_option * options,const char * const * usages,int flags)192 int argparse_init(struct argparse *self, struct argparse_option *options,
193 const char *const *usages, int flags) {
194 memset(self, 0, sizeof(*self));
195 self->options = options;
196 self->usages = usages;
197 self->flags = flags;
198 self->description = NULL;
199 self->epilog = NULL;
200 return 0;
201 }
202
argparse_describe(struct argparse * self,const char * description,const char * epilog)203 int argparse_describe(struct argparse *self, const char *description, const char *epilog) {
204 self->description = description;
205 self->epilog = epilog;
206 return 0;
207 }
208
argparse_parse(struct argparse * self,int argc,const char ** argv)209 int argparse_parse(struct argparse *self, int argc, const char **argv) {
210 self->argc = argc - 1;
211 self->argv = argv + 1;
212 self->out = argv;
213
214 argparse_options_check(self->options);
215
216 for (; self->argc; self->argc--, self->argv++) {
217 const char *arg = self->argv[0];
218 if (arg[0] != '-' || !arg[1]) {
219 if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) {
220 goto end;
221 }
222 // if it's not option or is a single char '-', copy verbatim
223 self->out[self->cpidx++] = self->argv[0];
224 continue;
225 }
226 // short option
227 if (arg[1] != '-') {
228 self->optvalue = arg + 1;
229 switch (argparse_short_opt(self, self->options)) {
230 case -1:
231 break;
232 case -2:
233 goto unknown;
234 }
235 while (self->optvalue) {
236 switch (argparse_short_opt(self, self->options)) {
237 case -1:
238 break;
239 case -2:
240 goto unknown;
241 }
242 }
243 continue;
244 }
245 // if '--' presents
246 if (!arg[2]) {
247 self->argc--;
248 self->argv++;
249 break;
250 }
251 // long option
252 switch (argparse_long_opt(self, self->options)) {
253 case -1:
254 break;
255 case -2:
256 goto unknown;
257 }
258 continue;
259
260 unknown:
261 fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]);
262 argparse_usage(self);
263 exit(1);
264 }
265
266 end:
267 memmove(self->out + self->cpidx, self->argv,
268 self->argc * sizeof(*self->out));
269 self->out[self->cpidx + self->argc] = NULL;
270
271 return self->cpidx + self->argc;
272 }
273
argparse_usage(struct argparse * self)274 int argparse_usage(struct argparse *self) {
275 const char *const *usages = self->usages;
276
277 if (usages) {
278 fprintf(stdout, "Usage: %s\n", *usages++);
279 while (*usages && **usages)
280 fprintf(stdout, " or: %s\n", *usages++);
281 } else {
282 fprintf(stdout, "Usage:\n");
283 }
284
285 // print description
286 if (self->description)
287 fprintf(stdout, "%s\n", self->description);
288
289 fputc('\n', stdout);
290
291 const struct argparse_option *options;
292
293 // figure out best width
294 size_t usage_opts_width = 0;
295 size_t len;
296 options = self->options;
297 for (; options->type != ARGPARSE_OPT_END; options++) {
298 len = 0;
299 if ((options)->short_name) {
300 len += 2;
301 }
302 if ((options)->short_name && (options)->long_name) {
303 len += 2; // separator ", "
304 }
305 if ((options)->long_name) {
306 len += strlen((options)->long_name) + 2;
307 }
308 if (options->type == ARGPARSE_OPT_INTEGER) {
309 len += strlen("=<int>");
310 }
311 if (options->type == ARGPARSE_OPT_FLOAT) {
312 len += strlen("=<flt>");
313 } else if (options->type == ARGPARSE_OPT_STRING) {
314 len += strlen("=<str>");
315 }
316 len = (len + 3) - ((len + 3) & 3);
317 if (usage_opts_width < len) {
318 usage_opts_width = len;
319 }
320 }
321 usage_opts_width += 4; // 4 spaces prefix
322
323 options = self->options;
324 for (; options->type != ARGPARSE_OPT_END; options++) {
325 size_t pos = 0;
326 int pad = 0;
327 if (options->type == ARGPARSE_OPT_GROUP) {
328 fputc('\n', stdout);
329 fprintf(stdout, "%s", options->help);
330 fputc('\n', stdout);
331 continue;
332 }
333 pos = fprintf(stdout, " ");
334 if (options->short_name) {
335 pos += fprintf(stdout, "-%c", options->short_name);
336 }
337 if (options->long_name && options->short_name) {
338 pos += fprintf(stdout, ", ");
339 }
340 if (options->long_name) {
341 pos += fprintf(stdout, "--%s", options->long_name);
342 }
343 if (options->type == ARGPARSE_OPT_INTEGER) {
344 pos += fprintf(stdout, "=<int>");
345 } else if (options->type == ARGPARSE_OPT_FLOAT) {
346 pos += fprintf(stdout, "=<flt>");
347 } else if (options->type == ARGPARSE_OPT_STRING) {
348 pos += fprintf(stdout, "=<str>");
349 }
350 if (pos <= usage_opts_width) {
351 pad = usage_opts_width - pos;
352 } else {
353 fputc('\n', stdout);
354 pad = usage_opts_width;
355 }
356 fprintf(stdout, "%*s%s\n", pad + 2, "", options->help);
357 }
358
359 // print epilog
360 if (self->epilog)
361 fprintf(stdout, "%s\n", self->epilog);
362
363 return 0;
364 }
365
argparse_help_cb(struct argparse * self,const struct argparse_option * option)366 int argparse_help_cb(struct argparse *self, const struct argparse_option *option) {
367 (void)option;
368 argparse_usage(self);
369 exit(0);
370 }
371