xref: /freebsd/contrib/file/src/getopt_long.c (revision 898496ee09ed2b7d25f6807edc4515628196ec0a)
1b6cee71dSXin LI /*	$NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $	*/
2b6cee71dSXin LI 
3b6cee71dSXin LI /*-
4b6cee71dSXin LI  * Copyright (c) 2000 The NetBSD Foundation, Inc.
5b6cee71dSXin LI  * All rights reserved.
6b6cee71dSXin LI  *
7b6cee71dSXin LI  * This code is derived from software contributed to The NetBSD Foundation
8b6cee71dSXin LI  * by Dieter Baron and Thomas Klausner.
9b6cee71dSXin LI  *
10b6cee71dSXin LI  * Redistribution and use in source and binary forms, with or without
11b6cee71dSXin LI  * modification, are permitted provided that the following conditions
12b6cee71dSXin LI  * are met:
13b6cee71dSXin LI  * 1. Redistributions of source code must retain the above copyright
14b6cee71dSXin LI  *    notice, this list of conditions and the following disclaimer.
15b6cee71dSXin LI  * 2. Redistributions in binary form must reproduce the above copyright
16b6cee71dSXin LI  *    notice, this list of conditions and the following disclaimer in the
17b6cee71dSXin LI  *    documentation and/or other materials provided with the distribution.
18b6cee71dSXin LI  *
19b6cee71dSXin LI  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20b6cee71dSXin LI  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21b6cee71dSXin LI  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22b6cee71dSXin LI  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23b6cee71dSXin LI  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24b6cee71dSXin LI  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25b6cee71dSXin LI  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26b6cee71dSXin LI  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27b6cee71dSXin LI  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28b6cee71dSXin LI  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29b6cee71dSXin LI  * POSSIBILITY OF SUCH DAMAGE.
30b6cee71dSXin LI  */
31b6cee71dSXin LI 
32b6cee71dSXin LI #include "file.h"
33b6cee71dSXin LI 
34b6cee71dSXin LI #ifndef	lint
35*898496eeSXin LI FILE_RCSID("@(#)$File: getopt_long.c,v 1.9 2022/09/24 20:30:13 christos Exp $")
36b6cee71dSXin LI #endif	/* lint */
37b6cee71dSXin LI 
38b6cee71dSXin LI #include <assert.h>
39b6cee71dSXin LI #ifdef HAVE_ERR_H
40b6cee71dSXin LI #include <err.h>
41b6cee71dSXin LI #else
42b6cee71dSXin LI #define warnx printf
43b6cee71dSXin LI #endif
44b6cee71dSXin LI #include <errno.h>
45b6cee71dSXin LI #if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
46b6cee71dSXin LI #include <getopt.h>
47b6cee71dSXin LI #else
48b6cee71dSXin LI #include "mygetopt.h"
49b6cee71dSXin LI #endif
50b6cee71dSXin LI #include <stdlib.h>
51b6cee71dSXin LI #include <string.h>
52b6cee71dSXin LI 
53b6cee71dSXin LI #define REPLACE_GETOPT
54b6cee71dSXin LI 
55b6cee71dSXin LI #ifndef _DIAGASSERT
56b6cee71dSXin LI #define _DIAGASSERT assert
57b6cee71dSXin LI #endif
58b6cee71dSXin LI 
59b6cee71dSXin LI #ifdef REPLACE_GETOPT
60b6cee71dSXin LI #ifdef __weak_alias
61b6cee71dSXin LI __weak_alias(getopt,_getopt)
62b6cee71dSXin LI #endif
63b6cee71dSXin LI int	opterr = 1;		/* if error message should be printed */
64b6cee71dSXin LI int	optind = 1;		/* index into parent argv vector */
65b6cee71dSXin LI int	optopt = '?';		/* character checked for validity */
66b6cee71dSXin LI int	optreset;		/* reset getopt */
67b6cee71dSXin LI char    *optarg;		/* argument associated with option */
68b6cee71dSXin LI #elif HAVE_NBTOOL_CONFIG_H && !HAVE_DECL_OPTRESET
69b6cee71dSXin LI static int optreset;
70b6cee71dSXin LI #endif
71b6cee71dSXin LI 
72b6cee71dSXin LI #ifdef __weak_alias
73b6cee71dSXin LI __weak_alias(getopt_long,_getopt_long)
74b6cee71dSXin LI #endif
75b6cee71dSXin LI 
76b6cee71dSXin LI #define IGNORE_FIRST	(*options == '-' || *options == '+')
77b6cee71dSXin LI #define PRINT_ERROR	((opterr) && ((*options != ':') \
78b6cee71dSXin LI 				      || (IGNORE_FIRST && options[1] != ':')))
79b6cee71dSXin LI #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
80b6cee71dSXin LI #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
81b6cee71dSXin LI /* XXX: GNU ignores PC if *options == '-' */
82b6cee71dSXin LI #define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
83b6cee71dSXin LI 
84b6cee71dSXin LI /* return values */
85b6cee71dSXin LI #define	BADCH	(int)'?'
86b6cee71dSXin LI #define	BADARG		((IGNORE_FIRST && options[1] == ':') \
87b6cee71dSXin LI 			 || (*options == ':') ? (int)':' : (int)'?')
88b6cee71dSXin LI #define INORDER (int)1
89b6cee71dSXin LI 
90b6cee71dSXin LI #define	EMSG	""
91b6cee71dSXin LI 
92b6cee71dSXin LI static int getopt_internal(int, char **, const char *);
93b6cee71dSXin LI static int gcd(int, int);
94b6cee71dSXin LI static void permute_args(int, int, int, char **);
95b6cee71dSXin LI 
96b6cee71dSXin LI static const char *place = EMSG; /* option letter processing */
97b6cee71dSXin LI 
98b6cee71dSXin LI /* XXX: set optreset to 1 rather than these two */
99b6cee71dSXin LI static int nonopt_start = -1; /* first non option argument (for permute) */
100b6cee71dSXin LI static int nonopt_end = -1;   /* first option after non options (for permute) */
101b6cee71dSXin LI 
102b6cee71dSXin LI /* Error messages */
103b6cee71dSXin LI static const char recargchar[] = "option requires an argument -- %c";
104b6cee71dSXin LI static const char recargstring[] = "option requires an argument -- %s";
105b6cee71dSXin LI static const char ambig[] = "ambiguous option -- %.*s";
106b6cee71dSXin LI static const char noarg[] = "option doesn't take an argument -- %.*s";
107b6cee71dSXin LI static const char illoptchar[] = "unknown option -- %c";
108b6cee71dSXin LI static const char illoptstring[] = "unknown option -- %s";
109b6cee71dSXin LI 
110b6cee71dSXin LI 
111b6cee71dSXin LI /*
112b6cee71dSXin LI  * Compute the greatest common divisor of a and b.
113b6cee71dSXin LI  */
114b6cee71dSXin LI static int
gcd(a,b)115b6cee71dSXin LI gcd(a, b)
116b6cee71dSXin LI 	int a;
117b6cee71dSXin LI 	int b;
118b6cee71dSXin LI {
119b6cee71dSXin LI 	int c;
120b6cee71dSXin LI 
121b6cee71dSXin LI 	c = a % b;
122b6cee71dSXin LI 	while (c != 0) {
123b6cee71dSXin LI 		a = b;
124b6cee71dSXin LI 		b = c;
125b6cee71dSXin LI 		c = a % b;
126b6cee71dSXin LI 	}
127b6cee71dSXin LI 
128b6cee71dSXin LI 	return b;
129b6cee71dSXin LI }
130b6cee71dSXin LI 
131b6cee71dSXin LI /*
132b6cee71dSXin LI  * Exchange the block from nonopt_start to nonopt_end with the block
133b6cee71dSXin LI  * from nonopt_end to opt_end (keeping the same order of arguments
134b6cee71dSXin LI  * in each block).
135b6cee71dSXin LI  */
136b6cee71dSXin LI static void
permute_args(panonopt_start,panonopt_end,opt_end,nargv)137b6cee71dSXin LI permute_args(panonopt_start, panonopt_end, opt_end, nargv)
138b6cee71dSXin LI 	int panonopt_start;
139b6cee71dSXin LI 	int panonopt_end;
140b6cee71dSXin LI 	int opt_end;
141b6cee71dSXin LI 	char **nargv;
142b6cee71dSXin LI {
143b6cee71dSXin LI 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
144b6cee71dSXin LI 	char *swap;
145b6cee71dSXin LI 
146b6cee71dSXin LI 	_DIAGASSERT(nargv != NULL);
147b6cee71dSXin LI 
148b6cee71dSXin LI 	/*
149b6cee71dSXin LI 	 * compute lengths of blocks and number and size of cycles
150b6cee71dSXin LI 	 */
151b6cee71dSXin LI 	nnonopts = panonopt_end - panonopt_start;
152b6cee71dSXin LI 	nopts = opt_end - panonopt_end;
153b6cee71dSXin LI 	ncycle = gcd(nnonopts, nopts);
154b6cee71dSXin LI 	cyclelen = (opt_end - panonopt_start) / ncycle;
155b6cee71dSXin LI 
156b6cee71dSXin LI 	for (i = 0; i < ncycle; i++) {
157b6cee71dSXin LI 		cstart = panonopt_end+i;
158b6cee71dSXin LI 		pos = cstart;
159b6cee71dSXin LI 		for (j = 0; j < cyclelen; j++) {
160b6cee71dSXin LI 			if (pos >= panonopt_end)
161b6cee71dSXin LI 				pos -= nnonopts;
162b6cee71dSXin LI 			else
163b6cee71dSXin LI 				pos += nopts;
164b6cee71dSXin LI 			swap = nargv[pos];
165b6cee71dSXin LI 			nargv[pos] = nargv[cstart];
166b6cee71dSXin LI 			nargv[cstart] = swap;
167b6cee71dSXin LI 		}
168b6cee71dSXin LI 	}
169b6cee71dSXin LI }
170b6cee71dSXin LI 
171b6cee71dSXin LI /*
172b6cee71dSXin LI  * getopt_internal --
173b6cee71dSXin LI  *	Parse argc/argv argument vector.  Called by user level routines.
174b6cee71dSXin LI  *  Returns -2 if -- is found (can be long option or end of options marker).
175b6cee71dSXin LI  */
176b6cee71dSXin LI static int
getopt_internal(nargc,nargv,options)177b6cee71dSXin LI getopt_internal(nargc, nargv, options)
178b6cee71dSXin LI 	int nargc;
179b6cee71dSXin LI 	char **nargv;
180b6cee71dSXin LI 	const char *options;
181b6cee71dSXin LI {
182b6cee71dSXin LI 	char *oli;				/* option letter list index */
183b6cee71dSXin LI 	int optchar;
184b6cee71dSXin LI 
185b6cee71dSXin LI 	_DIAGASSERT(nargv != NULL);
186b6cee71dSXin LI 	_DIAGASSERT(options != NULL);
187b6cee71dSXin LI 
188b6cee71dSXin LI 	optarg = NULL;
189b6cee71dSXin LI 
190b6cee71dSXin LI 	/*
191b6cee71dSXin LI 	 * XXX Some programs (like rsyncd) expect to be able to
192b6cee71dSXin LI 	 * XXX re-initialize optind to 0 and have getopt_long(3)
193b6cee71dSXin LI 	 * XXX properly function again.  Work around this braindamage.
194b6cee71dSXin LI 	 */
195b6cee71dSXin LI 	if (optind == 0)
196b6cee71dSXin LI 		optind = 1;
197b6cee71dSXin LI 
198b6cee71dSXin LI 	if (optreset)
199b6cee71dSXin LI 		nonopt_start = nonopt_end = -1;
200b6cee71dSXin LI start:
201b6cee71dSXin LI 	if (optreset || !*place) {		/* update scanning pointer */
202b6cee71dSXin LI 		optreset = 0;
203b6cee71dSXin LI 		if (optind >= nargc) {          /* end of argument vector */
204b6cee71dSXin LI 			place = EMSG;
205b6cee71dSXin LI 			if (nonopt_end != -1) {
206b6cee71dSXin LI 				/* do permutation, if we have to */
207b6cee71dSXin LI 				permute_args(nonopt_start, nonopt_end,
208b6cee71dSXin LI 				    optind, nargv);
209b6cee71dSXin LI 				optind -= nonopt_end - nonopt_start;
210b6cee71dSXin LI 			}
211b6cee71dSXin LI 			else if (nonopt_start != -1) {
212b6cee71dSXin LI 				/*
213b6cee71dSXin LI 				 * If we skipped non-options, set optind
214b6cee71dSXin LI 				 * to the first of them.
215b6cee71dSXin LI 				 */
216b6cee71dSXin LI 				optind = nonopt_start;
217b6cee71dSXin LI 			}
218b6cee71dSXin LI 			nonopt_start = nonopt_end = -1;
219b6cee71dSXin LI 			return -1;
220b6cee71dSXin LI 		}
221b6cee71dSXin LI 		if ((*(place = nargv[optind]) != '-')
222b6cee71dSXin LI 		    || (place[1] == '\0')) {    /* found non-option */
223b6cee71dSXin LI 			place = EMSG;
224b6cee71dSXin LI 			if (IN_ORDER) {
225b6cee71dSXin LI 				/*
226b6cee71dSXin LI 				 * GNU extension:
227b6cee71dSXin LI 				 * return non-option as argument to option 1
228b6cee71dSXin LI 				 */
229b6cee71dSXin LI 				optarg = nargv[optind++];
230b6cee71dSXin LI 				return INORDER;
231b6cee71dSXin LI 			}
232b6cee71dSXin LI 			if (!PERMUTE) {
233b6cee71dSXin LI 				/*
234b6cee71dSXin LI 				 * if no permutation wanted, stop parsing
235b6cee71dSXin LI 				 * at first non-option
236b6cee71dSXin LI 				 */
237b6cee71dSXin LI 				return -1;
238b6cee71dSXin LI 			}
239b6cee71dSXin LI 			/* do permutation */
240b6cee71dSXin LI 			if (nonopt_start == -1)
241b6cee71dSXin LI 				nonopt_start = optind;
242b6cee71dSXin LI 			else if (nonopt_end != -1) {
243b6cee71dSXin LI 				permute_args(nonopt_start, nonopt_end,
244b6cee71dSXin LI 				    optind, nargv);
245b6cee71dSXin LI 				nonopt_start = optind -
246b6cee71dSXin LI 				    (nonopt_end - nonopt_start);
247b6cee71dSXin LI 				nonopt_end = -1;
248b6cee71dSXin LI 			}
249b6cee71dSXin LI 			optind++;
250b6cee71dSXin LI 			/* process next argument */
251b6cee71dSXin LI 			goto start;
252b6cee71dSXin LI 		}
253b6cee71dSXin LI 		if (nonopt_start != -1 && nonopt_end == -1)
254b6cee71dSXin LI 			nonopt_end = optind;
255b6cee71dSXin LI 		if (place[1] && *++place == '-') {	/* found "--" */
256b6cee71dSXin LI 			place++;
257b6cee71dSXin LI 			return -2;
258b6cee71dSXin LI 		}
259b6cee71dSXin LI 	}
260b6cee71dSXin LI 	if ((optchar = (int)*place++) == (int)':' ||
261b6cee71dSXin LI 	    (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
262b6cee71dSXin LI 		/* option letter unknown or ':' */
263b6cee71dSXin LI 		if (!*place)
264b6cee71dSXin LI 			++optind;
265b6cee71dSXin LI 		if (PRINT_ERROR)
266b6cee71dSXin LI 			warnx(illoptchar, optchar);
267b6cee71dSXin LI 		optopt = optchar;
268b6cee71dSXin LI 		return BADCH;
269b6cee71dSXin LI 	}
270b6cee71dSXin LI 	if (optchar == 'W' && oli[1] == ';') {		/* -W long-option */
271b6cee71dSXin LI 		/* XXX: what if no long options provided (called by getopt)? */
272b6cee71dSXin LI 		if (*place)
273b6cee71dSXin LI 			return -2;
274b6cee71dSXin LI 
275b6cee71dSXin LI 		if (++optind >= nargc) {	/* no arg */
276b6cee71dSXin LI 			place = EMSG;
277b6cee71dSXin LI 			if (PRINT_ERROR)
278b6cee71dSXin LI 				warnx(recargchar, optchar);
279b6cee71dSXin LI 			optopt = optchar;
280b6cee71dSXin LI 			return BADARG;
281b6cee71dSXin LI 		} else				/* white space */
282b6cee71dSXin LI 			place = nargv[optind];
283b6cee71dSXin LI 		/*
284b6cee71dSXin LI 		 * Handle -W arg the same as --arg (which causes getopt to
285b6cee71dSXin LI 		 * stop parsing).
286b6cee71dSXin LI 		 */
287b6cee71dSXin LI 		return -2;
288b6cee71dSXin LI 	}
289b6cee71dSXin LI 	if (*++oli != ':') {			/* doesn't take argument */
290b6cee71dSXin LI 		if (!*place)
291b6cee71dSXin LI 			++optind;
292b6cee71dSXin LI 	} else {				/* takes (optional) argument */
293b6cee71dSXin LI 		optarg = NULL;
294b6cee71dSXin LI 		if (*place)			/* no white space */
295b6cee71dSXin LI 			optarg = (char *)place;
296b6cee71dSXin LI 		/* XXX: disable test for :: if PC? (GNU doesn't) */
297b6cee71dSXin LI 		else if (oli[1] != ':') {	/* arg not optional */
298b6cee71dSXin LI 			if (++optind >= nargc) {	/* no arg */
299b6cee71dSXin LI 				place = EMSG;
300b6cee71dSXin LI 				if (PRINT_ERROR)
301b6cee71dSXin LI 					warnx(recargchar, optchar);
302b6cee71dSXin LI 				optopt = optchar;
303b6cee71dSXin LI 				return BADARG;
304b6cee71dSXin LI 			} else
305b6cee71dSXin LI 				optarg = nargv[optind];
306b6cee71dSXin LI 		}
307b6cee71dSXin LI 		place = EMSG;
308b6cee71dSXin LI 		++optind;
309b6cee71dSXin LI 	}
310b6cee71dSXin LI 	/* dump back option letter */
311b6cee71dSXin LI 	return optchar;
312b6cee71dSXin LI }
313b6cee71dSXin LI 
314b6cee71dSXin LI #ifdef REPLACE_GETOPT
315b6cee71dSXin LI /*
316b6cee71dSXin LI  * getopt --
317b6cee71dSXin LI  *	Parse argc/argv argument vector.
318b6cee71dSXin LI  *
319b6cee71dSXin LI  * [eventually this will replace the real getopt]
320b6cee71dSXin LI  */
321b6cee71dSXin LI int
getopt(nargc,nargv,options)322b6cee71dSXin LI getopt(nargc, nargv, options)
323b6cee71dSXin LI 	int nargc;
324b6cee71dSXin LI 	char * const *nargv;
325b6cee71dSXin LI 	const char *options;
326b6cee71dSXin LI {
327b6cee71dSXin LI 	int retval;
328b6cee71dSXin LI 
329b6cee71dSXin LI 	_DIAGASSERT(nargv != NULL);
330b6cee71dSXin LI 	_DIAGASSERT(options != NULL);
331b6cee71dSXin LI 
332b6cee71dSXin LI 	retval = getopt_internal(nargc, (char **)nargv, options);
333b6cee71dSXin LI 	if (retval == -2) {
334b6cee71dSXin LI 		++optind;
335b6cee71dSXin LI 		/*
336b6cee71dSXin LI 		 * We found an option (--), so if we skipped non-options,
337b6cee71dSXin LI 		 * we have to permute.
338b6cee71dSXin LI 		 */
339b6cee71dSXin LI 		if (nonopt_end != -1) {
340b6cee71dSXin LI 			permute_args(nonopt_start, nonopt_end, optind,
341b6cee71dSXin LI 				     (char **)nargv);
342b6cee71dSXin LI 			optind -= nonopt_end - nonopt_start;
343b6cee71dSXin LI 		}
344b6cee71dSXin LI 		nonopt_start = nonopt_end = -1;
345b6cee71dSXin LI 		retval = -1;
346b6cee71dSXin LI 	}
347b6cee71dSXin LI 	return retval;
348b6cee71dSXin LI }
349b6cee71dSXin LI #endif
350b6cee71dSXin LI 
351b6cee71dSXin LI /*
352b6cee71dSXin LI  * getopt_long --
353b6cee71dSXin LI  *	Parse argc/argv argument vector.
354b6cee71dSXin LI  */
355b6cee71dSXin LI int
getopt_long(nargc,nargv,options,long_options,idx)356b6cee71dSXin LI getopt_long(nargc, nargv, options, long_options, idx)
357b6cee71dSXin LI 	int nargc;
358b6cee71dSXin LI 	char * const *nargv;
359b6cee71dSXin LI 	const char *options;
360b6cee71dSXin LI 	const struct option *long_options;
361b6cee71dSXin LI 	int *idx;
362b6cee71dSXin LI {
363b6cee71dSXin LI 	int retval;
364b6cee71dSXin LI 
365b6cee71dSXin LI #define IDENTICAL_INTERPRETATION(_x, _y)				\
366b6cee71dSXin LI 	(long_options[(_x)].has_arg == long_options[(_y)].has_arg &&	\
367b6cee71dSXin LI 	 long_options[(_x)].flag == long_options[(_y)].flag &&		\
368b6cee71dSXin LI 	 long_options[(_x)].val == long_options[(_y)].val)
369b6cee71dSXin LI 
370b6cee71dSXin LI 	_DIAGASSERT(nargv != NULL);
371b6cee71dSXin LI 	_DIAGASSERT(options != NULL);
372b6cee71dSXin LI 	_DIAGASSERT(long_options != NULL);
373b6cee71dSXin LI 	/* idx may be NULL */
374b6cee71dSXin LI 
375b6cee71dSXin LI 	retval = getopt_internal(nargc, (char **)nargv, options);
376b6cee71dSXin LI 	if (retval == -2) {
377b6cee71dSXin LI 		char *current_argv, *has_equal;
378b6cee71dSXin LI 		size_t current_argv_len;
379b6cee71dSXin LI 		int i, ambiguous, match;
380b6cee71dSXin LI 
381b6cee71dSXin LI 		current_argv = (char *)place;
382b6cee71dSXin LI 		match = -1;
383b6cee71dSXin LI 		ambiguous = 0;
384b6cee71dSXin LI 
385b6cee71dSXin LI 		optind++;
386b6cee71dSXin LI 		place = EMSG;
387b6cee71dSXin LI 
388b6cee71dSXin LI 		if (*current_argv == '\0') {		/* found "--" */
389b6cee71dSXin LI 			/*
390b6cee71dSXin LI 			 * We found an option (--), so if we skipped
391b6cee71dSXin LI 			 * non-options, we have to permute.
392b6cee71dSXin LI 			 */
393b6cee71dSXin LI 			if (nonopt_end != -1) {
394b6cee71dSXin LI 				permute_args(nonopt_start, nonopt_end,
395b6cee71dSXin LI 					     optind, (char **)nargv);
396b6cee71dSXin LI 				optind -= nonopt_end - nonopt_start;
397b6cee71dSXin LI 			}
398b6cee71dSXin LI 			nonopt_start = nonopt_end = -1;
399b6cee71dSXin LI 			return -1;
400b6cee71dSXin LI 		}
401b6cee71dSXin LI 		if ((has_equal = strchr(current_argv, '=')) != NULL) {
402b6cee71dSXin LI 			/* argument found (--option=arg) */
403b6cee71dSXin LI 			current_argv_len = has_equal - current_argv;
404b6cee71dSXin LI 			has_equal++;
405b6cee71dSXin LI 		} else
406b6cee71dSXin LI 			current_argv_len = strlen(current_argv);
407b6cee71dSXin LI 
408b6cee71dSXin LI 		for (i = 0; long_options[i].name; i++) {
409b6cee71dSXin LI 			/* find matching long option */
410b6cee71dSXin LI 			if (strncmp(current_argv, long_options[i].name,
411b6cee71dSXin LI 			    current_argv_len))
412b6cee71dSXin LI 				continue;
413b6cee71dSXin LI 
414b6cee71dSXin LI 			if (strlen(long_options[i].name) ==
415b6cee71dSXin LI 			    (unsigned)current_argv_len) {
416b6cee71dSXin LI 				/* exact match */
417b6cee71dSXin LI 				match = i;
418b6cee71dSXin LI 				ambiguous = 0;
419b6cee71dSXin LI 				break;
420b6cee71dSXin LI 			}
421b6cee71dSXin LI 			if (match == -1)		/* partial match */
422b6cee71dSXin LI 				match = i;
423b6cee71dSXin LI 			else if (!IDENTICAL_INTERPRETATION(i, match))
424b6cee71dSXin LI 				ambiguous = 1;
425b6cee71dSXin LI 		}
426b6cee71dSXin LI 		if (ambiguous) {
427b6cee71dSXin LI 			/* ambiguous abbreviation */
428b6cee71dSXin LI 			if (PRINT_ERROR)
429b6cee71dSXin LI 				warnx(ambig, (int)current_argv_len,
430b6cee71dSXin LI 				     current_argv);
431b6cee71dSXin LI 			optopt = 0;
432b6cee71dSXin LI 			return BADCH;
433b6cee71dSXin LI 		}
434b6cee71dSXin LI 		if (match != -1) {			/* option found */
435b6cee71dSXin LI 		        if (long_options[match].has_arg == no_argument
436b6cee71dSXin LI 			    && has_equal) {
437b6cee71dSXin LI 				if (PRINT_ERROR)
438b6cee71dSXin LI 					warnx(noarg, (int)current_argv_len,
439b6cee71dSXin LI 					     current_argv);
440b6cee71dSXin LI 				/*
441b6cee71dSXin LI 				 * XXX: GNU sets optopt to val regardless of
442b6cee71dSXin LI 				 * flag
443b6cee71dSXin LI 				 */
444b6cee71dSXin LI 				if (long_options[match].flag == NULL)
445b6cee71dSXin LI 					optopt = long_options[match].val;
446b6cee71dSXin LI 				else
447b6cee71dSXin LI 					optopt = 0;
448b6cee71dSXin LI 				return BADARG;
449b6cee71dSXin LI 			}
450b6cee71dSXin LI 			if (long_options[match].has_arg == required_argument ||
451b6cee71dSXin LI 			    long_options[match].has_arg == optional_argument) {
452b6cee71dSXin LI 				if (has_equal)
453b6cee71dSXin LI 					optarg = has_equal;
454b6cee71dSXin LI 				else if (long_options[match].has_arg ==
455b6cee71dSXin LI 				    required_argument) {
456b6cee71dSXin LI 					/*
457b6cee71dSXin LI 					 * optional argument doesn't use
458b6cee71dSXin LI 					 * next nargv
459b6cee71dSXin LI 					 */
460b6cee71dSXin LI 					optarg = nargv[optind++];
461b6cee71dSXin LI 				}
462b6cee71dSXin LI 			}
463b6cee71dSXin LI 			if ((long_options[match].has_arg == required_argument)
464b6cee71dSXin LI 			    && (optarg == NULL)) {
465b6cee71dSXin LI 				/*
466b6cee71dSXin LI 				 * Missing argument; leading ':'
467b6cee71dSXin LI 				 * indicates no error should be generated
468b6cee71dSXin LI 				 */
469b6cee71dSXin LI 				if (PRINT_ERROR)
470b6cee71dSXin LI 					warnx(recargstring, current_argv);
471b6cee71dSXin LI 				/*
472b6cee71dSXin LI 				 * XXX: GNU sets optopt to val regardless
473b6cee71dSXin LI 				 * of flag
474b6cee71dSXin LI 				 */
475b6cee71dSXin LI 				if (long_options[match].flag == NULL)
476b6cee71dSXin LI 					optopt = long_options[match].val;
477b6cee71dSXin LI 				else
478b6cee71dSXin LI 					optopt = 0;
479b6cee71dSXin LI 				--optind;
480b6cee71dSXin LI 				return BADARG;
481b6cee71dSXin LI 			}
482b6cee71dSXin LI 		} else {			/* unknown option */
483b6cee71dSXin LI 			if (PRINT_ERROR)
484b6cee71dSXin LI 				warnx(illoptstring, current_argv);
485b6cee71dSXin LI 			optopt = 0;
486b6cee71dSXin LI 			return BADCH;
487b6cee71dSXin LI 		}
488b6cee71dSXin LI 		if (long_options[match].flag) {
489b6cee71dSXin LI 			*long_options[match].flag = long_options[match].val;
490b6cee71dSXin LI 			retval = 0;
491b6cee71dSXin LI 		} else
492b6cee71dSXin LI 			retval = long_options[match].val;
493b6cee71dSXin LI 		if (idx)
494b6cee71dSXin LI 			*idx = match;
495b6cee71dSXin LI 	}
496b6cee71dSXin LI 	return retval;
497b6cee71dSXin LI #undef IDENTICAL_INTERPRETATION
498b6cee71dSXin LI }
499