Home | History | Annotate | Download | only in gen
      1 /*
      2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*
      7  * Copyright (c) 2002 Todd C. Miller <Todd.Miller (at) courtesan.com>
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. The name of the author may not be used to endorse or promote products
     19  *    derived from this software without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
     22  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
     23  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
     24  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     30  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 /*
     33  * Copyright (c) 2000 The NetBSD Foundation, Inc.
     34  * All rights reserved.
     35  *
     36  * This code is derived from software contributed to The NetBSD Foundation
     37  * by Dieter Baron and Thomas Klausner.
     38  *
     39  * Redistribution and use in source and binary forms, with or without
     40  * modification, are permitted provided that the following conditions
     41  * are met:
     42  * 1. Redistributions of source code must retain the above copyright
     43  *    notice, this list of conditions and the following disclaimer.
     44  * 2. Redistributions in binary form must reproduce the above copyright
     45  *    notice, this list of conditions and the following disclaimer in the
     46  *    documentation and/or other materials provided with the distribution.
     47  * 3. All advertising materials mentioning features or use of this software
     48  *    must display the following acknowledgement:
     49  *	This product includes software developed by the NetBSD
     50  *	Foundation, Inc. and its contributors.
     51  * 4. Neither the name of The NetBSD Foundation nor the names of its
     52  *	contributors may be used to endorse or promote products derived
     53  *	from this software without specific prior written permission.
     54  *
     55  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     56  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     57  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     58  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     59  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     60  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     61  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     62  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     63  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     64  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     65  * POSSIBILITY OF SUCH DAMAGE.
     66  */
     67 
     68 #pragma weak _getopt_clip = getopt_clip
     69 #pragma weak _getopt_long = getopt_long
     70 #pragma weak _getopt_long_only = getopt_long_only
     71 
     72 #include "lint.h"
     73 #include <getopt.h>
     74 #include <stdio.h>
     75 #include <errno.h>
     76 #include <unistd.h>
     77 #include <stdlib.h>
     78 #include <string.h>
     79 #include "_libc_gettext.h"
     80 
     81 static int optreset = 0;	/* keep track of first entry to getopt() */
     82 #define	PRINT_ERROR	((opterr) && (*options != ':'))
     83 #define	FLAG_IS_SET(flag) 	((flags & flag) != 0)	/* is flag turned on? */
     84 
     85 /* Determine if an argument is required for this long option */
     86 #define	LONGOPT_REQUIRES_ARG(longopt) \
     87 		((((longopt).has_arg == optional_argument) && \
     88 			(!FLAG_IS_SET(FLAG_OPTIONAL_ARGS))) || \
     89 			((longopt).has_arg == required_argument))
     90 
     91 #define	FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
     92 #define	FLAG_ALLARGS	0x02	/* treat non-options as args to option "1" */
     93 #define	FLAG_LONGONLY	0x04	/* operate as getopt_long_only() */
     94 #define	FLAG_OPTIONAL_ARGS 0x08	/* allow optional arguments to options */
     95 #define	FLAG_REQUIRE_EQUIVALENTS 0x10 /* require short<->long equivalents */
     96 #define	FLAG_ABBREV	0x20 	/* long option abbreviation allowed. */
     97 #define	FLAG_W_SEMICOLON 0x40 	/* Support for W; in optstring */
     98 #define	FLAG_PLUS_DASH_START 0x80	/* leading '+' or '-' in optstring */
     99 
    100 /* return values */
    101 #define	BADCH		(int)'?'
    102 #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
    103 #define	INORDER 	(int)1
    104 
    105 #define	EMSG		""
    106 
    107 static int getopt_internal(int, char * const *, const char *,
    108 				const struct option *, int *, uint_t);
    109 static int parse_long_options(int nargc, char * const *nargv, const char *,
    110 				const struct option *, int *, int,
    111 				uint_t flags);
    112 static int gcd(int, int);
    113 static void permute_args(int, int, int, char * const *);
    114 
    115 static char *place = EMSG; /* option letter processing */
    116 
    117 /* XXX: set optreset to 1 rather than these two */
    118 static int nonopt_start = -1; /* first non option argument (for permute) */
    119 static int nonopt_end = -1;   /* first option after non options (for permute) */
    120 
    121 /*
    122  * Generalized error message output.
    123  *
    124  * NOTE ON ERROR MESSAGES: All the error messages in this file
    125  * use %s (not %c) because they are all routed through warnx_getopt(),
    126  * which takes a string argument. Character arguments passed
    127  * to warnxchar() are converted to strings automatically before
    128  * being passed to warnx_getopt().
    129  */
    130 static void
    131 warnx_getopt(const char *argv0, const char *msg, const char *arg) {
    132 	char errbuf[256];
    133 	(void) snprintf(errbuf, sizeof (errbuf), msg, argv0, arg);
    134 	(void) write(2, errbuf, strlen(errbuf));
    135 	(void) write(2, "\n", 1);
    136 }
    137 
    138 /*
    139  * Generalized error message output.
    140  */
    141 static void
    142 warnxchar(const char *argv0, const char *msg, const char c) {
    143 	char charbuf[2];
    144 	charbuf[0] = c;
    145 	charbuf[1] = '\0';
    146 	warnx_getopt(argv0, msg, charbuf);
    147 }
    148 
    149 /*
    150  * Generalized error message output.
    151  */
    152 static void
    153 warnxlen(const char *argv0, const char *msg, int argLen, const char *arg) {
    154 	char argbuf[256];
    155 	(void) strncpy(argbuf, arg, argLen);
    156 	argbuf[argLen < (sizeof (argbuf)-1)? argLen:(sizeof (argbuf)-1)] = '\0';
    157 	warnx_getopt(argv0, msg, argbuf);
    158 }
    159 
    160 /*
    161  * Compute the greatest common divisor of a and b.
    162  */
    163 static int
    164 gcd(int a, int b)
    165 {
    166 	int c;
    167 
    168 	c = a % b;
    169 	while (c != 0) {
    170 		a = b;
    171 		b = c;
    172 		c = a % b;
    173 	}
    174 
    175 	return (b);
    176 }
    177 
    178 /*
    179  * Exchange the block from nonopt_start to nonopt_end with the block
    180  * from nonopt_end to opt_end (keeping the same order of arguments
    181  * in each block).
    182  */
    183 static void
    184 permute_args(int panonopt_start, int panonopt_end, int opt_end,
    185 	char * const *nargv)
    186 {
    187 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
    188 	char *swap;
    189 
    190 	/*
    191 	 * compute lengths of blocks and number and size of cycles
    192 	 */
    193 	nnonopts = panonopt_end - panonopt_start;
    194 	nopts = opt_end - panonopt_end;
    195 	ncycle = gcd(nnonopts, nopts);
    196 	cyclelen = (opt_end - panonopt_start) / ncycle;
    197 
    198 	for (i = 0; i < ncycle; i++) {
    199 		cstart = panonopt_end+i;
    200 		pos = cstart;
    201 		for (j = 0; j < cyclelen; j++) {
    202 			if (pos >= panonopt_end)
    203 				pos -= nnonopts;
    204 			else
    205 				pos += nopts;
    206 			swap = nargv[pos];
    207 			((char **)nargv)[pos] = nargv[cstart];
    208 			((char **)nargv)[cstart] = swap;
    209 		}
    210 	}
    211 } /* permute_args() */
    212 
    213 /*
    214  * Verify that each short option (character flag) has a long equivalent,
    215  * and that each long option has a short option equivalent. Note that
    216  * multiple long options can map to the same character.
    217  *
    218  * This behavior is defined by Sun's CLIP specification (11/12/02),
    219  * and currently getopt_clip() is the only getopt variant that
    220  * requires it.
    221  *
    222  * If error output is enabled and an error is found, this function
    223  * prints ONE error message (the first error found) and returns an
    224  * error value.
    225  *
    226  * ASSUMES: options != NULL
    227  * ASSUMES: long_options may be NULL
    228  *
    229  * Returns < 0 if an error is found
    230  * Returns >= 0 on success
    231  */
    232 static int
    233 /* LINTED: argument unused in function: nargc */
    234 verify_short_long_equivalents(int nargc,
    235 				char *const *nargv,
    236 				const char *options,
    237 				const struct option *long_options,
    238 				uint_t flags) {
    239 	int short_i = 0;
    240 	int long_i = 0;
    241 	int equivFound = 0;
    242 	int ch = 0;
    243 
    244 	/*
    245 	 * Find a long option for each short option
    246 	 */
    247 	equivFound = 1;
    248 	for (short_i = 0; equivFound && (options[short_i] != 0); ++short_i) {
    249 		ch = options[short_i];
    250 
    251 		if (ch == ':') {
    252 			continue;
    253 		}
    254 		if (FLAG_IS_SET(FLAG_W_SEMICOLON) &&
    255 			(ch == 'W') && (options[short_i+1] == ';')) {
    256 			/* W; is a special case */
    257 			++short_i;
    258 			continue;
    259 		}
    260 
    261 		equivFound = 0;
    262 		if (long_options != NULL) {
    263 			for (long_i = 0; ((!equivFound) &&
    264 					(long_options[long_i].name != NULL));
    265 								++long_i) {
    266 				equivFound = (ch == long_options[long_i].val);
    267 			}
    268 		}
    269 		if ((!equivFound) && (PRINT_ERROR)) {
    270 			warnxchar(nargv[0],
    271 				_libc_gettext(
    272 				"%s: equivalent long option required -- %s"),
    273 				ch);
    274 		}
    275 	} /* short_i */
    276 
    277 	/*
    278 	 * Find a short option for each long option. Note that if we came
    279 	 * out of the above loop with equivFound==0, we are already done.
    280 	 */
    281 	if (equivFound && (long_options != NULL)) {
    282 		for (long_i = 0; (equivFound &&
    283 				(long_options[long_i].name != NULL));
    284 								++long_i) {
    285 			equivFound = ((long_options[long_i].val != 0) &&
    286 				(strchr(options, long_options[long_i].val)
    287 								!= NULL));
    288 
    289 			if ((!equivFound) && (PRINT_ERROR)) {
    290 				warnx_getopt(nargv[0],
    291 					_libc_gettext(
    292 				"%s: equivalent short option required -- %s"),
    293 					long_options[long_i].name);
    294 			}
    295 		} /* for long_i */
    296 	}
    297 
    298 	return (equivFound? 0:-1);
    299 } /* verify_short_long_equivalents() */
    300 
    301 /*
    302  * parse_long_options --
    303  *	Parse long options in argc/argv argument vector.
    304  * Returns -1 if short_too is set and the option does not match long_options.
    305  */
    306 static int
    307 parse_long_options(int nargc, char * const *nargv, const char *options,
    308 	const struct option *long_options, int *idx, int short_too,
    309 	uint_t flags)
    310 {
    311 	char *current_argv = NULL;
    312 	char *argv_equal_ptr = NULL;
    313 	size_t current_argv_len = 0;
    314 	size_t long_option_len = 0;
    315 	int i = 0;
    316 	int match = 0;
    317 
    318 	current_argv = place;
    319 	match = -1;
    320 
    321 	optind++;
    322 
    323 	if ((argv_equal_ptr = strchr(current_argv, '=')) != NULL) {
    324 		/* argument found (--option=arg) */
    325 		current_argv_len = (argv_equal_ptr - current_argv);
    326 		argv_equal_ptr++;
    327 	} else {
    328 		current_argv_len = strlen(current_argv);
    329 	}
    330 
    331 	for (i = 0; (long_options[i].name != NULL); i++) {
    332 
    333 		/* find matching long option */
    334 		if (strncmp(current_argv, long_options[i].name,
    335 		    current_argv_len) != 0) {
    336 			continue;	/* no match  */
    337 		}
    338 		long_option_len = strlen(long_options[i].name);
    339 		if ((!FLAG_IS_SET(FLAG_ABBREV)) &&
    340 		    (long_option_len > current_argv_len)) {
    341 			continue;	/* Abbreviations are disabled */
    342 		}
    343 
    344 		if (long_option_len == current_argv_len) {
    345 			/* exact match */
    346 			match = i;
    347 			break;
    348 		}
    349 		/*
    350 		 * If this is a known short option, don't allow
    351 		 * a partial match of a single character.
    352 		 */
    353 		if (short_too && current_argv_len == 1)
    354 			continue;
    355 
    356 		if (match == -1)	/* partial match */
    357 			match = i;
    358 		else {
    359 			/* ambiguous abbreviation */
    360 			if (PRINT_ERROR) {
    361 				warnxlen(nargv[0],
    362 				    _libc_gettext(
    363 				    "%s: ambiguous option -- %s"),
    364 				    (int)current_argv_len,
    365 				    current_argv);
    366 			}
    367 			optopt = 0;
    368 			return (BADCH);
    369 		}
    370 	} /* for i */
    371 	if (match != -1) {		/* option found */
    372 		if ((long_options[match].has_arg == no_argument) &&
    373 		    (argv_equal_ptr != NULL)) {
    374 			if (PRINT_ERROR) {
    375 				warnxlen(nargv[0],
    376 				    _libc_gettext(
    377 				"%s: option doesn't take an argument -- %s"),
    378 				    (int)current_argv_len,
    379 				    current_argv);
    380 			}
    381 			/*
    382 			 * XXX: GNU sets optopt to val regardless of flag
    383 			 */
    384 			if (long_options[match].flag == NULL)
    385 				optopt = long_options[match].val;
    386 			else
    387 				optopt = 0;
    388 			return (BADARG);
    389 		}
    390 		if (long_options[match].has_arg == required_argument ||
    391 		    long_options[match].has_arg == optional_argument) {
    392 			if (argv_equal_ptr != NULL) {
    393 				optarg = argv_equal_ptr;
    394 			} else if (LONGOPT_REQUIRES_ARG(long_options[match])) {
    395 				/* The next argv must be the option argument */
    396 				if (optind < nargc) {
    397 					optarg = nargv[optind];
    398 				}
    399 				++optind; /* code below depends on this */
    400 			}
    401 		}
    402 		if (LONGOPT_REQUIRES_ARG(long_options[match]) &&
    403 		    (optarg == NULL)) {
    404 			/*
    405 			 * Missing argument; leading ':' indicates no error
    406 			 * should be generated.
    407 			 */
    408 			if (PRINT_ERROR) {
    409 				warnx_getopt(nargv[0],
    410 				    _libc_gettext(
    411 				"%s: option requires an argument -- %s"),
    412 				    current_argv);
    413 			}
    414 			/*
    415 			 * XXX: GNU sets optopt to val regardless of flag
    416 			 */
    417 			if (long_options[match].flag == NULL)
    418 				optopt = long_options[match].val;
    419 			else
    420 				optopt = 0;
    421 			--optind;
    422 			return (BADARG);
    423 		}
    424 	} else {			/* unknown option */
    425 		if (short_too) {
    426 			--optind;
    427 			return (-1);
    428 		}
    429 		if (PRINT_ERROR) {
    430 			warnx_getopt(nargv[0],
    431 			    _libc_gettext("%s: illegal option -- %s"),
    432 			    current_argv);
    433 		}
    434 		optopt = 0;
    435 		return (BADCH);
    436 	}
    437 	if (idx)
    438 		*idx = match;
    439 	if (long_options[match].flag != NULL) {
    440 		*long_options[match].flag = long_options[match].val;
    441 		return (0);
    442 	} else {
    443 		optopt = long_options[match].val;
    444 		return (optopt);
    445 	}
    446 } /* parse_long_options() */
    447 
    448 /*
    449  * getopt_internal() --
    450  *	Parse argc/argv argument vector.  Called by user level routines.
    451  *
    452  * This implements all of the getopt_long(), getopt_long_only(),
    453  * getopt_clip() variants.
    454  */
    455 static int
    456 getopt_internal(int nargc, char * const *nargv, const char *options,
    457 	const struct option *long_options, int *idx, uint_t flags)
    458 {
    459 	char *oli;				/* option letter list index */
    460 	int optchar, short_too;
    461 	static int posixly_correct = -1;
    462 
    463 	if (options == NULL)
    464 		return (-1);
    465 
    466 	/*
    467 	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
    468 	 * string begins with a '+'.
    469 	 */
    470 	if (posixly_correct == -1) {
    471 		posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
    472 	}
    473 	if (FLAG_IS_SET(FLAG_PLUS_DASH_START)) {
    474 		/*
    475 		 * + or - at start of optstring takes precedence
    476 		 * over POSIXLY_CORRECT.
    477 		 */
    478 		if (*options == '+') {
    479 			/*
    480 			 * leading + means POSIX-compliant; first non-option
    481 			 * ends option list. Therefore, don't permute args.
    482 			 */
    483 			posixly_correct = 1;
    484 		} else if (*options == '-') {
    485 			posixly_correct = 0;
    486 			flags |= FLAG_ALLARGS;
    487 		}
    488 		if ((*options == '+') || (*options == '-')) {
    489 			options++;
    490 		}
    491 	} /* if FLAG_PLUS_DASH_START */
    492 
    493 	if (posixly_correct) {
    494 		flags &= ~FLAG_PERMUTE;
    495 		flags &= ~FLAG_ALLARGS;
    496 	}
    497 
    498 	/*
    499 	 * Some programs (like GNU cvs) set optind to 0 to restart
    500 	 * option processing. Work around this braindamage.
    501 	 *
    502 	 * The above problem comes from using global variables. We
    503 	 * should avoid their use in the future.
    504 	 */
    505 	if (optind == 0) {
    506 		optind = optreset = 1;
    507 	}
    508 
    509 	optarg = NULL;
    510 	optopt = 0;
    511 
    512 	if (optreset) {
    513 		nonopt_start = nonopt_end = -1;
    514 	}
    515 
    516 	/*
    517 	 * On the first call, make sure that there is a short equivalent
    518 	 * for each long option, and vice versa. This is required by
    519 	 * Sun's CLIP specification (11/12/02).
    520 	 */
    521 	if ((optind == 1) && FLAG_IS_SET(FLAG_REQUIRE_EQUIVALENTS)) {
    522 		if (verify_short_long_equivalents(
    523 		    nargc, nargv, options, long_options, flags) < 0) {
    524 			/* function printed any necessary messages */
    525 			errno = EINVAL;		/* invalid argument */
    526 			return (-1);
    527 		}
    528 	}
    529 
    530 start:
    531 	if (optreset || !*place) {		/* update scanning pointer */
    532 		optreset = 0;
    533 		if (optind >= nargc) {		/* end of argument vector */
    534 			place = EMSG;
    535 			if (nonopt_end != -1) {
    536 				/* do permutation, if we have to */
    537 				permute_args(nonopt_start, nonopt_end,
    538 				    optind, nargv);
    539 				optind -= nonopt_end - nonopt_start;
    540 
    541 			} else if (nonopt_start != -1) {
    542 				/*
    543 				 * If we skipped non-options, set optind
    544 				 * to the first of them.
    545 				 */
    546 				optind = nonopt_start;
    547 			}
    548 			nonopt_start = nonopt_end = -1;
    549 			return (-1);
    550 		}
    551 		if ((*(place = nargv[optind]) != '-') || (place[1] == '\0')) {
    552 			place = EMSG;		/* found non-option */
    553 			if (flags & FLAG_ALLARGS) {
    554 				/*
    555 				 * GNU extension:
    556 				 * return non-option as argument to option '\1'
    557 				 */
    558 				optarg = nargv[optind++];
    559 				return (INORDER);
    560 			}
    561 			if (!(flags & FLAG_PERMUTE)) {
    562 				/*
    563 				 * If no permutation wanted, stop parsing
    564 				 * at first non-option.
    565 				 */
    566 				return (-1);
    567 			}
    568 			/* do permutation */
    569 			if (nonopt_start == -1)
    570 				nonopt_start = optind;
    571 			else if (nonopt_end != -1) {
    572 				permute_args(nonopt_start, nonopt_end,
    573 				    optind, nargv);
    574 				nonopt_start = optind -
    575 				    (nonopt_end - nonopt_start);
    576 				nonopt_end = -1;
    577 			}
    578 			optind++;
    579 			/* process next argument */
    580 			goto start;
    581 		}
    582 		if (nonopt_start != -1 && nonopt_end == -1)
    583 			nonopt_end = optind;
    584 
    585 		/*
    586 		 * Check for "--" or "--foo" with no long options
    587 		 * but if place is simply "-" leave it unmolested.
    588 		 */
    589 		if (place[1] != '\0' && *++place == '-' &&
    590 		    (place[1] == '\0' || long_options == NULL)) {
    591 			optind++;
    592 			place = EMSG;
    593 			/*
    594 			 * We found an option (--), so if we skipped
    595 			 * non-options, we have to permute.
    596 			 */
    597 			if (nonopt_end != -1) {
    598 				permute_args(nonopt_start, nonopt_end,
    599 				    optind, nargv);
    600 				optind -= nonopt_end - nonopt_start;
    601 			}
    602 			nonopt_start = nonopt_end = -1;
    603 			return (-1);
    604 		}
    605 	}
    606 
    607 	/*
    608 	 * Check long options if:
    609 	 *  1) we were passed some
    610 	 *  2) the arg is not just "-"
    611 	 *  3) either the arg starts with -- or we are getopt_long_only()
    612 	 */
    613 	if (long_options != NULL && place != nargv[optind] &&
    614 	    (*place == '-' || (FLAG_IS_SET(FLAG_LONGONLY)))) {
    615 		short_too = 0;
    616 		if (*place == '-')
    617 			place++;		/* --foo long option */
    618 		else if (*place != ':' && strchr(options, *place) != NULL)
    619 			short_too = 1;		/* could be short option too */
    620 
    621 		optchar = parse_long_options(nargc, nargv, options,
    622 		    long_options, idx, short_too, flags);
    623 		if (optchar != -1) {
    624 			place = EMSG;
    625 			return (optchar);
    626 		}
    627 	}
    628 
    629 	if ((optchar = (int)*place++) == (int)':' ||
    630 	    (oli = strchr(options, optchar)) == NULL) {
    631 		/*
    632 		 * If the user didn't specify '-' as an option,
    633 		 * assume it means -1 as POSIX specifies.
    634 		 */
    635 		if (optchar == (int)'-')
    636 			return (-1);
    637 		/* option letter unknown or ':' */
    638 		if (!*place)
    639 			++optind;
    640 		if (PRINT_ERROR)
    641 			warnxchar(nargv[0],
    642 			    _libc_gettext("%s: illegal option -- %s"),
    643 			    optchar);
    644 		optopt = optchar;
    645 		return (BADCH);
    646 	}
    647 	if (FLAG_IS_SET(FLAG_W_SEMICOLON) &&
    648 	    (long_options != NULL) && (optchar == 'W') && (oli[1] == ';')) {
    649 		/* -W long-option */
    650 		/* LINTED: statement has no consequent: if */
    651 		if (*place) {			/* no space */
    652 			/* NOTHING */;
    653 		} else if (++optind >= nargc) {	/* no long-option after -W */
    654 			place = EMSG;
    655 			if (PRINT_ERROR)
    656 				warnxchar(nargv[0],
    657 				    _libc_gettext(
    658 				"%s: option requires an argument -- %s"),
    659 				    optchar);
    660 			optopt = optchar;
    661 			return (BADARG);
    662 		} else {			/* white space */
    663 			place = nargv[optind];
    664 		}
    665 		optchar = parse_long_options(
    666 		    nargc, nargv, options, long_options,
    667 		    idx, 0, flags);
    668 
    669 		/*
    670 		 * PSARC 2003/645 - Match GNU behavior, set optarg to
    671 		 * the long-option.
    672 		 */
    673 		if (optarg == NULL) {
    674 			optarg = nargv[optind-1];
    675 		}
    676 		place = EMSG;
    677 		return (optchar);
    678 	}
    679 	if (*++oli != ':') {			/* doesn't take argument */
    680 		if (!*place)
    681 			++optind;
    682 	} else {				/* takes (optional) argument */
    683 		optarg = NULL;
    684 		if (*place) {			/* no white space */
    685 			optarg = place;
    686 		/* XXX: disable test for :: if PC? (GNU doesn't) */
    687 		} else if (!(FLAG_IS_SET(FLAG_OPTIONAL_ARGS) &&
    688 		    (oli[1] == ':'))) {
    689 			/* arg is required (not optional) */
    690 
    691 			if (++optind >= nargc) {	/* no arg */
    692 				place = EMSG;
    693 				if (PRINT_ERROR) {
    694 					warnxchar(nargv[0],
    695 					    _libc_gettext(
    696 				"%s: option requires an argument -- %s"),
    697 					    optchar);
    698 				}
    699 				optopt = optchar;
    700 				return (BADARG);
    701 			} else
    702 				optarg = nargv[optind];
    703 		}
    704 		place = EMSG;
    705 		++optind;
    706 	}
    707 	/* return valid option letter */
    708 	optopt = optchar;		/* preserve getopt() behavior */
    709 	return (optchar);
    710 } /* getopt_internal() */
    711 
    712 /*
    713  * getopt_long() --
    714  *	Parse argc/argv argument vector.
    715  *
    716  * Requires that long options be preceded with a two dashes
    717  * (e.g., --longoption).
    718  */
    719 int
    720 getopt_long(int nargc, char *const *nargv,
    721 		const char *optstring,
    722 		const struct option *long_options, int *long_index)
    723 {
    724 
    725 	return (getopt_internal(
    726 	    nargc, nargv, optstring, long_options, long_index,
    727 	    FLAG_PERMUTE
    728 	    | FLAG_OPTIONAL_ARGS
    729 	    | FLAG_ABBREV
    730 	    | FLAG_W_SEMICOLON
    731 	    | FLAG_PLUS_DASH_START));
    732 } /* getopt_long() */
    733 
    734 /*
    735  * getopt_long_only() --
    736  *	Parse argc/argv argument vector.
    737  *
    738  * Long options may be preceded with a single dash (e.g., -longoption)
    739  */
    740 int
    741 getopt_long_only(int nargc, char *const *nargv,
    742 		const char *optstring,
    743 		const struct option *long_options, int *long_index)
    744 {
    745 
    746 	return (getopt_internal(
    747 	    nargc, nargv, optstring, long_options, long_index,
    748 	    FLAG_PERMUTE
    749 	    | FLAG_OPTIONAL_ARGS
    750 	    | FLAG_ABBREV
    751 	    | FLAG_W_SEMICOLON
    752 	    | FLAG_PLUS_DASH_START
    753 	    | FLAG_LONGONLY));
    754 } /* getopt_long_only() */
    755 
    756 /*
    757  * getopt_clip() --
    758  *	Parse argc/argv argument vector, requiring compliance with
    759  * 	Sun's CLIP specification (11/12/02)
    760  *
    761  * o Does not allow arguments to be optional (optional_argument is
    762  *   treated as required_argument).
    763  *
    764  * o Does not allow long options to be abbreviated on the command line
    765  *
    766  * o Does not allow long argument to be preceded by a single dash
    767  *   (Double-dash '--' is required)
    768  *
    769  * o Stops option processing at the first non-option
    770  *
    771  * o Requires that every long option have a short-option (single
    772  *   character) equivalent and vice-versa. If a short option or
    773  *   long option without an equivalent is found, an error message
    774  *   is printed and -1 is returned on the first call, and errno
    775  *   is set to EINVAL.
    776  *
    777  * o Leading + or - in optstring is ignored, and opstring is
    778  *   treated as if it began after the + or - .
    779  */
    780 int
    781 getopt_clip(int nargc, char *const *nargv,
    782 		const char *optstring,
    783 		const struct option *long_options, int *long_index)
    784 {
    785 	return getopt_internal(
    786 	    nargc, nargv, optstring, long_options, long_index,
    787 		/*
    788 		 * no permutation,
    789 		 * no optional args,
    790 		 * no long-only,
    791 		 * no abbreviations
    792 		 * no support for +- at start of optstring
    793 		 * yes support for "W;" in optstring
    794 		 */
    795 	    FLAG_W_SEMICOLON
    796 	    | FLAG_REQUIRE_EQUIVALENTS);
    797 } /* getopt_clip() */
    798