source: libcfa/src/parseargs.cfa @ 7d18733

ADTast-experimentalenumpthread-emulationqualifiedEnum
Last change on this file since 7d18733 was 6f94958, checked in by Thierry Delisle <tdelisle@…>, 4 years ago

fix usage printing

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