Home | History | Annotate | Line # | Download | only in missing
      1 /*	$OpenBSD: getopt_long.c,v 1.22 2006/10/04 21:29:04 jmc 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 <Todd.Miller (at) courtesan.com>
      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 
     52 
     53 #include <errno.h>
     54 #include "getopt_long.h"
     55 #include <stdlib.h>
     56 #include <stdio.h>
     57 #include <string.h>
     58 #include <stdarg.h>
     59 
     60 #include "diag-control.h"
     61 
     62 #define GNU_COMPATIBLE		/* Be more compatible, configure's use us! */
     63 
     64 #define PRINT_ERROR	((opterr) && (*options != ':'))
     65 
     66 #define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
     67 #define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
     68 #define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
     69 
     70 /* return values */
     71 #define	BADCH		(int)'?'
     72 #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
     73 #define	INORDER		(int)1
     74 
     75 #define	EMSG		""
     76 
     77 #ifdef GNU_COMPATIBLE
     78 #define NO_PREFIX	(-1)
     79 #define D_PREFIX	0
     80 #define DD_PREFIX	1
     81 #define W_PREFIX	2
     82 #endif
     83 
     84 char *optarg;
     85 int optind, opterr = 1, optopt;
     86 
     87 static int getopt_internal(int, char * const *, const char *,
     88 			   const struct option *, int *, int);
     89 static int parse_long_options(char * const *, const char *,
     90 			      const struct option *, int *, int, int);
     91 static int gcd(int, int);
     92 static void permute_args(int, int, int, char * const *);
     93 
     94 static const char *place = EMSG; /* option letter processing */
     95 
     96 static int nonopt_start = -1; /* first non option argument (for permute) */
     97 static int nonopt_end = -1;   /* first option after non options (for permute) */
     98 
     99 /* Error messages */
    100 static const char recargchar[] = "option requires an argument -- %c";
    101 static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
    102 #ifdef GNU_COMPATIBLE
    103 static int dash_prefix = NO_PREFIX;
    104 static const char gnuoptchar[] = "invalid option -- %c";
    105 
    106 static const char recargstring[] = "option `%s%s' requires an argument";
    107 static const char ambig[] = "option `%s%.*s' is ambiguous";
    108 static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
    109 static const char illoptstring[] = "unrecognized option `%s%s'";
    110 #else
    111 static const char recargstring[] = "option requires an argument -- %s";
    112 static const char ambig[] = "ambiguous option -- %.*s";
    113 static const char noarg[] = "option doesn't take an argument -- %.*s";
    114 static const char illoptstring[] = "unknown option -- %s";
    115 #endif
    116 
    117 /*
    118  * Compute the greatest common divisor of a and b.
    119  */
    120 static int
    121 gcd(int a, int b)
    122 {
    123 	int c;
    124 
    125 	c = a % b;
    126 	while (c != 0) {
    127 		a = b;
    128 		b = c;
    129 		c = a % b;
    130 	}
    131 
    132 	return (b);
    133 }
    134 
    135 /*
    136  * Exchange the block from nonopt_start to nonopt_end with the block
    137  * from nonopt_end to opt_end (keeping the same order of arguments
    138  * in each block).
    139  */
    140 static void
    141 permute_args(int panonopt_start, int panonopt_end, int opt_end,
    142 	char * const *nargv)
    143 {
    144 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
    145 	char *swap;
    146 
    147 	/*
    148 	 * compute lengths of blocks and number and size of cycles
    149 	 */
    150 	nnonopts = panonopt_end - panonopt_start;
    151 	nopts = opt_end - panonopt_end;
    152 	ncycle = gcd(nnonopts, nopts);
    153 	cyclelen = (opt_end - panonopt_start) / ncycle;
    154 
    155 	for (i = 0; i < ncycle; i++) {
    156 		cstart = panonopt_end+i;
    157 		pos = cstart;
    158 		for (j = 0; j < cyclelen; j++) {
    159 			if (pos >= panonopt_end)
    160 				pos -= nnonopts;
    161 			else
    162 				pos += nopts;
    163 			/*
    164 			 * This is annoying - I guess the
    165 			 * "char * const argv[]" in the declaration
    166 			 * of getopt() - and thus getopt_long() -
    167 			 * means that it makes a promise not to
    168 			 * shuffle the arguments, but here we are,
    169 			 * shuffling the arguments.
    170 			 *
    171 			 * (No, it's not a promise that it won't
    172 			 * modify any of the argument strings.
    173 			 * It's a promise that it won't modify
    174 			 * the array of pointers to the argument
    175 			 * strings.)
    176 			 *
    177 			 * So squelch the cast warnings.
    178 			 */
    179 			swap = nargv[pos];
    180 DIAG_OFF_CAST_QUAL
    181 			/* LINTED const cast */
    182 			((char **) nargv)[pos] = nargv[cstart];
    183 			/* LINTED const cast */
    184 			((char **)nargv)[cstart] = swap;
    185 DIAG_ON_CAST_QUAL
    186 		}
    187 	}
    188 }
    189 
    190 static void
    191 warnx(const char *fmt, ...)
    192 {
    193 	extern char *program_name;
    194 	va_list ap;
    195 
    196 	va_start(ap, fmt);
    197 	fprintf(stderr, "%s: ", program_name);
    198 	vfprintf(stderr, fmt, ap);
    199 	fprintf(stderr, "\n");
    200 	va_end(ap);
    201 }
    202 
    203 /*
    204  * parse_long_options --
    205  *	Parse long options in argc/argv argument vector.
    206  * Returns -1 if short_too is set and the option does not match long_options.
    207  */
    208 static int
    209 parse_long_options(char * const *nargv, const char *options,
    210 	const struct option *long_options, int *idx, int short_too, int flags)
    211 {
    212 	const char *current_argv, *has_equal;
    213 #ifdef GNU_COMPATIBLE
    214 	const char *current_dash;
    215 #endif
    216 	size_t current_argv_len;
    217 	int i, match, exact_match, second_partial_match;
    218 
    219 	current_argv = place;
    220 #ifdef GNU_COMPATIBLE
    221 	switch (dash_prefix) {
    222 		case D_PREFIX:
    223 			current_dash = "-";
    224 			break;
    225 		case DD_PREFIX:
    226 			current_dash = "--";
    227 			break;
    228 		case W_PREFIX:
    229 			current_dash = "-W ";
    230 			break;
    231 		default:
    232 			current_dash = "";
    233 			break;
    234 	}
    235 #endif
    236 	match = -1;
    237 	exact_match = 0;
    238 	second_partial_match = 0;
    239 
    240 	optind++;
    241 
    242 	if ((has_equal = strchr(current_argv, '=')) != NULL) {
    243 		/* argument found (--option=arg) */
    244 		current_argv_len = has_equal - current_argv;
    245 		has_equal++;
    246 	} else
    247 		current_argv_len = strlen(current_argv);
    248 
    249 	for (i = 0; long_options[i].name; i++) {
    250 		/* find matching long option */
    251 		if (strncmp(current_argv, long_options[i].name,
    252 		    current_argv_len))
    253 			continue;
    254 
    255 		if (strlen(long_options[i].name) == current_argv_len) {
    256 			/* exact match */
    257 			match = i;
    258 			exact_match = 1;
    259 			break;
    260 		}
    261 		/*
    262 		 * If this is a known short option, don't allow
    263 		 * a partial match of a single character.
    264 		 */
    265 		if (short_too && current_argv_len == 1)
    266 			continue;
    267 
    268 		if (match == -1)        /* first partial match */
    269 			match = i;
    270 		else if ((flags & FLAG_LONGONLY) ||
    271 			 long_options[i].has_arg !=
    272 			     long_options[match].has_arg ||
    273 			 long_options[i].flag != long_options[match].flag ||
    274 			 long_options[i].val != long_options[match].val)
    275 			second_partial_match = 1;
    276 	}
    277 	if (!exact_match && second_partial_match) {
    278 		/* ambiguous abbreviation */
    279 		if (PRINT_ERROR)
    280 			warnx(ambig,
    281 #ifdef GNU_COMPATIBLE
    282 			     current_dash,
    283 #endif
    284 			     (int)current_argv_len,
    285 			     current_argv);
    286 		optopt = 0;
    287 		return (BADCH);
    288 	}
    289 	if (match != -1) {		/* option found */
    290 		if (long_options[match].has_arg == no_argument
    291 		    && has_equal) {
    292 			if (PRINT_ERROR)
    293 				warnx(noarg,
    294 #ifdef GNU_COMPATIBLE
    295 				     current_dash,
    296 #endif
    297 				     (int)current_argv_len,
    298 				     current_argv);
    299 			/*
    300 			 * XXX: GNU sets optopt to val regardless of flag
    301 			 */
    302 			if (long_options[match].flag == NULL)
    303 				optopt = long_options[match].val;
    304 			else
    305 				optopt = 0;
    306 #ifdef GNU_COMPATIBLE
    307 			return (BADCH);
    308 #else
    309 			return (BADARG);
    310 #endif
    311 		}
    312 		if (long_options[match].has_arg == required_argument ||
    313 		    long_options[match].has_arg == optional_argument) {
    314 DIAG_OFF_CAST_QUAL
    315 			if (has_equal)
    316 				optarg = (char *)has_equal;
    317 			else if (long_options[match].has_arg ==
    318 			    required_argument) {
    319 				/*
    320 				 * optional argument doesn't use next nargv
    321 				 */
    322 				optarg = nargv[optind++];
    323 			}
    324 DIAG_ON_CAST_QUAL
    325 		}
    326 		if ((long_options[match].has_arg == required_argument)
    327 		    && (optarg == NULL)) {
    328 			/*
    329 			 * Missing argument; leading ':' indicates no error
    330 			 * should be generated.
    331 			 */
    332 			if (PRINT_ERROR)
    333 				warnx(recargstring,
    334 #ifdef GNU_COMPATIBLE
    335 				    current_dash,
    336 #endif
    337 				    current_argv);
    338 			/*
    339 			 * XXX: GNU sets optopt to val regardless of flag
    340 			 */
    341 			if (long_options[match].flag == NULL)
    342 				optopt = long_options[match].val;
    343 			else
    344 				optopt = 0;
    345 			--optind;
    346 			return (BADARG);
    347 		}
    348 	} else {			/* unknown option */
    349 		if (short_too) {
    350 			--optind;
    351 			return (-1);
    352 		}
    353 		if (PRINT_ERROR)
    354 			warnx(illoptstring,
    355 #ifdef GNU_COMPATIBLE
    356 			      current_dash,
    357 #endif
    358 			      current_argv);
    359 		optopt = 0;
    360 		return (BADCH);
    361 	}
    362 	if (idx)
    363 		*idx = match;
    364 	if (long_options[match].flag) {
    365 		*long_options[match].flag = long_options[match].val;
    366 		return (0);
    367 	} else
    368 		return (long_options[match].val);
    369 }
    370 
    371 /*
    372  * getopt_internal --
    373  *	Parse argc/argv argument vector.  Called by user level routines.
    374  */
    375 static int
    376 getopt_internal(int nargc, char * const *nargv, const char *options,
    377 	const struct option *long_options, int *idx, int flags)
    378 {
    379 	char *oli;				/* option letter list index */
    380 	int optchar, short_too;
    381 	int posixly_correct;	/* no static, can be changed on the fly */
    382 
    383 	if (options == NULL)
    384 		return (-1);
    385 
    386 	/*
    387 	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
    388 	 * string begins with a '+'.
    389 	 */
    390 	posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
    391 #ifdef GNU_COMPATIBLE
    392 	if (*options == '-')
    393 		flags |= FLAG_ALLARGS;
    394 	else if (posixly_correct || *options == '+')
    395 		flags &= ~FLAG_PERMUTE;
    396 #else
    397 	if (posixly_correct || *options == '+')
    398 		flags &= ~FLAG_PERMUTE;
    399 	else if (*options == '-')
    400 		flags |= FLAG_ALLARGS;
    401 #endif
    402 	if (*options == '+' || *options == '-')
    403 		options++;
    404 
    405 	/*
    406 	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
    407 	 * XXX using optreset.  Work around this braindamage.
    408 	 */
    409 	if (optind == 0)
    410 		optind = 1;
    411 
    412 	optarg = NULL;
    413 start:
    414 	if (!*place) {				/* update scanning pointer */
    415 		if (optind >= nargc) {          /* end of argument vector */
    416 			place = EMSG;
    417 			if (nonopt_end != -1) {
    418 				/* do permutation, if we have to */
    419 				permute_args(nonopt_start, nonopt_end,
    420 				    optind, nargv);
    421 				optind -= nonopt_end - nonopt_start;
    422 			} else if (nonopt_start != -1) {
    423 				/*
    424 				 * If we skipped non-options, set optind
    425 				 * to the first of them.
    426 				 */
    427 				optind = nonopt_start;
    428 			}
    429 			nonopt_start = nonopt_end = -1;
    430 			return (-1);
    431 		}
    432 		if (*(place = nargv[optind]) != '-' ||
    433 #ifdef GNU_COMPATIBLE
    434 		    place[1] == '\0') {
    435 #else
    436 		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
    437 #endif
    438 			place = EMSG;		/* found non-option */
    439 			if (flags & FLAG_ALLARGS) {
    440 				/*
    441 				 * GNU extension:
    442 				 * return non-option as argument to option 1
    443 				 */
    444 				optarg = nargv[optind++];
    445 				return (INORDER);
    446 			}
    447 			if (!(flags & FLAG_PERMUTE)) {
    448 				/*
    449 				 * If no permutation wanted, stop parsing
    450 				 * at first non-option.
    451 				 */
    452 				return (-1);
    453 			}
    454 			/* do permutation */
    455 			if (nonopt_start == -1)
    456 				nonopt_start = optind;
    457 			else if (nonopt_end != -1) {
    458 				permute_args(nonopt_start, nonopt_end,
    459 				    optind, nargv);
    460 				nonopt_start = optind -
    461 				    (nonopt_end - nonopt_start);
    462 				nonopt_end = -1;
    463 			}
    464 			optind++;
    465 			/* process next argument */
    466 			goto start;
    467 		}
    468 		if (nonopt_start != -1 && nonopt_end == -1)
    469 			nonopt_end = optind;
    470 
    471 		/*
    472 		 * If we have "-" do nothing, if "--" we are done.
    473 		 */
    474 		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
    475 			optind++;
    476 			place = EMSG;
    477 			/*
    478 			 * We found an option (--), so if we skipped
    479 			 * non-options, we have to permute.
    480 			 */
    481 			if (nonopt_end != -1) {
    482 				permute_args(nonopt_start, nonopt_end,
    483 				    optind, nargv);
    484 				optind -= nonopt_end - nonopt_start;
    485 			}
    486 			nonopt_start = nonopt_end = -1;
    487 			return (-1);
    488 		}
    489 	}
    490 
    491 	/*
    492 	 * Check long options if:
    493 	 *  1) we were passed some
    494 	 *  2) the arg is not just "-"
    495 	 *  3) either the arg starts with -- we are getopt_long_only()
    496 	 */
    497 	if (long_options != NULL && place != nargv[optind] &&
    498 	    (*place == '-' || (flags & FLAG_LONGONLY))) {
    499 		short_too = 0;
    500 #ifdef GNU_COMPATIBLE
    501 		dash_prefix = D_PREFIX;
    502 #endif
    503 		if (*place == '-') {
    504 			place++;		/* --foo long option */
    505 #ifdef GNU_COMPATIBLE
    506 			dash_prefix = DD_PREFIX;
    507 #endif
    508 		} else if (*place != ':' && strchr(options, *place) != NULL)
    509 			short_too = 1;		/* could be short option too */
    510 
    511 		optchar = parse_long_options(nargv, options, long_options,
    512 		    idx, short_too, flags);
    513 		if (optchar != -1) {
    514 			place = EMSG;
    515 			return (optchar);
    516 		}
    517 	}
    518 
    519 	if ((optchar = (int)*place++) == (int)':' ||
    520 	    (optchar == (int)'-' && *place != '\0') ||
    521 	    (oli = strchr(options, optchar)) == NULL) {
    522 		/*
    523 		 * If the user specified "-" and  '-' isn't listed in
    524 		 * options, return -1 (non-option) as per POSIX.
    525 		 * Otherwise, it is an unknown option character (or ':').
    526 		 */
    527 		if (optchar == (int)'-' && *place == '\0')
    528 			return (-1);
    529 		if (!*place)
    530 			++optind;
    531 #ifdef GNU_COMPATIBLE
    532 		if (PRINT_ERROR)
    533 			warnx(posixly_correct ? illoptchar : gnuoptchar,
    534 			      optchar);
    535 #else
    536 		if (PRINT_ERROR)
    537 			warnx(illoptchar, optchar);
    538 #endif
    539 		optopt = optchar;
    540 		return (BADCH);
    541 	}
    542 	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
    543 		/* -W long-option */
    544 		if (*place)			/* no space */
    545 			/* NOTHING */;
    546 		else if (++optind >= nargc) {	/* no arg */
    547 			place = EMSG;
    548 			if (PRINT_ERROR)
    549 				warnx(recargchar, optchar);
    550 			optopt = optchar;
    551 			return (BADARG);
    552 		} else				/* white space */
    553 			place = nargv[optind];
    554 #ifdef GNU_COMPATIBLE
    555 		dash_prefix = W_PREFIX;
    556 #endif
    557 		optchar = parse_long_options(nargv, options, long_options,
    558 		    idx, 0, flags);
    559 		place = EMSG;
    560 		return (optchar);
    561 	}
    562 	if (*++oli != ':') {			/* doesn't take argument */
    563 		if (!*place)
    564 			++optind;
    565 	} else {				/* takes (optional) argument */
    566 		optarg = NULL;
    567 		if (*place) {			/* no white space */
    568 DIAG_OFF_CAST_QUAL
    569 			optarg = (char *)place;
    570 DIAG_ON_CAST_QUAL
    571 		} else if (oli[1] != ':') {	/* arg not optional */
    572 			if (++optind >= nargc) {	/* no arg */
    573 				place = EMSG;
    574 				if (PRINT_ERROR)
    575 					warnx(recargchar, optchar);
    576 				optopt = optchar;
    577 				return (BADARG);
    578 			} else
    579 				optarg = nargv[optind];
    580 		}
    581 		place = EMSG;
    582 		++optind;
    583 	}
    584 	/* dump back option letter */
    585 	return (optchar);
    586 }
    587 
    588 #ifdef REPLACE_GETOPT
    589 /*
    590  * getopt --
    591  *	Parse argc/argv argument vector.
    592  *
    593  * [eventually this will replace the BSD getopt]
    594  */
    595 int
    596 getopt(int nargc, char * const *nargv, const char *options)
    597 {
    598 
    599 	/*
    600 	 * We don't pass FLAG_PERMUTE to getopt_internal() since
    601 	 * the BSD getopt(3) (unlike GNU) has never done this.
    602 	 *
    603 	 * Furthermore, since many privileged programs call getopt()
    604 	 * before dropping privileges it makes sense to keep things
    605 	 * as simple (and bug-free) as possible.
    606 	 */
    607 	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
    608 }
    609 #endif /* REPLACE_GETOPT */
    610 
    611 /*
    612  * getopt_long --
    613  *	Parse argc/argv argument vector.
    614  */
    615 int
    616 getopt_long(int nargc, char * const *nargv, const char *options,
    617 	const struct option *long_options, int *idx)
    618 {
    619 
    620 	return (getopt_internal(nargc, nargv, options, long_options, idx,
    621 	    FLAG_PERMUTE));
    622 }
    623 
    624 /*
    625  * getopt_long_only --
    626  *	Parse argc/argv argument vector.
    627  */
    628 int
    629 getopt_long_only(int nargc, char * const *nargv, const char *options,
    630 	const struct option *long_options, int *idx)
    631 {
    632 
    633 	return (getopt_internal(nargc, nargv, options, long_options, idx,
    634 	    FLAG_PERMUTE|FLAG_LONGONLY));
    635 }
    636