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