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