1 /* $NetBSD: getopt_long.c,v 1.16 2003/10/27 00:12:42 lukem Exp $ */
2 /* $DragonFly: src/lib/libc/stdlib/getopt_long.c,v 1.5 2005/01/10 17:40:32 joerg Exp $ */
5 * Copyright (c) 2000 The NetBSD Foundation, Inc.
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Dieter Baron and Thomas Klausner.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
40 #include <sys/cdefs.h>
49 int opterr = 1; /* if error message should be printed */
50 int optind = 1; /* index into parent argv vector */
51 int optopt = '?'; /* character checked for validity */
52 int optreset; /* reset getopt */
53 char *optarg; /* argument associated with option */
56 #define IGNORE_FIRST (*options == '-' || *options == '+')
57 #define PRINT_ERROR ((opterr) && ((*options != ':') \
58 || (IGNORE_FIRST && options[1] != ':')))
59 #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
60 #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
61 /* XXX: GNU ignores PC if *options == '-' */
62 #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
65 #define BADCH (int)'?'
66 #define BADARG ((IGNORE_FIRST && options[1] == ':') \
67 || (*options == ':') ? (int)':' : (int)'?')
68 #define INORDER (int)1
70 static int getopt_internal(int, char * const *, const char *, int);
71 static int getopt_internal_short(int, char * const *, const char *, int);
72 static int getopt_long_internal(int, char * const *, const char *,
73 const struct option *, int *, int);
74 static int gcd(int, int);
75 static void permute_args(int, int, int, char * const *);
77 static char EMSG[] = {0};
78 static char *place = EMSG; /* option letter processing */
80 /* XXX: set optreset to 1 rather than these two */
81 static int nonopt_start = -1; /* first non option argument (for permute) */
82 static int nonopt_end = -1; /* first option after non options (for permute) */
85 static const char recargchar[] = "option requires an argument -- %c";
86 static const char recargstring[] = "option requires an argument -- %s";
87 static const char ambig[] = "ambiguous option -- %.*s";
88 static const char noarg[] = "option doesn't take an argument -- %.*s";
89 static const char illoptchar[] = "unknown option -- %c";
90 static const char illoptstring[] = "unknown option -- %s";
94 * Compute the greatest common divisor of a and b.
112 * Exchange the block from nonopt_start to nonopt_end with the block
113 * from nonopt_end to opt_end (keeping the same order of arguments
117 permute_args(int panonopt_start, int panonopt_end, int opt_end,
120 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
124 * compute lengths of blocks and number and size of cycles
126 nnonopts = panonopt_end - panonopt_start;
127 nopts = opt_end - panonopt_end;
128 ncycle = gcd(nnonopts, nopts);
129 cyclelen = (opt_end - panonopt_start) / ncycle;
131 for (i = 0; i < ncycle; i++) {
132 cstart = panonopt_end+i;
134 for (j = 0; j < cyclelen; j++) {
135 if (pos >= panonopt_end)
140 /* LINTED const cast */
141 ((char **) nargv)[pos] = nargv[cstart];
142 /* LINTED const cast */
143 ((char **)nargv)[cstart] = swap;
150 * Parse argc/argv argument vector. Called by user level routines.
151 * Returns -2 if -- is found (can be long option or end of options marker).
154 getopt_internal(int nargc, char * const *nargv, const char *options,
160 * XXX Some programs (like rsyncd) expect to be able to
161 * XXX re-initialize optind to 0 and have getopt_long(3)
162 * XXX properly function again. Work around this braindamage.
168 nonopt_start = nonopt_end = -1;
170 if (optreset || !*place) { /* update scanning pointer */
172 if (optind >= nargc) { /* end of argument vector */
174 if (nonopt_end != -1) {
175 /* do permutation, if we have to */
176 permute_args(nonopt_start, nonopt_end,
178 optind -= nonopt_end - nonopt_start;
180 else if (nonopt_start != -1) {
182 * If we skipped non-options, set optind
183 * to the first of them.
185 optind = nonopt_start;
187 nonopt_start = nonopt_end = -1;
190 if ((*(place = nargv[optind]) != '-')
191 || (place[1] == '\0')) { /* found non-option */
196 * return non-option as argument to option 1
198 optarg = nargv[optind++];
203 * if no permutation wanted, stop parsing
204 * at first non-option
209 if (nonopt_start == -1)
210 nonopt_start = optind;
211 else if (nonopt_end != -1) {
212 permute_args(nonopt_start, nonopt_end,
214 nonopt_start = optind -
215 (nonopt_end - nonopt_start);
219 /* process next argument */
222 if (nonopt_start != -1 && nonopt_end == -1)
224 if (place[1] && *++place == '-') { /* found "--" */
225 if (place[1] == '\0') {
228 * We found an option (--), so if we skipped
229 * non-options, we have to permute.
231 if (nonopt_end != -1) {
232 permute_args(nonopt_start, nonopt_end,
234 optind -= nonopt_end - nonopt_start;
236 nonopt_start = nonopt_end = -1;
238 } else if (long_support) {
244 if (long_support == 2 && (place[1] || strchr(options, *place) == NULL))
246 return getopt_internal_short(nargc, nargv, options, long_support);
250 getopt_internal_short(int nargc, char * const *nargv, const char *options,
253 const char *oli; /* option letter list index */
256 if ((optchar = (int)*place++) == (int)':' ||
257 (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
258 /* option letter unknown or ':' */
262 warnx(illoptchar, optchar);
266 if (long_support && optchar == 'W' && oli[1] == ';') {
271 if (++optind >= nargc) { /* no arg */
274 warnx(recargchar, optchar);
277 } else /* white space */
278 place = nargv[optind];
280 * Handle -W arg the same as --arg (which causes getopt to
285 if (*++oli != ':') { /* doesn't take argument */
288 } else { /* takes (optional) argument */
290 if (*place) /* no white space */
292 /* XXX: disable test for :: if PC? (GNU doesn't) */
293 else if (oli[1] != ':') { /* arg not optional */
294 if (++optind >= nargc) { /* no arg */
297 warnx(recargchar, optchar);
301 optarg = nargv[optind];
306 /* dump back option letter */
311 getopt_long_internal(int nargc, char * const *nargv, const char *options,
312 const struct option *long_options, int *idx, int long_only)
316 /* idx may be NULL */
318 retval = getopt_internal(nargc, nargv, options, long_only ? 2 : 1);
320 if (retval == -2 || retval == -3) {
321 char *current_argv, *has_equal;
322 size_t current_argv_len;
325 current_argv = place;
331 if ((has_equal = strchr(current_argv, '=')) != NULL) {
332 /* argument found (--option=arg) */
333 current_argv_len = has_equal - current_argv;
336 current_argv_len = strlen(current_argv);
338 for (i = 0; long_options[i].name; i++) {
339 /* find matching long option */
340 if (strncmp(current_argv, long_options[i].name,
344 if (strlen(long_options[i].name) ==
345 (unsigned)current_argv_len) {
350 if (match == -1) /* partial match */
353 /* ambiguous abbreviation */
355 warnx(ambig, (int)current_argv_len,
361 if (match != -1) { /* option found */
362 if (long_options[match].has_arg == no_argument
365 warnx(noarg, (int)current_argv_len,
368 * XXX: GNU sets optopt to val regardless of
371 if (long_options[match].flag == NULL)
372 optopt = long_options[match].val;
377 if (long_options[match].has_arg == required_argument ||
378 long_options[match].has_arg == optional_argument) {
381 else if (long_options[match].has_arg ==
384 * optional argument doesn't use
387 optarg = nargv[optind++];
390 if ((long_options[match].has_arg == required_argument)
391 && (optarg == NULL)) {
393 * Missing argument; leading ':'
394 * indicates no error should be generated
397 warnx(recargstring, current_argv);
399 * XXX: GNU sets optopt to val regardless
402 if (long_options[match].flag == NULL)
403 optopt = long_options[match].val;
409 } else if (retval == -3) {
411 place = current_argv;
412 retval = getopt_internal_short(nargc, nargv,
413 options, long_only ? 2 : 1);
415 } else { /* unknown option */
417 warnx(illoptstring, current_argv);
421 if (long_options[match].flag) {
422 *long_options[match].flag = long_options[match].val;
425 retval = long_options[match].val;
432 #ifdef REPLACE_GETOPT
435 * Parse argc/argv argument vector.
437 * [eventually this will replace the real getopt]
440 getopt(int nargc, char * const *nargv, const char *options)
444 return getopt_internal(nargc, nargv, options, 0);
450 * Parse argc/argv argument vector.
454 getopt_long(int nargc, char * const *nargv, const char *options,
455 const struct option *long_options, int *idx)
457 return getopt_long_internal(nargc, nargv, options, long_options,
462 * getopt_long_only --
463 * Parse argc/argv argument vector.
464 * Prefers long options over short options for single dash arguments.
467 int getopt_long_only(int nargc, char * const *nargv, const char *options,
468 const struct option *long_options, int *idx)
470 return getopt_long_internal(nargc, nargv, options, long_options,