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