1 
2 /*    $OpenBSD: getopt_long.c,v 1.17 2004/06/03 18:46:52 millert Exp $    */
3 /*    $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $    */
4 
5 /*
6  * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
13  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
14  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
15  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
17  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 /*-
21  * Copyright (c) 2000 The NetBSD Foundation, Inc.
22  * All rights reserved.
23  *
24  * This code is derived from software contributed to The NetBSD Foundation
25  * by Dieter Baron and Thomas Klausner.
26  *
27  * Redistribution and use in source and binary forms, with or without
28  * modification, are permitted provided that the following conditions
29  * are met:
30  * 1. Redistributions of source code must retain the above copyright
31  *    notice, this list of conditions and the following disclaimer.
32  * 2. Redistributions in binary form must reproduce the above copyright
33  *    notice, this list of conditions and the following disclaimer in the
34  *    documentation and/or other materials provided with the distribution.
35  * 3. All advertising materials mentioning features or use of this software
36  *    must display the following acknowledgement:
37  *        This product includes software developed by the NetBSD
38  *        Foundation, Inc. and its contributors.
39  * 4. Neither the name of The NetBSD Foundation nor the names of its
40  *    contributors may be used to endorse or promote products derived
41  *    from this software without specific prior written permission.
42  *
43  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
44  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
45  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
46  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
47  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
48  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
49  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
50  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
51  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
52  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
53  * POSSIBILITY OF SUCH DAMAGE.
54  */
55 
56 #define IN_GETOPT_LONG_C 1
57 
58 #include <config.h>
59 #include "blogbench.h"
60 
61 #ifndef HAVE_GETOPT_LONG
62 
63 # include "bsd-getopt_long.h"
64 
65 # ifdef WITH_DMALLOC
66 #  include <dmalloc.h>
67 # endif
68 
69 int    pure_opterr = 1;        /* if error message should be printed */
70 int    pure_optind = 1;        /* index into parent argv vector */
71 int    pure_optopt = '?';        /* character checked for validity */
72 int    pure_optreset;        /* reset getopt */
73 const char    *pure_optarg;        /* argument associated with option */
74 
75 # define PRINT_ERROR    ((pure_opterr) && (*options != ':'))
76 
77 # define FLAG_PERMUTE    0x01    /* permute non-options to the end of argv */
78 # define FLAG_ALLARGS    0x02    /* treat non-options as args to option "-1" */
79 # define FLAG_LONGONLY    0x04    /* operate as pure_pure_getopt_long_only */
80 
81 /* return values */
82 # define    BADCH        (int)'?'
83 # define    BADARG        ((*options == ':') ? (int)':' : (int)'?')
84 # define    INORDER     (int)1
85 
86 # define    EMSG        ""
87 
88 static int pure_getopt_internal(int, char * const *, const char *,
89                                 const struct pure_option *, int *, int);
90 static int pure_parse_long_options(char * const *, const char *,
91                                    const struct pure_option *, int *, int);
92 static int pure_gcd(int, int);
93 static void pure_permute_args(int, int, int, char * const *);
94 
95 static const char *pure_place = EMSG; /* option letter processing */
96 
97 /* XXX: set pure_optreset to 1 rather than these two */
98 static int nonopt_start = -1; /* first non option argument (for permute) */
99 static int nonopt_end = -1;   /* first option after non options (for permute) */
100 
101 /* Error messages */
102 static const char *recargchar = "option requires an argument -- %c";
103 static const char *recargstring = "option requires an argument -- %s";
104 static const char *ambig = "ambiguous option -- %.*s";
105 static const char *noarg = "option doesn't take an argument -- %.*s";
106 static const char *illoptchar = "unknown option -- %c";
107 static const char *illoptstring = "unknown option -- %s";
108 
109 /*
110  * Compute the greatest common divisor of a and b.
111  */
pure_gcd(int a,int b)112 static int pure_gcd(int a, int b)
113 {
114     int c;
115 
116     c = a % b;
117     while (c != 0) {
118         a = b;
119         b = c;
120         c = a % b;
121     }
122     return b;
123 }
124 
125 /*
126  * Exchange the block from nonopt_start to nonopt_end with the block
127  * from nonopt_end to opt_end (keeping the same order of arguments
128  * in each block).
129  */
pure_permute_args(int panonopt_start,int panonopt_end,int opt_end,char * const * nargv)130 static void pure_permute_args(int panonopt_start, int panonopt_end,
131                               int opt_end, char * const *nargv)
132 {
133     int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
134     char *swap;
135 
136     /*
137      * compute lengths of blocks and number and size of cycles
138      */
139     nnonopts = panonopt_end - panonopt_start;
140     nopts = opt_end - panonopt_end;
141     ncycle = pure_gcd(nnonopts, nopts);
142     cyclelen = (opt_end - panonopt_start) / ncycle;
143 
144     for (i = 0; i < ncycle; i++) {
145         cstart = panonopt_end+i;
146         pos = cstart;
147         for (j = 0; j < cyclelen; j++) {
148             if (pos >= panonopt_end)
149                 pos -= nnonopts;
150             else
151                 pos += nopts;
152             swap = nargv[pos];
153             /* LINTED const cast */
154             ((char **) nargv)[pos] = nargv[cstart];
155             /* LINTED const cast */
156             ((char **)nargv)[cstart] = swap;
157         }
158     }
159 }
160 
161 /*
162  * pure_parse_long_options --
163  *    Parse long options in argc/argv argument vector.
164  * Returns -1 if short_too is set and the option does not match long_options.
165  */
pure_parse_long_options(char * const * nargv,const char * options,const struct pure_option * long_options,int * idx,int short_too)166 static int pure_parse_long_options(char * const *nargv, const char *options,
167                                    const struct pure_option *long_options,
168                                    int *idx, int short_too)
169 {
170     const char *current_argv, *has_equal;
171     size_t current_argv_len;
172     int i, match;
173 
174     current_argv = pure_place;
175     match = -1;
176 
177     pure_optind++;
178 
179     if ((has_equal = strchr(current_argv, '=')) != NULL) {
180         /* argument found (--option=arg) */
181         current_argv_len = has_equal - current_argv;
182         has_equal++;
183     } else
184         current_argv_len = strlen(current_argv);
185 
186     for (i = 0; long_options[i].name; i++) {
187         /* find matching long option */
188         if (strncmp(current_argv, long_options[i].name,
189             current_argv_len))
190             continue;
191 
192         if (strlen(long_options[i].name) == current_argv_len) {
193             /* exact match */
194             match = i;
195             break;
196         }
197         /*
198          * If this is a known short option, don't allow
199          * a partial match of a single character.
200          */
201         if (short_too && current_argv_len == 1)
202             continue;
203 
204         if (match == -1)    /* partial match */
205             match = i;
206         else {
207             /* ambiguous abbreviation */
208             if (PRINT_ERROR)
209                 fprintf(stderr, ambig, (int)current_argv_len,
210                         current_argv);
211             pure_optopt = 0;
212             return BADCH;
213         }
214     }
215     if (match != -1) {        /* option found */
216         if (long_options[match].has_arg == no_argument
217             && has_equal) {
218             if (PRINT_ERROR)
219                 fprintf(stderr, noarg, (int)current_argv_len,
220                         current_argv);
221             /*
222              * XXX: GNU sets pure_optopt to val regardless of flag
223              */
224             if (long_options[match].flag == NULL)
225                 pure_optopt = long_options[match].val;
226             else
227                 pure_optopt = 0;
228             return BADARG;
229         }
230         if (long_options[match].has_arg == required_argument ||
231             long_options[match].has_arg == optional_argument) {
232             if (has_equal)
233                 pure_optarg = has_equal;
234             else if (long_options[match].has_arg ==
235                      required_argument) {
236                 /*
237                  * optional argument doesn't use next nargv
238                  */
239                 pure_optarg = nargv[pure_optind++];
240             }
241         }
242         if ((long_options[match].has_arg == required_argument)
243             && (pure_optarg == NULL)) {
244             /*
245              * Missing argument; leading ':' indicates no error
246              * should be generated.
247              */
248             if (PRINT_ERROR)
249                 fprintf(stderr, recargstring,
250                         current_argv);
251             /*
252              * XXX: GNU sets pure_optopt to val regardless of flag
253              */
254             if (long_options[match].flag == NULL)
255                 pure_optopt = long_options[match].val;
256             else
257                 pure_optopt = 0;
258             --pure_optind;
259             return BADARG;
260         }
261     } else {            /* unknown option */
262         if (short_too) {
263             --pure_optind;
264             return -1;
265         }
266         if (PRINT_ERROR)
267             fprintf(stderr, illoptstring, current_argv);
268         pure_optopt = 0;
269         return BADCH;
270     }
271     if (idx)
272         *idx = match;
273     if (long_options[match].flag) {
274         *long_options[match].flag = long_options[match].val;
275         return 0;
276     } else
277         return long_options[match].val;
278 }
279 
280 /*
281  * getopt_internal --
282  *    Parse argc/argv argument vector.  Called by user level routines.
283  */
pure_getopt_internal(int nargc,char * const * nargv,const char * options,const struct pure_option * long_options,int * idx,int flags)284 static int pure_getopt_internal(int nargc, char * const *nargv,
285                                 const char *options,
286                                 const struct pure_option *long_options,
287                                 int *idx, int flags)
288 {
289     char *oli;                /* option letter list index */
290     int optchar, short_too;
291     static int posixly_correct = -1;
292 
293     if (options == NULL)
294         return -1;
295 
296     /*
297      * Disable GNU extensions if POSIXLY_CORRECT is set or options
298      * string begins with a '+'.
299      */
300     if (posixly_correct == -1)
301         posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
302     if (posixly_correct || *options == '+')
303         flags &= ~FLAG_PERMUTE;
304     else if (*options == '-')
305         flags |= FLAG_ALLARGS;
306     if (*options == '+' || *options == '-')
307         options++;
308 
309     /*
310      * XXX Some GNU programs (like cvs) set pure_optind to 0 instead of
311      * XXX using pure_optreset.  Work around this braindamage.
312      */
313     if (pure_optind == 0)
314         pure_optind = pure_optreset = 1;
315 
316     pure_optarg = NULL;
317     if (pure_optreset)
318         nonopt_start = nonopt_end = -1;
319     start:
320     if (pure_optreset || !*pure_place) {        /* update scanning pointer */
321         pure_optreset = 0;
322         if (pure_optind >= nargc) {          /* end of argument vector */
323             pure_place = EMSG;
324             if (nonopt_end != -1) {
325                 /* do permutation, if we have to */
326                 pure_permute_args(nonopt_start, nonopt_end,
327                                   pure_optind, nargv);
328                 pure_optind -= nonopt_end - nonopt_start;
329             }
330             else if (nonopt_start != -1) {
331                 /*
332                  * If we skipped non-options, set pure_optind
333                  * to the first of them.
334                  */
335                 pure_optind = nonopt_start;
336             }
337             nonopt_start = nonopt_end = -1;
338             return -1;
339         }
340         if (*(pure_place = nargv[pure_optind]) != '-' ||
341             (pure_place[1] == '\0' && strchr(options, '-') == NULL)) {
342             pure_place = EMSG;        /* found non-option */
343             if (flags & FLAG_ALLARGS) {
344                 /*
345                  * GNU extension:
346                  * return non-option as argument to option 1
347                  */
348                 pure_optarg = nargv[pure_optind++];
349                 return INORDER;
350             }
351             if (!(flags & FLAG_PERMUTE)) {
352                 /*
353                  * If no permutation wanted, stop parsing
354                  * at first non-option.
355                  */
356                 return -1;
357             }
358             /* do permutation */
359             if (nonopt_start == -1)
360                 nonopt_start = pure_optind;
361             else if (nonopt_end != -1) {
362                 pure_permute_args(nonopt_start, nonopt_end,
363                                   pure_optind, nargv);
364                 nonopt_start = pure_optind -
365                     (nonopt_end - nonopt_start);
366                 nonopt_end = -1;
367             }
368             pure_optind++;
369             /* process next argument */
370             goto start;
371         }
372         if (nonopt_start != -1 && nonopt_end == -1)
373             nonopt_end = pure_optind;
374 
375         /* If we have "-" do nothing, if "--" we are done. */
376 
377         if (pure_place[1] != '\0' && *++pure_place == '-' &&
378             pure_place[1] == '\0') {
379             pure_optind++;
380             pure_place = EMSG;
381             /*
382              * We found an option (--), so if we skipped
383              * non-options, we have to permute.
384              */
385             if (nonopt_end != -1) {
386                 pure_permute_args(nonopt_start, nonopt_end,
387                                   pure_optind, nargv);
388                 pure_optind -= nonopt_end - nonopt_start;
389             }
390             nonopt_start = nonopt_end = -1;
391             return -1;
392         }
393     }
394 
395     /*
396      * Check long options if:
397      *  1) we were passed some
398      *  2) the arg is not just "-"
399      *  3) either the arg starts with -- we are pure_pure_getopt_long_only()
400      */
401     if (long_options != NULL && pure_place != nargv[pure_optind] &&
402         (*pure_place == '-' || (flags & FLAG_LONGONLY))) {
403         short_too = 0;
404         if (*pure_place == '-')
405             pure_place++;        /* --foo long option */
406         else if (*pure_place != ':' && strchr(options, *pure_place) != NULL)
407             short_too = 1;        /* could be short option too */
408 
409         optchar = pure_parse_long_options(nargv, options, long_options,
410                                           idx, short_too);
411         if (optchar != -1) {
412             pure_place = EMSG;
413             return optchar;
414         }
415     }
416 
417     if ((optchar = (int)*pure_place++) == (int)':' ||
418         (oli = strchr(options, optchar)) == NULL) {
419         /*
420          * If the user specified "-" and '-' isn't listed in
421          * options, return -1 (non-option) as per POSIX.
422          * Otherwise, it is an unknown option character (or :').
423          */
424         if (optchar == (int) '-' && *pure_place == '\0')
425             return -1;
426         if (!*pure_place)
427             ++pure_optind;
428         if (PRINT_ERROR)
429             fprintf(stderr, illoptchar, optchar);
430         pure_optopt = optchar;
431         return BADCH;
432     }
433     if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
434         /* -W long-option */
435         if (*pure_place)            /* no space */
436             /* NOTHING */;
437         else if (++pure_optind >= nargc) {    /* no arg */
438             pure_place = EMSG;
439             if (PRINT_ERROR)
440                 fprintf(stderr, recargchar, optchar);
441             pure_optopt = optchar;
442             return BADARG;
443         } else                /* white space */
444             pure_place = nargv[pure_optind];
445         optchar = pure_parse_long_options(nargv, options, long_options,
446                                           idx, 0);
447         pure_place = EMSG;
448         return optchar;
449     }
450     if (*++oli != ':') {            /* doesn't take argument */
451         if (!*pure_place)
452             ++pure_optind;
453     } else {                /* takes (optional) argument */
454         pure_optarg = NULL;
455         if (*pure_place)            /* no white space */
456             pure_optarg = pure_place;
457         /* XXX: disable test for :: if PC? (GNU doesn't) */
458         else if (oli[1] != ':') {    /* arg not optional */
459             if (++pure_optind >= nargc) {    /* no arg */
460                 pure_place = EMSG;
461                 if (PRINT_ERROR)
462                     fprintf(stderr, recargchar, optchar);
463                 pure_optopt = optchar;
464                 return BADARG;
465             } else if (!(flags & FLAG_PERMUTE)) {
466                 /*
467                  * If permutation is disabled, we can accept an
468                  * optional arg separated by whitespace so long
469                  * as it does not start with a dash (-).
470                  */
471                  if (pure_optind + 1 < nargc && pure_optind + 1 > 0 &&
472                      *nargv[pure_optind + 1] != '-') {
473                      pure_optarg = nargv[++pure_optind];
474                  }
475             }
476         }
477         pure_place = EMSG;
478         ++pure_optind;
479     }
480     /* dump back option letter */
481     return optchar;
482 }
483 
484 /*
485  * getopt --
486  *    Parse argc/argv argument vector.
487  */
pure_getopt(int nargc,char * const * nargv,const char * options)488 int pure_getopt(int nargc, char * const *nargv, const char *options)
489 {
490 
491     /*
492      * We dont' pass FLAG_PERMUTE to pure_getopt_internal() since
493      * the BSD getopt(3) (unlike GNU) has never done this.
494      *
495      * Furthermore, since many privileged programs call getopt()
496      * before dropping privileges it makes sense to keep things
497      * as simple (and bug-free) as possible.
498      */
499     return pure_getopt_internal(nargc, nargv, options, NULL, NULL, 0);
500 }
501 
502 /*
503  * pure_getopt_long --
504  *    Parse argc/argv argument vector.
505  */
pure_getopt_long(int nargc,char * const * nargv,const char * options,const struct pure_option * long_options,int * idx)506 int pure_getopt_long(int nargc, char * const *nargv, const char *options,
507                      const struct pure_option *long_options, int *idx)
508 {
509     return pure_getopt_internal(nargc, nargv, options, long_options, idx,
510                                 FLAG_PERMUTE);
511 }
512 
513 /*
514  * pure_pure_getopt_long_only --
515  *    Parse argc/argv argument vector.
516  */
pure_pure_getopt_long_only(int nargc,char * const * nargv,const char * options,const struct pure_option * long_options,int * idx)517 int pure_pure_getopt_long_only(int nargc, char * const *nargv,
518                                const char *options,
519                                const struct pure_option *long_options,
520                                int *idx)
521 {
522     return pure_getopt_internal(nargc, nargv, options, long_options, idx,
523                                 FLAG_PERMUTE|FLAG_LONGONLY);
524 }
525 
526 #endif
527