source: libcfa/src/parseargs.cfa @ 38cc59f

ADTast-experimental
Last change on this file since 38cc59f was 481f882, checked in by Thierry Delisle <tdelisle@…>, 20 months ago

Added some missing headers and cleaned up some of the fork+exec stuff.

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