source: libcfa/src/parseargs.cfa @ 5ce7f4a

ADTast-experimentalpthread-emulationqualifiedEnum
Last change on this file since 5ce7f4a was 789f279, checked in by Thierry Delisle <tdelisle@…>, 3 years ago

More standard lib visibility

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