source: libcfa/src/parseargs.cfa@ 2f4c910

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