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