source: libcfa/src/parseargs.cfa @ 72b5805e

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

Fixes to usage and corresponding tests

  • Property mode set to 100644
File size: 9.7 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2022 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// parseargs.cfa
8// implementation of arguments parsing (argc, argv)
9//
10// Author           : Thierry Delisle
11// Created On       : Wed Oct 12 15:28:01 2022
12// Last Modified By :
13// Last Modified On :
14// Update Count     :
15//
16
17#include "parseargs.hfa"
18
19#include <assert.h>
20#include <ctype.h>
21#include <stdint.h>
22#include <string.h>
23#include <errno.h>
24#include <unistd.h>
25
26extern "C" {
27        #include <getopt.h>
28        #include <sys/ioctl.h>
29
30        struct FILE;
31        extern FILE * stderr;
32        extern FILE * stdout;
33
34        extern int fileno(FILE *stream);
35
36        extern int fprintf ( FILE * stream, const char * format, ... );
37
38        extern          long long int strtoll (const char* str, char** endptr, int base);
39        extern unsigned long long int strtoull(const char* str, char** endptr, int base);
40        extern                 double strtod  (const char* str, char** endptr);
41}
42
43#include "common.hfa"
44#include "limits.hfa"
45
46#pragma GCC visibility push(default)
47
48extern int cfa_args_argc __attribute__((weak));
49extern char ** cfa_args_argv __attribute__((weak));
50extern char ** cfa_args_envp __attribute__((weak));
51
52static void usage(char * cmd, cfa_option options[], size_t opt_count, const char * usage, FILE * out)  __attribute__ ((noreturn));
53//-----------------------------------------------------------------------------
54// checking
55static void check_args(cfa_option options[], size_t opt_count) {
56        for(i; opt_count) {
57                for(j; opt_count) {
58                        if(i == j) continue;
59
60                        if( options[i].short_name != '\0'
61                        && options[i].short_name == options[j].short_name)
62                                abort("Parse Args error: two options have short name '%c' (%zu & %zu)", options[i].short_name, i, j);
63
64                        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);
65                }
66        }
67}
68
69
70//-----------------------------------------------------------------------------
71// Parsing args
72void parse_args( cfa_option options[], size_t opt_count, const char * usage, char ** & left ) {
73        if( 0p != &cfa_args_argc ) {
74                parse_args(cfa_args_argc, cfa_args_argv, options, opt_count, usage, left );
75        }
76        else {
77                char * temp = "";
78                parse_args(0, &temp, options, opt_count, usage, left );
79        }
80}
81
82void parse_args(
83        int argc,
84        char * argv[],
85        cfa_option options[],
86        size_t opt_count,
87        const char * usage,
88        char ** & left
89) {
90        check_args(options, opt_count);
91
92        int maxv = 'h';
93        char optstring[(opt_count * 3) + 2] = { '\0' };
94        {
95                int idx = 0;
96                for(i; opt_count) {
97                        if (options[i].short_name) {
98                                maxv = max(options[i].short_name, maxv);
99                                optstring[idx] = options[i].short_name;
100                                idx++;
101                                if(    ((intptr_t)options[i].parse) != ((intptr_t)parse_settrue)
102                                && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) {
103                                        optstring[idx] = ':';
104                                        idx++;
105                                }
106                        }
107                }
108                optstring[idx+0] = 'h';
109                optstring[idx+1] = '\0';
110        }
111
112        struct option optarr[opt_count + 2];
113        {
114                int idx = 0;
115                for(i; opt_count) {
116                        if(options[i].long_name) {
117                                options[i].val = (options[i].short_name != '\0') ? ((int)options[i].short_name) : ++maxv;
118                                optarr[idx].name = options[i].long_name;
119                                optarr[idx].flag = 0p;
120                                optarr[idx].val  = options[i].val;
121                                if(    ((intptr_t)options[i].parse) == ((intptr_t)parse_settrue)
122                                    || ((intptr_t)options[i].parse) == ((intptr_t)parse_setfalse) ) {
123                                        optarr[idx].has_arg = no_argument;
124                                } else {
125                                        optarr[idx].has_arg = required_argument;
126                                }
127                                idx++;
128                        }
129                }
130                optarr[idx+0].[name, has_arg, flag, val] = ["help", no_argument, 0, 'h'];
131                optarr[idx+1].[name, has_arg, flag, val] = [0, no_argument, 0, 0];
132        }
133
134        FILE * out = stderr;
135        NEXT_ARG:
136        for() {
137                int idx = 0;
138                int opt = getopt_long(argc, argv, optstring, optarr, &idx);
139                switch(opt) {
140                        case -1:
141                                if(&left != 0p) left = argv + optind;
142                                return;
143                        case 'h':
144                                out = stdout;
145                        case '?':
146                                usage(argv[0], options, opt_count, usage, out);
147                        default:
148                                for(i; opt_count) {
149                                        if(opt == options[i].val) {
150                                                const char * arg = optarg ? optarg : "";
151                                                if( arg[0] == '=' ) { arg++; }
152                                                bool success = options[i].parse( arg, options[i].variable );
153                                                if(success) continue NEXT_ARG;
154
155                                                fprintf(out, "Argument '%s' for option %c could not be parsed\n\n", arg, (char)opt);
156                                                usage(argv[0], options, opt_count, usage, out);
157                                        }
158                                }
159                                abort("Internal parse arg error\n");
160                }
161
162        }
163}
164
165static inline int next_newline(const char * str) {
166        int ret;
167        const char * ptr = strstr(str, "\n");
168        if(!ptr) return MAX;
169
170        /* paranoid */ verify( str <= ptr);
171        intptr_t low = (intptr_t)str;
172        intptr_t hi  = (intptr_t)ptr;
173        ret = hi - low;
174
175        return ret;
176}
177
178//-----------------------------------------------------------------------------
179// Print usage
180static void printopt(FILE * out, int width, int max, char sn, const char * ln, const char * help) {
181        // check how wide we should be printing
182        // this includes all options and the help message
183        int hwidth = max - (11 + width);
184        if(hwidth <= 0) hwidth = max;
185
186        // check which pieces we have
187        bool has_ln = ln && strcmp("", ln);
188        bool has_help = help && strcmp("", help);
189
190        // print the small name if present
191        if(sn != '\0') fprintf(out, "  -%c", sn);
192        else fprintf(out, "    ");
193
194        // print a comma if we have both short and long names
195        if(sn != '\0' && has_ln) fprintf(out, ", ");
196        else fprintf(out, "  ");
197
198        // print the long name if present
199        if(has_ln)        fprintf(out, "--%-*s", width, ln);
200        else if(has_help) fprintf(out, "  %-*s", width, "");
201
202        if(has_help) {
203                // print the help
204                // We need to wrap at the max width, and also indent newlines so everything is nice and pretty
205
206                // for each line to print
207                for() {
208                        //find out if there is a newline
209                        int nextnl = next_newline(help);
210                        int real = min(min(strlen(help), hwidth), nextnl);
211
212                        fprintf(out, "   %.*s", real, help);
213                        // printf("%d %d\n", real, nextnl);
214                        help += real;
215                        if( nextnl == real ) help++;
216                        if('\0' == *help) break;
217                        fprintf(out, "\n%*s", width + 8, "");
218                }
219        }
220        fprintf(out, "\n");
221}
222
223void print_args_usage(cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn)) {
224        usage(cfa_args_argv[0], options, opt_count, usage, error ? stderr : stdout);
225}
226
227void print_args_usage(int , char * argv[], cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn)) {
228        usage(argv[0], options, opt_count, usage, error ? stderr : stdout);
229}
230
231static void usage(char * cmd, cfa_option options[], size_t opt_count, const char * help, FILE * out) __attribute__((noreturn)) {
232        int width = 0;
233        {
234                for(i; opt_count) {
235                        if(options[i].long_name) {
236                                int w = strlen(options[i].long_name);
237                                if(w > width) width = w;
238                        }
239                }
240        }
241
242        int max_width = 1_000_000;
243        int outfd = fileno(out);
244        if(isatty(outfd)) {
245                struct winsize size;
246                int ret = ioctl(outfd, TIOCGWINSZ, &size);
247                if(ret < 0) abort( "ioctl error: (%d) %s\n", (int)errno, strerror(errno) );
248                max_width = size.ws_col;
249        }
250
251        fprintf(out, "Usage:\n  %s %s\n", cmd, help);
252
253        for(i; opt_count) {
254                printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help);
255        }
256        fprintf(out, "  -%c, --%-*s   %s\n", 'h', width, "help", "print this help message");
257        exit(out == stdout ? 0 : 1);
258}
259
260//-----------------------------------------------------------------------------
261// Typed argument parsing
262bool parse_yesno(const char * arg, bool & value ) {
263        if(strcmp(arg, "yes") == 0) {
264                value = true;
265                return true;
266        }
267
268        if(strcmp(arg, "Y") == 0) {
269                value = true;
270                return true;
271        }
272
273        if(strcmp(arg, "y") == 0) {
274                value = true;
275                return true;
276        }
277
278        if(strcmp(arg, "no") == 0) {
279                value = false;
280                return true;
281        }
282
283        if(strcmp(arg, "N") == 0) {
284                value = false;
285                return true;
286        }
287
288        if(strcmp(arg, "n") == 0) {
289                value = false;
290                return true;
291        }
292
293        return false;
294}
295
296bool parse_truefalse(const char * arg, bool & value) {
297        if(strcmp(arg, "true") == 0) {
298                value = true;
299                return true;
300        }
301
302        if(strcmp(arg, "false") == 0) {
303                value = false;
304                return true;
305        }
306
307        return false;
308}
309
310bool parse_settrue (const char *, bool & value ) {
311        value = true;
312        return true;
313}
314
315bool parse_setfalse(const char *, bool & value )  {
316        value = false;
317        return true;
318}
319
320bool parse(const char * arg, const char * & value ) {
321        value = arg;
322        return true;
323}
324
325bool parse(const char * arg, int & value) {
326        char * end;
327
328        errno = 0;
329        long long int r = strtoll(arg, &end, 0);
330        if(errno) return false;
331        if(*end != '\0') return false;
332        if(r > (int)MAX) return false;
333        if(r < (int)MIN) return false;
334
335        value = r;
336        return true;
337}
338
339static unsigned long long int strict_strtoull( const char * arg, int base) {
340        errno = 0;
341        {
342                const char * in = arg;
343                for() {
344                        if('\0' == *in) {
345                                errno = EINVAL;
346                                return 0;
347                        }
348                        if(!isspace(*in)) break;
349                        in++;
350                }
351                if(!isdigit(*in)) {
352                        errno = EINVAL;
353                        return 0;
354                }
355        }
356
357        *char end;
358        unsigned long long int r = strtoull(arg, &end, base);
359        if(*end != '\0') errno = EINVAL;
360        if(errno) return 0;
361        return r;
362}
363
364bool parse(const char * arg, unsigned & value) {
365        unsigned long long int r = strict_strtoull(arg, 0);
366        if(errno) return false;
367        if(r > (unsigned)MAX) return false;
368
369        value = r;
370        return true;
371}
372
373bool parse(const char * arg, unsigned long & value) {
374        unsigned long long int r = strict_strtoull(arg, 0);
375        if(errno) return false;
376        if(r > (unsigned long)MAX) return false;
377
378        value = r;
379        return true;
380}
381
382bool parse(const char * arg, unsigned long long & value) {
383        unsigned long long int r = strict_strtoull(arg, 0);
384        if(errno) return false;
385        if(r > (unsigned long long)MAX) return false;
386
387        value = r;
388        return true;
389}
390
391bool parse(const char * arg, double & value) {
392        char * end;
393        double r = strtod(arg, &end);
394        if(*end != '\0') return false;
395
396        value = r;
397        return true;
398}
Note: See TracBrowser for help on using the repository browser.