source: libcfa/src/parseargs.cfa @ 910e1d0

ADTast-experimental
Last change on this file since 910e1d0 was d1abc63c, checked in by Thierry Delisle <tdelisle@…>, 2 years ago

Change parse args to use new arrays instead of C arrays.
Also added const ?? to arrays.

  • Property mode set to 100644
File size: 10.5 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
52forall([N])
53static void usage(char * cmd, const array( cfa_option, N ) & options, const char * usage, FILE * out)  __attribute__ ((noreturn));
54//-----------------------------------------------------------------------------
55// checking
56forall([N])
57static void check_args( const array( cfa_option, N ) & options ) {
58        for(i; N) {
59                for(j; N) {
60                        if(i == j) continue;
61
62                        if( options[i].short_name != '\0'
63                        && options[i].short_name == options[j].short_name)
64                                abort("Parse Args error: two options have short name '%c' (%zu & %zu)", options[i].short_name, i, j);
65
66                        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);
67                }
68        }
69}
70
71
72//-----------------------------------------------------------------------------
73// Parsing args
74forall([opt_count]) {
75        void parse_args( const array( cfa_option, opt_count ) & options, const char * usage, char ** & left ) {
76                if( 0p != &cfa_args_argc ) {
77                        parse_args(cfa_args_argc, cfa_args_argv, options, usage, left );
78                }
79                else {
80                        char * temp = "";
81                        parse_args(0, &temp, options, usage, left );
82                }
83        }
84
85        void parse_args(
86                int argc,
87                char * argv[],
88                const array( cfa_option, opt_count ) & options,
89                const char * usage,
90                char ** & left
91        ) {
92                check_args(options);
93
94                int maxv = 'h';
95                char optstring[(opt_count * 3) + 2] = { '\0' };
96                {
97                        int idx = 0;
98                        for(i; opt_count) {
99                                if (options[i].short_name) {
100                                        maxv = max(options[i].short_name, maxv);
101                                        optstring[idx] = options[i].short_name;
102                                        idx++;
103                                        if(    ((intptr_t)options[i].parse) != ((intptr_t)parse_settrue)
104                                        && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) {
105                                                optstring[idx] = ':';
106                                                idx++;
107                                        }
108                                }
109                        }
110                        optstring[idx+0] = 'h';
111                        optstring[idx+1] = '\0';
112                }
113
114                struct option optarr[opt_count + 2];
115                {
116                        int idx = 0;
117                        for(i; opt_count) {
118                                if(options[i].long_name) {
119                                        // we don't have the mutable keyword here, which is really what we would want
120                                        int & val_ref = (int &)(const int &)options[i].val;
121                                        val_ref = (options[i].short_name != '\0') ? ((int)options[i].short_name) : ++maxv;
122
123                                        optarr[idx].name = options[i].long_name;
124                                        optarr[idx].flag = 0p;
125                                        optarr[idx].val  = options[i].val;
126                                        if(    ((intptr_t)options[i].parse) == ((intptr_t)parse_settrue)
127                                        || ((intptr_t)options[i].parse) == ((intptr_t)parse_setfalse) ) {
128                                                optarr[idx].has_arg = no_argument;
129                                        } else {
130                                                optarr[idx].has_arg = required_argument;
131                                        }
132                                        idx++;
133                                }
134                        }
135                        optarr[idx+0].[name, has_arg, flag, val] = ["help", no_argument, 0, 'h'];
136                        optarr[idx+1].[name, has_arg, flag, val] = [0, no_argument, 0, 0];
137                }
138
139                FILE * out = stderr;
140                NEXT_ARG:
141                for() {
142                        int idx = 0;
143                        int opt = getopt_long(argc, argv, optstring, optarr, &idx);
144                        switch(opt) {
145                                case -1:
146                                        if(&left != 0p) left = argv + optind;
147                                        return;
148                                case 'h':
149                                        out = stdout;
150                                case '?':
151                                        usage(argv[0], options, usage, out);
152                                default:
153                                        for(i; opt_count) {
154                                                if(opt == options[i].val) {
155                                                        const char * arg = optarg ? optarg : "";
156                                                        if( arg[0] == '=' ) { arg++; }
157                                                        // work around for some weird bug
158                                                        void * variable = options[i].variable;
159                                                        bool (*parse_func)(const char *, void * ) = options[i].parse;
160                                                        bool success = parse_func( arg, variable );
161                                                        if(success) continue NEXT_ARG;
162
163                                                        fprintf(out, "Argument '%s' for option %c could not be parsed\n\n", arg, (char)opt);
164                                                        usage(argv[0], options, usage, out);
165                                                }
166                                        }
167                                        abort("Internal parse arg error\n");
168                        }
169
170                }
171        }
172}
173
174static inline int next_newline(const char * str) {
175        int ret;
176        const char * ptr = strstr(str, "\n");
177        if(!ptr) return MAX;
178
179        /* paranoid */ verify( str <= ptr);
180        intptr_t low = (intptr_t)str;
181        intptr_t hi  = (intptr_t)ptr;
182        ret = hi - low;
183
184        return ret;
185}
186
187//-----------------------------------------------------------------------------
188// Print usage
189static void printopt(FILE * out, int width, int max, char sn, const char * ln, const char * help) {
190        // check how wide we should be printing
191        // this includes all options and the help message
192        int hwidth = max - (11 + width);
193        if(hwidth <= 0) hwidth = max;
194
195        // check which pieces we have
196        bool has_ln = ln && strcmp("", ln);
197        bool has_help = help && strcmp("", help);
198
199        // print the small name if present
200        if(sn != '\0') fprintf(out, "  -%c", sn);
201        else fprintf(out, "    ");
202
203        // print a comma if we have both short and long names
204        if(sn != '\0' && has_ln) fprintf(out, ", ");
205        else fprintf(out, "  ");
206
207        // print the long name if present
208        if(has_ln)        fprintf(out, "--%-*s", width, ln);
209        else if(has_help) fprintf(out, "  %-*s", width, "");
210
211        if(has_help) {
212                // print the help
213                // We need to wrap at the max width, and also indent newlines so everything is nice and pretty
214
215                // for each line to print
216                for() {
217                        //find out if there is a newline
218                        int nextnl = next_newline(help);
219                        int real = min(min(strlen(help), hwidth), nextnl);
220
221                        fprintf(out, "   %.*s", real, help);
222                        // printf("%d %d\n", real, nextnl);
223                        help += real;
224                        if( nextnl == real ) help++;
225                        if('\0' == *help) break;
226                        fprintf(out, "\n%*s", width + 8, "");
227                }
228        }
229        fprintf(out, "\n");
230}
231
232void print_args_usage(cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn)) {
233        const array( cfa_option, opt_count ) & arr = (const array( cfa_option, opt_count ) &) *options;
234        usage(cfa_args_argv[0], arr, usage, error ? stderr : stdout);
235}
236
237void print_args_usage(int , char * argv[], cfa_option options[], size_t opt_count, const char * usage, bool error)  __attribute__ ((noreturn)) {
238        const array( cfa_option, opt_count ) & arr = (const array( cfa_option, opt_count ) &) *options;
239        usage(argv[0], arr, usage, error ? stderr : stdout);
240}
241
242forall( [N] ) {
243        void print_args_usage( const array(cfa_option, N ) & options, const char * usage, bool error) {
244                usage(cfa_args_argv[0], options, usage, error ? stderr : stdout);
245        }
246
247        void print_args_usage(int argc, char * argv[], const array( cfa_option, N ) & options, const char * usage, bool error) {
248                usage(argv[0], options, usage, error ? stderr : stdout);
249        }
250}
251
252forall([N])
253static void usage(char * cmd, const array( cfa_option, N ) & options, const char * help, FILE * out) __attribute__((noreturn)) {
254        int width = 0;
255        {
256                for(i; N) {
257                        if(options[i].long_name) {
258                                int w = strlen(options[i].long_name);
259                                if(w > width) width = w;
260                        }
261                }
262        }
263
264        int max_width = 1_000_000;
265        int outfd = fileno(out);
266        if(isatty(outfd)) {
267                struct winsize size;
268                int ret = ioctl(outfd, TIOCGWINSZ, &size);
269                if(ret < 0) abort( "ioctl error: (%d) %s\n", (int)errno, strerror(errno) );
270                max_width = size.ws_col;
271        }
272
273        fprintf(out, "Usage:\n  %s %s\n", cmd, help);
274
275        for(i; N) {
276                printopt(out, width, max_width, options[i].short_name, options[i].long_name, options[i].help);
277        }
278        fprintf(out, "  -%c, --%-*s   %s\n", 'h', width, "help", "print this help message");
279        exit(out == stdout ? 0 : 1);
280}
281
282//-----------------------------------------------------------------------------
283// Typed argument parsing
284bool parse_yesno(const char * arg, bool & value ) {
285        if(strcmp(arg, "yes") == 0) {
286                value = true;
287                return true;
288        }
289
290        if(strcmp(arg, "Y") == 0) {
291                value = true;
292                return true;
293        }
294
295        if(strcmp(arg, "y") == 0) {
296                value = true;
297                return true;
298        }
299
300        if(strcmp(arg, "no") == 0) {
301                value = false;
302                return true;
303        }
304
305        if(strcmp(arg, "N") == 0) {
306                value = false;
307                return true;
308        }
309
310        if(strcmp(arg, "n") == 0) {
311                value = false;
312                return true;
313        }
314
315        return false;
316}
317
318bool parse_truefalse(const char * arg, bool & value) {
319        if(strcmp(arg, "true") == 0) {
320                value = true;
321                return true;
322        }
323
324        if(strcmp(arg, "false") == 0) {
325                value = false;
326                return true;
327        }
328
329        return false;
330}
331
332bool parse_settrue (const char *, bool & value ) {
333        value = true;
334        return true;
335}
336
337bool parse_setfalse(const char *, bool & value )  {
338        value = false;
339        return true;
340}
341
342bool parse(const char * arg, const char * & value ) {
343        value = arg;
344        return true;
345}
346
347bool parse(const char * arg, int & value) {
348        char * end;
349
350        errno = 0;
351        long long int r = strtoll(arg, &end, 0);
352        if(errno) return false;
353        if(*end != '\0') return false;
354        if(r > (int)MAX) return false;
355        if(r < (int)MIN) return false;
356
357        value = r;
358        return true;
359}
360
361static unsigned long long int strict_strtoull( const char * arg, int base) {
362        errno = 0;
363        {
364                const char * in = arg;
365                for() {
366                        if('\0' == *in) {
367                                errno = EINVAL;
368                                return 0;
369                        }
370                        if(!isspace(*in)) break;
371                        in++;
372                }
373                if(!isdigit(*in)) {
374                        errno = EINVAL;
375                        return 0;
376                }
377        }
378
379        *char end;
380        unsigned long long int r = strtoull(arg, &end, base);
381        if(*end != '\0') errno = EINVAL;
382        if(errno) return 0;
383        return r;
384}
385
386bool parse(const char * arg, unsigned & value) {
387        unsigned long long int r = strict_strtoull(arg, 0);
388        if(errno) return false;
389        if(r > (unsigned)MAX) return false;
390
391        value = r;
392        return true;
393}
394
395bool parse(const char * arg, unsigned long & value) {
396        unsigned long long int r = strict_strtoull(arg, 0);
397        if(errno) return false;
398        if(r > (unsigned long)MAX) return false;
399
400        value = r;
401        return true;
402}
403
404bool parse(const char * arg, unsigned long long & value) {
405        unsigned long long int r = strict_strtoull(arg, 0);
406        if(errno) return false;
407        if(r > (unsigned long long)MAX) return false;
408
409        value = r;
410        return true;
411}
412
413bool parse(const char * arg, double & value) {
414        char * end;
415        double r = strtod(arg, &end);
416        if(*end != '\0') return false;
417
418        value = r;
419        return true;
420}
Note: See TracBrowser for help on using the repository browser.