1 /*	$Id: getopt.c 15794 2009-03-18 09:10:28Z moritz $	*/
2 /*	$OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $	*/
3 /*	$NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $	*/
4 
5 /*
6  * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  *
20  * Sponsored in part by the Defense Advanced Research Projects
21  * Agency (DARPA) and Air Force Research Laboratory, Air Force
22  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
23  */
24 /*-
25  * Copyright (c) 2000 The NetBSD Foundation, Inc.
26  * All rights reserved.
27  *
28  * This code is derived from software contributed to The NetBSD Foundation
29  * by Dieter Baron and Thomas Klausner.
30  *
31  * Redistribution and use in source and binary forms, with or without
32  * modification, are permitted provided that the following conditions
33  * are met:
34  * 1. Redistributions of source code must retain the above copyright
35  *    notice, this list of conditions and the following disclaimer.
36  * 2. Redistributions in binary form must reproduce the above copyright
37  *    notice, this list of conditions and the following disclaimer in the
38  *    documentation and/or other materials provided with the distribution.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
41  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
45  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
46  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
50  * POSSIBILITY OF SUCH DAMAGE.
51  */
52 
53 #ifdef HAVE_CONFIG_H
54 # include "config.h"
55 #endif /* HAVE_CONFIG_H */
56 
57 #include "ezstream.h"
58 
59 int	local_getopt(int, char * const *, const char *);
60 
61 #ifndef HAVE_GETOPT
62 
63 int	 opterr = 1;		/* if error message should be printed */
64 int	 optind = 1;		/* index into parent argv vector */
65 int	 optopt = '?';		/* character checked for validity */
66 int	 optreset;		/* reset getopt */
67 char	*optarg;		/* argument associated with option */
68 
69 #define PRINT_ERROR	((opterr) && (*options != ':'))
70 
71 #define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
72 #define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
73 #define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
74 
75 /* return values */
76 #define BADCH		(int)'?'
77 #define BADARG		((*options == ':') ? (int)':' : (int)'?')
78 #define INORDER 	(int)1
79 
80 #define EMSG		""
81 
82 static int getopt_internal(int, char * const *, const char *, int);
83 static int gcd(int, int);
84 static void permute_args(int, int, int, char * const *);
85 
86 static char *place = EMSG; /* option letter processing */
87 
88 /* XXX: set optreset to 1 rather than these two */
89 static int nonopt_start = -1; /* first non option argument (for permute) */
90 static int nonopt_end = -1;   /* first option after non options (for permute) */
91 
92 /* Error messages */
93 static const char recargchar[] = "option requires an argument -- %c\n";
94 static const char illoptchar[] = "unknown option -- %c\n";
95 
96 /*
97  * Compute the greatest common divisor of a and b.
98  */
99 static int
gcd(int a,int b)100 gcd(int a, int b)
101 {
102 	int c;
103 
104 	c = a % b;
105 	while (c != 0) {
106 		a = b;
107 		b = c;
108 		c = a % b;
109 	}
110 
111 	return (b);
112 }
113 
114 /*
115  * Exchange the block from nonopt_start to nonopt_end with the block
116  * from nonopt_end to opt_end (keeping the same order of arguments
117  * in each block).
118  */
119 static void
permute_args(int panonopt_start,int panonopt_end,int opt_end,char * const * nargv)120 permute_args(int panonopt_start, int panonopt_end, int opt_end,
121 	char * const *nargv)
122 {
123 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
124 	char *swap;
125 
126 	/*
127 	 * compute lengths of blocks and number and size of cycles
128 	 */
129 	nnonopts = panonopt_end - panonopt_start;
130 	nopts = opt_end - panonopt_end;
131 	ncycle = gcd(nnonopts, nopts);
132 	cyclelen = (opt_end - panonopt_start) / ncycle;
133 
134 	for (i = 0; i < ncycle; i++) {
135 		cstart = panonopt_end+i;
136 		pos = cstart;
137 		for (j = 0; j < cyclelen; j++) {
138 			if (pos >= panonopt_end)
139 				pos -= nnonopts;
140 			else
141 				pos += nopts;
142 			swap = nargv[pos];
143 			/* LINTED const cast */
144 			((char **) nargv)[pos] = nargv[cstart];
145 			/* LINTED const cast */
146 			((char **)nargv)[cstart] = swap;
147 		}
148 	}
149 }
150 
151 /*
152  * getopt_internal --
153  *	Parse argc/argv argument vector.  Called by user level routines.
154  */
155 static int
getopt_internal(int nargc,char * const * nargv,const char * options,int flags)156 getopt_internal(int nargc, char * const *nargv, const char *options, int flags)
157 {
158 	char *oli;				/* option letter list index */
159 	int optchar;
160 	static int posixly_correct = -1;
161 
162 	if (options == NULL)
163 		return (-1);
164 
165 	/*
166 	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
167 	 * string begins with a '+'.
168 	 */
169 	if (posixly_correct == -1)
170 		posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
171 	if (posixly_correct || *options == '+')
172 		flags &= ~FLAG_PERMUTE;
173 	else if (*options == '-')
174 		flags |= FLAG_ALLARGS;
175 	if (*options == '+' || *options == '-')
176 		options++;
177 
178 	/*
179 	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
180 	 * XXX using optreset.  Work around this braindamage.
181 	 */
182 	if (optind == 0)
183 		optind = optreset = 1;
184 
185 	optarg = NULL;
186 	if (optreset)
187 		nonopt_start = nonopt_end = -1;
188 start:
189 	if (optreset || !*place) {		/* update scanning pointer */
190 		optreset = 0;
191 		if (optind >= nargc) {          /* end of argument vector */
192 			place = EMSG;
193 			if (nonopt_end != -1) {
194 				/* do permutation, if we have to */
195 				permute_args(nonopt_start, nonopt_end,
196 				    optind, nargv);
197 				optind -= nonopt_end - nonopt_start;
198 			}
199 			else if (nonopt_start != -1) {
200 				/*
201 				 * If we skipped non-options, set optind
202 				 * to the first of them.
203 				 */
204 				optind = nonopt_start;
205 			}
206 			nonopt_start = nonopt_end = -1;
207 			return (-1);
208 		}
209 		if (*(place = nargv[optind]) != '-' ||
210 		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
211 			place = EMSG;		/* found non-option */
212 			if (flags & FLAG_ALLARGS) {
213 				/*
214 				 * GNU extension:
215 				 * return non-option as argument to option 1
216 				 */
217 				optarg = nargv[optind++];
218 				return (INORDER);
219 			}
220 			if (!(flags & FLAG_PERMUTE)) {
221 				/*
222 				 * If no permutation wanted, stop parsing
223 				 * at first non-option.
224 				 */
225 				return (-1);
226 			}
227 			/* do permutation */
228 			if (nonopt_start == -1)
229 				nonopt_start = optind;
230 			else if (nonopt_end != -1) {
231 				permute_args(nonopt_start, nonopt_end,
232 				    optind, nargv);
233 				nonopt_start = optind -
234 				    (nonopt_end - nonopt_start);
235 				nonopt_end = -1;
236 			}
237 			optind++;
238 			/* process next argument */
239 			goto start;
240 		}
241 		if (nonopt_start != -1 && nonopt_end == -1)
242 			nonopt_end = optind;
243 
244 		/*
245 		 * If we have "-" do nothing, if "--" we are done.
246 		 */
247 		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
248 			optind++;
249 			place = EMSG;
250 			/*
251 			 * We found an option (--), so if we skipped
252 			 * non-options, we have to permute.
253 			 */
254 			if (nonopt_end != -1) {
255 				permute_args(nonopt_start, nonopt_end,
256 				    optind, nargv);
257 				optind -= nonopt_end - nonopt_start;
258 			}
259 			nonopt_start = nonopt_end = -1;
260 			return (-1);
261 		}
262 	}
263 
264 	if ((optchar = (int)*place++) == (int)':' ||
265 	    (optchar == (int)'-' && *place != '\0') ||
266 	    (oli = strchr(options, optchar)) == NULL) {
267 		/*
268 		 * If the user specified "-" and  '-' isn't listed in
269 		 * options, return -1 (non-option) as per POSIX.
270 		 * Otherwise, it is an unknown option character (or ':').
271 		 */
272 		if (optchar == (int)'-' && *place == '\0')
273 			return (-1);
274 		if (!*place)
275 			++optind;
276 		if (PRINT_ERROR)
277 			fprintf(stderr, illoptchar, optchar);
278 		optopt = optchar;
279 		return (BADCH);
280 	}
281 	if (*++oli != ':') {			/* doesn't take argument */
282 		if (!*place)
283 			++optind;
284 	} else {				/* takes (optional) argument */
285 		optarg = NULL;
286 		if (*place)			/* no white space */
287 			optarg = place;
288 		else if (oli[1] != ':') {	/* arg not optional */
289 			if (++optind >= nargc) {	/* no arg */
290 				place = EMSG;
291 				if (PRINT_ERROR)
292 					fprintf(stderr, recargchar, optchar);
293 				optopt = optchar;
294 				return (BADARG);
295 			} else
296 				optarg = nargv[optind];
297 		}
298 		place = EMSG;
299 		++optind;
300 	}
301 	/* dump back option letter */
302 	return (optchar);
303 }
304 
305 #endif /* !HAVE_GETOPT */
306 
307 /*
308  * getopt --
309  *	Parse argc/argv argument vector.
310  *
311  * [eventually this will replace the BSD getopt]
312  */
313 int
local_getopt(int nargc,char * const * nargv,const char * options)314 local_getopt(int nargc, char * const *nargv, const char *options)
315 {
316 #ifdef HAVE_GETOPT
317 	return (getopt(nargc, nargv, options));
318 #else /* HAVE_GETOPT */
319 
320 	/*
321 	 * We don't pass FLAG_PERMUTE to getopt_internal() since
322 	 * the BSD getopt(3) (unlike GNU) has never done this.
323 	 *
324 	 * Furthermore, since many privileged programs call getopt()
325 	 * before dropping privileges it makes sense to keep things
326 	 * as simple (and bug-free) as possible.
327 	 */
328 	return (getopt_internal(nargc, nargv, options, 0));
329 #endif /* HAVE_GETOPT */
330 }
331