source: libcfa/src/parseargs.cfa @ e1358c0

Last change on this file since e1358c0 was 54f70c6, checked in by Andrew Beach <ajbeach@…>, 8 weeks ago

Removing some unneeded distributions.

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