1 /*
2 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 *
16 * Sponsored in part by the Defense Advanced Research Projects
17 * Agency (DARPA) and Air Force Research Laboratory, Air Force
18 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
19 */
20
21 /*-
22 * Copyright (c) 2000 The NetBSD Foundation, Inc.
23 * All rights reserved.
24 *
25 * This code is derived from software contributed to The NetBSD Foundation
26 * by Dieter Baron and Thomas Klausner.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
38 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
39 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
40 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
41 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
47 * POSSIBILITY OF SUCH DAMAGE.
48 */
49
50 /** @file
51 * @brief Implementations of getopt() and getopt_long() work-alike functions.
52 *
53 * This code was taken from FreeBSD and slightly modified, mostly to rename
54 * symbols with external linkage to avoid naming conflicts in systems where
55 * there are real getopt()/getopt_long() implementations, and for portability.
56 */
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60
61 #include <redfs.h>
62 #include <redgetopt.h>
63 #include <redtestutils.h>
64 #include <rederrno.h>
65
66
67 int32_t red_opterr = 1; /* if error message should be printed */
68 int32_t red_optind = 1; /* index into parent argv vector */
69 int32_t red_optopt = '?'; /* character checked for validity */
70 int32_t red_optreset; /* reset RedGetopt */
71 const char * red_optarg; /* argument associated with option */
72
73 #define PRINT_ERROR ( ( red_opterr ) && ( *options != ':' ) )
74
75 #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
76 #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
77 #define FLAG_LONGONLY 0x04 /* operate as RedGetoptLongOnly */
78
79 /* return values */
80 #define BADCH ( int ) '?'
81 #define BADARG ( ( *options == ':' ) ? ( int ) ':' : ( int ) '?' )
82 #define INORDER ( int ) 1
83
84 #define EMSG ""
85
86 #define NO_PREFIX ( -1 )
87 #define D_PREFIX 0
88 #define DD_PREFIX 1
89 #define W_PREFIX 2
90
91 static int gcd( int a,
92 int b );
93 static void permute_args( int panonopt_start,
94 int panonopt_end,
95 int opt_end,
96 char * const * nargv );
97 static int parse_long_options( char * const * nargv,
98 const char * options,
99 const REDOPTION * long_options,
100 int32_t * idx,
101 int short_too,
102 int flags );
103 static int getopt_internal( int nargc,
104 char * const * nargv,
105 const char * options,
106 const REDOPTION * long_options,
107 int32_t * idx,
108 int flags );
109
110 static const char * place = EMSG; /* option letter processing */
111
112 /* XXX: set red_optreset to 1 rather than these two */
113 static int nonopt_start = -1; /* first non option argument (for permute) */
114 static int nonopt_end = -1; /* first option after non options (for permute) */
115
116 /* Error messages */
117 static const char recargchar[] = "option requires an argument -- %c\n";
118 static const char illoptchar[] = "illegal option -- %c\n"; /* From P1003.2 */
119 static int dash_prefix = NO_PREFIX;
120 static const char gnuoptchar[] = "invalid option -- %c\n";
121
122 static const char recargstring[] = "option `%s%s' requires an argument\n";
123 static const char ambig[] = "option `%s%s' is ambiguous\n";
124 static const char noarg[] = "option `%s%s' doesn't allow an argument\n";
125 static const char illoptstring[] = "unrecognized option `%s%s'\n";
126
127 /*
128 * Compute the greatest common divisor of a and b.
129 */
gcd(int a,int b)130 static int gcd( int a,
131 int b )
132 {
133 int c;
134
135 c = a % b;
136
137 while( c != 0 )
138 {
139 a = b;
140 b = c;
141 c = a % b;
142 }
143
144 return( b );
145 }
146
147 /*
148 * Exchange the block from nonopt_start to nonopt_end with the block
149 * from nonopt_end to opt_end (keeping the same order of arguments
150 * in each block).
151 */
permute_args(int panonopt_start,int panonopt_end,int opt_end,char * const * nargv)152 static void permute_args( int panonopt_start,
153 int panonopt_end,
154 int opt_end,
155 char * const * nargv )
156 {
157 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
158 char * swap;
159
160 /*
161 * compute lengths of blocks and number and size of cycles
162 */
163 nnonopts = panonopt_end - panonopt_start;
164 nopts = opt_end - panonopt_end;
165 ncycle = gcd( nnonopts, nopts );
166 cyclelen = ( opt_end - panonopt_start ) / ncycle;
167
168 for( i = 0; i < ncycle; i++ )
169 {
170 cstart = panonopt_end + i;
171 pos = cstart;
172
173 for( j = 0; j < cyclelen; j++ )
174 {
175 if( pos >= panonopt_end )
176 {
177 pos -= nnonopts;
178 }
179 else
180 {
181 pos += nopts;
182 }
183
184 swap = nargv[ pos ];
185 ( ( char ** ) nargv )[ pos ] = nargv[ cstart ];
186 ( ( char ** ) nargv )[ cstart ] = swap;
187 }
188 }
189 }
190
191 /*
192 * parse_long_options --
193 * Parse long options in argc/argv argument vector.
194 * Returns -1 if short_too is set and the option does not match long_options.
195 */
parse_long_options(char * const * nargv,const char * options,const REDOPTION * long_options,int32_t * idx,int short_too,int flags)196 static int parse_long_options( char * const * nargv,
197 const char * options,
198 const REDOPTION * long_options,
199 int32_t * idx,
200 int short_too,
201 int flags )
202 {
203 const char * current_argv, * has_equal, * current_dash;
204 size_t current_argv_len;
205 int i, match, exact_match, second_partial_match;
206
207 current_argv = place;
208
209 switch( dash_prefix )
210 {
211 case D_PREFIX:
212 current_dash = "-";
213 break;
214
215 case DD_PREFIX:
216 current_dash = "--";
217 break;
218
219 case W_PREFIX:
220 current_dash = "-W ";
221 break;
222
223 default:
224 current_dash = "";
225 break;
226 }
227
228 match = -1;
229 exact_match = 0;
230 second_partial_match = 0;
231
232 red_optind++;
233
234 if( ( has_equal = strchr( current_argv, '=' ) ) != NULL )
235 {
236 /* argument found (--option=arg) */
237 current_argv_len = has_equal - current_argv;
238 has_equal++;
239 }
240 else
241 {
242 current_argv_len = strlen( current_argv );
243 }
244
245 for( i = 0; long_options[ i ].name; i++ )
246 {
247 /* find matching long option */
248 if( strncmp( current_argv, long_options[ i ].name,
249 current_argv_len ) )
250 {
251 continue;
252 }
253
254 if( strlen( long_options[ i ].name ) == current_argv_len )
255 {
256 /* exact match */
257 match = i;
258 exact_match = 1;
259 break;
260 }
261
262 /*
263 * If this is a known short option, don't allow
264 * a partial match of a single character.
265 */
266 if( short_too && ( current_argv_len == 1 ) )
267 {
268 continue;
269 }
270
271 if( match == -1 ) /* first partial match */
272 {
273 match = i;
274 }
275 else if( ( flags & FLAG_LONGONLY ) ||
276 ( long_options[ i ].has_arg !=
277 long_options[ match ].has_arg ) ||
278 ( long_options[ i ].flag != long_options[ match ].flag ) ||
279 ( long_options[ i ].val != long_options[ match ].val ) )
280 {
281 second_partial_match = 1;
282 }
283 }
284
285 if( !exact_match && second_partial_match )
286 {
287 /* ambiguous abbreviation */
288 if( PRINT_ERROR )
289 {
290 fprintf( stderr,
291 ambig,
292 current_dash,
293 current_argv );
294 }
295
296 red_optopt = 0;
297 return( BADCH );
298 }
299
300 if( match != -1 ) /* option found */
301 {
302 if( ( long_options[ match ].has_arg == red_no_argument ) &&
303 has_equal )
304 {
305 if( PRINT_ERROR )
306 {
307 fprintf( stderr,
308 noarg,
309 current_dash,
310 current_argv );
311 }
312
313 /*
314 * XXX: GNU sets red_optopt to val regardless of flag
315 */
316 if( long_options[ match ].flag == NULL )
317 {
318 red_optopt = long_options[ match ].val;
319 }
320 else
321 {
322 red_optopt = 0;
323 }
324
325 return( BADCH );
326 }
327
328 if( ( long_options[ match ].has_arg == red_required_argument ) ||
329 ( long_options[ match ].has_arg == red_optional_argument ) )
330 {
331 if( has_equal )
332 {
333 red_optarg = has_equal;
334 }
335 else if( long_options[ match ].has_arg ==
336 red_required_argument )
337 {
338 /*
339 * optional argument doesn't use next nargv
340 */
341 red_optarg = nargv[ red_optind++ ];
342 }
343 }
344
345 if( ( long_options[ match ].has_arg == red_required_argument ) &&
346 ( red_optarg == NULL ) )
347 {
348 /*
349 * Missing argument; leading ':' indicates no error
350 * should be generated.
351 */
352 if( PRINT_ERROR )
353 {
354 fprintf( stderr,
355 recargstring,
356 current_dash,
357 current_argv );
358 }
359
360 /*
361 * XXX: GNU sets red_optopt to val regardless of flag
362 */
363 if( long_options[ match ].flag == NULL )
364 {
365 red_optopt = long_options[ match ].val;
366 }
367 else
368 {
369 red_optopt = 0;
370 }
371
372 --red_optind;
373 return( BADARG );
374 }
375 }
376 else /* unknown option */
377 {
378 if( short_too )
379 {
380 --red_optind;
381 return( -1 );
382 }
383
384 if( PRINT_ERROR )
385 {
386 fprintf( stderr,
387 illoptstring,
388 current_dash,
389 current_argv );
390 }
391
392 red_optopt = 0;
393 return( BADCH );
394 }
395
396 if( idx )
397 {
398 *idx = match;
399 }
400
401 if( long_options[ match ].flag )
402 {
403 *long_options[ match ].flag = long_options[ match ].val;
404 return( 0 );
405 }
406 else
407 {
408 return( long_options[ match ].val );
409 }
410 }
411
412 /*
413 * getopt_internal --
414 * Parse argc/argv argument vector. Called by user level routines.
415 */
getopt_internal(int nargc,char * const * nargv,const char * options,const REDOPTION * long_options,int32_t * idx,int flags)416 static int getopt_internal( int nargc,
417 char * const * nargv,
418 const char * options,
419 const REDOPTION * long_options,
420 int32_t * idx,
421 int flags )
422 {
423 char * oli; /* option letter list index */
424 int optchar, short_too;
425
426 if( options == NULL )
427 {
428 return( -1 );
429 }
430
431 /*
432 * XXX Some GNU programs (like cvs) set red_optind to 0 instead of
433 * XXX using red_optreset. Work around this braindamage.
434 */
435 if( red_optind == 0 )
436 {
437 red_optind = red_optreset = 1;
438 }
439
440 /*
441 * Disable GNU extensions if options string begins with a '+'.
442 */
443 if( *options == '-' )
444 {
445 flags |= FLAG_ALLARGS;
446 }
447 else if( *options == '+' )
448 {
449 flags &= ~FLAG_PERMUTE;
450 }
451
452 if( ( *options == '+' ) || ( *options == '-' ) )
453 {
454 options++;
455 }
456
457 red_optarg = NULL;
458
459 if( red_optreset )
460 {
461 nonopt_start = nonopt_end = -1;
462 }
463
464 start:
465
466 if( red_optreset || !*place ) /* update scanning pointer */
467 {
468 red_optreset = 0;
469
470 if( red_optind >= nargc ) /* end of argument vector */
471 {
472 place = EMSG;
473
474 if( nonopt_end != -1 )
475 {
476 /* do permutation, if we have to */
477 permute_args( nonopt_start, nonopt_end,
478 red_optind, nargv );
479 red_optind -= nonopt_end - nonopt_start;
480 }
481 else if( nonopt_start != -1 )
482 {
483 /*
484 * If we skipped non-options, set red_optind
485 * to the first of them.
486 */
487 red_optind = nonopt_start;
488 }
489
490 nonopt_start = nonopt_end = -1;
491 return( -1 );
492 }
493
494 if( ( *( place = nargv[ red_optind ] ) != '-' ) || ( place[ 1 ] == '\0' ) )
495 {
496 place = EMSG; /* found non-option */
497
498 if( flags & FLAG_ALLARGS )
499 {
500 /*
501 * GNU extension:
502 * return non-option as argument to option 1
503 */
504 red_optarg = nargv[ red_optind++ ];
505 return( INORDER );
506 }
507
508 if( !( flags & FLAG_PERMUTE ) )
509 {
510 /*
511 * If no permutation wanted, stop parsing
512 * at first non-option.
513 */
514 return( -1 );
515 }
516
517 /* do permutation */
518 if( nonopt_start == -1 )
519 {
520 nonopt_start = red_optind;
521 }
522 else if( nonopt_end != -1 )
523 {
524 permute_args( nonopt_start, nonopt_end,
525 red_optind, nargv );
526 nonopt_start = red_optind -
527 ( nonopt_end - nonopt_start );
528 nonopt_end = -1;
529 }
530
531 red_optind++;
532 /* process next argument */
533 goto start;
534 }
535
536 if( ( nonopt_start != -1 ) && ( nonopt_end == -1 ) )
537 {
538 nonopt_end = red_optind;
539 }
540
541 /*
542 * If we have "-" do nothing, if "--" we are done.
543 */
544 if( ( place[ 1 ] != '\0' ) && ( *++place == '-' ) && ( place[ 1 ] == '\0' ) )
545 {
546 red_optind++;
547 place = EMSG;
548
549 /*
550 * We found an option (--), so if we skipped
551 * non-options, we have to permute.
552 */
553 if( nonopt_end != -1 )
554 {
555 permute_args( nonopt_start, nonopt_end,
556 red_optind, nargv );
557 red_optind -= nonopt_end - nonopt_start;
558 }
559
560 nonopt_start = nonopt_end = -1;
561 return( -1 );
562 }
563 }
564
565 /*
566 * Check long options if:
567 * 1) we were passed some
568 * 2) the arg is not just "-"
569 * 3) either the arg starts with -- we are RedGetoptLongOnly()
570 */
571 if( ( long_options != NULL ) && ( place != nargv[ red_optind ] ) &&
572 ( ( *place == '-' ) || ( flags & FLAG_LONGONLY ) ) )
573 {
574 short_too = 0;
575 dash_prefix = D_PREFIX;
576
577 if( *place == '-' )
578 {
579 place++; /* --foo long option */
580 dash_prefix = DD_PREFIX;
581 }
582 else if( ( *place != ':' ) && ( strchr( options, *place ) != NULL ) )
583 {
584 short_too = 1; /* could be short option too */
585 }
586
587 optchar = parse_long_options( nargv, options, long_options,
588 idx, short_too, flags );
589
590 if( optchar != -1 )
591 {
592 place = EMSG;
593 return( optchar );
594 }
595 }
596
597 if( ( ( optchar = ( int ) *place++ ) == ( int ) ':' ) ||
598 ( ( optchar == ( int ) '-' ) && ( *place != '\0' ) ) ||
599 ( ( oli = strchr( options, optchar ) ) == NULL ) )
600 {
601 /*
602 * If the user specified "-" and '-' isn't listed in
603 * options, return -1 (non-option) as per POSIX.
604 * Otherwise, it is an unknown option character (or ':').
605 */
606 if( ( optchar == ( int ) '-' ) && ( *place == '\0' ) )
607 {
608 return( -1 );
609 }
610
611 if( !*place )
612 {
613 ++red_optind;
614 }
615
616 if( PRINT_ERROR )
617 {
618 fprintf( stderr, gnuoptchar, optchar );
619 }
620
621 red_optopt = optchar;
622 return( BADCH );
623 }
624
625 if( ( long_options != NULL ) && ( optchar == 'W' ) && ( oli[ 1 ] == ';' ) )
626 {
627 /* -W long-option */
628 if( *place ) /* no space */
629 { /* NOTHING */
630 }
631 else if( ++red_optind >= nargc ) /* no arg */
632 {
633 place = EMSG;
634
635 if( PRINT_ERROR )
636 {
637 fprintf( stderr, recargchar, optchar );
638 }
639
640 red_optopt = optchar;
641 return( BADARG );
642 }
643 else /* white space */
644 {
645 place = nargv[ red_optind ];
646 }
647
648 dash_prefix = W_PREFIX;
649 optchar = parse_long_options( nargv, options, long_options,
650 idx, 0, flags );
651 place = EMSG;
652 return( optchar );
653 }
654
655 if( *++oli != ':' ) /* doesn't take argument */
656 {
657 if( !*place )
658 {
659 ++red_optind;
660 }
661 }
662 else /* takes (optional) argument */
663 {
664 red_optarg = NULL;
665
666 if( *place ) /* no white space */
667 {
668 red_optarg = place;
669 }
670 else if( oli[ 1 ] != ':' ) /* arg not optional */
671 {
672 if( ++red_optind >= nargc ) /* no arg */
673 {
674 place = EMSG;
675
676 if( PRINT_ERROR )
677 {
678 fprintf( stderr, recargchar, optchar );
679 }
680
681 red_optopt = optchar;
682 return( BADARG );
683 }
684 else
685 {
686 red_optarg = nargv[ red_optind ];
687 }
688 }
689
690 place = EMSG;
691 ++red_optind;
692 }
693
694 /* dump back option letter */
695 return( optchar );
696 }
697
698
699 /** @brief Get option character from command line argument list.
700 *
701 * For more details, consult the getopt() man pages, since this function is
702 * generally similar.
703 *
704 * @param nargc Number of arguments (argc passed into main()).
705 * @param nargv Argument vector (argv passed into main()).
706 * @param options String of option characters.
707 *
708 * @return The next known option character in @p options. If a character not
709 * found in @p options is found or if an option is missing an argument,
710 * it returns '?'. Returns -1 when the argument list is exhausted.
711 */
RedGetopt(int32_t nargc,char * const * nargv,const char * options)712 int32_t RedGetopt( int32_t nargc,
713 char * const * nargv,
714 const char * options )
715 {
716 return getopt_internal( nargc, nargv, options, NULL, NULL, FLAG_PERMUTE );
717 }
718
719
720 /** @brief Get long options from command line argument list.
721 *
722 * For more details, consult the getopt_long() man pages, since this function
723 * is generally similar.
724 *
725 * @param nargc Number of arguments (argc passed into main()).
726 * @param nargv Argument vector (argv passed into main()).
727 * @param options String of option characters.
728 * @param long_options The long options; the last element of this array must be
729 * filled with zeroes.
730 * @param idx If non-NULL, then populated with the index of the long
731 * option relative to @p long_options.
732 *
733 * @return If the flag field in REDOPTION is NULL, returns the value specified
734 * in the val field, which is usually just the corresponding short
735 * option. If flag is non-NULL, returns zero and stores val in the
736 * location pointed to by flag. Returns ':' if an option was missing
737 * its argument, '?' for an unknown option, and -1 when the argument
738 * list is exhausted.
739 */
RedGetoptLong(int32_t nargc,char * const * nargv,const char * options,const REDOPTION * long_options,int32_t * idx)740 int32_t RedGetoptLong( int32_t nargc,
741 char * const * nargv,
742 const char * options,
743 const REDOPTION * long_options,
744 int32_t * idx )
745 {
746 return getopt_internal( nargc, nargv, options, long_options, idx, FLAG_PERMUTE );
747 }
748