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