source: libcfa/src/parseargs.cfa @ 926d358

ADTast-experimental
Last change on this file since 926d358 was affb51b, checked in by Thierry Delisle <tdelisle@…>, 19 months ago

A few small fix to parseargs

  • Property mode set to 100644
File size: 8.2 KB
Line 
1#include "parseargs.hfa"
2
3#include <ctype.h>
4#include <stdint.h>
5#include <string.h>
6#include <errno.h>
7#include <unistd.h>
8
9extern "C" {
10        #include <getopt.h>
11        #include <sys/ioctl.h>
12
13        struct FILE;
14        extern FILE * stderr;
15        extern FILE * stdout;
16
17        extern int fileno(FILE *stream);
18
19        extern int fprintf ( FILE * stream, const char * format, ... );
20
21        extern          long long int strtoll (const char* str, char** endptr, int base);
22        extern unsigned long long int strtoull(const char* str, char** endptr, int base);
23        extern                 double strtod  (const char* str, char** endptr);
24}
25
26#include "common.hfa"
27#include "limits.hfa"
28
29#pragma GCC visibility push(default)
30
31extern int cfa_args_argc __attribute__((weak));
32extern char ** cfa_args_argv __attribute__((weak));
33extern char ** cfa_args_envp __attribute__((weak));
34
35static void usage(char * cmd, cfa_option options[], size_t opt_count, const char * usage, FILE * out)  __attribute__ ((noreturn));
36//-----------------------------------------------------------------------------
37// checking
38static void check_args(cfa_option options[], size_t opt_count) {
39        for(i; opt_count) {
40                for(j; opt_count) {
41                        if(i == j) continue;
42
43                        if( options[i].short_name != '\0'
44                        && options[i].short_name == options[j].short_name)
45                                abort("Parse Args error: two options have short name '%c' (%zu & %zu)", options[i].short_name, i, j);
46
47                        if(0 == strcmp(options[i].long_name, options[j].long_name)) abort("Parse Args error: two options have long name '%s' (%zu & %zu)", options[i].long_name, i, j);
48                }
49        }
50}
51
52
53//-----------------------------------------------------------------------------
54// Parsing args
55void parse_args( cfa_option options[], size_t opt_count, const char * usage, char ** & left ) {
56        if( 0p != &cfa_args_argc ) {
57                parse_args(cfa_args_argc, cfa_args_argv, options, opt_count, usage, left );
58        }
59        else {
60                char * temp = "";
61                parse_args(0, &temp, options, opt_count, usage, left );
62        }
63}
64
65void parse_args(
66        int argc,
67        char * argv[],
68        cfa_option options[],
69        size_t opt_count,
70        const char * usage,
71        char ** & left
72) {
73        check_args(options, opt_count);
74
75        int maxv = 'h';
76        char optstring[(opt_count * 3) + 2] = { '\0' };
77        {
78                int idx = 0;
79                for(i; opt_count) {
80                        if (options[i].short_name) {
81                                maxv = max(options[i].short_name, maxv);
82                                optstring[idx] = options[i].short_name;
83                                idx++;
84                                if(    ((intptr_t)options[i].parse) != ((intptr_t)parse_settrue)
85                                && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) {
86                                        optstring[idx] = ':';
87                                        idx++;
88                                }
89                        }
90                }
91                optstring[idx+0] = 'h';
92                optstring[idx+1] = '\0';
93        }
94
95        struct option optarr[opt_count + 2];
96        {
97                int idx = 0;
98                for(i; opt_count) {
99                        if(options[i].long_name) {
100                                options[i].val = (options[i].short_name != '\0') ? ((int)options[i].short_name) : ++maxv;
101                                optarr[idx].name = options[i].long_name;
102                                optarr[idx].flag = 0p;
103                                optarr[idx].val  = options[i].val;
104                                if(    ((intptr_t)options[i].parse) == ((intptr_t)parse_settrue)
105                                    || ((intptr_t)options[i].parse) == ((intptr_t)parse_setfalse) ) {
106                                        optarr[idx].has_arg = no_argument;
107                                } else {
108                                        optarr[idx].has_arg = required_argument;
109                                }
110                                idx++;
111                        }
112                }
113                optarr[idx+0].[name, has_arg, flag, val] = ["help", no_argument, 0, 'h'];
114                optarr[idx+1].[name, has_arg, flag, val] = [0, no_argument, 0, 0];
115        }
116
117        FILE * out = stderr;
118        NEXT_ARG:
119        for() {
120                int idx = 0;
121                int opt = getopt_long(argc, argv, optstring, optarr, &idx);
122                switch(opt) {
123                        case -1:
124                                if(&left != 0p) left = argv + optind;
125                                return;
126                        case 'h':
127                                out = stdout;
128                        case '?':
129                                usage(argv[0], options, opt_count, usage, out);
130                        default:
131                                for(i; opt_count) {
132                                        if(opt == options[i].val) {
133                                                const char * arg = optarg ? optarg : "";
134                                                if( arg[0] == '=' ) { arg++; }
135                                                bool success = options[i].parse( arg, options[i].variable );
136                                                if(success) continue NEXT_ARG;
137
138                                                fprintf(out, "Argument '%s' for option %c could not be parsed\n\n", arg, (char)opt);
139                                                usage(argv[0], options, opt_count, usage, out);
140                                        }
141                                }
142                                abort("Internal parse arg error\n");
143                }
144
145        }
146}
147
148//-----------------------------------------------------------------------------
149// Print usage
150static void printopt(FILE * out, int width, int max, char sn, const char * ln, const char * help) {
151        int hwidth = max - (11 + width);
152        if(hwidth <= 0) hwidth = max;
153
154        char sname[4] = { ' ', ' ', ' ', '\0' };
155        if(sn != '\0') {
156                sname[0] = '-';
157                sname[1] = sn;
158                sname[2] = ',';
159        }
160
161        fprintf(out, "  %s --%-*s   %.*s\n", sname, width, ln, hwidth, help);
162        for() {
163                help += min(strlen(help), hwidth);
164                if('\0' == *help) break;
165                fprintf(out, "%*s%.*s\n", width + 11, "", hwidth, help);
166        }
167}
168
169void print_args_usage(cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn)) {
170        usage(cfa_args_argv[0], options, opt_count, usage, error ? stderr : stdout);
171}
172
173void print_args_usage(int , char * argv[], cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn)) {
174        usage(argv[0], options, opt_count, usage, error ? stderr : stdout);
175}
176
177static void usage(char * cmd, cfa_option options[], size_t opt_count, const char * help, FILE * out) __attribute__((noreturn)) {
178        int width = 0;
179        {
180                for(i; opt_count) {
181                        if(options[i].long_name) {
182                                int w = strlen(options[i].long_name);
183                                if(w > width) width = w;
184                        }
185                }
186        }
187
188        int max_width = 1_000_000;
189        int outfd = fileno(out);
190        if(isatty(outfd)) {
191                struct winsize size;
192                int ret = ioctl(outfd, TIOCGWINSZ, &size);
193                if(ret < 0) abort( "ioctl error: (%d) %s\n", (int)errno, strerror(errno) );
194                max_width = size.ws_col;
195        }
196
197        fprintf(out, "Usage:\n  %s %s\n", cmd, help);
198
199        for(i; opt_count) {
200                printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help);
201        }
202        fprintf(out, "  -%c, --%-*s   %s\n", 'h', width, "help", "print this help message");
203        exit(out == stdout ? 0 : 1);
204}
205
206//-----------------------------------------------------------------------------
207// Typed argument parsing
208bool parse_yesno(const char * arg, bool & value ) {
209        if(strcmp(arg, "yes") == 0) {
210                value = true;
211                return true;
212        }
213
214        if(strcmp(arg, "Y") == 0) {
215                value = true;
216                return true;
217        }
218
219        if(strcmp(arg, "y") == 0) {
220                value = true;
221                return true;
222        }
223
224        if(strcmp(arg, "no") == 0) {
225                value = false;
226                return true;
227        }
228
229        if(strcmp(arg, "N") == 0) {
230                value = false;
231                return true;
232        }
233
234        if(strcmp(arg, "n") == 0) {
235                value = false;
236                return true;
237        }
238
239        return false;
240}
241
242bool parse_truefalse(const char * arg, bool & value) {
243        if(strcmp(arg, "true") == 0) {
244                value = true;
245                return true;
246        }
247
248        if(strcmp(arg, "false") == 0) {
249                value = false;
250                return true;
251        }
252
253        return false;
254}
255
256bool parse_settrue (const char *, bool & value ) {
257        value = true;
258        return true;
259}
260
261bool parse_setfalse(const char *, bool & value )  {
262        value = false;
263        return true;
264}
265
266bool parse(const char * arg, const char * & value ) {
267        value = arg;
268        return true;
269}
270
271bool parse(const char * arg, int & value) {
272        char * end;
273
274        errno = 0;
275        long long int r = strtoll(arg, &end, 0);
276        if(errno) return false;
277        if(*end != '\0') return false;
278        if(r > (int)MAX) return false;
279        if(r < (int)MIN) return false;
280
281        value = r;
282        return true;
283}
284
285static unsigned long long int strict_strtoull( const char * arg, int base) {
286        errno = 0;
287        {
288                const char * in = arg;
289                for() {
290                        if('\0' == *in) {
291                                errno = EINVAL;
292                                return 0;
293                        }
294                        if(!isspace(*in)) break;
295                        in++;
296                }
297                if(!isdigit(*in)) {
298                        errno = EINVAL;
299                        return 0;
300                }
301        }
302
303        *char end;
304        unsigned long long int r = strtoull(arg, &end, base);
305        if(*end != '\0') errno = EINVAL;
306        if(errno) return 0;
307        return r;
308}
309
310bool parse(const char * arg, unsigned & value) {
311        unsigned long long int r = strict_strtoull(arg, 0);
312        if(errno) return false;
313        if(r > (unsigned)MAX) return false;
314
315        value = r;
316        return true;
317}
318
319bool parse(const char * arg, unsigned long & value) {
320        unsigned long long int r = strict_strtoull(arg, 0);
321        if(errno) return false;
322        if(r > (unsigned long)MAX) return false;
323
324        value = r;
325        return true;
326}
327
328bool parse(const char * arg, unsigned long long & value) {
329        unsigned long long int r = strict_strtoull(arg, 0);
330        if(errno) return false;
331        if(r > (unsigned long long)MAX) return false;
332
333        value = r;
334        return true;
335}
336
337bool parse(const char * arg, double & value) {
338        char * end;
339        double r = strtod(arg, &end);
340        if(*end != '\0') return false;
341
342        value = r;
343        return true;
344}
Note: See TracBrowser for help on using the repository browser.