1/*	$NetBSD: getopt_long.c,v 1.1.1.1 2021/04/07 02:43:15 christos Exp $	*/
2
3/*
4 * Copyright (c) 1987, 1993, 1994, 1996
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the names of the copyright holders nor the names of its
16 *    contributors may be used to endorse or promote products derived from
17 *    this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31#include <assert.h>
32#include <errno.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include "getopt.h"
37
38extern int	  opterr;	/* if error message should be printed */
39extern int	  optind;	/* index into parent argv vector */
40extern int	  optopt;	/* character checked for validity */
41extern int	  optreset;	/* reset getopt */
42extern char *optarg;	/* argument associated with option */
43
44#define __P(x) x
45#define _DIAGASSERT(x) assert(x)
46
47static char * __progname __P((char *));
48int getopt_internal __P((int, char * const *, const char *));
49
50static char *
51__progname(nargv0)
52	char * nargv0;
53{
54	char * tmp;
55
56	_DIAGASSERT(nargv0 != NULL);
57
58	tmp = strrchr(nargv0, '/');
59	if (tmp)
60		tmp++;
61	else
62		tmp = nargv0;
63	return(tmp);
64}
65
66#define	BADCH	(int)'?'
67#define	BADARG	(int)':'
68#define	EMSG	""
69
70/*
71 * getopt --
72 *	Parse argc/argv argument vector.
73 */
74int
75getopt_internal(nargc, nargv, ostr)
76	int nargc;
77	char * const *nargv;
78	const char *ostr;
79{
80	static char *place = EMSG;		/* option letter processing */
81	char *oli;				/* option letter list index */
82
83	_DIAGASSERT(nargv != NULL);
84	_DIAGASSERT(ostr != NULL);
85
86	if (optreset || !*place) {		/* update scanning pointer */
87		optreset = 0;
88		if (optind >= nargc || *(place = nargv[optind]) != '-') {
89			place = EMSG;
90			return (-1);
91		}
92		if (place[1] && *++place == '-') {	/* found "--" */
93			/* ++optind; */
94			place = EMSG;
95			return (-2);
96		}
97	}					/* option letter okay? */
98	if ((optopt = (int)*place++) == (int)':' ||
99	    !(oli = strchr(ostr, optopt))) {
100		/*
101		 * if the user didn't specify '-' as an option,
102		 * assume it means -1.
103		 */
104		if (optopt == (int)'-')
105			return (-1);
106		if (!*place)
107			++optind;
108		if (opterr && *ostr != ':')
109			(void)fprintf(stderr,
110			    "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
111		return (BADCH);
112	}
113	if (*++oli != ':') {			/* don't need argument */
114		optarg = NULL;
115		if (!*place)
116			++optind;
117	} else {				/* need an argument */
118		if (*place)			/* no white space */
119			optarg = place;
120		else if (nargc <= ++optind) {	/* no arg */
121			place = EMSG;
122			if ((opterr) && (*ostr != ':'))
123				(void)fprintf(stderr,
124				    "%s: option requires an argument -- %c\n",
125				    __progname(nargv[0]), optopt);
126			return (BADARG);
127		} else				/* white space */
128			optarg = nargv[optind];
129		place = EMSG;
130		++optind;
131	}
132	return (optopt);			/* dump back option letter */
133}
134
135#if 0
136/*
137 * getopt --
138 *	Parse argc/argv argument vector.
139 */
140int
141getopt2(nargc, nargv, ostr)
142	int nargc;
143	char * const *nargv;
144	const char *ostr;
145{
146	int retval;
147
148	if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
149		retval = -1;
150		++optind;
151	}
152	return(retval);
153}
154#endif
155
156/*
157 * getopt_long --
158 *	Parse argc/argv argument vector.
159 */
160int
161getopt_long(nargc, nargv, options, long_options, index)
162	int nargc;
163	char ** nargv;
164	const char * options;
165	const struct option * long_options;
166	int * index;
167{
168	int retval;
169
170	_DIAGASSERT(nargv != NULL);
171	_DIAGASSERT(options != NULL);
172	_DIAGASSERT(long_options != NULL);
173	/* index may be NULL */
174
175	if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
176		char *current_argv = nargv[optind++] + 2, *has_equal;
177		int i, match = -1;
178		size_t current_argv_len;
179
180		if (*current_argv == '\0') {
181			return(-1);
182		}
183		if ((has_equal = strchr(current_argv, '=')) != NULL) {
184			current_argv_len = has_equal - current_argv;
185			has_equal++;
186		} else
187			current_argv_len = strlen(current_argv);
188
189		for (i = 0; long_options[i].name; i++) {
190			if (strncmp(current_argv, long_options[i].name, current_argv_len))
191				continue;
192
193			if (strlen(long_options[i].name) == current_argv_len) {
194				match = i;
195				break;
196			}
197			if (match == -1)
198				match = i;
199		}
200		if (match != -1) {
201			if (long_options[match].has_arg == required_argument ||
202			    long_options[match].has_arg == optional_argument) {
203				if (has_equal)
204					optarg = has_equal;
205				else
206					optarg = nargv[optind++];
207			}
208			if ((long_options[match].has_arg == required_argument)
209			    && (optarg == NULL)) {
210				/*
211				 * Missing argument, leading :
212				 * indicates no error should be generated
213				 */
214				if ((opterr) && (*options != ':'))
215					(void)fprintf(stderr,
216				      "%s: option requires an argument -- %s\n",
217				      __progname(nargv[0]), current_argv);
218				return (BADARG);
219			}
220		} else { /* No matching argument */
221			if ((opterr) && (*options != ':'))
222				(void)fprintf(stderr,
223				    "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
224			return (BADCH);
225		}
226		if (long_options[match].flag) {
227			*long_options[match].flag = long_options[match].val;
228			retval = 0;
229		} else
230			retval = long_options[match].val;
231		if (index)
232			*index = match;
233	}
234	return(retval);
235}
236