xref: /OK3568_Linux_fs/external/rockit/mpi/example/common/test_comm_argparse.cpp (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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