xref: /netbsd-src/lib/libc/stdlib/getopt_long.c (revision 689fdc5827b0709bbad93224dd2a1b221c8cca94)
1*689fdc58Schristos /*	$NetBSD: getopt_long.c,v 1.28 2024/01/19 18:41:38 christos Exp $	*/
26dfafca1Schristos 
36dfafca1Schristos /*-
46dfafca1Schristos  * Copyright (c) 2000 The NetBSD Foundation, Inc.
56dfafca1Schristos  * All rights reserved.
66dfafca1Schristos  *
76dfafca1Schristos  * This code is derived from software contributed to The NetBSD Foundation
86dfafca1Schristos  * by Dieter Baron and Thomas Klausner.
962da59d7Smcr  *
1062da59d7Smcr  * Redistribution and use in source and binary forms, with or without
1162da59d7Smcr  * modification, are permitted provided that the following conditions
1262da59d7Smcr  * are met:
1362da59d7Smcr  * 1. Redistributions of source code must retain the above copyright
1462da59d7Smcr  *    notice, this list of conditions and the following disclaimer.
1562da59d7Smcr  * 2. Redistributions in binary form must reproduce the above copyright
1662da59d7Smcr  *    notice, this list of conditions and the following disclaimer in the
1762da59d7Smcr  *    documentation and/or other materials provided with the distribution.
1862da59d7Smcr  *
196dfafca1Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
206dfafca1Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216dfafca1Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
226dfafca1Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
236dfafca1Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
246dfafca1Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
256dfafca1Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
266dfafca1Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
276dfafca1Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
286dfafca1Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
296dfafca1Schristos  * POSSIBILITY OF SUCH DAMAGE.
3062da59d7Smcr  */
3162da59d7Smcr 
32b2f78261Sjmc #if HAVE_NBTOOL_CONFIG_H
33b2f78261Sjmc #include "nbtool_config.h"
34b2f78261Sjmc #endif
35b2f78261Sjmc 
36b48252f3Slukem #include <sys/cdefs.h>
37*689fdc58Schristos __RCSID("$NetBSD: getopt_long.c,v 1.28 2024/01/19 18:41:38 christos Exp $");
38b48252f3Slukem 
396d34da15Schristos #include "namespace.h"
406d34da15Schristos 
41b48252f3Slukem #include <assert.h>
429fbd8888Stv #include <err.h>
43b48252f3Slukem #include <errno.h>
44b2f78261Sjmc #if HAVE_NBTOOL_CONFIG_H
45b2f78261Sjmc #include "compat_getopt.h"
46b2f78261Sjmc #else
479fbd8888Stv #include <getopt.h>
48b2f78261Sjmc #endif
4962da59d7Smcr #include <stdlib.h>
5062da59d7Smcr #include <string.h>
5162da59d7Smcr 
52b3b90d74Schristos #if HAVE_NBTOOL_CONFIG_H && !HAVE_GETOPT_LONG && !HAVE_DECL_OPTIND
53b3b90d74Schristos #define REPLACE_GETOPT
54b3b90d74Schristos #endif
55b3b90d74Schristos 
56b3b90d74Schristos #ifdef REPLACE_GETOPT
576dfafca1Schristos #ifdef __weak_alias
586dfafca1Schristos __weak_alias(getopt,_getopt)
596dfafca1Schristos #endif
606dfafca1Schristos int	opterr = 1;		/* if error message should be printed */
616dfafca1Schristos int	optind = 1;		/* index into parent argv vector */
626dfafca1Schristos int	optopt = '?';		/* character checked for validity */
636dfafca1Schristos int	optreset;		/* reset getopt */
646dfafca1Schristos char    *optarg;		/* argument associated with option */
65171d6532Slukem #elif HAVE_NBTOOL_CONFIG_H && !HAVE_DECL_OPTRESET
66a328e341Stv static int optreset;
676dfafca1Schristos #endif
6862da59d7Smcr 
696dfafca1Schristos #ifdef __weak_alias
706dfafca1Schristos __weak_alias(getopt_long,_getopt_long)
716dfafca1Schristos #endif
7262da59d7Smcr 
736dfafca1Schristos #define IGNORE_FIRST	(*options == '-' || *options == '+')
746dfafca1Schristos #define PRINT_ERROR	((opterr) && ((*options != ':') \
756dfafca1Schristos 				      || (IGNORE_FIRST && options[1] != ':')))
766dfafca1Schristos #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
776dfafca1Schristos #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
786dfafca1Schristos /* XXX: GNU ignores PC if *options == '-' */
796dfafca1Schristos #define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
80b48252f3Slukem 
816dfafca1Schristos /* return values */
8262da59d7Smcr #define	BADCH	(int)'?'
83e7775dbfSwiz #define	BADARG		((IGNORE_FIRST && options[1] == ':') \
84e7775dbfSwiz 			 || (*options == ':') ? (int)':' : (int)'?')
856dfafca1Schristos #define INORDER (int)1
866dfafca1Schristos 
8762da59d7Smcr #define	EMSG	""
8862da59d7Smcr 
89cc420b6cSjoerg static int getopt_internal(int, char **, const char *);
90cc420b6cSjoerg static int gcd(int, int);
91cc420b6cSjoerg static void permute_args(int, int, int, char **);
926dfafca1Schristos 
9303256c6eSchristos static const char *place = EMSG; /* option letter processing */
946dfafca1Schristos 
956dfafca1Schristos /* XXX: set optreset to 1 rather than these two */
966dfafca1Schristos static int nonopt_start = -1; /* first non option argument (for permute) */
976dfafca1Schristos static int nonopt_end = -1;   /* first option after non options (for permute) */
986dfafca1Schristos 
996dfafca1Schristos /* Error messages */
100045f0427Snathanw static const char recargchar[] = "option requires an argument -- %c";
101045f0427Snathanw static const char recargstring[] = "option requires an argument -- %s";
1026dfafca1Schristos static const char ambig[] = "ambiguous option -- %.*s";
1036dfafca1Schristos static const char noarg[] = "option doesn't take an argument -- %.*s";
10477dec036Sjoda static const char illoptchar[] = "unknown option -- %c";
10577dec036Sjoda static const char illoptstring[] = "unknown option -- %s";
1066dfafca1Schristos 
1076dfafca1Schristos 
10862da59d7Smcr /*
1096dfafca1Schristos  * Compute the greatest common divisor of a and b.
11062da59d7Smcr  */
1116dfafca1Schristos static int
gcd(int a,int b)112cc420b6cSjoerg gcd(int a, int b)
1136dfafca1Schristos {
1146dfafca1Schristos 	int c;
1156dfafca1Schristos 
1166dfafca1Schristos 	c = a % b;
1176dfafca1Schristos 	while (c != 0) {
1186dfafca1Schristos 		a = b;
1196dfafca1Schristos 		b = c;
1206dfafca1Schristos 		c = a % b;
1216dfafca1Schristos 	}
1226dfafca1Schristos 
1236dfafca1Schristos 	return b;
1246dfafca1Schristos }
1256dfafca1Schristos 
1266dfafca1Schristos /*
1276dfafca1Schristos  * Exchange the block from nonopt_start to nonopt_end with the block
1286dfafca1Schristos  * from nonopt_end to opt_end (keeping the same order of arguments
1296dfafca1Schristos  * in each block).
1306dfafca1Schristos  */
1316dfafca1Schristos static void
permute_args(int panonopt_start,int panonopt_end,int opt_end,char ** nargv)132cc420b6cSjoerg permute_args(int panonopt_start, int panonopt_end, int opt_end, char **nargv)
1336dfafca1Schristos {
1346dfafca1Schristos 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
1356dfafca1Schristos 	char *swap;
1366dfafca1Schristos 
137c5b9e1ceSlukem 	_DIAGASSERT(nargv != NULL);
138c5b9e1ceSlukem 
1396dfafca1Schristos 	/*
1406dfafca1Schristos 	 * compute lengths of blocks and number and size of cycles
1416dfafca1Schristos 	 */
14250f5afd5Slukem 	nnonopts = panonopt_end - panonopt_start;
14350f5afd5Slukem 	nopts = opt_end - panonopt_end;
1446dfafca1Schristos 	ncycle = gcd(nnonopts, nopts);
14550f5afd5Slukem 	cyclelen = (opt_end - panonopt_start) / ncycle;
1466dfafca1Schristos 
1476dfafca1Schristos 	for (i = 0; i < ncycle; i++) {
14850f5afd5Slukem 		cstart = panonopt_end+i;
1496dfafca1Schristos 		pos = cstart;
1506dfafca1Schristos 		for (j = 0; j < cyclelen; j++) {
15150f5afd5Slukem 			if (pos >= panonopt_end)
1526dfafca1Schristos 				pos -= nnonopts;
1536dfafca1Schristos 			else
1546dfafca1Schristos 				pos += nopts;
1556dfafca1Schristos 			swap = nargv[pos];
1569de32b7fSyamt 			nargv[pos] = nargv[cstart];
1579de32b7fSyamt 			nargv[cstart] = swap;
1586dfafca1Schristos 		}
1596dfafca1Schristos 	}
1606dfafca1Schristos }
1616dfafca1Schristos 
1626dfafca1Schristos /*
1636dfafca1Schristos  * getopt_internal --
1646dfafca1Schristos  *	Parse argc/argv argument vector.  Called by user level routines.
1656dfafca1Schristos  *  Returns -2 if -- is found (can be long option or end of options marker).
1666dfafca1Schristos  */
1676dfafca1Schristos static int
getopt_internal(int nargc,char ** nargv,const char * options)168cc420b6cSjoerg getopt_internal(int nargc, char **nargv, const char *options)
16962da59d7Smcr {
170*689fdc58Schristos 	const char *oli;			/* option letter list index */
1716dfafca1Schristos 	int optchar;
17262da59d7Smcr 
173b48252f3Slukem 	_DIAGASSERT(nargv != NULL);
1746dfafca1Schristos 	_DIAGASSERT(options != NULL);
175b48252f3Slukem 
1766dfafca1Schristos 	optarg = NULL;
1776dfafca1Schristos 
178a3fe3d3cSthorpej 	/*
179a3fe3d3cSthorpej 	 * XXX Some programs (like rsyncd) expect to be able to
180a3fe3d3cSthorpej 	 * XXX re-initialize optind to 0 and have getopt_long(3)
181a3fe3d3cSthorpej 	 * XXX properly function again.  Work around this braindamage.
182a3fe3d3cSthorpej 	 */
183a3fe3d3cSthorpej 	if (optind == 0)
184a3fe3d3cSthorpej 		optind = 1;
185a3fe3d3cSthorpej 
1866dfafca1Schristos 	if (optreset)
1876dfafca1Schristos 		nonopt_start = nonopt_end = -1;
1886dfafca1Schristos start:
18962da59d7Smcr 	if (optreset || !*place) {		/* update scanning pointer */
19062da59d7Smcr 		optreset = 0;
1916dfafca1Schristos 		if (optind >= nargc) {          /* end of argument vector */
19262da59d7Smcr 			place = EMSG;
1936dfafca1Schristos 			if (nonopt_end != -1) {
1946dfafca1Schristos 				/* do permutation, if we have to */
1956dfafca1Schristos 				permute_args(nonopt_start, nonopt_end,
1966dfafca1Schristos 				    optind, nargv);
1976dfafca1Schristos 				optind -= nonopt_end - nonopt_start;
19862da59d7Smcr 			}
1996dfafca1Schristos 			else if (nonopt_start != -1) {
20062da59d7Smcr 				/*
2016dfafca1Schristos 				 * If we skipped non-options, set optind
2026dfafca1Schristos 				 * to the first of them.
20362da59d7Smcr 				 */
2046dfafca1Schristos 				optind = nonopt_start;
20562da59d7Smcr 			}
2066dfafca1Schristos 			nonopt_start = nonopt_end = -1;
2076dfafca1Schristos 			return -1;
2086dfafca1Schristos 		}
209e7775dbfSwiz 		if ((*(place = nargv[optind]) != '-')
210e7775dbfSwiz 		    || (place[1] == '\0')) {    /* found non-option */
2116dfafca1Schristos 			place = EMSG;
2126dfafca1Schristos 			if (IN_ORDER) {
2136dfafca1Schristos 				/*
2146dfafca1Schristos 				 * GNU extension:
2156dfafca1Schristos 				 * return non-option as argument to option 1
2166dfafca1Schristos 				 */
2176dfafca1Schristos 				optarg = nargv[optind++];
2186dfafca1Schristos 				return INORDER;
2196dfafca1Schristos 			}
2206dfafca1Schristos 			if (!PERMUTE) {
2216dfafca1Schristos 				/*
2226dfafca1Schristos 				 * if no permutation wanted, stop parsing
2236dfafca1Schristos 				 * at first non-option
2246dfafca1Schristos 				 */
2256dfafca1Schristos 				return -1;
2266dfafca1Schristos 			}
2276dfafca1Schristos 			/* do permutation */
2286dfafca1Schristos 			if (nonopt_start == -1)
2296dfafca1Schristos 				nonopt_start = optind;
2306dfafca1Schristos 			else if (nonopt_end != -1) {
2316dfafca1Schristos 				permute_args(nonopt_start, nonopt_end,
2326dfafca1Schristos 				    optind, nargv);
2336dfafca1Schristos 				nonopt_start = optind -
2346dfafca1Schristos 				    (nonopt_end - nonopt_start);
2356dfafca1Schristos 				nonopt_end = -1;
2366dfafca1Schristos 			}
2376dfafca1Schristos 			optind++;
2386dfafca1Schristos 			/* process next argument */
2396dfafca1Schristos 			goto start;
2406dfafca1Schristos 		}
2416dfafca1Schristos 		if (nonopt_start != -1 && nonopt_end == -1)
2426dfafca1Schristos 			nonopt_end = optind;
2436dfafca1Schristos 		if (place[1] && *++place == '-') {	/* found "--" */
2446dfafca1Schristos 			place++;
2456dfafca1Schristos 			return -2;
2466dfafca1Schristos 		}
2476dfafca1Schristos 	}
2486dfafca1Schristos 	if ((optchar = (int)*place++) == (int)':' ||
2496dfafca1Schristos 	    (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
2506dfafca1Schristos 		/* option letter unknown or ':' */
25162da59d7Smcr 		if (!*place)
25262da59d7Smcr 			++optind;
2536dfafca1Schristos 		if (PRINT_ERROR)
254045f0427Snathanw 			warnx(illoptchar, optchar);
2556dfafca1Schristos 		optopt = optchar;
2566dfafca1Schristos 		return BADCH;
2576dfafca1Schristos 	}
2586dfafca1Schristos 	if (optchar == 'W' && oli[1] == ';') {		/* -W long-option */
2596dfafca1Schristos 		/* XXX: what if no long options provided (called by getopt)? */
2606dfafca1Schristos 		if (*place)
2616dfafca1Schristos 			return -2;
2626dfafca1Schristos 
2636dfafca1Schristos 		if (++optind >= nargc) {	/* no arg */
2646dfafca1Schristos 			place = EMSG;
2656dfafca1Schristos 			if (PRINT_ERROR)
266045f0427Snathanw 				warnx(recargchar, optchar);
2676dfafca1Schristos 			optopt = optchar;
2686dfafca1Schristos 			return BADARG;
2696dfafca1Schristos 		} else				/* white space */
2706dfafca1Schristos 			place = nargv[optind];
2716dfafca1Schristos 		/*
2726dfafca1Schristos 		 * Handle -W arg the same as --arg (which causes getopt to
2736dfafca1Schristos 		 * stop parsing).
2746dfafca1Schristos 		 */
2756dfafca1Schristos 		return -2;
2766dfafca1Schristos 	}
2776dfafca1Schristos 	if (*++oli != ':') {			/* doesn't take argument */
2786dfafca1Schristos 		if (!*place)
2796dfafca1Schristos 			++optind;
2806dfafca1Schristos 	} else {				/* takes (optional) argument */
2816dfafca1Schristos 		optarg = NULL;
28262da59d7Smcr 		if (*place)			/* no white space */
28303256c6eSchristos 			optarg = __UNCONST(place);
2846dfafca1Schristos 		/* XXX: disable test for :: if PC? (GNU doesn't) */
2856dfafca1Schristos 		else if (oli[1] != ':') {	/* arg not optional */
2866dfafca1Schristos 			if (++optind >= nargc) {	/* no arg */
28762da59d7Smcr 				place = EMSG;
2886dfafca1Schristos 				if (PRINT_ERROR)
289045f0427Snathanw 					warnx(recargchar, optchar);
2906dfafca1Schristos 				optopt = optchar;
2916dfafca1Schristos 				return BADARG;
2926dfafca1Schristos 			} else
29362da59d7Smcr 				optarg = nargv[optind];
2946dfafca1Schristos 		}
29562da59d7Smcr 		place = EMSG;
29662da59d7Smcr 		++optind;
29762da59d7Smcr 	}
2986dfafca1Schristos 	/* dump back option letter */
2996dfafca1Schristos 	return optchar;
30062da59d7Smcr }
30162da59d7Smcr 
3026dfafca1Schristos #ifdef REPLACE_GETOPT
30362da59d7Smcr /*
30462da59d7Smcr  * getopt --
30562da59d7Smcr  *	Parse argc/argv argument vector.
3066dfafca1Schristos  *
3076dfafca1Schristos  * [eventually this will replace the real getopt]
30862da59d7Smcr  */
30962da59d7Smcr int
getopt(int nargc,char * const * nargv,const char * options)3101c79b1c7Skamil getopt(int nargc, char * const *nargv, const char *options)
31162da59d7Smcr {
31262da59d7Smcr 	int retval;
31362da59d7Smcr 
314c5b9e1ceSlukem 	_DIAGASSERT(nargv != NULL);
315c5b9e1ceSlukem 	_DIAGASSERT(options != NULL);
316c5b9e1ceSlukem 
3179de32b7fSyamt 	retval = getopt_internal(nargc, __UNCONST(nargv), options);
3189de32b7fSyamt 	if (retval == -2) {
31962da59d7Smcr 		++optind;
3206dfafca1Schristos 		/*
3216dfafca1Schristos 		 * We found an option (--), so if we skipped non-options,
3226dfafca1Schristos 		 * we have to permute.
3236dfafca1Schristos 		 */
3246dfafca1Schristos 		if (nonopt_end != -1) {
3256dfafca1Schristos 			permute_args(nonopt_start, nonopt_end, optind,
326596fd566Skamil 				       __UNCONST(nargv));
3276dfafca1Schristos 			optind -= nonopt_end - nonopt_start;
32862da59d7Smcr 		}
3296dfafca1Schristos 		nonopt_start = nonopt_end = -1;
3306dfafca1Schristos 		retval = -1;
3316dfafca1Schristos 	}
3326dfafca1Schristos 	return retval;
33362da59d7Smcr }
33462da59d7Smcr #endif
33562da59d7Smcr 
33662da59d7Smcr /*
33762da59d7Smcr  * getopt_long --
33862da59d7Smcr  *	Parse argc/argv argument vector.
33962da59d7Smcr  */
34062da59d7Smcr int
getopt_long(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx)341cc420b6cSjoerg getopt_long(int nargc, char * const *nargv, const char *options,
342cc420b6cSjoerg     const struct option *long_options, int *idx)
34362da59d7Smcr {
34462da59d7Smcr 	int retval;
34562da59d7Smcr 
34615b661abSginsbach #define IDENTICAL_INTERPRETATION(_x, _y)				\
34715b661abSginsbach 	(long_options[(_x)].has_arg == long_options[(_y)].has_arg &&	\
34815b661abSginsbach 	 long_options[(_x)].flag == long_options[(_y)].flag &&		\
34915b661abSginsbach 	 long_options[(_x)].val == long_options[(_y)].val)
35015b661abSginsbach 
351b48252f3Slukem 	_DIAGASSERT(nargv != NULL);
3526dfafca1Schristos 	_DIAGASSERT(options != NULL);
353b48252f3Slukem 	_DIAGASSERT(long_options != NULL);
3546dfafca1Schristos 	/* idx may be NULL */
355b48252f3Slukem 
3569de32b7fSyamt 	retval = getopt_internal(nargc, __UNCONST(nargv), options);
3579de32b7fSyamt 	if (retval == -2) {
3586dfafca1Schristos 		char *current_argv, *has_equal;
3596dfafca1Schristos 		size_t current_argv_len;
36015b661abSginsbach 		int i, ambiguous, match;
36162da59d7Smcr 
36203256c6eSchristos 		current_argv = __UNCONST(place);
3636dfafca1Schristos 		match = -1;
36415b661abSginsbach 		ambiguous = 0;
3656dfafca1Schristos 
3666dfafca1Schristos 		optind++;
3676dfafca1Schristos 		place = EMSG;
3686dfafca1Schristos 
3696dfafca1Schristos 		if (*current_argv == '\0') {		/* found "--" */
3706dfafca1Schristos 			/*
3716dfafca1Schristos 			 * We found an option (--), so if we skipped
3726dfafca1Schristos 			 * non-options, we have to permute.
3736dfafca1Schristos 			 */
3746dfafca1Schristos 			if (nonopt_end != -1) {
3756dfafca1Schristos 				permute_args(nonopt_start, nonopt_end,
3769de32b7fSyamt 				    optind, __UNCONST(nargv));
3776dfafca1Schristos 				optind -= nonopt_end - nonopt_start;
3786dfafca1Schristos 			}
3796dfafca1Schristos 			nonopt_start = nonopt_end = -1;
3806dfafca1Schristos 			return -1;
38162da59d7Smcr 		}
38262da59d7Smcr 		if ((has_equal = strchr(current_argv, '=')) != NULL) {
3836dfafca1Schristos 			/* argument found (--option=arg) */
38462da59d7Smcr 			current_argv_len = has_equal - current_argv;
38562da59d7Smcr 			has_equal++;
38662da59d7Smcr 		} else
38762da59d7Smcr 			current_argv_len = strlen(current_argv);
38862da59d7Smcr 
38962da59d7Smcr 		for (i = 0; long_options[i].name; i++) {
3906dfafca1Schristos 			/* find matching long option */
3916dfafca1Schristos 			if (strncmp(current_argv, long_options[i].name,
3926dfafca1Schristos 			    current_argv_len))
39362da59d7Smcr 				continue;
39462da59d7Smcr 
3956dfafca1Schristos 			if (strlen(long_options[i].name) ==
3966dfafca1Schristos 			    (unsigned)current_argv_len) {
3976dfafca1Schristos 				/* exact match */
39862da59d7Smcr 				match = i;
39915b661abSginsbach 				ambiguous = 0;
40062da59d7Smcr 				break;
40162da59d7Smcr 			}
4026dfafca1Schristos 			if (match == -1)		/* partial match */
40362da59d7Smcr 				match = i;
40415b661abSginsbach 			else if (!IDENTICAL_INTERPRETATION(i, match))
40515b661abSginsbach 				ambiguous = 1;
40615b661abSginsbach 		}
40715b661abSginsbach 		if (ambiguous) {
4086dfafca1Schristos 			/* ambiguous abbreviation */
4096dfafca1Schristos 			if (PRINT_ERROR)
410ac7a8f6bSsommerfeld 				warnx(ambig, (int)current_argv_len,
4116dfafca1Schristos 				     current_argv);
4126dfafca1Schristos 			optopt = 0;
4136dfafca1Schristos 			return BADCH;
41462da59d7Smcr 		}
4156dfafca1Schristos 		if (match != -1) {			/* option found */
4166dfafca1Schristos 		        if (long_options[match].has_arg == no_argument
4176dfafca1Schristos 			    && has_equal) {
4186dfafca1Schristos 				if (PRINT_ERROR)
419ac7a8f6bSsommerfeld 					warnx(noarg, (int)current_argv_len,
4206dfafca1Schristos 					     current_argv);
4216dfafca1Schristos 				/*
4226dfafca1Schristos 				 * XXX: GNU sets optopt to val regardless of
4236dfafca1Schristos 				 * flag
4246dfafca1Schristos 				 */
4256dfafca1Schristos 				if (long_options[match].flag == NULL)
4266dfafca1Schristos 					optopt = long_options[match].val;
4276dfafca1Schristos 				else
4286dfafca1Schristos 					optopt = 0;
4296dfafca1Schristos 				return BADARG;
4306dfafca1Schristos 			}
43162da59d7Smcr 			if (long_options[match].has_arg == required_argument ||
43262da59d7Smcr 			    long_options[match].has_arg == optional_argument) {
43362da59d7Smcr 				if (has_equal)
43462da59d7Smcr 					optarg = has_equal;
4356dfafca1Schristos 				else if (long_options[match].has_arg ==
4366dfafca1Schristos 				    required_argument) {
4376dfafca1Schristos 					/*
4386dfafca1Schristos 					 * optional argument doesn't use
4396dfafca1Schristos 					 * next nargv
4406dfafca1Schristos 					 */
44162da59d7Smcr 					optarg = nargv[optind++];
44262da59d7Smcr 				}
4436dfafca1Schristos 			}
44462da59d7Smcr 			if ((long_options[match].has_arg == required_argument)
44562da59d7Smcr 			    && (optarg == NULL)) {
44662da59d7Smcr 				/*
4476dfafca1Schristos 				 * Missing argument; leading ':'
44862da59d7Smcr 				 * indicates no error should be generated
44962da59d7Smcr 				 */
4506dfafca1Schristos 				if (PRINT_ERROR)
451045f0427Snathanw 					warnx(recargstring, current_argv);
4526dfafca1Schristos 				/*
4536dfafca1Schristos 				 * XXX: GNU sets optopt to val regardless
4546dfafca1Schristos 				 * of flag
4556dfafca1Schristos 				 */
4566dfafca1Schristos 				if (long_options[match].flag == NULL)
4576dfafca1Schristos 					optopt = long_options[match].val;
4586dfafca1Schristos 				else
4596dfafca1Schristos 					optopt = 0;
4606dfafca1Schristos 				--optind;
4616dfafca1Schristos 				return BADARG;
46262da59d7Smcr 			}
4636dfafca1Schristos 		} else {			/* unknown option */
4646dfafca1Schristos 			if (PRINT_ERROR)
465045f0427Snathanw 				warnx(illoptstring, current_argv);
4666dfafca1Schristos 			optopt = 0;
4676dfafca1Schristos 			return BADCH;
46862da59d7Smcr 		}
46962da59d7Smcr 		if (long_options[match].flag) {
47062da59d7Smcr 			*long_options[match].flag = long_options[match].val;
47162da59d7Smcr 			retval = 0;
47262da59d7Smcr 		} else
47362da59d7Smcr 			retval = long_options[match].val;
4746dfafca1Schristos 		if (idx)
4756dfafca1Schristos 			*idx = match;
47662da59d7Smcr 	}
4776dfafca1Schristos 	return retval;
47815b661abSginsbach #undef IDENTICAL_INTERPRETATION
47962da59d7Smcr }
480