source: libcfa/src/parseargs.cfa@ 6b765d5

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

Removing some unneeded distributions.

  • Property mode set to 100644
File size: 10.8 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
[54f70c6]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 );
[433d352]82 }
[54f70c6]83}
[7874d77]84
[54f70c6]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] = ':';
[80d3b1b]107 idx++;
108 }
109 }
110 }
[54f70c6]111 optstring[idx+0] = 'h';
112 optstring[idx+1] = '\0';
113 }
[80d3b1b]114
[54f70c6]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;
[7f389a5c]132 }
[54f70c6]133 idx++;
[7f389a5c]134 }
135 }
[54f70c6]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 }
[7f389a5c]139
[54f70c6]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 );
[a4e1b09]165 usage( argv[0], options, usage, out );
[54f70c6]166 }
[d1abc63c]167 }
[54f70c6]168 abort( "Internal parse arg error\n" );
[d1abc63c]169 }
[7f389a5c]170 }
[419c434]171}
[7f389a5c]172
[a4e1b09]173static inline int next_newline( const char * str ) {
[f82f07e]174 int ret;
[a4e1b09]175 const char * ptr = strstr( str, "\n" );
176 if ( ! ptr ) return MAX;
[f82f07e]177
[a4e1b09]178 /* paranoid */ verify( str <= ptr );
[f82f07e]179 intptr_t low = (intptr_t)str;
180 intptr_t hi = (intptr_t)ptr;
181 ret = hi - low;
182
183 return ret;
184}
185
[419c434]186//-----------------------------------------------------------------------------
187// Print usage
[a4e1b09]188static void printopt( FILE * out, int width, int max, char sn, const char * ln, const char * help ) {
[f82f07e]189 // check how wide we should be printing
190 // this includes all options and the help message
[419c434]191 int hwidth = max - (11 + width);
[a4e1b09]192 if ( hwidth <= 0 ) hwidth = max;
[419c434]193
[f82f07e]194 // check which pieces we have
[a4e1b09]195 bool has_ln = ln && strcmp( "", ln );
196 bool has_help = help && strcmp( "", help );
[f82f07e]197
198 // print the small name if present
[a4e1b09]199 if ( sn != '\0') fprintf( out, " -%c", sn );
200 else fprintf( out, " " );
[f82f07e]201
202 // print a comma if we have both short and long names
[a4e1b09]203 if ( sn != '\0' && has_ln ) fprintf( out, ", " );
204 else fprintf( out, " " );
[f82f07e]205
206 // print the long name if present
[a4e1b09]207 if ( has_ln ) fprintf( out, "--%-*s", width, ln );
208 else if ( has_help ) fprintf( out, " %-*s", width, "" );
[f82f07e]209
[a4e1b09]210 if ( has_help ) {
[f82f07e]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
[a4e1b09]215 for () {
[f82f07e]216 //find out if there is a newline
[a4e1b09]217 int nextnl = next_newline( help );
218 int real = min( min( strlen( help ), hwidth ), nextnl );
[f82f07e]219
[a4e1b09]220 fprintf( out, " %.*s", real, help );
221 // printf( "%d %d\n", real, nextnl );
[f82f07e]222 help += real;
[a4e1b09]223 if ( nextnl == real ) help++;
224 if ('\0' == *help ) break;
225 fprintf( out, "\n%*s", width + 8, "" );
[f82f07e]226 }
[419c434]227 }
[a4e1b09]228 fprintf( out, "\n" );
[419c434]229}
230
[a4e1b09]231void print_args_usage( cfa_option options[], const size_t opt_count, const char * usage, bool error ) __attribute__ ((noreturn )) {
[d1abc63c]232 const array( cfa_option, opt_count ) & arr = (const array( cfa_option, opt_count ) &) *options;
[a4e1b09]233 usage( cfa_args_argv[0], arr, usage, error ? stderr : stdout );
[419c434]234}
235
[a4e1b09]236void print_args_usage( int , char * argv[], cfa_option options[], const size_t opt_count, const char * usage, bool error ) __attribute__ (( noreturn )) {
[d1abc63c]237 const array( cfa_option, opt_count ) & arr = (const array( cfa_option, opt_count ) &) *options;
[a4e1b09]238 usage( argv[0], arr, usage, error ? stderr : stdout );
[d1abc63c]239}
240
[54f70c6]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}
[d1abc63c]245
[54f70c6]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 );
[419c434]249}
250
[d1abc63c]251forall([N])
[a4e1b09]252static void usage( char * cmd, const array( cfa_option, N ) & options, const char * help, FILE * out ) __attribute__(( noreturn )) {
[419c434]253 int width = 0;
254 {
[a4e1b09]255 for ( i; N ) {
256 if ( options[i].long_name ) {
257 int w = strlen( options[i].long_name );
258 if ( w > width ) width = w;
[419c434]259 }
260 }
261 }
262
263 int max_width = 1_000_000;
[a4e1b09]264 int outfd = fileno( out );
265 if ( isatty( outfd ) ) {
[3f1d9b5]266 struct winsize size;
[a4e1b09]267 int ret = ioctl( outfd, TIOCGWINSZ, &size );
268 if ( ret < 0 ) abort( "ioctl error: (%d) %s\n", (int)errno, strerror( errno) );
[3f1d9b5]269 max_width = size.ws_col;
270 }
271
[a4e1b09]272 fprintf( out, "Usage:\n %s %s\n", cmd, help );
[7f389a5c]273
[a4e1b09]274 for ( i; N ) {
275 printopt( out, width, max_width, options[i].short_name, options[i].long_name, options[i].help );
[7f389a5c]276 }
[a4e1b09]277 fprintf( out, " -%c, --%-*s %s\n", 'h', width, "help", "print this help message" );
278 exit( out == stdout ? 0 : 1 );
[7f389a5c]279}
280
[419c434]281//-----------------------------------------------------------------------------
282// Typed argument parsing
[a4e1b09]283bool parse_yesno( const char * arg, bool & value ) {
284 if ( strcmp( arg, "yes" ) == 0 ) {
[7f389a5c]285 value = true;
286 return true;
287 }
[a4e1b09]288 if ( strcmp( arg, "Y" ) == 0 ) {
[e07187d]289 value = true;
290 return true;
291 }
[a4e1b09]292 if ( strcmp( arg, "y" ) == 0 ) {
[e07187d]293 value = true;
294 return true;
295 }
[a4e1b09]296 if ( strcmp( arg, "no" ) == 0 ) {
[7f389a5c]297 value = false;
298 return true;
299 }
[a4e1b09]300 if ( strcmp( arg, "N" ) == 0 ) {
[e07187d]301 value = false;
302 return true;
303 }
[a4e1b09]304 if ( strcmp( arg, "n" ) == 0 ) {
[e07187d]305 value = false;
306 return true;
307 }
[7f389a5c]308 return false;
309}
310
[a4e1b09]311bool parse_truefalse( const char * arg, bool & value ) {
312 if ( strcmp( arg, "true" ) == 0 ) {
[d411769c]313 value = true;
314 return true;
315 }
[a4e1b09]316 if ( strcmp( arg, "false" ) == 0 ) {
[d411769c]317 value = false;
318 return true;
319 }
320 return false;
321}
322
[a4e1b09]323bool parse_settrue ( const char *, bool & value ) {
[7f389a5c]324 value = true;
325 return true;
326}
327
[a4e1b09]328bool parse_setfalse( const char *, bool & value ) {
[7f389a5c]329 value = false;
330 return true;
331}
332
[a4e1b09]333bool parse( const char * arg, const char * & value ) {
[7f389a5c]334 value = arg;
335 return true;
336}
337
[a4e1b09]338bool parse( const char * arg, int & value ) {
[7f6e9eb]339 char * end;
[affb51b]340
341 errno = 0;
[a4e1b09]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;
[7f6e9eb]347 value = r;
348 return true;
349}
350
[a4e1b09]351static unsigned long long int strict_strtoull( const char * arg, int base ) {
[affb51b]352 errno = 0;
353 {
354 const char * in = arg;
[a4e1b09]355 for () {
356 if ( '\0' == *in ) {
[affb51b]357 errno = EINVAL;
358 return 0;
359 }
[a4e1b09]360 if ( ! isspace(*in )) break;
[affb51b]361 in++;
362 }
[a4e1b09]363 if ( ! isdigit(*in )) {
[affb51b]364 errno = EINVAL;
365 return 0;
366 }
367 }
368 *char end;
[a4e1b09]369 unsigned long long int r = strtoull( arg, &end, base );
370 if (*end != '\0') errno = EINVAL;
371 if ( errno ) return 0;
[affb51b]372 return r;
373}
374
[a4e1b09]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;
[53e4562]379 value = r;
380 return true;
381}
382
[a4e1b09]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;
[53e4562]387 value = r;
388 return true;
389}
390
[a4e1b09]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;
[affb51b]395 value = r;
396 return true;
[56e8cb3]397}
398
[a4e1b09]399bool parse( const char * arg, double & value ) {
[7f389a5c]400 char * end;
[a4e1b09]401 double r = strtod( arg, &end );
402 if ( *end != '\0') return false;
[7f389a5c]403 value = r;
404 return true;
[56e8cb3]405}
Note: See TracBrowser for help on using the repository browser.