source: libcfa/src/parseargs.cfa @ 876739f

Last change on this file since 876739f was a4e1b09, checked in by Peter A. Buhr <pabuhr@…>, 3 weeks ago

formatting

  • Property mode set to 100644
File size: 10.9 KB
RevLine 
[481f882]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
[a4e1b09]12// Last Modified By : Peter A. Buhr
13// Last Modified On : Mon Jul  8 18:18:23 2024
14// Update Count     : 7
[481f882]15//
16
[7f389a5c]17#include "parseargs.hfa"
18
[f82f07e]19#include <assert.h>
[affb51b]20#include <ctype.h>
[e699eb6]21#include <stdint.h>
[7f389a5c]22#include <string.h>
[e699eb6]23#include <errno.h>
[3f1d9b5]24#include <unistd.h>
[1c893ae]25
[7f389a5c]26extern "C" {
27        #include <getopt.h>
[3f1d9b5]28        #include <sys/ioctl.h>
[7f389a5c]29
30        struct FILE;
31        extern FILE * stderr;
32        extern FILE * stdout;
33
[a4e1b09]34        extern int fileno( FILE *stream );
[3f1d9b5]35
[7f389a5c]36        extern int fprintf ( FILE * stream, const char * format, ... );
[53e4562]37
[a4e1b09]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 );
[7f389a5c]41}
42
[e699eb6]43#include "common.hfa"
44#include "limits.hfa"
[3f1d9b5]45
[a4e1b09]46#pragma GCC visibility push( default )
[789f279]47
[a4e1b09]48extern int cfa_args_argc __attribute__(( weak ));
49extern char ** cfa_args_argv __attribute__(( weak ));
50extern char ** cfa_args_envp __attribute__(( weak ));
[7874d77]51
[d1abc63c]52forall([N])
[a4e1b09]53static void usage( char * cmd, const array( cfa_option, N ) & options, const char * usage, FILE * out )  __attribute__ (( noreturn ));
[80d3b1b]54//-----------------------------------------------------------------------------
55// checking
[d1abc63c]56forall([N])
57static void check_args( const array( cfa_option, N ) & options ) {
[a4e1b09]58        for ( i; N ) {
59                for ( j; N ) {
60                        if ( i == j ) continue;
[80d3b1b]61
[a4e1b09]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 );
[3f1d9b5]65
[a4e1b09]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 );
[80d3b1b]68                }
69        }
70}
71
72
73//-----------------------------------------------------------------------------
74// Parsing args
[d1abc63c]75forall([opt_count]) {
76        void parse_args( const array( cfa_option, opt_count ) & options, const char * usage, char ** & left ) {
[a4e1b09]77                if ( 0p != &cfa_args_argc ) {
78                        parse_args( cfa_args_argc, cfa_args_argv, options, usage, left );
79                } else {
[550afde2]80                        char * temp[1] = { 0p };
81                        parse_args(0, temp, options, usage, left );
[d1abc63c]82                }
[433d352]83        }
[7874d77]84
[d1abc63c]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        ) {
[a4e1b09]92                check_args( options );
[d1abc63c]93
94                int maxv = 'h';
95                char optstring[(opt_count * 3) + 2] = { '\0' };
96                {
97                        int idx = 0;
[a4e1b09]98                        for ( i; opt_count ) {
99                                if ( options[i].short_name ) {
100                                        maxv = max( options[i].short_name, maxv );
[d1abc63c]101                                        optstring[idx] = options[i].short_name;
[80d3b1b]102                                        idx++;
[a4e1b09]103                                        if ( (intptr_t)options[i].parse != (intptr_t)parse_settrue
104                                                 && ((intptr_t)options[i].parse) != ((intptr_t)parse_setfalse) ) {
[d1abc63c]105                                                optstring[idx] = ':';
106                                                idx++;
107                                        }
[80d3b1b]108                                }
109                        }
[d1abc63c]110                        optstring[idx+0] = 'h';
111                        optstring[idx+1] = '\0';
[80d3b1b]112                }
113
[d1abc63c]114                struct option optarr[opt_count + 2];
115                {
116                        int idx = 0;
[a4e1b09]117                        for ( i; opt_count ) {
118                                if ( options[i].long_name ) {
[d1abc63c]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;
[a4e1b09]126                                        if ( ((intptr_t)options[i].parse) == ((intptr_t)parse_settrue)
127                                                 || ((intptr_t)options[i].parse) == ((intptr_t)parse_setfalse) ) {
[d1abc63c]128                                                optarr[idx].has_arg = no_argument;
129                                        } else {
130                                                optarr[idx].has_arg = required_argument;
131                                        }
132                                        idx++;
[7f389a5c]133                                }
134                        }
[d1abc63c]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];
[7f389a5c]137                }
138
[d1abc63c]139                FILE * out = stderr;
140                NEXT_ARG:
[a4e1b09]141                for () {
[d1abc63c]142                        int idx = 0;
[a4e1b09]143                        int opt = getopt_long( argc, argv, optstring, optarr, &idx );
144                        switch( opt ) {
[d1abc63c]145                                case -1:
[a4e1b09]146                                        if ( &left != 0p ) left = argv + optind;
[d1abc63c]147                                        return;
148                                case 'h':
149                                        out = stdout;
150                                case '?':
[a4e1b09]151                                        usage( argv[0], options, usage, out );
[d1abc63c]152                                default:
[a4e1b09]153                                        for ( i; opt_count ) {
154                                                if ( opt == options[i].val ) {
[d1abc63c]155                                                        const char * arg = optarg ? optarg : "";
[a4e1b09]156                                                        if ( arg[0] == '=' ) { arg++; }
[d1abc63c]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 );
[a4e1b09]161                                                        if ( success ) continue NEXT_ARG;
[d1abc63c]162
[a4e1b09]163                                                        fprintf( out, "Argument '%s' for option %c could not be parsed\n\n", arg, (char)opt );
164                                                        usage( argv[0], options, usage, out );
[d1abc63c]165                                                }
[7f389a5c]166                                        }
[a4e1b09]167                                        abort( "Internal parse arg error\n" );
[d1abc63c]168                        }
[7f389a5c]169
[d1abc63c]170                }
[7f389a5c]171        }
[419c434]172}
[7f389a5c]173
[a4e1b09]174static inline int next_newline( const char * str ) {
[f82f07e]175        int ret;
[a4e1b09]176        const char * ptr = strstr( str, "\n" );
177        if ( ! ptr ) return MAX;
[f82f07e]178
[a4e1b09]179        /* paranoid */ verify( str <= ptr );
[f82f07e]180        intptr_t low = (intptr_t)str;
181        intptr_t hi  = (intptr_t)ptr;
182        ret = hi - low;
183
184        return ret;
185}
186
[419c434]187//-----------------------------------------------------------------------------
188// Print usage
[a4e1b09]189static void printopt( FILE * out, int width, int max, char sn, const char * ln, const char * help ) {
[f82f07e]190        // check how wide we should be printing
191        // this includes all options and the help message
[419c434]192        int hwidth = max - (11 + width);
[a4e1b09]193        if ( hwidth <= 0 ) hwidth = max;
[419c434]194
[f82f07e]195        // check which pieces we have
[a4e1b09]196        bool has_ln = ln && strcmp( "", ln );
197        bool has_help = help && strcmp( "", help );
[f82f07e]198
199        // print the small name if present
[a4e1b09]200        if ( sn != '\0') fprintf( out, "  -%c", sn );
201        else fprintf( out, "    " );
[f82f07e]202
203        // print a comma if we have both short and long names
[a4e1b09]204        if ( sn != '\0' && has_ln ) fprintf( out, ", " );
205        else fprintf( out, "  " );
[f82f07e]206
207        // print the long name if present
[a4e1b09]208        if ( has_ln ) fprintf( out, "--%-*s", width, ln );
209        else if ( has_help ) fprintf( out, "  %-*s", width, "" );
[f82f07e]210
[a4e1b09]211        if ( has_help ) {
[f82f07e]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
[a4e1b09]216                for () {
[f82f07e]217                        //find out if there is a newline
[a4e1b09]218                        int nextnl = next_newline( help );
219                        int real = min( min( strlen( help ), hwidth ), nextnl );
[f82f07e]220
[a4e1b09]221                        fprintf( out, "   %.*s", real, help );
222                        // printf( "%d %d\n", real, nextnl );
[f82f07e]223                        help += real;
[a4e1b09]224                        if ( nextnl == real ) help++;
225                        if ('\0' == *help ) break;
226                        fprintf( out, "\n%*s", width + 8, "" );
[f82f07e]227                }
[419c434]228        }
[a4e1b09]229        fprintf( out, "\n" );
[419c434]230}
231
[a4e1b09]232void print_args_usage( cfa_option options[], const size_t opt_count, const char * usage, bool error )  __attribute__ ((noreturn )) {
[d1abc63c]233        const array( cfa_option, opt_count ) & arr = (const array( cfa_option, opt_count ) &) *options;
[a4e1b09]234        usage( cfa_args_argv[0], arr, usage, error ? stderr : stdout );
[419c434]235}
236
[a4e1b09]237void print_args_usage( int , char * argv[], cfa_option options[], const size_t opt_count, const char * usage, bool error )  __attribute__ (( noreturn )) {
[d1abc63c]238        const array( cfa_option, opt_count ) & arr = (const array( cfa_option, opt_count ) &) *options;
[a4e1b09]239        usage( argv[0], arr, usage, error ? stderr : stdout );
[d1abc63c]240}
241
242forall( [N] ) {
[a4e1b09]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 );
[d1abc63c]245        }
246
[a4e1b09]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 );
[d1abc63c]249        }
[419c434]250}
251
[d1abc63c]252forall([N])
[a4e1b09]253static void usage( char * cmd, const array( cfa_option, N ) & options, const char * help, FILE * out ) __attribute__(( noreturn )) {
[419c434]254        int width = 0;
255        {
[a4e1b09]256                for ( i; N ) {
257                        if ( options[i].long_name ) {
258                                int w = strlen( options[i].long_name );
259                                if ( w > width ) width = w;
[419c434]260                        }
261                }
262        }
263
264        int max_width = 1_000_000;
[a4e1b09]265        int outfd = fileno( out );
266        if ( isatty( outfd ) ) {
[3f1d9b5]267                struct winsize size;
[a4e1b09]268                int ret = ioctl( outfd, TIOCGWINSZ, &size );
269                if ( ret < 0 ) abort( "ioctl error: (%d) %s\n", (int)errno, strerror( errno) );
[3f1d9b5]270                max_width = size.ws_col;
271        }
272
[a4e1b09]273        fprintf( out, "Usage:\n  %s %s\n", cmd, help );
[7f389a5c]274
[a4e1b09]275        for ( i; N ) {
276                printopt( out, width, max_width, options[i].short_name, options[i].long_name, options[i].help );
[7f389a5c]277        }
[a4e1b09]278        fprintf( out, "  -%c, --%-*s   %s\n", 'h', width, "help", "print this help message" );
279        exit( out == stdout ? 0 : 1 );
[7f389a5c]280}
281
[419c434]282//-----------------------------------------------------------------------------
283// Typed argument parsing
[a4e1b09]284bool parse_yesno( const char * arg, bool & value ) {
285        if ( strcmp( arg, "yes" ) == 0 ) {
[7f389a5c]286                value = true;
287                return true;
288        }
[a4e1b09]289        if ( strcmp( arg, "Y" ) == 0 ) {
[e07187d]290                value = true;
291                return true;
292        }
[a4e1b09]293        if ( strcmp( arg, "y" ) == 0 ) {
[e07187d]294                value = true;
295                return true;
296        }
[a4e1b09]297        if ( strcmp( arg, "no" ) == 0 ) {
[7f389a5c]298                value = false;
299                return true;
300        }
[a4e1b09]301        if ( strcmp( arg, "N" ) == 0 ) {
[e07187d]302                value = false;
303                return true;
304        }
[a4e1b09]305        if ( strcmp( arg, "n" ) == 0 ) {
[e07187d]306                value = false;
307                return true;
308        }
[7f389a5c]309        return false;
310}
311
[a4e1b09]312bool parse_truefalse( const char * arg, bool & value ) {
313        if ( strcmp( arg, "true" ) == 0 ) {
[d411769c]314                value = true;
315                return true;
316        }
[a4e1b09]317        if ( strcmp( arg, "false" )  == 0 ) {
[d411769c]318                value = false;
319                return true;
320        }
321        return false;
322}
323
[a4e1b09]324bool parse_settrue ( const char *, bool & value ) {
[7f389a5c]325        value = true;
326        return true;
327}
328
[a4e1b09]329bool parse_setfalse( const char *, bool & value )  {
[7f389a5c]330        value = false;
331        return true;
332}
333
[a4e1b09]334bool parse( const char * arg, const char * & value ) {
[7f389a5c]335        value = arg;
336        return true;
337}
338
[a4e1b09]339bool parse( const char * arg, int & value ) {
[7f6e9eb]340        char * end;
[affb51b]341
342        errno = 0;
[a4e1b09]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;
[7f6e9eb]348        value = r;
349        return true;
350}
351
[a4e1b09]352static unsigned long long int strict_strtoull( const char * arg, int base ) {
[affb51b]353        errno = 0;
354        {
355                const char * in = arg;
[a4e1b09]356                for () {
357                        if ( '\0' == *in ) {
[affb51b]358                                errno = EINVAL;
359                                return 0;
360                        }
[a4e1b09]361                        if ( ! isspace(*in )) break;
[affb51b]362                        in++;
363                }
[a4e1b09]364                if ( ! isdigit(*in )) {
[affb51b]365                        errno = EINVAL;
366                        return 0;
367                }
368        }
369        *char end;
[a4e1b09]370        unsigned long long int r = strtoull( arg, &end, base );
371        if (*end != '\0') errno = EINVAL;
372        if ( errno ) return 0;
[affb51b]373        return r;
374}
375
[a4e1b09]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;
[53e4562]380        value = r;
381        return true;
382}
383
[a4e1b09]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;
[53e4562]388        value = r;
389        return true;
390}
391
[a4e1b09]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;
[affb51b]396        value = r;
397        return true;
[56e8cb3]398}
399
[a4e1b09]400bool parse( const char * arg, double & value ) {
[7f389a5c]401        char * end;
[a4e1b09]402        double r = strtod( arg, &end );
403        if ( *end != '\0') return false;
[7f389a5c]404        value = r;
405        return true;
[56e8cb3]406}
Note: See TracBrowser for help on using the repository browser.