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
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.