xref: /openbsd-src/lib/libc/stdlib/getopt_long.c (revision 3106aed52841065b84398a9956d54f0ac19026af)
1*3106aed5Smillert /*	$OpenBSD: getopt_long.c,v 1.4 2002/12/05 22:26:04 millert Exp $	*/
247f6c619Smillert /*	$NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $	*/
347f6c619Smillert 
447f6c619Smillert /*-
547f6c619Smillert  * Copyright (c) 2000 The NetBSD Foundation, Inc.
647f6c619Smillert  * All rights reserved.
747f6c619Smillert  *
847f6c619Smillert  * This code is derived from software contributed to The NetBSD Foundation
947f6c619Smillert  * by Dieter Baron and Thomas Klausner.
1047f6c619Smillert  *
1147f6c619Smillert  * Redistribution and use in source and binary forms, with or without
1247f6c619Smillert  * modification, are permitted provided that the following conditions
1347f6c619Smillert  * are met:
1447f6c619Smillert  * 1. Redistributions of source code must retain the above copyright
1547f6c619Smillert  *    notice, this list of conditions and the following disclaimer.
1647f6c619Smillert  * 2. Redistributions in binary form must reproduce the above copyright
1747f6c619Smillert  *    notice, this list of conditions and the following disclaimer in the
1847f6c619Smillert  *    documentation and/or other materials provided with the distribution.
1947f6c619Smillert  * 3. All advertising materials mentioning features or use of this software
2047f6c619Smillert  *    must display the following acknowledgement:
2147f6c619Smillert  *        This product includes software developed by the NetBSD
2247f6c619Smillert  *        Foundation, Inc. and its contributors.
2347f6c619Smillert  * 4. Neither the name of The NetBSD Foundation nor the names of its
2447f6c619Smillert  *    contributors may be used to endorse or promote products derived
2547f6c619Smillert  *    from this software without specific prior written permission.
2647f6c619Smillert  *
2747f6c619Smillert  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2847f6c619Smillert  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2947f6c619Smillert  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3047f6c619Smillert  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3147f6c619Smillert  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3247f6c619Smillert  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3347f6c619Smillert  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3447f6c619Smillert  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3547f6c619Smillert  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3647f6c619Smillert  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3747f6c619Smillert  * POSSIBILITY OF SUCH DAMAGE.
3847f6c619Smillert  */
3947f6c619Smillert 
4047f6c619Smillert #if defined(LIBC_SCCS) && !defined(lint)
41*3106aed5Smillert static char *rcsid = "$OpenBSD: getopt_long.c,v 1.4 2002/12/05 22:26:04 millert Exp $";
4247f6c619Smillert #endif /* LIBC_SCCS and not lint */
4347f6c619Smillert 
4447f6c619Smillert #include <err.h>
4547f6c619Smillert #include <errno.h>
4647f6c619Smillert #include <getopt.h>
4747f6c619Smillert #include <stdlib.h>
4847f6c619Smillert #include <string.h>
4947f6c619Smillert 
503f02fa7aSmillert #ifdef REPLACE_GETOPT
5147f6c619Smillert int	opterr = 1;		/* if error message should be printed */
5247f6c619Smillert int	optind = 1;		/* index into parent argv vector */
5347f6c619Smillert int	optopt = '?';		/* character checked for validity */
5447f6c619Smillert int	optreset;		/* reset getopt */
5547f6c619Smillert char    *optarg;		/* argument associated with option */
563f02fa7aSmillert #endif
5747f6c619Smillert 
5847f6c619Smillert #define PRINT_ERROR	((opterr) && (*options != ':'))
5947f6c619Smillert 
60a4cca5d9Smillert #define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
61a4cca5d9Smillert #define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
62a4cca5d9Smillert #define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
6347f6c619Smillert 
6447f6c619Smillert /* return values */
6547f6c619Smillert #define	BADCH		(int)'?'
6647f6c619Smillert #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
6747f6c619Smillert #define	INORDER 	(int)1
6847f6c619Smillert 
6947f6c619Smillert #define	EMSG		""
7047f6c619Smillert 
71a4cca5d9Smillert static int getopt_internal(int, char * const *, const char *,
72a4cca5d9Smillert 			   const struct option *, int *, int);
73a4cca5d9Smillert static int parse_long_options(int, char * const *, const char *,
7447f6c619Smillert 			      const struct option *, int *, int);
7547f6c619Smillert static int gcd(int, int);
7647f6c619Smillert static void permute_args(int, int, int, char * const *);
7747f6c619Smillert 
7847f6c619Smillert static char *place = EMSG; /* option letter processing */
7947f6c619Smillert 
8047f6c619Smillert /* XXX: set optreset to 1 rather than these two */
8147f6c619Smillert static int nonopt_start = -1; /* first non option argument (for permute) */
8247f6c619Smillert static int nonopt_end = -1;   /* first option after non options (for permute) */
8347f6c619Smillert 
8447f6c619Smillert /* Error messages */
8547f6c619Smillert static const char recargchar[] = "option requires an argument -- %c";
8647f6c619Smillert static const char recargstring[] = "option requires an argument -- %s";
8747f6c619Smillert static const char ambig[] = "ambiguous option -- %.*s";
8847f6c619Smillert static const char noarg[] = "option doesn't take an argument -- %.*s";
8947f6c619Smillert static const char illoptchar[] = "unknown option -- %c";
9047f6c619Smillert static const char illoptstring[] = "unknown option -- %s";
9147f6c619Smillert 
9247f6c619Smillert /*
9347f6c619Smillert  * Compute the greatest common divisor of a and b.
9447f6c619Smillert  */
9547f6c619Smillert static int
96a4cca5d9Smillert gcd(int a, int b)
9747f6c619Smillert {
9847f6c619Smillert 	int c;
9947f6c619Smillert 
10047f6c619Smillert 	c = a % b;
10147f6c619Smillert 	while (c != 0) {
10247f6c619Smillert 		a = b;
10347f6c619Smillert 		b = c;
10447f6c619Smillert 		c = a % b;
10547f6c619Smillert 	}
10647f6c619Smillert 
107a4cca5d9Smillert 	return (b);
10847f6c619Smillert }
10947f6c619Smillert 
11047f6c619Smillert /*
11147f6c619Smillert  * Exchange the block from nonopt_start to nonopt_end with the block
11247f6c619Smillert  * from nonopt_end to opt_end (keeping the same order of arguments
11347f6c619Smillert  * in each block).
11447f6c619Smillert  */
11547f6c619Smillert static void
116a4cca5d9Smillert permute_args(int panonopt_start, int panonopt_end, int opt_end,
117a4cca5d9Smillert 	char * const *nargv)
11847f6c619Smillert {
11947f6c619Smillert 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
12047f6c619Smillert 	char *swap;
12147f6c619Smillert 
12247f6c619Smillert 	/*
12347f6c619Smillert 	 * compute lengths of blocks and number and size of cycles
12447f6c619Smillert 	 */
12547f6c619Smillert 	nnonopts = panonopt_end - panonopt_start;
12647f6c619Smillert 	nopts = opt_end - panonopt_end;
12747f6c619Smillert 	ncycle = gcd(nnonopts, nopts);
12847f6c619Smillert 	cyclelen = (opt_end - panonopt_start) / ncycle;
12947f6c619Smillert 
13047f6c619Smillert 	for (i = 0; i < ncycle; i++) {
13147f6c619Smillert 		cstart = panonopt_end+i;
13247f6c619Smillert 		pos = cstart;
13347f6c619Smillert 		for (j = 0; j < cyclelen; j++) {
13447f6c619Smillert 			if (pos >= panonopt_end)
13547f6c619Smillert 				pos -= nnonopts;
13647f6c619Smillert 			else
13747f6c619Smillert 				pos += nopts;
13847f6c619Smillert 			swap = nargv[pos];
13947f6c619Smillert 			/* LINTED const cast */
14047f6c619Smillert 			((char **) nargv)[pos] = nargv[cstart];
14147f6c619Smillert 			/* LINTED const cast */
14247f6c619Smillert 			((char **)nargv)[cstart] = swap;
14347f6c619Smillert 		}
14447f6c619Smillert 	}
14547f6c619Smillert }
14647f6c619Smillert 
14747f6c619Smillert /*
148a4cca5d9Smillert  * parse_long_options --
149a4cca5d9Smillert  *	Parse long options in argc/argv argument vector.
150a4cca5d9Smillert  *  Returns -2 if long_only is set and the current option could be a short
151a4cca5d9Smillert  *	(single character) option instead.
15247f6c619Smillert  */
15347f6c619Smillert static int
154a4cca5d9Smillert parse_long_options(int nargc, char * const *nargv, const char *options,
155a4cca5d9Smillert 	const struct option *long_options, int *idx, int long_only)
156a4cca5d9Smillert {
157a4cca5d9Smillert 	char *current_argv, *has_equal;
158a4cca5d9Smillert 	size_t current_argv_len;
159a4cca5d9Smillert 	int i, match;
160a4cca5d9Smillert 
161a4cca5d9Smillert 	current_argv = place;
162a4cca5d9Smillert 	match = -1;
163a4cca5d9Smillert 
164a4cca5d9Smillert 	optind++;
165a4cca5d9Smillert 
166a4cca5d9Smillert 	if ((has_equal = strchr(current_argv, '=')) != NULL) {
167a4cca5d9Smillert 		/* argument found (--option=arg) */
168a4cca5d9Smillert 		current_argv_len = has_equal - current_argv;
169a4cca5d9Smillert 		has_equal++;
170a4cca5d9Smillert 	} else
171a4cca5d9Smillert 		current_argv_len = strlen(current_argv);
172a4cca5d9Smillert 
173a4cca5d9Smillert 	for (i = 0; long_options[i].name; i++) {
174a4cca5d9Smillert 		/* find matching long option */
175a4cca5d9Smillert 		if (strncmp(current_argv, long_options[i].name,
176a4cca5d9Smillert 		    current_argv_len))
177a4cca5d9Smillert 			continue;
178a4cca5d9Smillert 
179a4cca5d9Smillert 		if (strlen(long_options[i].name) == current_argv_len) {
180a4cca5d9Smillert 			/* exact match */
181a4cca5d9Smillert 			match = i;
182a4cca5d9Smillert 			break;
183a4cca5d9Smillert 		}
184a4cca5d9Smillert 		/*
185a4cca5d9Smillert 		 * Don't try a partial match of a short option when in
186a4cca5d9Smillert 		 * long_only mode.  Otherwise there is a potential conflict
187a4cca5d9Smillert 		 * between partial matches and short options.
188a4cca5d9Smillert 		 */
189a4cca5d9Smillert 		if (long_only && current_argv_len == 1)
190a4cca5d9Smillert 			continue;
191a4cca5d9Smillert 
192a4cca5d9Smillert 		if (match == -1)	/* partial match */
193a4cca5d9Smillert 			match = i;
194a4cca5d9Smillert 		else {
195a4cca5d9Smillert 			/* ambiguous abbreviation */
196a4cca5d9Smillert 			if (PRINT_ERROR)
197a4cca5d9Smillert 				warnx(ambig, (int)current_argv_len,
198a4cca5d9Smillert 				     current_argv);
199a4cca5d9Smillert 			optopt = 0;
200a4cca5d9Smillert 			return (BADCH);
201a4cca5d9Smillert 		}
202a4cca5d9Smillert 	}
203a4cca5d9Smillert 	if (match != -1) {		/* option found */
204a4cca5d9Smillert 		if (long_options[match].has_arg == no_argument
205a4cca5d9Smillert 		    && has_equal) {
206a4cca5d9Smillert 			if (PRINT_ERROR)
207a4cca5d9Smillert 				warnx(noarg, (int)current_argv_len,
208a4cca5d9Smillert 				     current_argv);
209a4cca5d9Smillert 			/*
210a4cca5d9Smillert 			 * XXX: GNU sets optopt to val regardless of flag
211a4cca5d9Smillert 			 */
212a4cca5d9Smillert 			if (long_options[match].flag == NULL)
213a4cca5d9Smillert 				optopt = long_options[match].val;
214a4cca5d9Smillert 			else
215a4cca5d9Smillert 				optopt = 0;
216a4cca5d9Smillert 			return (BADARG);
217a4cca5d9Smillert 		}
218a4cca5d9Smillert 		if (long_options[match].has_arg == required_argument ||
219a4cca5d9Smillert 		    long_options[match].has_arg == optional_argument) {
220a4cca5d9Smillert 			if (has_equal)
221a4cca5d9Smillert 				optarg = has_equal;
222a4cca5d9Smillert 			else if (long_options[match].has_arg ==
223a4cca5d9Smillert 			    required_argument) {
224a4cca5d9Smillert 				/*
225a4cca5d9Smillert 				 * optional argument doesn't use next nargv
226a4cca5d9Smillert 				 */
227a4cca5d9Smillert 				optarg = nargv[optind++];
228a4cca5d9Smillert 			}
229a4cca5d9Smillert 		}
230a4cca5d9Smillert 		if ((long_options[match].has_arg == required_argument)
231a4cca5d9Smillert 		    && (optarg == NULL)) {
232a4cca5d9Smillert 			/*
233a4cca5d9Smillert 			 * Missing argument; leading ':' indicates no error
234a4cca5d9Smillert 			 * should be generated.
235a4cca5d9Smillert 			 */
236a4cca5d9Smillert 			if (PRINT_ERROR)
237a4cca5d9Smillert 				warnx(recargstring,
238a4cca5d9Smillert 				    current_argv);
239a4cca5d9Smillert 			/*
240a4cca5d9Smillert 			 * XXX: GNU sets optopt to val regardless of flag
241a4cca5d9Smillert 			 */
242a4cca5d9Smillert 			if (long_options[match].flag == NULL)
243a4cca5d9Smillert 				optopt = long_options[match].val;
244a4cca5d9Smillert 			else
245a4cca5d9Smillert 				optopt = 0;
246a4cca5d9Smillert 			--optind;
247a4cca5d9Smillert 			return (BADARG);
248a4cca5d9Smillert 		}
249a4cca5d9Smillert 	} else {			/* unknown option */
250a4cca5d9Smillert 		if (long_only) {
251a4cca5d9Smillert 			--optind;
252a4cca5d9Smillert 			return (-2);
253a4cca5d9Smillert 		}
254a4cca5d9Smillert 		if (PRINT_ERROR)
255a4cca5d9Smillert 			warnx(illoptstring, current_argv);
256a4cca5d9Smillert 		optopt = 0;
257a4cca5d9Smillert 		return (BADCH);
258a4cca5d9Smillert 	}
259a4cca5d9Smillert 	if (idx)
260a4cca5d9Smillert 		*idx = match;
261a4cca5d9Smillert 	if (long_options[match].flag) {
262a4cca5d9Smillert 		*long_options[match].flag = long_options[match].val;
263a4cca5d9Smillert 		return (0);
264a4cca5d9Smillert 	} else
265a4cca5d9Smillert 		return (long_options[match].val);
266a4cca5d9Smillert }
267a4cca5d9Smillert 
268a4cca5d9Smillert /*
269a4cca5d9Smillert  * getopt_internal --
270a4cca5d9Smillert  *	Parse argc/argv argument vector.  Called by user level routines.
271a4cca5d9Smillert  */
272a4cca5d9Smillert static int
273a4cca5d9Smillert getopt_internal(int nargc, char * const *nargv, const char *options,
274a4cca5d9Smillert 	const struct option *long_options, int *idx, int flags)
27547f6c619Smillert {
27647f6c619Smillert 	char *oli;				/* option letter list index */
27747f6c619Smillert 	int optchar;
278a4cca5d9Smillert 	static int posixly_correct = -1;
27947f6c619Smillert 
28047f6c619Smillert 	optarg = NULL;
28147f6c619Smillert 
28247f6c619Smillert 	/*
283a4cca5d9Smillert 	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
284a4cca5d9Smillert 	 * string begins with a '+'.
285a4cca5d9Smillert 	 */
286a4cca5d9Smillert 	if (posixly_correct == -1)
287a4cca5d9Smillert 		posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
288a4cca5d9Smillert 	if (posixly_correct || *options == '+')
289a4cca5d9Smillert 		flags &= ~FLAG_PERMUTE;
290a4cca5d9Smillert 	else if (*options == '-')
291a4cca5d9Smillert 		flags |= FLAG_ALLARGS;
292a4cca5d9Smillert 	if (*options == '+' || *options == '-')
293a4cca5d9Smillert 		options++;
294a4cca5d9Smillert 
295a4cca5d9Smillert 	/*
29647f6c619Smillert 	 * XXX Some programs (like rsyncd) expect to be able to
29747f6c619Smillert 	 * XXX re-initialize optind to 0 and have getopt_long(3)
29847f6c619Smillert 	 * XXX properly function again.  Work around this braindamage.
29947f6c619Smillert 	 */
30047f6c619Smillert 	if (optind == 0)
30147f6c619Smillert 		optind = 1;
30247f6c619Smillert 
30347f6c619Smillert 	if (optreset)
30447f6c619Smillert 		nonopt_start = nonopt_end = -1;
30547f6c619Smillert start:
30647f6c619Smillert 	if (optreset || !*place) {		/* update scanning pointer */
30747f6c619Smillert 		optreset = 0;
30847f6c619Smillert 		if (optind >= nargc) {          /* end of argument vector */
30947f6c619Smillert 			place = EMSG;
31047f6c619Smillert 			if (nonopt_end != -1) {
31147f6c619Smillert 				/* do permutation, if we have to */
31247f6c619Smillert 				permute_args(nonopt_start, nonopt_end,
31347f6c619Smillert 				    optind, nargv);
31447f6c619Smillert 				optind -= nonopt_end - nonopt_start;
31547f6c619Smillert 			}
31647f6c619Smillert 			else if (nonopt_start != -1) {
31747f6c619Smillert 				/*
31847f6c619Smillert 				 * If we skipped non-options, set optind
31947f6c619Smillert 				 * to the first of them.
32047f6c619Smillert 				 */
32147f6c619Smillert 				optind = nonopt_start;
32247f6c619Smillert 			}
32347f6c619Smillert 			nonopt_start = nonopt_end = -1;
324a4cca5d9Smillert 			return (-1);
32547f6c619Smillert 		}
32647f6c619Smillert 		if ((*(place = nargv[optind]) != '-')
32747f6c619Smillert 		    || (place[1] == '\0')) {    /* found non-option */
32847f6c619Smillert 			place = EMSG;
32947f6c619Smillert 			if (flags & FLAG_ALLARGS) {
33047f6c619Smillert 				/*
33147f6c619Smillert 				 * GNU extension:
33247f6c619Smillert 				 * return non-option as argument to option 1
33347f6c619Smillert 				 */
33447f6c619Smillert 				optarg = nargv[optind++];
335a4cca5d9Smillert 				return (INORDER);
33647f6c619Smillert 			}
33747f6c619Smillert 			if (!(flags & FLAG_PERMUTE)) {
33847f6c619Smillert 				/*
33947f6c619Smillert 				 * If no permutation wanted, stop parsing
34047f6c619Smillert 				 * at first non-option.
34147f6c619Smillert 				 */
342a4cca5d9Smillert 				return (-1);
34347f6c619Smillert 			}
34447f6c619Smillert 			/* do permutation */
34547f6c619Smillert 			if (nonopt_start == -1)
34647f6c619Smillert 				nonopt_start = optind;
34747f6c619Smillert 			else if (nonopt_end != -1) {
34847f6c619Smillert 				permute_args(nonopt_start, nonopt_end,
34947f6c619Smillert 				    optind, nargv);
35047f6c619Smillert 				nonopt_start = optind -
35147f6c619Smillert 				    (nonopt_end - nonopt_start);
35247f6c619Smillert 				nonopt_end = -1;
35347f6c619Smillert 			}
35447f6c619Smillert 			optind++;
35547f6c619Smillert 			/* process next argument */
35647f6c619Smillert 			goto start;
35747f6c619Smillert 		}
35847f6c619Smillert 		if (nonopt_start != -1 && nonopt_end == -1)
35947f6c619Smillert 			nonopt_end = optind;
360a4cca5d9Smillert 		if (strcmp(place, "--") == 0) {
361a4cca5d9Smillert 			optind++;
362a4cca5d9Smillert 			place = EMSG;
363a4cca5d9Smillert 			/*
364a4cca5d9Smillert 			 * We found an option (--), so if we skipped
365a4cca5d9Smillert 			 * non-options, we have to permute.
366a4cca5d9Smillert 			 */
367a4cca5d9Smillert 			if (nonopt_end != -1) {
368a4cca5d9Smillert 				permute_args(nonopt_start, nonopt_end,
369a4cca5d9Smillert 				    optind, nargv);
370a4cca5d9Smillert 				optind -= nonopt_end - nonopt_start;
371a4cca5d9Smillert 			}
372a4cca5d9Smillert 			nonopt_start = nonopt_end = -1;
373a4cca5d9Smillert 			return (-1);
374a4cca5d9Smillert 		}
37547f6c619Smillert 		place++;
376a4cca5d9Smillert 
377a4cca5d9Smillert 		/* Check long options if we have any */
378a4cca5d9Smillert 		if (long_options != NULL) {
379a4cca5d9Smillert 			int long_only = 0;
380a4cca5d9Smillert 
381a4cca5d9Smillert 			if (*place == '-' ||
382a4cca5d9Smillert 			    (long_only = (flags & FLAG_LONGONLY))) {
383a4cca5d9Smillert 				if (!long_only)
384a4cca5d9Smillert 					place++;
385a4cca5d9Smillert 				optchar = parse_long_options(nargc, nargv,
386a4cca5d9Smillert 				    options, long_options, idx, long_only);
387a4cca5d9Smillert 				if (optchar != -2) {
388a4cca5d9Smillert 					place = EMSG;
389a4cca5d9Smillert 					return (optchar);
390a4cca5d9Smillert 				}
391a4cca5d9Smillert 			}
39247f6c619Smillert 		}
39347f6c619Smillert 	}
39447f6c619Smillert 	if ((optchar = (int)*place++) == (int)':' ||
39547f6c619Smillert 	    (oli = strchr(options, optchar)) == NULL) {
39647f6c619Smillert 		/* option letter unknown or ':' */
39747f6c619Smillert 		if (!*place)
39847f6c619Smillert 			++optind;
39947f6c619Smillert 		if (PRINT_ERROR)
40047f6c619Smillert 			warnx(illoptchar, optchar);
40147f6c619Smillert 		optopt = optchar;
402a4cca5d9Smillert 		return (BADCH);
40347f6c619Smillert 	}
404a4cca5d9Smillert 	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
405a4cca5d9Smillert 		/* -W long-option */
406*3106aed5Smillert 		if (*place)			/* no space */
407*3106aed5Smillert 			/* NOTHING */;
408*3106aed5Smillert 		else if (++optind >= nargc) {	/* no arg */
40947f6c619Smillert 			place = EMSG;
41047f6c619Smillert 			if (PRINT_ERROR)
41147f6c619Smillert 				warnx(recargchar, optchar);
41247f6c619Smillert 			optopt = optchar;
413a4cca5d9Smillert 			return (BADARG);
41447f6c619Smillert 		} else				/* white space */
41547f6c619Smillert 			place = nargv[optind];
416a4cca5d9Smillert 		optchar = parse_long_options(nargc, nargv, options,
417a4cca5d9Smillert 		    long_options, idx, 0);
418a4cca5d9Smillert 		place = EMSG;
419a4cca5d9Smillert 		return (optchar);
42047f6c619Smillert 	}
42147f6c619Smillert 	if (*++oli != ':') {			/* doesn't take argument */
42247f6c619Smillert 		if (!*place)
42347f6c619Smillert 			++optind;
42447f6c619Smillert 	} else {				/* takes (optional) argument */
42547f6c619Smillert 		optarg = NULL;
42647f6c619Smillert 		if (*place)			/* no white space */
42747f6c619Smillert 			optarg = place;
42847f6c619Smillert 		/* XXX: disable test for :: if PC? (GNU doesn't) */
42947f6c619Smillert 		else if (oli[1] != ':') {	/* arg not optional */
43047f6c619Smillert 			if (++optind >= nargc) {	/* no arg */
43147f6c619Smillert 				place = EMSG;
43247f6c619Smillert 				if (PRINT_ERROR)
43347f6c619Smillert 					warnx(recargchar, optchar);
43447f6c619Smillert 				optopt = optchar;
435a4cca5d9Smillert 				return (BADARG);
43647f6c619Smillert 			} else
43747f6c619Smillert 				optarg = nargv[optind];
43847f6c619Smillert 		}
43947f6c619Smillert 		place = EMSG;
44047f6c619Smillert 		++optind;
44147f6c619Smillert 	}
44247f6c619Smillert 	/* dump back option letter */
443a4cca5d9Smillert 	return (optchar);
44447f6c619Smillert }
44547f6c619Smillert 
44647f6c619Smillert #ifdef REPLACE_GETOPT
44747f6c619Smillert /*
44847f6c619Smillert  * getopt --
44947f6c619Smillert  *	Parse argc/argv argument vector.
45047f6c619Smillert  *
45147f6c619Smillert  * [eventually this will replace the BSD getopt]
45247f6c619Smillert  */
45347f6c619Smillert int
454a4cca5d9Smillert getopt(int nargc, char * const *nargv, const char *options)
45547f6c619Smillert {
45647f6c619Smillert 
45747f6c619Smillert 	/*
458a4cca5d9Smillert 	 * We dont' pass FLAG_PERMUTE to getopt_internal() since
459a4cca5d9Smillert 	 * the BSD getopt(3) (unlike GNU) has never done this.
460a4cca5d9Smillert 	 *
461a4cca5d9Smillert 	 * Furthermore, since many privileged programs call getopt()
462a4cca5d9Smillert 	 * before dropping privileges it makes sense to keep things
463a4cca5d9Smillert 	 * as simple (and bug-free) as possible.
46447f6c619Smillert 	 */
465a4cca5d9Smillert 	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
46647f6c619Smillert }
46747f6c619Smillert #endif /* REPLACE_GETOPT */
46847f6c619Smillert 
46947f6c619Smillert /*
47047f6c619Smillert  * getopt_long --
47147f6c619Smillert  *	Parse argc/argv argument vector.
47247f6c619Smillert  */
47347f6c619Smillert int
47447f6c619Smillert getopt_long(nargc, nargv, options, long_options, idx)
47547f6c619Smillert 	int nargc;
47647f6c619Smillert 	char * const *nargv;
47747f6c619Smillert 	const char *options;
47847f6c619Smillert 	const struct option *long_options;
47947f6c619Smillert 	int *idx;
48047f6c619Smillert {
48147f6c619Smillert 
482a4cca5d9Smillert 	return (getopt_internal(nargc, nargv, options, long_options, idx,
483a4cca5d9Smillert 	    FLAG_PERMUTE));
48447f6c619Smillert }
48547f6c619Smillert 
48647f6c619Smillert /*
48747f6c619Smillert  * getopt_long_only --
48847f6c619Smillert  *	Parse argc/argv argument vector.
48947f6c619Smillert  */
49047f6c619Smillert int
49147f6c619Smillert getopt_long_only(nargc, nargv, options, long_options, idx)
49247f6c619Smillert 	int nargc;
49347f6c619Smillert 	char * const *nargv;
49447f6c619Smillert 	const char *options;
49547f6c619Smillert 	const struct option *long_options;
49647f6c619Smillert 	int *idx;
49747f6c619Smillert {
49847f6c619Smillert 
499a4cca5d9Smillert 	return (getopt_internal(nargc, nargv, options, long_options, idx,
500a4cca5d9Smillert 	    FLAG_PERMUTE|FLAG_LONGONLY));
50147f6c619Smillert }
502