1276761Sdelphij/*	$OpenBSD: getopt_long.c,v 1.22 2006/10/04 21:29:04 jmc Exp $	*/
2276761Sdelphij/*	$NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $	*/
3276761Sdelphij
4276761Sdelphij/*
5276761Sdelphij * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
6276761Sdelphij *
7276761Sdelphij * Permission to use, copy, modify, and distribute this software for any
8276761Sdelphij * purpose with or without fee is hereby granted, provided that the above
9276761Sdelphij * copyright notice and this permission notice appear in all copies.
10276761Sdelphij *
11276761Sdelphij * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12276761Sdelphij * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13276761Sdelphij * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14276761Sdelphij * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15276761Sdelphij * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16276761Sdelphij * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17276761Sdelphij * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18276761Sdelphij *
19276761Sdelphij * Sponsored in part by the Defense Advanced Research Projects
20276761Sdelphij * Agency (DARPA) and Air Force Research Laboratory, Air Force
21276761Sdelphij * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22276761Sdelphij */
23276761Sdelphij/*-
24276761Sdelphij * Copyright (c) 2000 The NetBSD Foundation, Inc.
25276761Sdelphij * All rights reserved.
26276761Sdelphij *
27276761Sdelphij * This code is derived from software contributed to The NetBSD Foundation
28276761Sdelphij * by Dieter Baron and Thomas Klausner.
29276761Sdelphij *
30276761Sdelphij * Redistribution and use in source and binary forms, with or without
31276761Sdelphij * modification, are permitted provided that the following conditions
32276761Sdelphij * are met:
33276761Sdelphij * 1. Redistributions of source code must retain the above copyright
34276761Sdelphij *    notice, this list of conditions and the following disclaimer.
35276761Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
36276761Sdelphij *    notice, this list of conditions and the following disclaimer in the
37276761Sdelphij *    documentation and/or other materials provided with the distribution.
38276761Sdelphij *
39276761Sdelphij * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
40276761Sdelphij * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41276761Sdelphij * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42276761Sdelphij * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
43276761Sdelphij * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
44276761Sdelphij * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
45276761Sdelphij * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
46276761Sdelphij * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47276761Sdelphij * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48276761Sdelphij * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49276761Sdelphij * POSSIBILITY OF SUCH DAMAGE.
50276761Sdelphij */
51276761Sdelphij
52276761Sdelphij
53276761Sdelphij#include <errno.h>
54276761Sdelphij#include "getopt_long.h"
55276761Sdelphij#include <stdlib.h>
56276761Sdelphij#include <stdio.h>
57276761Sdelphij#include <string.h>
58276761Sdelphij#include <stdarg.h>
59276761Sdelphij
60276761Sdelphij#define GNU_COMPATIBLE		/* Be more compatible, configure's use us! */
61276761Sdelphij
62276761Sdelphij#define PRINT_ERROR	((opterr) && (*options != ':'))
63276761Sdelphij
64276761Sdelphij#define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
65276761Sdelphij#define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
66276761Sdelphij#define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
67276761Sdelphij
68276761Sdelphij/* return values */
69276761Sdelphij#define	BADCH		(int)'?'
70276761Sdelphij#define	BADARG		((*options == ':') ? (int)':' : (int)'?')
71276761Sdelphij#define	INORDER 	(int)1
72276761Sdelphij
73276761Sdelphij#define	EMSG		""
74276761Sdelphij
75276761Sdelphij#ifdef GNU_COMPATIBLE
76276761Sdelphij#define NO_PREFIX	(-1)
77276761Sdelphij#define D_PREFIX	0
78276761Sdelphij#define DD_PREFIX	1
79276761Sdelphij#define W_PREFIX	2
80276761Sdelphij#endif
81276761Sdelphij
82276761Sdelphijchar *optarg;
83276761Sdelphijint optind, opterr = 1, optopt;
84276761Sdelphij
85276761Sdelphijstatic int getopt_internal(int, char * const *, const char *,
86276761Sdelphij			   const struct option *, int *, int);
87276761Sdelphijstatic int parse_long_options(char * const *, const char *,
88276761Sdelphij			      const struct option *, int *, int, int);
89276761Sdelphijstatic int gcd(int, int);
90276761Sdelphijstatic void permute_args(int, int, int, char * const *);
91276761Sdelphij
92276761Sdelphijstatic const char *place = EMSG; /* option letter processing */
93276761Sdelphij
94276761Sdelphijstatic int nonopt_start = -1; /* first non option argument (for permute) */
95276761Sdelphijstatic int nonopt_end = -1;   /* first option after non options (for permute) */
96276761Sdelphij
97276761Sdelphij/* Error messages */
98276761Sdelphijstatic const char recargchar[] = "option requires an argument -- %c";
99276761Sdelphijstatic const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
100276761Sdelphij#ifdef GNU_COMPATIBLE
101276761Sdelphijstatic int dash_prefix = NO_PREFIX;
102276761Sdelphijstatic const char gnuoptchar[] = "invalid option -- %c";
103276761Sdelphij
104276761Sdelphijstatic const char recargstring[] = "option `%s%s' requires an argument";
105276761Sdelphijstatic const char ambig[] = "option `%s%.*s' is ambiguous";
106276761Sdelphijstatic const char noarg[] = "option `%s%.*s' doesn't allow an argument";
107276761Sdelphijstatic const char illoptstring[] = "unrecognized option `%s%s'";
108276761Sdelphij#else
109276761Sdelphijstatic const char recargstring[] = "option requires an argument -- %s";
110276761Sdelphijstatic const char ambig[] = "ambiguous option -- %.*s";
111276761Sdelphijstatic const char noarg[] = "option doesn't take an argument -- %.*s";
112276761Sdelphijstatic const char illoptstring[] = "unknown option -- %s";
113276761Sdelphij#endif
114276761Sdelphij
115276761Sdelphij/*
116276761Sdelphij * Compute the greatest common divisor of a and b.
117276761Sdelphij */
118276761Sdelphijstatic int
119276761Sdelphijgcd(int a, int b)
120276761Sdelphij{
121276761Sdelphij	int c;
122276761Sdelphij
123276761Sdelphij	c = a % b;
124276761Sdelphij	while (c != 0) {
125276761Sdelphij		a = b;
126276761Sdelphij		b = c;
127276761Sdelphij		c = a % b;
128276761Sdelphij	}
129276761Sdelphij
130276761Sdelphij	return (b);
131276761Sdelphij}
132276761Sdelphij
133276761Sdelphij/*
134276761Sdelphij * Exchange the block from nonopt_start to nonopt_end with the block
135276761Sdelphij * from nonopt_end to opt_end (keeping the same order of arguments
136276761Sdelphij * in each block).
137276761Sdelphij */
138276761Sdelphijstatic void
139276761Sdelphijpermute_args(int panonopt_start, int panonopt_end, int opt_end,
140276761Sdelphij	char * const *nargv)
141276761Sdelphij{
142276761Sdelphij	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
143276761Sdelphij	char *swap;
144276761Sdelphij
145276761Sdelphij	/*
146276761Sdelphij	 * compute lengths of blocks and number and size of cycles
147276761Sdelphij	 */
148276761Sdelphij	nnonopts = panonopt_end - panonopt_start;
149276761Sdelphij	nopts = opt_end - panonopt_end;
150276761Sdelphij	ncycle = gcd(nnonopts, nopts);
151276761Sdelphij	cyclelen = (opt_end - panonopt_start) / ncycle;
152276761Sdelphij
153276761Sdelphij	for (i = 0; i < ncycle; i++) {
154276761Sdelphij		cstart = panonopt_end+i;
155276761Sdelphij		pos = cstart;
156276761Sdelphij		for (j = 0; j < cyclelen; j++) {
157276761Sdelphij			if (pos >= panonopt_end)
158276761Sdelphij				pos -= nnonopts;
159276761Sdelphij			else
160276761Sdelphij				pos += nopts;
161276761Sdelphij			swap = nargv[pos];
162276761Sdelphij			/* LINTED const cast */
163276761Sdelphij			((char **) nargv)[pos] = nargv[cstart];
164276761Sdelphij			/* LINTED const cast */
165276761Sdelphij			((char **)nargv)[cstart] = swap;
166276761Sdelphij		}
167276761Sdelphij	}
168276761Sdelphij}
169276761Sdelphij
170276761Sdelphijstatic void
171276761Sdelphijwarnx(const char *fmt, ...)
172276761Sdelphij{
173276761Sdelphij	extern char *program_name;
174276761Sdelphij	va_list ap;
175276761Sdelphij
176276761Sdelphij	va_start(ap, fmt);
177276761Sdelphij	fprintf(stderr, "%s: ", program_name);
178276761Sdelphij	vfprintf(stderr, fmt, ap);
179276761Sdelphij	fprintf(stderr, "\n");
180276761Sdelphij	va_end(ap);
181276761Sdelphij}
182276761Sdelphij
183276761Sdelphij/*
184276761Sdelphij * parse_long_options --
185276761Sdelphij *	Parse long options in argc/argv argument vector.
186276761Sdelphij * Returns -1 if short_too is set and the option does not match long_options.
187276761Sdelphij */
188276761Sdelphijstatic int
189276761Sdelphijparse_long_options(char * const *nargv, const char *options,
190276761Sdelphij	const struct option *long_options, int *idx, int short_too, int flags)
191276761Sdelphij{
192276761Sdelphij	const char *current_argv, *has_equal;
193276761Sdelphij#ifdef GNU_COMPATIBLE
194276761Sdelphij	const char *current_dash;
195276761Sdelphij#endif
196276761Sdelphij	size_t current_argv_len;
197276761Sdelphij	int i, match, exact_match, second_partial_match;
198276761Sdelphij
199276761Sdelphij	current_argv = place;
200276761Sdelphij#ifdef GNU_COMPATIBLE
201276761Sdelphij	switch (dash_prefix) {
202276761Sdelphij		case D_PREFIX:
203276761Sdelphij			current_dash = "-";
204276761Sdelphij			break;
205276761Sdelphij		case DD_PREFIX:
206276761Sdelphij			current_dash = "--";
207276761Sdelphij			break;
208276761Sdelphij		case W_PREFIX:
209276761Sdelphij			current_dash = "-W ";
210276761Sdelphij			break;
211276761Sdelphij		default:
212276761Sdelphij			current_dash = "";
213276761Sdelphij			break;
214276761Sdelphij	}
215276761Sdelphij#endif
216276761Sdelphij	match = -1;
217276761Sdelphij	exact_match = 0;
218276761Sdelphij	second_partial_match = 0;
219276761Sdelphij
220276761Sdelphij	optind++;
221276761Sdelphij
222276761Sdelphij	if ((has_equal = strchr(current_argv, '=')) != NULL) {
223276761Sdelphij		/* argument found (--option=arg) */
224276761Sdelphij		current_argv_len = has_equal - current_argv;
225276761Sdelphij		has_equal++;
226276761Sdelphij	} else
227276761Sdelphij		current_argv_len = strlen(current_argv);
228276761Sdelphij
229276761Sdelphij	for (i = 0; long_options[i].name; i++) {
230276761Sdelphij		/* find matching long option */
231276761Sdelphij		if (strncmp(current_argv, long_options[i].name,
232276761Sdelphij		    current_argv_len))
233276761Sdelphij			continue;
234276761Sdelphij
235276761Sdelphij		if (strlen(long_options[i].name) == current_argv_len) {
236276761Sdelphij			/* exact match */
237276761Sdelphij			match = i;
238276761Sdelphij			exact_match = 1;
239276761Sdelphij			break;
240276761Sdelphij		}
241276761Sdelphij		/*
242276761Sdelphij		 * If this is a known short option, don't allow
243276761Sdelphij		 * a partial match of a single character.
244276761Sdelphij		 */
245276761Sdelphij		if (short_too && current_argv_len == 1)
246276761Sdelphij			continue;
247276761Sdelphij
248276761Sdelphij		if (match == -1)        /* first partial match */
249276761Sdelphij			match = i;
250276761Sdelphij		else if ((flags & FLAG_LONGONLY) ||
251276761Sdelphij			 long_options[i].has_arg !=
252276761Sdelphij			     long_options[match].has_arg ||
253276761Sdelphij			 long_options[i].flag != long_options[match].flag ||
254276761Sdelphij			 long_options[i].val != long_options[match].val)
255276761Sdelphij			second_partial_match = 1;
256276761Sdelphij	}
257276761Sdelphij	if (!exact_match && second_partial_match) {
258276761Sdelphij		/* ambiguous abbreviation */
259276761Sdelphij		if (PRINT_ERROR)
260276761Sdelphij			warnx(ambig,
261276761Sdelphij#ifdef GNU_COMPATIBLE
262276761Sdelphij			     current_dash,
263276761Sdelphij#endif
264276761Sdelphij			     (int)current_argv_len,
265276761Sdelphij			     current_argv);
266276761Sdelphij		optopt = 0;
267276761Sdelphij		return (BADCH);
268276761Sdelphij	}
269276761Sdelphij	if (match != -1) {		/* option found */
270276761Sdelphij		if (long_options[match].has_arg == no_argument
271276761Sdelphij		    && has_equal) {
272276761Sdelphij			if (PRINT_ERROR)
273276761Sdelphij				warnx(noarg,
274276761Sdelphij#ifdef GNU_COMPATIBLE
275276761Sdelphij				     current_dash,
276276761Sdelphij#endif
277276761Sdelphij				     (int)current_argv_len,
278276761Sdelphij				     current_argv);
279276761Sdelphij			/*
280276761Sdelphij			 * XXX: GNU sets optopt to val regardless of flag
281276761Sdelphij			 */
282276761Sdelphij			if (long_options[match].flag == NULL)
283276761Sdelphij				optopt = long_options[match].val;
284276761Sdelphij			else
285276761Sdelphij				optopt = 0;
286276761Sdelphij#ifdef GNU_COMPATIBLE
287276761Sdelphij			return (BADCH);
288276761Sdelphij#else
289276761Sdelphij			return (BADARG);
290276761Sdelphij#endif
291276761Sdelphij		}
292276761Sdelphij		if (long_options[match].has_arg == required_argument ||
293276761Sdelphij		    long_options[match].has_arg == optional_argument) {
294276761Sdelphij			if (has_equal)
295276761Sdelphij				optarg = (char *)has_equal;
296276761Sdelphij			else if (long_options[match].has_arg ==
297276761Sdelphij			    required_argument) {
298276761Sdelphij				/*
299276761Sdelphij				 * optional argument doesn't use next nargv
300276761Sdelphij				 */
301276761Sdelphij				optarg = nargv[optind++];
302276761Sdelphij			}
303276761Sdelphij		}
304276761Sdelphij		if ((long_options[match].has_arg == required_argument)
305276761Sdelphij		    && (optarg == NULL)) {
306276761Sdelphij			/*
307276761Sdelphij			 * Missing argument; leading ':' indicates no error
308276761Sdelphij			 * should be generated.
309276761Sdelphij			 */
310276761Sdelphij			if (PRINT_ERROR)
311276761Sdelphij				warnx(recargstring,
312276761Sdelphij#ifdef GNU_COMPATIBLE
313276761Sdelphij				    current_dash,
314276761Sdelphij#endif
315276761Sdelphij				    current_argv);
316276761Sdelphij			/*
317276761Sdelphij			 * XXX: GNU sets optopt to val regardless of flag
318276761Sdelphij			 */
319276761Sdelphij			if (long_options[match].flag == NULL)
320276761Sdelphij				optopt = long_options[match].val;
321276761Sdelphij			else
322276761Sdelphij				optopt = 0;
323276761Sdelphij			--optind;
324276761Sdelphij			return (BADARG);
325276761Sdelphij		}
326276761Sdelphij	} else {			/* unknown option */
327276761Sdelphij		if (short_too) {
328276761Sdelphij			--optind;
329276761Sdelphij			return (-1);
330276761Sdelphij		}
331276761Sdelphij		if (PRINT_ERROR)
332276761Sdelphij			warnx(illoptstring,
333276761Sdelphij#ifdef GNU_COMPATIBLE
334276761Sdelphij			      current_dash,
335276761Sdelphij#endif
336276761Sdelphij			      current_argv);
337276761Sdelphij		optopt = 0;
338276761Sdelphij		return (BADCH);
339276761Sdelphij	}
340276761Sdelphij	if (idx)
341276761Sdelphij		*idx = match;
342276761Sdelphij	if (long_options[match].flag) {
343276761Sdelphij		*long_options[match].flag = long_options[match].val;
344276761Sdelphij		return (0);
345276761Sdelphij	} else
346276761Sdelphij		return (long_options[match].val);
347276761Sdelphij}
348276761Sdelphij
349276761Sdelphij/*
350276761Sdelphij * getopt_internal --
351276761Sdelphij *	Parse argc/argv argument vector.  Called by user level routines.
352276761Sdelphij */
353276761Sdelphijstatic int
354276761Sdelphijgetopt_internal(int nargc, char * const *nargv, const char *options,
355276761Sdelphij	const struct option *long_options, int *idx, int flags)
356276761Sdelphij{
357276761Sdelphij	char *oli;				/* option letter list index */
358276761Sdelphij	int optchar, short_too;
359276761Sdelphij	int posixly_correct;	/* no static, can be changed on the fly */
360276761Sdelphij
361276761Sdelphij	if (options == NULL)
362276761Sdelphij		return (-1);
363276761Sdelphij
364276761Sdelphij	/*
365276761Sdelphij	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
366276761Sdelphij	 * string begins with a '+'.
367276761Sdelphij	 */
368276761Sdelphij	posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
369276761Sdelphij#ifdef GNU_COMPATIBLE
370276761Sdelphij	if (*options == '-')
371276761Sdelphij		flags |= FLAG_ALLARGS;
372276761Sdelphij	else if (posixly_correct || *options == '+')
373276761Sdelphij		flags &= ~FLAG_PERMUTE;
374276761Sdelphij#else
375276761Sdelphij	if (posixly_correct || *options == '+')
376276761Sdelphij		flags &= ~FLAG_PERMUTE;
377276761Sdelphij	else if (*options == '-')
378276761Sdelphij		flags |= FLAG_ALLARGS;
379276761Sdelphij#endif
380276761Sdelphij	if (*options == '+' || *options == '-')
381276761Sdelphij		options++;
382276761Sdelphij
383276761Sdelphij	/*
384276761Sdelphij	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
385276761Sdelphij	 * XXX using optreset.  Work around this braindamage.
386276761Sdelphij	 */
387276761Sdelphij	if (optind == 0)
388276761Sdelphij		optind = 1;
389276761Sdelphij
390276761Sdelphij	optarg = NULL;
391276761Sdelphijstart:
392276761Sdelphij	if (!*place) {				/* update scanning pointer */
393276761Sdelphij		if (optind >= nargc) {          /* end of argument vector */
394276761Sdelphij			place = EMSG;
395276761Sdelphij			if (nonopt_end != -1) {
396276761Sdelphij				/* do permutation, if we have to */
397276761Sdelphij				permute_args(nonopt_start, nonopt_end,
398276761Sdelphij				    optind, nargv);
399276761Sdelphij				optind -= nonopt_end - nonopt_start;
400276761Sdelphij			}
401276761Sdelphij			else if (nonopt_start != -1) {
402276761Sdelphij				/*
403276761Sdelphij				 * If we skipped non-options, set optind
404276761Sdelphij				 * to the first of them.
405276761Sdelphij				 */
406276761Sdelphij				optind = nonopt_start;
407276761Sdelphij			}
408276761Sdelphij			nonopt_start = nonopt_end = -1;
409276761Sdelphij			return (-1);
410276761Sdelphij		}
411276761Sdelphij		if (*(place = nargv[optind]) != '-' ||
412276761Sdelphij#ifdef GNU_COMPATIBLE
413276761Sdelphij		    place[1] == '\0') {
414276761Sdelphij#else
415276761Sdelphij		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
416276761Sdelphij#endif
417276761Sdelphij			place = EMSG;		/* found non-option */
418276761Sdelphij			if (flags & FLAG_ALLARGS) {
419276761Sdelphij				/*
420276761Sdelphij				 * GNU extension:
421276761Sdelphij				 * return non-option as argument to option 1
422276761Sdelphij				 */
423276761Sdelphij				optarg = nargv[optind++];
424276761Sdelphij				return (INORDER);
425276761Sdelphij			}
426276761Sdelphij			if (!(flags & FLAG_PERMUTE)) {
427276761Sdelphij				/*
428276761Sdelphij				 * If no permutation wanted, stop parsing
429276761Sdelphij				 * at first non-option.
430276761Sdelphij				 */
431276761Sdelphij				return (-1);
432276761Sdelphij			}
433276761Sdelphij			/* do permutation */
434276761Sdelphij			if (nonopt_start == -1)
435276761Sdelphij				nonopt_start = optind;
436276761Sdelphij			else if (nonopt_end != -1) {
437276761Sdelphij				permute_args(nonopt_start, nonopt_end,
438276761Sdelphij				    optind, nargv);
439276761Sdelphij				nonopt_start = optind -
440276761Sdelphij				    (nonopt_end - nonopt_start);
441276761Sdelphij				nonopt_end = -1;
442276761Sdelphij			}
443276761Sdelphij			optind++;
444276761Sdelphij			/* process next argument */
445276761Sdelphij			goto start;
446276761Sdelphij		}
447276761Sdelphij		if (nonopt_start != -1 && nonopt_end == -1)
448276761Sdelphij			nonopt_end = optind;
449276761Sdelphij
450276761Sdelphij		/*
451276761Sdelphij		 * If we have "-" do nothing, if "--" we are done.
452276761Sdelphij		 */
453276761Sdelphij		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
454276761Sdelphij			optind++;
455276761Sdelphij			place = EMSG;
456276761Sdelphij			/*
457276761Sdelphij			 * We found an option (--), so if we skipped
458276761Sdelphij			 * non-options, we have to permute.
459276761Sdelphij			 */
460276761Sdelphij			if (nonopt_end != -1) {
461276761Sdelphij				permute_args(nonopt_start, nonopt_end,
462276761Sdelphij				    optind, nargv);
463276761Sdelphij				optind -= nonopt_end - nonopt_start;
464276761Sdelphij			}
465276761Sdelphij			nonopt_start = nonopt_end = -1;
466276761Sdelphij			return (-1);
467276761Sdelphij		}
468276761Sdelphij	}
469276761Sdelphij
470276761Sdelphij	/*
471276761Sdelphij	 * Check long options if:
472276761Sdelphij	 *  1) we were passed some
473276761Sdelphij	 *  2) the arg is not just "-"
474276761Sdelphij	 *  3) either the arg starts with -- we are getopt_long_only()
475276761Sdelphij	 */
476276761Sdelphij	if (long_options != NULL && place != nargv[optind] &&
477276761Sdelphij	    (*place == '-' || (flags & FLAG_LONGONLY))) {
478276761Sdelphij		short_too = 0;
479276761Sdelphij#ifdef GNU_COMPATIBLE
480276761Sdelphij		dash_prefix = D_PREFIX;
481276761Sdelphij#endif
482276761Sdelphij		if (*place == '-') {
483276761Sdelphij			place++;		/* --foo long option */
484276761Sdelphij#ifdef GNU_COMPATIBLE
485276761Sdelphij			dash_prefix = DD_PREFIX;
486276761Sdelphij#endif
487276761Sdelphij		} else if (*place != ':' && strchr(options, *place) != NULL)
488276761Sdelphij			short_too = 1;		/* could be short option too */
489276761Sdelphij
490276761Sdelphij		optchar = parse_long_options(nargv, options, long_options,
491276761Sdelphij		    idx, short_too, flags);
492276761Sdelphij		if (optchar != -1) {
493276761Sdelphij			place = EMSG;
494276761Sdelphij			return (optchar);
495276761Sdelphij		}
496276761Sdelphij	}
497276761Sdelphij
498276761Sdelphij	if ((optchar = (int)*place++) == (int)':' ||
499276761Sdelphij	    (optchar == (int)'-' && *place != '\0') ||
500276761Sdelphij	    (oli = strchr(options, optchar)) == NULL) {
501276761Sdelphij		/*
502276761Sdelphij		 * If the user specified "-" and  '-' isn't listed in
503276761Sdelphij		 * options, return -1 (non-option) as per POSIX.
504276761Sdelphij		 * Otherwise, it is an unknown option character (or ':').
505276761Sdelphij		 */
506276761Sdelphij		if (optchar == (int)'-' && *place == '\0')
507276761Sdelphij			return (-1);
508276761Sdelphij		if (!*place)
509276761Sdelphij			++optind;
510276761Sdelphij#ifdef GNU_COMPATIBLE
511276761Sdelphij		if (PRINT_ERROR)
512276761Sdelphij			warnx(posixly_correct ? illoptchar : gnuoptchar,
513276761Sdelphij			      optchar);
514276761Sdelphij#else
515276761Sdelphij		if (PRINT_ERROR)
516276761Sdelphij			warnx(illoptchar, optchar);
517276761Sdelphij#endif
518276761Sdelphij		optopt = optchar;
519276761Sdelphij		return (BADCH);
520276761Sdelphij	}
521276761Sdelphij	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
522276761Sdelphij		/* -W long-option */
523276761Sdelphij		if (*place)			/* no space */
524276761Sdelphij			/* NOTHING */;
525276761Sdelphij		else if (++optind >= nargc) {	/* no arg */
526276761Sdelphij			place = EMSG;
527276761Sdelphij			if (PRINT_ERROR)
528276761Sdelphij				warnx(recargchar, optchar);
529276761Sdelphij			optopt = optchar;
530276761Sdelphij			return (BADARG);
531276761Sdelphij		} else				/* white space */
532276761Sdelphij			place = nargv[optind];
533276761Sdelphij#ifdef GNU_COMPATIBLE
534276761Sdelphij		dash_prefix = W_PREFIX;
535276761Sdelphij#endif
536276761Sdelphij		optchar = parse_long_options(nargv, options, long_options,
537276761Sdelphij		    idx, 0, flags);
538276761Sdelphij		place = EMSG;
539276761Sdelphij		return (optchar);
540276761Sdelphij	}
541276761Sdelphij	if (*++oli != ':') {			/* doesn't take argument */
542276761Sdelphij		if (!*place)
543276761Sdelphij			++optind;
544276761Sdelphij	} else {				/* takes (optional) argument */
545276761Sdelphij		optarg = NULL;
546276761Sdelphij		if (*place)			/* no white space */
547276761Sdelphij			optarg = (char *)place;
548276761Sdelphij		else if (oli[1] != ':') {	/* arg not optional */
549276761Sdelphij			if (++optind >= nargc) {	/* no arg */
550276761Sdelphij				place = EMSG;
551276761Sdelphij				if (PRINT_ERROR)
552276761Sdelphij					warnx(recargchar, optchar);
553276761Sdelphij				optopt = optchar;
554276761Sdelphij				return (BADARG);
555276761Sdelphij			} else
556276761Sdelphij				optarg = nargv[optind];
557276761Sdelphij		}
558276761Sdelphij		place = EMSG;
559276761Sdelphij		++optind;
560276761Sdelphij	}
561276761Sdelphij	/* dump back option letter */
562276761Sdelphij	return (optchar);
563276761Sdelphij}
564276761Sdelphij
565276761Sdelphij#ifdef REPLACE_GETOPT
566276761Sdelphij/*
567276761Sdelphij * getopt --
568276761Sdelphij *	Parse argc/argv argument vector.
569276761Sdelphij *
570276761Sdelphij * [eventually this will replace the BSD getopt]
571276761Sdelphij */
572276761Sdelphijint
573276761Sdelphijgetopt(int nargc, char * const *nargv, const char *options)
574276761Sdelphij{
575276761Sdelphij
576276761Sdelphij	/*
577276761Sdelphij	 * We don't pass FLAG_PERMUTE to getopt_internal() since
578276761Sdelphij	 * the BSD getopt(3) (unlike GNU) has never done this.
579276761Sdelphij	 *
580276761Sdelphij	 * Furthermore, since many privileged programs call getopt()
581276761Sdelphij	 * before dropping privileges it makes sense to keep things
582276761Sdelphij	 * as simple (and bug-free) as possible.
583276761Sdelphij	 */
584276761Sdelphij	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
585276761Sdelphij}
586276761Sdelphij#endif /* REPLACE_GETOPT */
587276761Sdelphij
588276761Sdelphij/*
589276761Sdelphij * getopt_long --
590276761Sdelphij *	Parse argc/argv argument vector.
591276761Sdelphij */
592276761Sdelphijint
593276761Sdelphijgetopt_long(int nargc, char * const *nargv, const char *options,
594276761Sdelphij	const struct option *long_options, int *idx)
595276761Sdelphij{
596276761Sdelphij
597276761Sdelphij	return (getopt_internal(nargc, nargv, options, long_options, idx,
598276761Sdelphij	    FLAG_PERMUTE));
599276761Sdelphij}
600276761Sdelphij
601276761Sdelphij/*
602276761Sdelphij * getopt_long_only --
603276761Sdelphij *	Parse argc/argv argument vector.
604276761Sdelphij */
605276761Sdelphijint
606276761Sdelphijgetopt_long_only(int nargc, char * const *nargv, const char *options,
607276761Sdelphij	const struct option *long_options, int *idx)
608276761Sdelphij{
609276761Sdelphij
610276761Sdelphij	return (getopt_internal(nargc, nargv, options, long_options, idx,
611276761Sdelphij	    FLAG_PERMUTE|FLAG_LONGONLY));
612276761Sdelphij}
613