xref: /minix3/external/bsd/tcpdump/dist/missing/getopt_long.c (revision b636d99d91c3d54204248f643c14627405d4afd1)
1*b636d99dSDavid van Moolenbroek /*	$OpenBSD: getopt_long.c,v 1.22 2006/10/04 21:29:04 jmc Exp $	*/
2*b636d99dSDavid van Moolenbroek /*	NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp 	*/
3*b636d99dSDavid van Moolenbroek 
4*b636d99dSDavid van Moolenbroek /*
5*b636d99dSDavid van Moolenbroek  * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
6*b636d99dSDavid van Moolenbroek  *
7*b636d99dSDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software for any
8*b636d99dSDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
9*b636d99dSDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
10*b636d99dSDavid van Moolenbroek  *
11*b636d99dSDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12*b636d99dSDavid van Moolenbroek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*b636d99dSDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14*b636d99dSDavid van Moolenbroek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*b636d99dSDavid van Moolenbroek  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16*b636d99dSDavid van Moolenbroek  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17*b636d99dSDavid van Moolenbroek  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*b636d99dSDavid van Moolenbroek  *
19*b636d99dSDavid van Moolenbroek  * Sponsored in part by the Defense Advanced Research Projects
20*b636d99dSDavid van Moolenbroek  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21*b636d99dSDavid van Moolenbroek  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22*b636d99dSDavid van Moolenbroek  */
23*b636d99dSDavid van Moolenbroek /*-
24*b636d99dSDavid van Moolenbroek  * Copyright (c) 2000 The NetBSD Foundation, Inc.
25*b636d99dSDavid van Moolenbroek  * All rights reserved.
26*b636d99dSDavid van Moolenbroek  *
27*b636d99dSDavid van Moolenbroek  * This code is derived from software contributed to The NetBSD Foundation
28*b636d99dSDavid van Moolenbroek  * by Dieter Baron and Thomas Klausner.
29*b636d99dSDavid van Moolenbroek  *
30*b636d99dSDavid van Moolenbroek  * Redistribution and use in source and binary forms, with or without
31*b636d99dSDavid van Moolenbroek  * modification, are permitted provided that the following conditions
32*b636d99dSDavid van Moolenbroek  * are met:
33*b636d99dSDavid van Moolenbroek  * 1. Redistributions of source code must retain the above copyright
34*b636d99dSDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer.
35*b636d99dSDavid van Moolenbroek  * 2. Redistributions in binary form must reproduce the above copyright
36*b636d99dSDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer in the
37*b636d99dSDavid van Moolenbroek  *    documentation and/or other materials provided with the distribution.
38*b636d99dSDavid van Moolenbroek  *
39*b636d99dSDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
40*b636d99dSDavid van Moolenbroek  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41*b636d99dSDavid van Moolenbroek  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42*b636d99dSDavid van Moolenbroek  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
43*b636d99dSDavid van Moolenbroek  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
44*b636d99dSDavid van Moolenbroek  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
45*b636d99dSDavid van Moolenbroek  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
46*b636d99dSDavid van Moolenbroek  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47*b636d99dSDavid van Moolenbroek  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48*b636d99dSDavid van Moolenbroek  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49*b636d99dSDavid van Moolenbroek  * POSSIBILITY OF SUCH DAMAGE.
50*b636d99dSDavid van Moolenbroek  */
51*b636d99dSDavid van Moolenbroek 
52*b636d99dSDavid van Moolenbroek 
53*b636d99dSDavid van Moolenbroek #include <errno.h>
54*b636d99dSDavid van Moolenbroek #include "getopt_long.h"
55*b636d99dSDavid van Moolenbroek #include <stdlib.h>
56*b636d99dSDavid van Moolenbroek #include <stdio.h>
57*b636d99dSDavid van Moolenbroek #include <string.h>
58*b636d99dSDavid van Moolenbroek #include <stdarg.h>
59*b636d99dSDavid van Moolenbroek 
60*b636d99dSDavid van Moolenbroek #define GNU_COMPATIBLE		/* Be more compatible, configure's use us! */
61*b636d99dSDavid van Moolenbroek 
62*b636d99dSDavid van Moolenbroek #define PRINT_ERROR	((opterr) && (*options != ':'))
63*b636d99dSDavid van Moolenbroek 
64*b636d99dSDavid van Moolenbroek #define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
65*b636d99dSDavid van Moolenbroek #define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
66*b636d99dSDavid van Moolenbroek #define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
67*b636d99dSDavid van Moolenbroek 
68*b636d99dSDavid van Moolenbroek /* return values */
69*b636d99dSDavid van Moolenbroek #define	BADCH		(int)'?'
70*b636d99dSDavid van Moolenbroek #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
71*b636d99dSDavid van Moolenbroek #define	INORDER 	(int)1
72*b636d99dSDavid van Moolenbroek 
73*b636d99dSDavid van Moolenbroek #define	EMSG		""
74*b636d99dSDavid van Moolenbroek 
75*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
76*b636d99dSDavid van Moolenbroek #define NO_PREFIX	(-1)
77*b636d99dSDavid van Moolenbroek #define D_PREFIX	0
78*b636d99dSDavid van Moolenbroek #define DD_PREFIX	1
79*b636d99dSDavid van Moolenbroek #define W_PREFIX	2
80*b636d99dSDavid van Moolenbroek #endif
81*b636d99dSDavid van Moolenbroek 
82*b636d99dSDavid van Moolenbroek char *optarg;
83*b636d99dSDavid van Moolenbroek int optind, opterr = 1, optopt;
84*b636d99dSDavid van Moolenbroek 
85*b636d99dSDavid van Moolenbroek static int getopt_internal(int, char * const *, const char *,
86*b636d99dSDavid van Moolenbroek 			   const struct option *, int *, int);
87*b636d99dSDavid van Moolenbroek static int parse_long_options(char * const *, const char *,
88*b636d99dSDavid van Moolenbroek 			      const struct option *, int *, int, int);
89*b636d99dSDavid van Moolenbroek static int gcd(int, int);
90*b636d99dSDavid van Moolenbroek static void permute_args(int, int, int, char * const *);
91*b636d99dSDavid van Moolenbroek 
92*b636d99dSDavid van Moolenbroek static const char *place = EMSG; /* option letter processing */
93*b636d99dSDavid van Moolenbroek 
94*b636d99dSDavid van Moolenbroek static int nonopt_start = -1; /* first non option argument (for permute) */
95*b636d99dSDavid van Moolenbroek static int nonopt_end = -1;   /* first option after non options (for permute) */
96*b636d99dSDavid van Moolenbroek 
97*b636d99dSDavid van Moolenbroek /* Error messages */
98*b636d99dSDavid van Moolenbroek static const char recargchar[] = "option requires an argument -- %c";
99*b636d99dSDavid van Moolenbroek static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
100*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
101*b636d99dSDavid van Moolenbroek static int dash_prefix = NO_PREFIX;
102*b636d99dSDavid van Moolenbroek static const char gnuoptchar[] = "invalid option -- %c";
103*b636d99dSDavid van Moolenbroek 
104*b636d99dSDavid van Moolenbroek static const char recargstring[] = "option `%s%s' requires an argument";
105*b636d99dSDavid van Moolenbroek static const char ambig[] = "option `%s%.*s' is ambiguous";
106*b636d99dSDavid van Moolenbroek static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
107*b636d99dSDavid van Moolenbroek static const char illoptstring[] = "unrecognized option `%s%s'";
108*b636d99dSDavid van Moolenbroek #else
109*b636d99dSDavid van Moolenbroek static const char recargstring[] = "option requires an argument -- %s";
110*b636d99dSDavid van Moolenbroek static const char ambig[] = "ambiguous option -- %.*s";
111*b636d99dSDavid van Moolenbroek static const char noarg[] = "option doesn't take an argument -- %.*s";
112*b636d99dSDavid van Moolenbroek static const char illoptstring[] = "unknown option -- %s";
113*b636d99dSDavid van Moolenbroek #endif
114*b636d99dSDavid van Moolenbroek 
115*b636d99dSDavid van Moolenbroek /*
116*b636d99dSDavid van Moolenbroek  * Compute the greatest common divisor of a and b.
117*b636d99dSDavid van Moolenbroek  */
118*b636d99dSDavid van Moolenbroek static int
gcd(int a,int b)119*b636d99dSDavid van Moolenbroek gcd(int a, int b)
120*b636d99dSDavid van Moolenbroek {
121*b636d99dSDavid van Moolenbroek 	int c;
122*b636d99dSDavid van Moolenbroek 
123*b636d99dSDavid van Moolenbroek 	c = a % b;
124*b636d99dSDavid van Moolenbroek 	while (c != 0) {
125*b636d99dSDavid van Moolenbroek 		a = b;
126*b636d99dSDavid van Moolenbroek 		b = c;
127*b636d99dSDavid van Moolenbroek 		c = a % b;
128*b636d99dSDavid van Moolenbroek 	}
129*b636d99dSDavid van Moolenbroek 
130*b636d99dSDavid van Moolenbroek 	return (b);
131*b636d99dSDavid van Moolenbroek }
132*b636d99dSDavid van Moolenbroek 
133*b636d99dSDavid van Moolenbroek /*
134*b636d99dSDavid van Moolenbroek  * Exchange the block from nonopt_start to nonopt_end with the block
135*b636d99dSDavid van Moolenbroek  * from nonopt_end to opt_end (keeping the same order of arguments
136*b636d99dSDavid van Moolenbroek  * in each block).
137*b636d99dSDavid van Moolenbroek  */
138*b636d99dSDavid van Moolenbroek static void
permute_args(int panonopt_start,int panonopt_end,int opt_end,char * const * nargv)139*b636d99dSDavid van Moolenbroek permute_args(int panonopt_start, int panonopt_end, int opt_end,
140*b636d99dSDavid van Moolenbroek 	char * const *nargv)
141*b636d99dSDavid van Moolenbroek {
142*b636d99dSDavid van Moolenbroek 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
143*b636d99dSDavid van Moolenbroek 	char *swap;
144*b636d99dSDavid van Moolenbroek 
145*b636d99dSDavid van Moolenbroek 	/*
146*b636d99dSDavid van Moolenbroek 	 * compute lengths of blocks and number and size of cycles
147*b636d99dSDavid van Moolenbroek 	 */
148*b636d99dSDavid van Moolenbroek 	nnonopts = panonopt_end - panonopt_start;
149*b636d99dSDavid van Moolenbroek 	nopts = opt_end - panonopt_end;
150*b636d99dSDavid van Moolenbroek 	ncycle = gcd(nnonopts, nopts);
151*b636d99dSDavid van Moolenbroek 	cyclelen = (opt_end - panonopt_start) / ncycle;
152*b636d99dSDavid van Moolenbroek 
153*b636d99dSDavid van Moolenbroek 	for (i = 0; i < ncycle; i++) {
154*b636d99dSDavid van Moolenbroek 		cstart = panonopt_end+i;
155*b636d99dSDavid van Moolenbroek 		pos = cstart;
156*b636d99dSDavid van Moolenbroek 		for (j = 0; j < cyclelen; j++) {
157*b636d99dSDavid van Moolenbroek 			if (pos >= panonopt_end)
158*b636d99dSDavid van Moolenbroek 				pos -= nnonopts;
159*b636d99dSDavid van Moolenbroek 			else
160*b636d99dSDavid van Moolenbroek 				pos += nopts;
161*b636d99dSDavid van Moolenbroek 			swap = nargv[pos];
162*b636d99dSDavid van Moolenbroek 			/* LINTED const cast */
163*b636d99dSDavid van Moolenbroek 			((char **) nargv)[pos] = nargv[cstart];
164*b636d99dSDavid van Moolenbroek 			/* LINTED const cast */
165*b636d99dSDavid van Moolenbroek 			((char **)nargv)[cstart] = swap;
166*b636d99dSDavid van Moolenbroek 		}
167*b636d99dSDavid van Moolenbroek 	}
168*b636d99dSDavid van Moolenbroek }
169*b636d99dSDavid van Moolenbroek 
170*b636d99dSDavid van Moolenbroek static void
warnx(const char * fmt,...)171*b636d99dSDavid van Moolenbroek warnx(const char *fmt, ...)
172*b636d99dSDavid van Moolenbroek {
173*b636d99dSDavid van Moolenbroek 	extern char *program_name;
174*b636d99dSDavid van Moolenbroek 	va_list ap;
175*b636d99dSDavid van Moolenbroek 
176*b636d99dSDavid van Moolenbroek 	va_start(ap, fmt);
177*b636d99dSDavid van Moolenbroek 	fprintf(stderr, "%s: ", program_name);
178*b636d99dSDavid van Moolenbroek 	vfprintf(stderr, fmt, ap);
179*b636d99dSDavid van Moolenbroek 	fprintf(stderr, "\n");
180*b636d99dSDavid van Moolenbroek 	va_end(ap);
181*b636d99dSDavid van Moolenbroek }
182*b636d99dSDavid van Moolenbroek 
183*b636d99dSDavid van Moolenbroek /*
184*b636d99dSDavid van Moolenbroek  * parse_long_options --
185*b636d99dSDavid van Moolenbroek  *	Parse long options in argc/argv argument vector.
186*b636d99dSDavid van Moolenbroek  * Returns -1 if short_too is set and the option does not match long_options.
187*b636d99dSDavid van Moolenbroek  */
188*b636d99dSDavid van Moolenbroek static int
parse_long_options(char * const * nargv,const char * options,const struct option * long_options,int * idx,int short_too,int flags)189*b636d99dSDavid van Moolenbroek parse_long_options(char * const *nargv, const char *options,
190*b636d99dSDavid van Moolenbroek 	const struct option *long_options, int *idx, int short_too, int flags)
191*b636d99dSDavid van Moolenbroek {
192*b636d99dSDavid van Moolenbroek 	const char *current_argv, *has_equal;
193*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
194*b636d99dSDavid van Moolenbroek 	const char *current_dash;
195*b636d99dSDavid van Moolenbroek #endif
196*b636d99dSDavid van Moolenbroek 	size_t current_argv_len;
197*b636d99dSDavid van Moolenbroek 	int i, match, exact_match, second_partial_match;
198*b636d99dSDavid van Moolenbroek 
199*b636d99dSDavid van Moolenbroek 	current_argv = place;
200*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
201*b636d99dSDavid van Moolenbroek 	switch (dash_prefix) {
202*b636d99dSDavid van Moolenbroek 		case D_PREFIX:
203*b636d99dSDavid van Moolenbroek 			current_dash = "-";
204*b636d99dSDavid van Moolenbroek 			break;
205*b636d99dSDavid van Moolenbroek 		case DD_PREFIX:
206*b636d99dSDavid van Moolenbroek 			current_dash = "--";
207*b636d99dSDavid van Moolenbroek 			break;
208*b636d99dSDavid van Moolenbroek 		case W_PREFIX:
209*b636d99dSDavid van Moolenbroek 			current_dash = "-W ";
210*b636d99dSDavid van Moolenbroek 			break;
211*b636d99dSDavid van Moolenbroek 		default:
212*b636d99dSDavid van Moolenbroek 			current_dash = "";
213*b636d99dSDavid van Moolenbroek 			break;
214*b636d99dSDavid van Moolenbroek 	}
215*b636d99dSDavid van Moolenbroek #endif
216*b636d99dSDavid van Moolenbroek 	match = -1;
217*b636d99dSDavid van Moolenbroek 	exact_match = 0;
218*b636d99dSDavid van Moolenbroek 	second_partial_match = 0;
219*b636d99dSDavid van Moolenbroek 
220*b636d99dSDavid van Moolenbroek 	optind++;
221*b636d99dSDavid van Moolenbroek 
222*b636d99dSDavid van Moolenbroek 	if ((has_equal = strchr(current_argv, '=')) != NULL) {
223*b636d99dSDavid van Moolenbroek 		/* argument found (--option=arg) */
224*b636d99dSDavid van Moolenbroek 		current_argv_len = has_equal - current_argv;
225*b636d99dSDavid van Moolenbroek 		has_equal++;
226*b636d99dSDavid van Moolenbroek 	} else
227*b636d99dSDavid van Moolenbroek 		current_argv_len = strlen(current_argv);
228*b636d99dSDavid van Moolenbroek 
229*b636d99dSDavid van Moolenbroek 	for (i = 0; long_options[i].name; i++) {
230*b636d99dSDavid van Moolenbroek 		/* find matching long option */
231*b636d99dSDavid van Moolenbroek 		if (strncmp(current_argv, long_options[i].name,
232*b636d99dSDavid van Moolenbroek 		    current_argv_len))
233*b636d99dSDavid van Moolenbroek 			continue;
234*b636d99dSDavid van Moolenbroek 
235*b636d99dSDavid van Moolenbroek 		if (strlen(long_options[i].name) == current_argv_len) {
236*b636d99dSDavid van Moolenbroek 			/* exact match */
237*b636d99dSDavid van Moolenbroek 			match = i;
238*b636d99dSDavid van Moolenbroek 			exact_match = 1;
239*b636d99dSDavid van Moolenbroek 			break;
240*b636d99dSDavid van Moolenbroek 		}
241*b636d99dSDavid van Moolenbroek 		/*
242*b636d99dSDavid van Moolenbroek 		 * If this is a known short option, don't allow
243*b636d99dSDavid van Moolenbroek 		 * a partial match of a single character.
244*b636d99dSDavid van Moolenbroek 		 */
245*b636d99dSDavid van Moolenbroek 		if (short_too && current_argv_len == 1)
246*b636d99dSDavid van Moolenbroek 			continue;
247*b636d99dSDavid van Moolenbroek 
248*b636d99dSDavid van Moolenbroek 		if (match == -1)        /* first partial match */
249*b636d99dSDavid van Moolenbroek 			match = i;
250*b636d99dSDavid van Moolenbroek 		else if ((flags & FLAG_LONGONLY) ||
251*b636d99dSDavid van Moolenbroek 			 long_options[i].has_arg !=
252*b636d99dSDavid van Moolenbroek 			     long_options[match].has_arg ||
253*b636d99dSDavid van Moolenbroek 			 long_options[i].flag != long_options[match].flag ||
254*b636d99dSDavid van Moolenbroek 			 long_options[i].val != long_options[match].val)
255*b636d99dSDavid van Moolenbroek 			second_partial_match = 1;
256*b636d99dSDavid van Moolenbroek 	}
257*b636d99dSDavid van Moolenbroek 	if (!exact_match && second_partial_match) {
258*b636d99dSDavid van Moolenbroek 		/* ambiguous abbreviation */
259*b636d99dSDavid van Moolenbroek 		if (PRINT_ERROR)
260*b636d99dSDavid van Moolenbroek 			warnx(ambig,
261*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
262*b636d99dSDavid van Moolenbroek 			     current_dash,
263*b636d99dSDavid van Moolenbroek #endif
264*b636d99dSDavid van Moolenbroek 			     (int)current_argv_len,
265*b636d99dSDavid van Moolenbroek 			     current_argv);
266*b636d99dSDavid van Moolenbroek 		optopt = 0;
267*b636d99dSDavid van Moolenbroek 		return (BADCH);
268*b636d99dSDavid van Moolenbroek 	}
269*b636d99dSDavid van Moolenbroek 	if (match != -1) {		/* option found */
270*b636d99dSDavid van Moolenbroek 		if (long_options[match].has_arg == no_argument
271*b636d99dSDavid van Moolenbroek 		    && has_equal) {
272*b636d99dSDavid van Moolenbroek 			if (PRINT_ERROR)
273*b636d99dSDavid van Moolenbroek 				warnx(noarg,
274*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
275*b636d99dSDavid van Moolenbroek 				     current_dash,
276*b636d99dSDavid van Moolenbroek #endif
277*b636d99dSDavid van Moolenbroek 				     (int)current_argv_len,
278*b636d99dSDavid van Moolenbroek 				     current_argv);
279*b636d99dSDavid van Moolenbroek 			/*
280*b636d99dSDavid van Moolenbroek 			 * XXX: GNU sets optopt to val regardless of flag
281*b636d99dSDavid van Moolenbroek 			 */
282*b636d99dSDavid van Moolenbroek 			if (long_options[match].flag == NULL)
283*b636d99dSDavid van Moolenbroek 				optopt = long_options[match].val;
284*b636d99dSDavid van Moolenbroek 			else
285*b636d99dSDavid van Moolenbroek 				optopt = 0;
286*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
287*b636d99dSDavid van Moolenbroek 			return (BADCH);
288*b636d99dSDavid van Moolenbroek #else
289*b636d99dSDavid van Moolenbroek 			return (BADARG);
290*b636d99dSDavid van Moolenbroek #endif
291*b636d99dSDavid van Moolenbroek 		}
292*b636d99dSDavid van Moolenbroek 		if (long_options[match].has_arg == required_argument ||
293*b636d99dSDavid van Moolenbroek 		    long_options[match].has_arg == optional_argument) {
294*b636d99dSDavid van Moolenbroek 			if (has_equal)
295*b636d99dSDavid van Moolenbroek 				optarg = (char *)has_equal;
296*b636d99dSDavid van Moolenbroek 			else if (long_options[match].has_arg ==
297*b636d99dSDavid van Moolenbroek 			    required_argument) {
298*b636d99dSDavid van Moolenbroek 				/*
299*b636d99dSDavid van Moolenbroek 				 * optional argument doesn't use next nargv
300*b636d99dSDavid van Moolenbroek 				 */
301*b636d99dSDavid van Moolenbroek 				optarg = nargv[optind++];
302*b636d99dSDavid van Moolenbroek 			}
303*b636d99dSDavid van Moolenbroek 		}
304*b636d99dSDavid van Moolenbroek 		if ((long_options[match].has_arg == required_argument)
305*b636d99dSDavid van Moolenbroek 		    && (optarg == NULL)) {
306*b636d99dSDavid van Moolenbroek 			/*
307*b636d99dSDavid van Moolenbroek 			 * Missing argument; leading ':' indicates no error
308*b636d99dSDavid van Moolenbroek 			 * should be generated.
309*b636d99dSDavid van Moolenbroek 			 */
310*b636d99dSDavid van Moolenbroek 			if (PRINT_ERROR)
311*b636d99dSDavid van Moolenbroek 				warnx(recargstring,
312*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
313*b636d99dSDavid van Moolenbroek 				    current_dash,
314*b636d99dSDavid van Moolenbroek #endif
315*b636d99dSDavid van Moolenbroek 				    current_argv);
316*b636d99dSDavid van Moolenbroek 			/*
317*b636d99dSDavid van Moolenbroek 			 * XXX: GNU sets optopt to val regardless of flag
318*b636d99dSDavid van Moolenbroek 			 */
319*b636d99dSDavid van Moolenbroek 			if (long_options[match].flag == NULL)
320*b636d99dSDavid van Moolenbroek 				optopt = long_options[match].val;
321*b636d99dSDavid van Moolenbroek 			else
322*b636d99dSDavid van Moolenbroek 				optopt = 0;
323*b636d99dSDavid van Moolenbroek 			--optind;
324*b636d99dSDavid van Moolenbroek 			return (BADARG);
325*b636d99dSDavid van Moolenbroek 		}
326*b636d99dSDavid van Moolenbroek 	} else {			/* unknown option */
327*b636d99dSDavid van Moolenbroek 		if (short_too) {
328*b636d99dSDavid van Moolenbroek 			--optind;
329*b636d99dSDavid van Moolenbroek 			return (-1);
330*b636d99dSDavid van Moolenbroek 		}
331*b636d99dSDavid van Moolenbroek 		if (PRINT_ERROR)
332*b636d99dSDavid van Moolenbroek 			warnx(illoptstring,
333*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
334*b636d99dSDavid van Moolenbroek 			      current_dash,
335*b636d99dSDavid van Moolenbroek #endif
336*b636d99dSDavid van Moolenbroek 			      current_argv);
337*b636d99dSDavid van Moolenbroek 		optopt = 0;
338*b636d99dSDavid van Moolenbroek 		return (BADCH);
339*b636d99dSDavid van Moolenbroek 	}
340*b636d99dSDavid van Moolenbroek 	if (idx)
341*b636d99dSDavid van Moolenbroek 		*idx = match;
342*b636d99dSDavid van Moolenbroek 	if (long_options[match].flag) {
343*b636d99dSDavid van Moolenbroek 		*long_options[match].flag = long_options[match].val;
344*b636d99dSDavid van Moolenbroek 		return (0);
345*b636d99dSDavid van Moolenbroek 	} else
346*b636d99dSDavid van Moolenbroek 		return (long_options[match].val);
347*b636d99dSDavid van Moolenbroek }
348*b636d99dSDavid van Moolenbroek 
349*b636d99dSDavid van Moolenbroek /*
350*b636d99dSDavid van Moolenbroek  * getopt_internal --
351*b636d99dSDavid van Moolenbroek  *	Parse argc/argv argument vector.  Called by user level routines.
352*b636d99dSDavid van Moolenbroek  */
353*b636d99dSDavid van Moolenbroek static int
getopt_internal(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx,int flags)354*b636d99dSDavid van Moolenbroek getopt_internal(int nargc, char * const *nargv, const char *options,
355*b636d99dSDavid van Moolenbroek 	const struct option *long_options, int *idx, int flags)
356*b636d99dSDavid van Moolenbroek {
357*b636d99dSDavid van Moolenbroek 	char *oli;				/* option letter list index */
358*b636d99dSDavid van Moolenbroek 	int optchar, short_too;
359*b636d99dSDavid van Moolenbroek 	int posixly_correct;	/* no static, can be changed on the fly */
360*b636d99dSDavid van Moolenbroek 
361*b636d99dSDavid van Moolenbroek 	if (options == NULL)
362*b636d99dSDavid van Moolenbroek 		return (-1);
363*b636d99dSDavid van Moolenbroek 
364*b636d99dSDavid van Moolenbroek 	/*
365*b636d99dSDavid van Moolenbroek 	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
366*b636d99dSDavid van Moolenbroek 	 * string begins with a '+'.
367*b636d99dSDavid van Moolenbroek 	 */
368*b636d99dSDavid van Moolenbroek 	posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
369*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
370*b636d99dSDavid van Moolenbroek 	if (*options == '-')
371*b636d99dSDavid van Moolenbroek 		flags |= FLAG_ALLARGS;
372*b636d99dSDavid van Moolenbroek 	else if (posixly_correct || *options == '+')
373*b636d99dSDavid van Moolenbroek 		flags &= ~FLAG_PERMUTE;
374*b636d99dSDavid van Moolenbroek #else
375*b636d99dSDavid van Moolenbroek 	if (posixly_correct || *options == '+')
376*b636d99dSDavid van Moolenbroek 		flags &= ~FLAG_PERMUTE;
377*b636d99dSDavid van Moolenbroek 	else if (*options == '-')
378*b636d99dSDavid van Moolenbroek 		flags |= FLAG_ALLARGS;
379*b636d99dSDavid van Moolenbroek #endif
380*b636d99dSDavid van Moolenbroek 	if (*options == '+' || *options == '-')
381*b636d99dSDavid van Moolenbroek 		options++;
382*b636d99dSDavid van Moolenbroek 
383*b636d99dSDavid van Moolenbroek 	/*
384*b636d99dSDavid van Moolenbroek 	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
385*b636d99dSDavid van Moolenbroek 	 * XXX using optreset.  Work around this braindamage.
386*b636d99dSDavid van Moolenbroek 	 */
387*b636d99dSDavid van Moolenbroek 	if (optind == 0)
388*b636d99dSDavid van Moolenbroek 		optind = 1;
389*b636d99dSDavid van Moolenbroek 
390*b636d99dSDavid van Moolenbroek 	optarg = NULL;
391*b636d99dSDavid van Moolenbroek start:
392*b636d99dSDavid van Moolenbroek 	if (!*place) {				/* update scanning pointer */
393*b636d99dSDavid van Moolenbroek 		if (optind >= nargc) {          /* end of argument vector */
394*b636d99dSDavid van Moolenbroek 			place = EMSG;
395*b636d99dSDavid van Moolenbroek 			if (nonopt_end != -1) {
396*b636d99dSDavid van Moolenbroek 				/* do permutation, if we have to */
397*b636d99dSDavid van Moolenbroek 				permute_args(nonopt_start, nonopt_end,
398*b636d99dSDavid van Moolenbroek 				    optind, nargv);
399*b636d99dSDavid van Moolenbroek 				optind -= nonopt_end - nonopt_start;
400*b636d99dSDavid van Moolenbroek 			}
401*b636d99dSDavid van Moolenbroek 			else if (nonopt_start != -1) {
402*b636d99dSDavid van Moolenbroek 				/*
403*b636d99dSDavid van Moolenbroek 				 * If we skipped non-options, set optind
404*b636d99dSDavid van Moolenbroek 				 * to the first of them.
405*b636d99dSDavid van Moolenbroek 				 */
406*b636d99dSDavid van Moolenbroek 				optind = nonopt_start;
407*b636d99dSDavid van Moolenbroek 			}
408*b636d99dSDavid van Moolenbroek 			nonopt_start = nonopt_end = -1;
409*b636d99dSDavid van Moolenbroek 			return (-1);
410*b636d99dSDavid van Moolenbroek 		}
411*b636d99dSDavid van Moolenbroek 		if (*(place = nargv[optind]) != '-' ||
412*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
413*b636d99dSDavid van Moolenbroek 		    place[1] == '\0') {
414*b636d99dSDavid van Moolenbroek #else
415*b636d99dSDavid van Moolenbroek 		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
416*b636d99dSDavid van Moolenbroek #endif
417*b636d99dSDavid van Moolenbroek 			place = EMSG;		/* found non-option */
418*b636d99dSDavid van Moolenbroek 			if (flags & FLAG_ALLARGS) {
419*b636d99dSDavid van Moolenbroek 				/*
420*b636d99dSDavid van Moolenbroek 				 * GNU extension:
421*b636d99dSDavid van Moolenbroek 				 * return non-option as argument to option 1
422*b636d99dSDavid van Moolenbroek 				 */
423*b636d99dSDavid van Moolenbroek 				optarg = nargv[optind++];
424*b636d99dSDavid van Moolenbroek 				return (INORDER);
425*b636d99dSDavid van Moolenbroek 			}
426*b636d99dSDavid van Moolenbroek 			if (!(flags & FLAG_PERMUTE)) {
427*b636d99dSDavid van Moolenbroek 				/*
428*b636d99dSDavid van Moolenbroek 				 * If no permutation wanted, stop parsing
429*b636d99dSDavid van Moolenbroek 				 * at first non-option.
430*b636d99dSDavid van Moolenbroek 				 */
431*b636d99dSDavid van Moolenbroek 				return (-1);
432*b636d99dSDavid van Moolenbroek 			}
433*b636d99dSDavid van Moolenbroek 			/* do permutation */
434*b636d99dSDavid van Moolenbroek 			if (nonopt_start == -1)
435*b636d99dSDavid van Moolenbroek 				nonopt_start = optind;
436*b636d99dSDavid van Moolenbroek 			else if (nonopt_end != -1) {
437*b636d99dSDavid van Moolenbroek 				permute_args(nonopt_start, nonopt_end,
438*b636d99dSDavid van Moolenbroek 				    optind, nargv);
439*b636d99dSDavid van Moolenbroek 				nonopt_start = optind -
440*b636d99dSDavid van Moolenbroek 				    (nonopt_end - nonopt_start);
441*b636d99dSDavid van Moolenbroek 				nonopt_end = -1;
442*b636d99dSDavid van Moolenbroek 			}
443*b636d99dSDavid van Moolenbroek 			optind++;
444*b636d99dSDavid van Moolenbroek 			/* process next argument */
445*b636d99dSDavid van Moolenbroek 			goto start;
446*b636d99dSDavid van Moolenbroek 		}
447*b636d99dSDavid van Moolenbroek 		if (nonopt_start != -1 && nonopt_end == -1)
448*b636d99dSDavid van Moolenbroek 			nonopt_end = optind;
449*b636d99dSDavid van Moolenbroek 
450*b636d99dSDavid van Moolenbroek 		/*
451*b636d99dSDavid van Moolenbroek 		 * If we have "-" do nothing, if "--" we are done.
452*b636d99dSDavid van Moolenbroek 		 */
453*b636d99dSDavid van Moolenbroek 		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
454*b636d99dSDavid van Moolenbroek 			optind++;
455*b636d99dSDavid van Moolenbroek 			place = EMSG;
456*b636d99dSDavid van Moolenbroek 			/*
457*b636d99dSDavid van Moolenbroek 			 * We found an option (--), so if we skipped
458*b636d99dSDavid van Moolenbroek 			 * non-options, we have to permute.
459*b636d99dSDavid van Moolenbroek 			 */
460*b636d99dSDavid van Moolenbroek 			if (nonopt_end != -1) {
461*b636d99dSDavid van Moolenbroek 				permute_args(nonopt_start, nonopt_end,
462*b636d99dSDavid van Moolenbroek 				    optind, nargv);
463*b636d99dSDavid van Moolenbroek 				optind -= nonopt_end - nonopt_start;
464*b636d99dSDavid van Moolenbroek 			}
465*b636d99dSDavid van Moolenbroek 			nonopt_start = nonopt_end = -1;
466*b636d99dSDavid van Moolenbroek 			return (-1);
467*b636d99dSDavid van Moolenbroek 		}
468*b636d99dSDavid van Moolenbroek 	}
469*b636d99dSDavid van Moolenbroek 
470*b636d99dSDavid van Moolenbroek 	/*
471*b636d99dSDavid van Moolenbroek 	 * Check long options if:
472*b636d99dSDavid van Moolenbroek 	 *  1) we were passed some
473*b636d99dSDavid van Moolenbroek 	 *  2) the arg is not just "-"
474*b636d99dSDavid van Moolenbroek 	 *  3) either the arg starts with -- we are getopt_long_only()
475*b636d99dSDavid van Moolenbroek 	 */
476*b636d99dSDavid van Moolenbroek 	if (long_options != NULL && place != nargv[optind] &&
477*b636d99dSDavid van Moolenbroek 	    (*place == '-' || (flags & FLAG_LONGONLY))) {
478*b636d99dSDavid van Moolenbroek 		short_too = 0;
479*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
480*b636d99dSDavid van Moolenbroek 		dash_prefix = D_PREFIX;
481*b636d99dSDavid van Moolenbroek #endif
482*b636d99dSDavid van Moolenbroek 		if (*place == '-') {
483*b636d99dSDavid van Moolenbroek 			place++;		/* --foo long option */
484*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
485*b636d99dSDavid van Moolenbroek 			dash_prefix = DD_PREFIX;
486*b636d99dSDavid van Moolenbroek #endif
487*b636d99dSDavid van Moolenbroek 		} else if (*place != ':' && strchr(options, *place) != NULL)
488*b636d99dSDavid van Moolenbroek 			short_too = 1;		/* could be short option too */
489*b636d99dSDavid van Moolenbroek 
490*b636d99dSDavid van Moolenbroek 		optchar = parse_long_options(nargv, options, long_options,
491*b636d99dSDavid van Moolenbroek 		    idx, short_too, flags);
492*b636d99dSDavid van Moolenbroek 		if (optchar != -1) {
493*b636d99dSDavid van Moolenbroek 			place = EMSG;
494*b636d99dSDavid van Moolenbroek 			return (optchar);
495*b636d99dSDavid van Moolenbroek 		}
496*b636d99dSDavid van Moolenbroek 	}
497*b636d99dSDavid van Moolenbroek 
498*b636d99dSDavid van Moolenbroek 	if ((optchar = (int)*place++) == (int)':' ||
499*b636d99dSDavid van Moolenbroek 	    (optchar == (int)'-' && *place != '\0') ||
500*b636d99dSDavid van Moolenbroek 	    (oli = strchr(options, optchar)) == NULL) {
501*b636d99dSDavid van Moolenbroek 		/*
502*b636d99dSDavid van Moolenbroek 		 * If the user specified "-" and  '-' isn't listed in
503*b636d99dSDavid van Moolenbroek 		 * options, return -1 (non-option) as per POSIX.
504*b636d99dSDavid van Moolenbroek 		 * Otherwise, it is an unknown option character (or ':').
505*b636d99dSDavid van Moolenbroek 		 */
506*b636d99dSDavid van Moolenbroek 		if (optchar == (int)'-' && *place == '\0')
507*b636d99dSDavid van Moolenbroek 			return (-1);
508*b636d99dSDavid van Moolenbroek 		if (!*place)
509*b636d99dSDavid van Moolenbroek 			++optind;
510*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
511*b636d99dSDavid van Moolenbroek 		if (PRINT_ERROR)
512*b636d99dSDavid van Moolenbroek 			warnx(posixly_correct ? illoptchar : gnuoptchar,
513*b636d99dSDavid van Moolenbroek 			      optchar);
514*b636d99dSDavid van Moolenbroek #else
515*b636d99dSDavid van Moolenbroek 		if (PRINT_ERROR)
516*b636d99dSDavid van Moolenbroek 			warnx(illoptchar, optchar);
517*b636d99dSDavid van Moolenbroek #endif
518*b636d99dSDavid van Moolenbroek 		optopt = optchar;
519*b636d99dSDavid van Moolenbroek 		return (BADCH);
520*b636d99dSDavid van Moolenbroek 	}
521*b636d99dSDavid van Moolenbroek 	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
522*b636d99dSDavid van Moolenbroek 		/* -W long-option */
523*b636d99dSDavid van Moolenbroek 		if (*place)			/* no space */
524*b636d99dSDavid van Moolenbroek 			/* NOTHING */;
525*b636d99dSDavid van Moolenbroek 		else if (++optind >= nargc) {	/* no arg */
526*b636d99dSDavid van Moolenbroek 			place = EMSG;
527*b636d99dSDavid van Moolenbroek 			if (PRINT_ERROR)
528*b636d99dSDavid van Moolenbroek 				warnx(recargchar, optchar);
529*b636d99dSDavid van Moolenbroek 			optopt = optchar;
530*b636d99dSDavid van Moolenbroek 			return (BADARG);
531*b636d99dSDavid van Moolenbroek 		} else				/* white space */
532*b636d99dSDavid van Moolenbroek 			place = nargv[optind];
533*b636d99dSDavid van Moolenbroek #ifdef GNU_COMPATIBLE
534*b636d99dSDavid van Moolenbroek 		dash_prefix = W_PREFIX;
535*b636d99dSDavid van Moolenbroek #endif
536*b636d99dSDavid van Moolenbroek 		optchar = parse_long_options(nargv, options, long_options,
537*b636d99dSDavid van Moolenbroek 		    idx, 0, flags);
538*b636d99dSDavid van Moolenbroek 		place = EMSG;
539*b636d99dSDavid van Moolenbroek 		return (optchar);
540*b636d99dSDavid van Moolenbroek 	}
541*b636d99dSDavid van Moolenbroek 	if (*++oli != ':') {			/* doesn't take argument */
542*b636d99dSDavid van Moolenbroek 		if (!*place)
543*b636d99dSDavid van Moolenbroek 			++optind;
544*b636d99dSDavid van Moolenbroek 	} else {				/* takes (optional) argument */
545*b636d99dSDavid van Moolenbroek 		optarg = NULL;
546*b636d99dSDavid van Moolenbroek 		if (*place)			/* no white space */
547*b636d99dSDavid van Moolenbroek 			optarg = (char *)place;
548*b636d99dSDavid van Moolenbroek 		else if (oli[1] != ':') {	/* arg not optional */
549*b636d99dSDavid van Moolenbroek 			if (++optind >= nargc) {	/* no arg */
550*b636d99dSDavid van Moolenbroek 				place = EMSG;
551*b636d99dSDavid van Moolenbroek 				if (PRINT_ERROR)
552*b636d99dSDavid van Moolenbroek 					warnx(recargchar, optchar);
553*b636d99dSDavid van Moolenbroek 				optopt = optchar;
554*b636d99dSDavid van Moolenbroek 				return (BADARG);
555*b636d99dSDavid van Moolenbroek 			} else
556*b636d99dSDavid van Moolenbroek 				optarg = nargv[optind];
557*b636d99dSDavid van Moolenbroek 		}
558*b636d99dSDavid van Moolenbroek 		place = EMSG;
559*b636d99dSDavid van Moolenbroek 		++optind;
560*b636d99dSDavid van Moolenbroek 	}
561*b636d99dSDavid van Moolenbroek 	/* dump back option letter */
562*b636d99dSDavid van Moolenbroek 	return (optchar);
563*b636d99dSDavid van Moolenbroek }
564*b636d99dSDavid van Moolenbroek 
565*b636d99dSDavid van Moolenbroek #ifdef REPLACE_GETOPT
566*b636d99dSDavid van Moolenbroek /*
567*b636d99dSDavid van Moolenbroek  * getopt --
568*b636d99dSDavid van Moolenbroek  *	Parse argc/argv argument vector.
569*b636d99dSDavid van Moolenbroek  *
570*b636d99dSDavid van Moolenbroek  * [eventually this will replace the BSD getopt]
571*b636d99dSDavid van Moolenbroek  */
572*b636d99dSDavid van Moolenbroek int
573*b636d99dSDavid van Moolenbroek getopt(int nargc, char * const *nargv, const char *options)
574*b636d99dSDavid van Moolenbroek {
575*b636d99dSDavid van Moolenbroek 
576*b636d99dSDavid van Moolenbroek 	/*
577*b636d99dSDavid van Moolenbroek 	 * We don't pass FLAG_PERMUTE to getopt_internal() since
578*b636d99dSDavid van Moolenbroek 	 * the BSD getopt(3) (unlike GNU) has never done this.
579*b636d99dSDavid van Moolenbroek 	 *
580*b636d99dSDavid van Moolenbroek 	 * Furthermore, since many privileged programs call getopt()
581*b636d99dSDavid van Moolenbroek 	 * before dropping privileges it makes sense to keep things
582*b636d99dSDavid van Moolenbroek 	 * as simple (and bug-free) as possible.
583*b636d99dSDavid van Moolenbroek 	 */
584*b636d99dSDavid van Moolenbroek 	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
585*b636d99dSDavid van Moolenbroek }
586*b636d99dSDavid van Moolenbroek #endif /* REPLACE_GETOPT */
587*b636d99dSDavid van Moolenbroek 
588*b636d99dSDavid van Moolenbroek /*
589*b636d99dSDavid van Moolenbroek  * getopt_long --
590*b636d99dSDavid van Moolenbroek  *	Parse argc/argv argument vector.
591*b636d99dSDavid van Moolenbroek  */
592*b636d99dSDavid van Moolenbroek int
593*b636d99dSDavid van Moolenbroek getopt_long(int nargc, char * const *nargv, const char *options,
594*b636d99dSDavid van Moolenbroek 	const struct option *long_options, int *idx)
595*b636d99dSDavid van Moolenbroek {
596*b636d99dSDavid van Moolenbroek 
597*b636d99dSDavid van Moolenbroek 	return (getopt_internal(nargc, nargv, options, long_options, idx,
598*b636d99dSDavid van Moolenbroek 	    FLAG_PERMUTE));
599*b636d99dSDavid van Moolenbroek }
600*b636d99dSDavid van Moolenbroek 
601*b636d99dSDavid van Moolenbroek /*
602*b636d99dSDavid van Moolenbroek  * getopt_long_only --
603*b636d99dSDavid van Moolenbroek  *	Parse argc/argv argument vector.
604*b636d99dSDavid van Moolenbroek  */
605*b636d99dSDavid van Moolenbroek int
606*b636d99dSDavid van Moolenbroek getopt_long_only(int nargc, char * const *nargv, const char *options,
607*b636d99dSDavid van Moolenbroek 	const struct option *long_options, int *idx)
608*b636d99dSDavid van Moolenbroek {
609*b636d99dSDavid van Moolenbroek 
610*b636d99dSDavid van Moolenbroek 	return (getopt_internal(nargc, nargv, options, long_options, idx,
611*b636d99dSDavid van Moolenbroek 	    FLAG_PERMUTE|FLAG_LONGONLY));
612*b636d99dSDavid van Moolenbroek }
613