source: libcfa/src/parseargs.cfa @ 95bda0a

ADTast-experimental
Last change on this file since 95bda0a was 1c893ae, checked in by Peter A. Buhr <pabuhr@…>, 20 months ago

add assert so compiler does not generate spurious warnings

  • 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>
7#include <assert.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        assert( opt_count > 0 );
77        char optstring[opt_count * 3] = { '\0' };
78        {
79                int idx = 0;
80                for(i; opt_count) {
81                        if (options[i].short_name) {
82                                maxv = max(options[i].short_name, maxv);
83                                optstring[idx] = options[i].short_name;
84                                idx++;
85                                if(    ((intptr_t)options[i].parse) != ((intptr_t)parse_settrue)
86                                && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) {
87                                        optstring[idx] = ':';
88                                        idx++;
89                                }
90                        }
91                }
92                optstring[idx+0] = 'h';
93                optstring[idx+1] = '\0';
94        }
95
96        struct option optarr[opt_count + 2];
97        {
98                int idx = 0;
99                for(i; opt_count) {
100                        if(options[i].long_name) {
101                                options[i].val = (options[i].short_name != '\0') ? ((int)options[i].short_name) : ++maxv;
102                                optarr[idx].name = options[i].long_name;
103                                optarr[idx].flag = 0p;
104                                optarr[idx].val  = options[i].val;
105                                if(    ((intptr_t)options[i].parse) == ((intptr_t)parse_settrue)
106                                    || ((intptr_t)options[i].parse) == ((intptr_t)parse_setfalse) ) {
107                                        optarr[idx].has_arg = no_argument;
108                                } else {
109                                        optarr[idx].has_arg = required_argument;
110                                }
111                                idx++;
112                        }
113                }
114                optarr[idx+0].[name, has_arg, flag, val] = ["help", no_argument, 0, 'h'];
115                optarr[idx+1].[name, has_arg, flag, val] = [0, no_argument, 0, 0];
116        }
117
118        FILE * out = stderr;
119        NEXT_ARG:
120        for() {
121                int idx = 0;
122                int opt = getopt_long(argc, argv, optstring, optarr, &idx);
123                switch(opt) {
124                        case -1:
125                                if(&left != 0p) left = argv + optind;
126                                return;
127                        case 'h':
128                                out = stdout;
129                        case '?':
130                                usage(argv[0], options, opt_count, usage, out);
131                        default:
132                                for(i; opt_count) {
133                                        if(opt == options[i].val) {
134                                                const char * arg = optarg ? optarg : "";
135                                                if( arg[0] == '=' ) { arg++; }
136                                                bool success = options[i].parse( arg, options[i].variable );
137                                                if(success) continue NEXT_ARG;
138
139                                                fprintf(out, "Argument '%s' for option %c could not be parsed\n\n", arg, (char)opt);
140                                                usage(argv[0], options, opt_count, usage, out);
141                                        }
142                                }
143                                abort("Internal parse arg error\n");
144                }
145
146        }
147}
148
149//-----------------------------------------------------------------------------
150// Print usage
151static void printopt(FILE * out, int width, int max, char sn, const char * ln, const char * help) {
152        int hwidth = max - (11 + width);
153        if(hwidth <= 0) hwidth = max;
154
155        char sname[4] = { ' ', ' ', ' ', '\0' };
156        if(sn != '\0') {
157                sname[0] = '-';
158                sname[1] = sn;
159                sname[2] = ',';
160        }
161
162        fprintf(out, "  %s --%-*s   %.*s\n", sname, width, ln, hwidth, help);
163        for() {
164                help += min(strlen(help), hwidth);
165                if('\0' == *help) break;
166                fprintf(out, "%*s%.*s\n", width + 11, "", hwidth, help);
167        }
168}
169
170void print_args_usage(cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn)) {
171        usage(cfa_args_argv[0], options, opt_count, usage, error ? stderr : stdout);
172}
173
174void print_args_usage(int , char * argv[], cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn)) {
175        usage(argv[0], options, opt_count, usage, error ? stderr : stdout);
176}
177
178static void usage(char * cmd, cfa_option options[], size_t opt_count, const char * help, FILE * out) __attribute__((noreturn)) {
179        int width = 0;
180        {
181                for(i; opt_count) {
182                        if(options[i].long_name) {
183                                int w = strlen(options[i].long_name);
184                                if(w > width) width = w;
185                        }
186                }
187        }
188
189        int max_width = 1_000_000;
190        int outfd = fileno(out);
191        if(isatty(outfd)) {
192                struct winsize size;
193                int ret = ioctl(outfd, TIOCGWINSZ, &size);
194                if(ret < 0) abort( "ioctl error: (%d) %s\n", (int)errno, strerror(errno) );
195                max_width = size.ws_col;
196        }
197
198        fprintf(out, "Usage:\n  %s %s\n", cmd, help);
199
200        for(i; opt_count) {
201                printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help);
202        }
203        fprintf(out, "  -%c, --%-*s   %s\n", 'h', width, "help", "print this help message");
204        exit(out == stdout ? 0 : 1);
205}
206
207//-----------------------------------------------------------------------------
208// Typed argument parsing
209bool parse_yesno(const char * arg, bool & value ) {
210        if(strcmp(arg, "yes") == 0) {
211                value = true;
212                return true;
213        }
214
215        if(strcmp(arg, "Y") == 0) {
216                value = true;
217                return true;
218        }
219
220        if(strcmp(arg, "y") == 0) {
221                value = true;
222                return true;
223        }
224
225        if(strcmp(arg, "no") == 0) {
226                value = false;
227                return true;
228        }
229
230        if(strcmp(arg, "N") == 0) {
231                value = false;
232                return true;
233        }
234
235        if(strcmp(arg, "n") == 0) {
236                value = false;
237                return true;
238        }
239
240        return false;
241}
242
243bool parse_truefalse(const char * arg, bool & value) {
244        if(strcmp(arg, "true") == 0) {
245                value = true;
246                return true;
247        }
248
249        if(strcmp(arg, "false") == 0) {
250                value = false;
251                return true;
252        }
253
254        return false;
255}
256
257bool parse_settrue (const char *, bool & value ) {
258        value = true;
259        return true;
260}
261
262bool parse_setfalse(const char *, bool & value )  {
263        value = false;
264        return true;
265}
266
267bool parse(const char * arg, const char * & value ) {
268        value = arg;
269        return true;
270}
271
272bool parse(const char * arg, int & value) {
273        char * end;
274        int r = strtoll(arg, &end, 10);
275        if(*end != '\0') return false;
276
277        value = r;
278        return true;
279}
280
281bool parse(const char * arg, unsigned & value) {
282        char * end;
283        unsigned long long int r = strtoull(arg, &end, 10);
284        if(*end != '\0') return false;
285        if(r > (unsigned)MAX) return false;
286
287        value = r;
288        return true;
289}
290
291bool parse(const char * arg, unsigned long & value) {
292        char * end;
293        unsigned long long int r = strtoull(arg, &end, 10);
294        if(*end != '\0') return false;
295        if(r > (unsigned long)MAX) return false;
296
297        value = r;
298        return true;
299}
300
301bool parse(const char * arg, unsigned long long & value) {
302        char * end;
303        unsigned long long int r = strtoull(arg, &end, 10);
304        if(*end != '\0') return false;
305        if(r > (unsigned long long)MAX) return false;
306
307        value = r;
308        return true;
309}
310
311bool parse(const char * arg, double & value) {
312        char * end;
313        double r = strtod(arg, &end);
314        if(*end != '\0') return false;
315
316        value = r;
317        return true;
318}
Note: See TracBrowser for help on using the repository browser.