source: libcfa/src/parseargs.cfa@ d60a4c2

Last change on this file since d60a4c2 was a4e1b09, checked in by Peter A. Buhr <pabuhr@…>, 15 months 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.