1/*	$OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $	*/
2/*	$NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $	*/
3
4/*
5 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 * Sponsored in part by the Defense Advanced Research Projects
20 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22 */
23/*-
24 * Copyright (c) 2000 The NetBSD Foundation, Inc.
25 * All rights reserved.
26 *
27 * This code is derived from software contributed to The NetBSD Foundation
28 * by Dieter Baron and Thomas Klausner.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 *    notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 *    notice, this list of conditions and the following disclaimer in the
37 *    documentation and/or other materials provided with the distribution.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
40 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
43 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
44 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
45 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
46 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49 * POSSIBILITY OF SUCH DAMAGE.
50 */
51
52#include <err.h>
53#include <errno.h>
54#include <getopt.h>
55#include <stdlib.h>
56#include <string.h>
57
58#define GNU_COMPATIBLE		/* Be more compatible, configure's use us! */
59
60#if 0				/* we prefer to keep our getopt(3) */
61#define	REPLACE_GETOPT		/* use this getopt as the system getopt(3) */
62#endif
63
64#ifdef REPLACE_GETOPT
65int	opterr = 1;		/* if error message should be printed */
66int	optind = 1;		/* index into parent argv vector */
67int	optopt = '?';		/* character checked for validity */
68int	optreset;		/* reset getopt */
69char    *optarg;		/* argument associated with option */
70#endif
71
72#define PRINT_ERROR	((opterr) && (*options != ':'))
73
74#define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
75#define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
76#define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
77
78/* return values */
79#define	BADCH		(int)'?'
80#define	BADARG		((*options == ':') ? (int)':' : (int)'?')
81#define	INORDER 	(int)1
82
83static char EMSG[] = "";
84
85#ifdef GNU_COMPATIBLE
86#define NO_PREFIX	(-1)
87#define D_PREFIX	0
88#define DD_PREFIX	1
89#define W_PREFIX	2
90#endif
91
92static int getopt_internal(int, char * const *, const char *,
93			   const struct option *, int *, int);
94static int parse_long_options(char * const *, const char *,
95			      const struct option *, int *, int, int);
96static int gcd(int, int);
97static void permute_args(int, int, int, char * const *);
98
99static char *place = EMSG; /* option letter processing */
100
101/* XXX: set optreset to 1 rather than these two */
102static int nonopt_start = -1; /* first non option argument (for permute) */
103static int nonopt_end = -1;   /* first option after non options (for permute) */
104
105/* Error messages */
106static const char recargchar[] = "option requires an argument -- %c";
107static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
108#ifdef GNU_COMPATIBLE
109static int dash_prefix = NO_PREFIX;
110static const char gnuoptchar[] = "invalid option -- %c";
111
112static const char recargstring[] = "option `%s%s' requires an argument";
113static const char ambig[] = "option `%s%.*s' is ambiguous";
114static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
115static const char illoptstring[] = "unrecognized option `%s%s'";
116#else
117static const char recargstring[] = "option requires an argument -- %s";
118static const char ambig[] = "ambiguous option -- %.*s";
119static const char noarg[] = "option doesn't take an argument -- %.*s";
120static const char illoptstring[] = "unknown option -- %s";
121#endif
122
123/*
124 * Compute the greatest common divisor of a and b.
125 */
126static int
127gcd(int a, int b)
128{
129	int c;
130
131	c = a % b;
132	while (c != 0) {
133		a = b;
134		b = c;
135		c = a % b;
136	}
137
138	return (b);
139}
140
141/*
142 * Exchange the block from nonopt_start to nonopt_end with the block
143 * from nonopt_end to opt_end (keeping the same order of arguments
144 * in each block).
145 */
146static void
147permute_args(int panonopt_start, int panonopt_end, int opt_end,
148	char * const *nargv)
149{
150	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
151	char *swap;
152
153	/*
154	 * compute lengths of blocks and number and size of cycles
155	 */
156	nnonopts = panonopt_end - panonopt_start;
157	nopts = opt_end - panonopt_end;
158	ncycle = gcd(nnonopts, nopts);
159	cyclelen = (opt_end - panonopt_start) / ncycle;
160
161	for (i = 0; i < ncycle; i++) {
162		cstart = panonopt_end+i;
163		pos = cstart;
164		for (j = 0; j < cyclelen; j++) {
165			if (pos >= panonopt_end)
166				pos -= nnonopts;
167			else
168				pos += nopts;
169			swap = nargv[pos];
170			/* LINTED const cast */
171			((char **) nargv)[pos] = nargv[cstart];
172			/* LINTED const cast */
173			((char **)nargv)[cstart] = swap;
174		}
175	}
176}
177
178/*
179 * parse_long_options --
180 *	Parse long options in argc/argv argument vector.
181 * Returns -1 if short_too is set and the option does not match long_options.
182 */
183static int
184parse_long_options(char * const *nargv, const char *options,
185	const struct option *long_options, int *idx, int short_too, int flags)
186{
187	char *current_argv, *has_equal;
188#ifdef GNU_COMPATIBLE
189	const char *current_dash;
190#endif
191	size_t current_argv_len;
192	int i, match, exact_match, second_partial_match;
193
194	current_argv = place;
195#ifdef GNU_COMPATIBLE
196	switch (dash_prefix) {
197		case D_PREFIX:
198			current_dash = "-";
199			break;
200		case DD_PREFIX:
201			current_dash = "--";
202			break;
203		case W_PREFIX:
204			current_dash = "-W ";
205			break;
206		default:
207			current_dash = "";
208			break;
209	}
210#endif
211	match = -1;
212	exact_match = 0;
213	second_partial_match = 0;
214
215	optind++;
216
217	if ((has_equal = strchr(current_argv, '=')) != NULL) {
218		/* argument found (--option=arg) */
219		current_argv_len = has_equal - current_argv;
220		has_equal++;
221	} else
222		current_argv_len = strlen(current_argv);
223
224	for (i = 0; long_options[i].name; i++) {
225		/* find matching long option */
226		if (strncmp(current_argv, long_options[i].name,
227		    current_argv_len))
228			continue;
229
230		if (strlen(long_options[i].name) == current_argv_len) {
231			/* exact match */
232			match = i;
233			exact_match = 1;
234			break;
235		}
236		/*
237		 * If this is a known short option, don't allow
238		 * a partial match of a single character.
239		 */
240		if (short_too && current_argv_len == 1)
241			continue;
242
243		if (match == -1)	/* first partial match */
244			match = i;
245		else if ((flags & FLAG_LONGONLY) ||
246			 long_options[i].has_arg !=
247			     long_options[match].has_arg ||
248			 long_options[i].flag != long_options[match].flag ||
249			 long_options[i].val != long_options[match].val)
250			second_partial_match = 1;
251	}
252	if (!exact_match && second_partial_match) {
253		/* ambiguous abbreviation */
254		if (PRINT_ERROR)
255			warnx(ambig,
256#ifdef GNU_COMPATIBLE
257			     current_dash,
258#endif
259			     (int)current_argv_len,
260			     current_argv);
261		optopt = 0;
262		return (BADCH);
263	}
264	if (match != -1) {		/* option found */
265		if (long_options[match].has_arg == no_argument
266		    && has_equal) {
267			if (PRINT_ERROR)
268				warnx(noarg,
269#ifdef GNU_COMPATIBLE
270				     current_dash,
271#endif
272				     (int)current_argv_len,
273				     current_argv);
274			/*
275			 * XXX: GNU sets optopt to val regardless of flag
276			 */
277			if (long_options[match].flag == NULL)
278				optopt = long_options[match].val;
279			else
280				optopt = 0;
281#ifdef GNU_COMPATIBLE
282			return (BADCH);
283#else
284			return (BADARG);
285#endif
286		}
287		if (long_options[match].has_arg == required_argument ||
288		    long_options[match].has_arg == optional_argument) {
289			if (has_equal)
290				optarg = has_equal;
291			else if (long_options[match].has_arg ==
292			    required_argument) {
293				/*
294				 * optional argument doesn't use next nargv
295				 */
296				optarg = nargv[optind++];
297			}
298		}
299		if ((long_options[match].has_arg == required_argument)
300		    && (optarg == NULL)) {
301			/*
302			 * Missing argument; leading ':' indicates no error
303			 * should be generated.
304			 */
305			if (PRINT_ERROR)
306				warnx(recargstring,
307#ifdef GNU_COMPATIBLE
308				    current_dash,
309#endif
310				    current_argv);
311			/*
312			 * XXX: GNU sets optopt to val regardless of flag
313			 */
314			if (long_options[match].flag == NULL)
315				optopt = long_options[match].val;
316			else
317				optopt = 0;
318			--optind;
319			return (BADARG);
320		}
321	} else {			/* unknown option */
322		if (short_too) {
323			--optind;
324			return (-1);
325		}
326		if (PRINT_ERROR)
327			warnx(illoptstring,
328#ifdef GNU_COMPATIBLE
329			      current_dash,
330#endif
331			      current_argv);
332		optopt = 0;
333		return (BADCH);
334	}
335	if (idx)
336		*idx = match;
337	if (long_options[match].flag) {
338		*long_options[match].flag = long_options[match].val;
339		return (0);
340	} else
341		return (long_options[match].val);
342}
343
344/*
345 * getopt_internal --
346 *	Parse argc/argv argument vector.  Called by user level routines.
347 */
348static int
349getopt_internal(int nargc, char * const *nargv, const char *options,
350	const struct option *long_options, int *idx, int flags)
351{
352	char *oli;				/* option letter list index */
353	int optchar, short_too;
354	static int posixly_correct = -1;
355
356	if (options == NULL)
357		return (-1);
358
359	/*
360	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
361	 * XXX using optreset.  Work around this braindamage.
362	 */
363	if (optind == 0)
364		optind = optreset = 1;
365
366	/*
367	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
368	 * string begins with a '+'.
369	 */
370	if (posixly_correct == -1 || optreset)
371		posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
372	if (*options == '-')
373		flags |= FLAG_ALLARGS;
374	else if (posixly_correct || *options == '+')
375		flags &= ~FLAG_PERMUTE;
376	if (*options == '+' || *options == '-')
377		options++;
378
379	optarg = NULL;
380	if (optreset)
381		nonopt_start = nonopt_end = -1;
382start:
383	if (optreset || !*place) {		/* update scanning pointer */
384		optreset = 0;
385		if (optind >= nargc) {          /* end of argument vector */
386			place = EMSG;
387			if (nonopt_end != -1) {
388				/* do permutation, if we have to */
389				permute_args(nonopt_start, nonopt_end,
390				    optind, nargv);
391				optind -= nonopt_end - nonopt_start;
392			}
393			else if (nonopt_start != -1) {
394				/*
395				 * If we skipped non-options, set optind
396				 * to the first of them.
397				 */
398				optind = nonopt_start;
399			}
400			nonopt_start = nonopt_end = -1;
401			return (-1);
402		}
403		if (*(place = nargv[optind]) != '-' ||
404#ifdef GNU_COMPATIBLE
405		    place[1] == '\0') {
406#else
407		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
408#endif
409			place = EMSG;		/* found non-option */
410			if (flags & FLAG_ALLARGS) {
411				/*
412				 * GNU extension:
413				 * return non-option as argument to option 1
414				 */
415				optarg = nargv[optind++];
416				return (INORDER);
417			}
418			if (!(flags & FLAG_PERMUTE)) {
419				/*
420				 * If no permutation wanted, stop parsing
421				 * at first non-option.
422				 */
423				return (-1);
424			}
425			/* do permutation */
426			if (nonopt_start == -1)
427				nonopt_start = optind;
428			else if (nonopt_end != -1) {
429				permute_args(nonopt_start, nonopt_end,
430				    optind, nargv);
431				nonopt_start = optind -
432				    (nonopt_end - nonopt_start);
433				nonopt_end = -1;
434			}
435			optind++;
436			/* process next argument */
437			goto start;
438		}
439		if (nonopt_start != -1 && nonopt_end == -1)
440			nonopt_end = optind;
441
442		/*
443		 * If we have "-" do nothing, if "--" we are done.
444		 */
445		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
446			optind++;
447			place = EMSG;
448			/*
449			 * We found an option (--), so if we skipped
450			 * non-options, we have to permute.
451			 */
452			if (nonopt_end != -1) {
453				permute_args(nonopt_start, nonopt_end,
454				    optind, nargv);
455				optind -= nonopt_end - nonopt_start;
456			}
457			nonopt_start = nonopt_end = -1;
458			return (-1);
459		}
460	}
461
462	/*
463	 * Check long options if:
464	 *  1) we were passed some
465	 *  2) the arg is not just "-"
466	 *  3) either the arg starts with -- we are getopt_long_only()
467	 */
468	if (long_options != NULL && place != nargv[optind] &&
469	    (*place == '-' || (flags & FLAG_LONGONLY))) {
470		short_too = 0;
471#ifdef GNU_COMPATIBLE
472		dash_prefix = D_PREFIX;
473#endif
474		if (*place == '-') {
475			place++;		/* --foo long option */
476			if (*place == '\0')
477				return (BADARG);	/* malformed option */
478#ifdef GNU_COMPATIBLE
479			dash_prefix = DD_PREFIX;
480#endif
481		} else if (*place != ':' && strchr(options, *place) != NULL)
482			short_too = 1;		/* could be short option too */
483
484		optchar = parse_long_options(nargv, options, long_options,
485		    idx, short_too, flags);
486		if (optchar != -1) {
487			place = EMSG;
488			return (optchar);
489		}
490	}
491
492	if ((optchar = (int)*place++) == (int)':' ||
493	    (optchar == (int)'-' && *place != '\0') ||
494	    (oli = strchr(options, optchar)) == NULL) {
495		/*
496		 * If the user specified "-" and  '-' isn't listed in
497		 * options, return -1 (non-option) as per POSIX.
498		 * Otherwise, it is an unknown option character (or ':').
499		 */
500		if (optchar == (int)'-' && *place == '\0')
501			return (-1);
502		if (!*place)
503			++optind;
504#ifdef GNU_COMPATIBLE
505		if (PRINT_ERROR)
506			warnx(posixly_correct ? illoptchar : gnuoptchar,
507			      optchar);
508#else
509		if (PRINT_ERROR)
510			warnx(illoptchar, optchar);
511#endif
512		optopt = optchar;
513		return (BADCH);
514	}
515	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
516		/* -W long-option */
517		if (*place)			/* no space */
518			/* NOTHING */;
519		else if (++optind >= nargc) {	/* no arg */
520			place = EMSG;
521			if (PRINT_ERROR)
522				warnx(recargchar, optchar);
523			optopt = optchar;
524			return (BADARG);
525		} else				/* white space */
526			place = nargv[optind];
527#ifdef GNU_COMPATIBLE
528		dash_prefix = W_PREFIX;
529#endif
530		optchar = parse_long_options(nargv, options, long_options,
531		    idx, 0, flags);
532		place = EMSG;
533		return (optchar);
534	}
535	if (*++oli != ':') {			/* doesn't take argument */
536		if (!*place)
537			++optind;
538	} else {				/* takes (optional) argument */
539		optarg = NULL;
540		if (*place)			/* no white space */
541			optarg = place;
542		else if (oli[1] != ':') {	/* arg not optional */
543			if (++optind >= nargc) {	/* no arg */
544				place = EMSG;
545				if (PRINT_ERROR)
546					warnx(recargchar, optchar);
547				optopt = optchar;
548				return (BADARG);
549			} else
550				optarg = nargv[optind];
551		}
552		place = EMSG;
553		++optind;
554	}
555	/* dump back option letter */
556	return (optchar);
557}
558
559#ifdef REPLACE_GETOPT
560/*
561 * getopt --
562 *	Parse argc/argv argument vector.
563 *
564 * [eventually this will replace the BSD getopt]
565 */
566int
567getopt(int nargc, char * const *nargv, const char *options)
568{
569
570	/*
571	 * We don't pass FLAG_PERMUTE to getopt_internal() since
572	 * the BSD getopt(3) (unlike GNU) has never done this.
573	 *
574	 * Furthermore, since many privileged programs call getopt()
575	 * before dropping privileges it makes sense to keep things
576	 * as simple (and bug-free) as possible.
577	 */
578	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
579}
580#endif /* REPLACE_GETOPT */
581
582/*
583 * getopt_long --
584 *	Parse argc/argv argument vector.
585 */
586int
587getopt_long(int nargc, char * const *nargv, const char *options,
588	const struct option *long_options, int *idx)
589{
590
591	return (getopt_internal(nargc, nargv, options, long_options, idx,
592	    FLAG_PERMUTE));
593}
594
595/*
596 * getopt_long_only --
597 *	Parse argc/argv argument vector.
598 */
599int
600getopt_long_only(int nargc, char * const *nargv, const char *options,
601	const struct option *long_options, int *idx)
602{
603
604	return (getopt_internal(nargc, nargv, options, long_options, idx,
605	    FLAG_PERMUTE|FLAG_LONGONLY));
606}
607