source: libcfa/src/parseargs.cfa

Last change on this file was a4e1b09, checked in by Peter A. Buhr <pabuhr@…>, 4 months ago

formatting

  • Property mode set to 100644
File size: 10.9 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 : Peter A. Buhr
13// Last Modified On : Mon Jul  8 18:18:23 2024
14// Update Count     : 7
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 ))
67                                abort( "Parse Args error: two options have long name '%s' (%zu & %zu)", options[i].long_name, i, j );
68                }
69        }
70}
71
72
73//-----------------------------------------------------------------------------
74// Parsing args
75forall([opt_count]) {
76        void parse_args( const array( cfa_option, opt_count ) & options, const char * usage, char ** & left ) {
77                if ( 0p != &cfa_args_argc ) {
78                        parse_args( cfa_args_argc, cfa_args_argv, options, usage, left );
79                } else {
80                        char * temp[1] = { 0p };
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[], const 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[], const 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        if ( strcmp( arg, "Y" ) == 0 ) {
290                value = true;
291                return true;
292        }
293        if ( strcmp( arg, "y" ) == 0 ) {
294                value = true;
295                return true;
296        }
297        if ( strcmp( arg, "no" ) == 0 ) {
298                value = false;
299                return true;
300        }
301        if ( strcmp( arg, "N" ) == 0 ) {
302                value = false;
303                return true;
304        }
305        if ( strcmp( arg, "n" ) == 0 ) {
306                value = false;
307                return true;
308        }
309        return false;
310}
311
312bool parse_truefalse( const char * arg, bool & value ) {
313        if ( strcmp( arg, "true" ) == 0 ) {
314                value = true;
315                return true;
316        }
317        if ( strcmp( arg, "false" )  == 0 ) {
318                value = false;
319                return true;
320        }
321        return false;
322}
323
324bool parse_settrue ( const char *, bool & value ) {
325        value = true;
326        return true;
327}
328
329bool parse_setfalse( const char *, bool & value )  {
330        value = false;
331        return true;
332}
333
334bool parse( const char * arg, const char * & value ) {
335        value = arg;
336        return true;
337}
338
339bool parse( const char * arg, int & value ) {
340        char * end;
341
342        errno = 0;
343        long long int r = strtoll( arg, &end, 0 );
344        if ( errno ) return false;
345        if (*end != '\0') return false;
346        if ( r > (int)MAX ) return false;
347        if ( r < (int)MIN ) return false;
348        value = r;
349        return true;
350}
351
352static unsigned long long int strict_strtoull( const char * arg, int base ) {
353        errno = 0;
354        {
355                const char * in = arg;
356                for () {
357                        if ( '\0' == *in ) {
358                                errno = EINVAL;
359                                return 0;
360                        }
361                        if ( ! isspace(*in )) break;
362                        in++;
363                }
364                if ( ! isdigit(*in )) {
365                        errno = EINVAL;
366                        return 0;
367                }
368        }
369        *char end;
370        unsigned long long int r = strtoull( arg, &end, base );
371        if (*end != '\0') errno = EINVAL;
372        if ( errno ) return 0;
373        return r;
374}
375
376bool parse( const char * arg, unsigned & value ) {
377        unsigned long long int r = strict_strtoull( arg, 0 );
378        if ( errno ) return false;
379        if ( r > (unsigned)MAX ) return false;
380        value = r;
381        return true;
382}
383
384bool parse( const char * arg, unsigned long & value ) {
385        unsigned long long int r = strict_strtoull( arg, 0 );
386        if ( errno ) return false;
387        if ( r > (unsigned long)MAX ) return false;
388        value = r;
389        return true;
390}
391
392bool parse( const char * arg, unsigned long long & value ) {
393        unsigned long long int r = strict_strtoull( arg, 0 );
394        if ( errno ) return false;
395        if ( r > (unsigned long long)MAX ) return false;
396        value = r;
397        return true;
398}
399
400bool parse( const char * arg, double & value ) {
401        char * end;
402        double r = strtod( arg, &end );
403        if ( *end != '\0') return false;
404        value = r;
405        return true;
406}
Note: See TracBrowser for help on using the repository browser.