• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*	$OpenBSD: getopt_long.c,v 1.22 2006/10/04 21:29:04 jmc 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  
53  #include <errno.h>
54  #include "getopt_long.h"
55  #include <stdlib.h>
56  #include <stdio.h>
57  #include <string.h>
58  #include <stdarg.h>
59  
60  #define GNU_COMPATIBLE		/* Be more compatible, configure's use us! */
61  
62  #define PRINT_ERROR	((opterr) && (*options != ':'))
63  
64  #define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
65  #define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
66  #define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
67  
68  /* return values */
69  #define	BADCH		(int)'?'
70  #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
71  #define	INORDER 	(int)1
72  
73  #define	EMSG		""
74  
75  #ifdef GNU_COMPATIBLE
76  #define NO_PREFIX	(-1)
77  #define D_PREFIX	0
78  #define DD_PREFIX	1
79  #define W_PREFIX	2
80  #endif
81  
82  char *optarg;
83  int optind, opterr = 1, optopt;
84  
85  static int getopt_internal(int, char * const *, const char *,
86  			   const struct option *, int *, int);
87  static int parse_long_options(char * const *, const char *,
88  			      const struct option *, int *, int, int);
89  static int gcd(int, int);
90  static void permute_args(int, int, int, char * const *);
91  
92  static const char *place = EMSG; /* option letter processing */
93  
94  static int nonopt_start = -1; /* first non option argument (for permute) */
95  static int nonopt_end = -1;   /* first option after non options (for permute) */
96  
97  /* Error messages */
98  static const char recargchar[] = "option requires an argument -- %c";
99  static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
100  #ifdef GNU_COMPATIBLE
101  static int dash_prefix = NO_PREFIX;
102  static const char gnuoptchar[] = "invalid option -- %c";
103  
104  static const char recargstring[] = "option `%s%s' requires an argument";
105  static const char ambig[] = "option `%s%.*s' is ambiguous";
106  static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
107  static const char illoptstring[] = "unrecognized option `%s%s'";
108  #else
109  static const char recargstring[] = "option requires an argument -- %s";
110  static const char ambig[] = "ambiguous option -- %.*s";
111  static const char noarg[] = "option doesn't take an argument -- %.*s";
112  static const char illoptstring[] = "unknown option -- %s";
113  #endif
114  
115  /*
116   * Compute the greatest common divisor of a and b.
117   */
118  static int
gcd(int a,int b)119  gcd(int a, int b)
120  {
121  	int c;
122  
123  	c = a % b;
124  	while (c != 0) {
125  		a = b;
126  		b = c;
127  		c = a % b;
128  	}
129  
130  	return (b);
131  }
132  
133  /*
134   * Exchange the block from nonopt_start to nonopt_end with the block
135   * from nonopt_end to opt_end (keeping the same order of arguments
136   * in each block).
137   */
138  static void
permute_args(int panonopt_start,int panonopt_end,int opt_end,char * const * nargv)139  permute_args(int panonopt_start, int panonopt_end, int opt_end,
140  	char * const *nargv)
141  {
142  	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
143  	char *swap;
144  
145  	/*
146  	 * compute lengths of blocks and number and size of cycles
147  	 */
148  	nnonopts = panonopt_end - panonopt_start;
149  	nopts = opt_end - panonopt_end;
150  	ncycle = gcd(nnonopts, nopts);
151  	cyclelen = (opt_end - panonopt_start) / ncycle;
152  
153  	for (i = 0; i < ncycle; i++) {
154  		cstart = panonopt_end+i;
155  		pos = cstart;
156  		for (j = 0; j < cyclelen; j++) {
157  			if (pos >= panonopt_end)
158  				pos -= nnonopts;
159  			else
160  				pos += nopts;
161  			swap = nargv[pos];
162  			/* LINTED const cast */
163  			((char **) nargv)[pos] = nargv[cstart];
164  			/* LINTED const cast */
165  			((char **)nargv)[cstart] = swap;
166  		}
167  	}
168  }
169  
170  static void
warnx(const char * fmt,...)171  warnx(const char *fmt, ...)
172  {
173  	extern char *program_name;
174  	va_list ap;
175  
176  	va_start(ap, fmt);
177  	fprintf(stderr, "%s: ", program_name);
178  	vfprintf(stderr, fmt, ap);
179  	fprintf(stderr, "\n");
180  	va_end(ap);
181  }
182  
183  /*
184   * parse_long_options --
185   *	Parse long options in argc/argv argument vector.
186   * Returns -1 if short_too is set and the option does not match long_options.
187   */
188  static int
parse_long_options(char * const * nargv,const char * options,const struct option * long_options,int * idx,int short_too,int flags)189  parse_long_options(char * const *nargv, const char *options,
190  	const struct option *long_options, int *idx, int short_too, int flags)
191  {
192  	const char *current_argv, *has_equal;
193  #ifdef GNU_COMPATIBLE
194  	const char *current_dash;
195  #endif
196  	size_t current_argv_len;
197  	int i, match, exact_match, second_partial_match;
198  
199  	current_argv = place;
200  #ifdef GNU_COMPATIBLE
201  	switch (dash_prefix) {
202  		case D_PREFIX:
203  			current_dash = "-";
204  			break;
205  		case DD_PREFIX:
206  			current_dash = "--";
207  			break;
208  		case W_PREFIX:
209  			current_dash = "-W ";
210  			break;
211  		default:
212  			current_dash = "";
213  			break;
214  	}
215  #endif
216  	match = -1;
217  	exact_match = 0;
218  	second_partial_match = 0;
219  
220  	optind++;
221  
222  	if ((has_equal = strchr(current_argv, '=')) != NULL) {
223  		/* argument found (--option=arg) */
224  		current_argv_len = has_equal - current_argv;
225  		has_equal++;
226  	} else
227  		current_argv_len = strlen(current_argv);
228  
229  	for (i = 0; long_options[i].name; i++) {
230  		/* find matching long option */
231  		if (strncmp(current_argv, long_options[i].name,
232  		    current_argv_len))
233  			continue;
234  
235  		if (strlen(long_options[i].name) == current_argv_len) {
236  			/* exact match */
237  			match = i;
238  			exact_match = 1;
239  			break;
240  		}
241  		/*
242  		 * If this is a known short option, don't allow
243  		 * a partial match of a single character.
244  		 */
245  		if (short_too && current_argv_len == 1)
246  			continue;
247  
248  		if (match == -1)        /* first partial match */
249  			match = i;
250  		else if ((flags & FLAG_LONGONLY) ||
251  			 long_options[i].has_arg !=
252  			     long_options[match].has_arg ||
253  			 long_options[i].flag != long_options[match].flag ||
254  			 long_options[i].val != long_options[match].val)
255  			second_partial_match = 1;
256  	}
257  	if (!exact_match && second_partial_match) {
258  		/* ambiguous abbreviation */
259  		if (PRINT_ERROR)
260  			warnx(ambig,
261  #ifdef GNU_COMPATIBLE
262  			     current_dash,
263  #endif
264  			     (int)current_argv_len,
265  			     current_argv);
266  		optopt = 0;
267  		return (BADCH);
268  	}
269  	if (match != -1) {		/* option found */
270  		if (long_options[match].has_arg == no_argument
271  		    && has_equal) {
272  			if (PRINT_ERROR)
273  				warnx(noarg,
274  #ifdef GNU_COMPATIBLE
275  				     current_dash,
276  #endif
277  				     (int)current_argv_len,
278  				     current_argv);
279  			/*
280  			 * XXX: GNU sets optopt to val regardless of flag
281  			 */
282  			if (long_options[match].flag == NULL)
283  				optopt = long_options[match].val;
284  			else
285  				optopt = 0;
286  #ifdef GNU_COMPATIBLE
287  			return (BADCH);
288  #else
289  			return (BADARG);
290  #endif
291  		}
292  		if (long_options[match].has_arg == required_argument ||
293  		    long_options[match].has_arg == optional_argument) {
294  			if (has_equal)
295  				optarg = (char *)has_equal;
296  			else if (long_options[match].has_arg ==
297  			    required_argument) {
298  				/*
299  				 * optional argument doesn't use next nargv
300  				 */
301  				optarg = nargv[optind++];
302  			}
303  		}
304  		if ((long_options[match].has_arg == required_argument)
305  		    && (optarg == NULL)) {
306  			/*
307  			 * Missing argument; leading ':' indicates no error
308  			 * should be generated.
309  			 */
310  			if (PRINT_ERROR)
311  				warnx(recargstring,
312  #ifdef GNU_COMPATIBLE
313  				    current_dash,
314  #endif
315  				    current_argv);
316  			/*
317  			 * XXX: GNU sets optopt to val regardless of flag
318  			 */
319  			if (long_options[match].flag == NULL)
320  				optopt = long_options[match].val;
321  			else
322  				optopt = 0;
323  			--optind;
324  			return (BADARG);
325  		}
326  	} else {			/* unknown option */
327  		if (short_too) {
328  			--optind;
329  			return (-1);
330  		}
331  		if (PRINT_ERROR)
332  			warnx(illoptstring,
333  #ifdef GNU_COMPATIBLE
334  			      current_dash,
335  #endif
336  			      current_argv);
337  		optopt = 0;
338  		return (BADCH);
339  	}
340  	if (idx)
341  		*idx = match;
342  	if (long_options[match].flag) {
343  		*long_options[match].flag = long_options[match].val;
344  		return (0);
345  	} else
346  		return (long_options[match].val);
347  }
348  
349  /*
350   * getopt_internal --
351   *	Parse argc/argv argument vector.  Called by user level routines.
352   */
353  static int
getopt_internal(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx,int flags)354  getopt_internal(int nargc, char * const *nargv, const char *options,
355  	const struct option *long_options, int *idx, int flags)
356  {
357  	char *oli;				/* option letter list index */
358  	int optchar, short_too;
359  	int posixly_correct;	/* no static, can be changed on the fly */
360  
361  	if (options == NULL)
362  		return (-1);
363  
364  	/*
365  	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
366  	 * string begins with a '+'.
367  	 */
368  	posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
369  #ifdef GNU_COMPATIBLE
370  	if (*options == '-')
371  		flags |= FLAG_ALLARGS;
372  	else if (posixly_correct || *options == '+')
373  		flags &= ~FLAG_PERMUTE;
374  #else
375  	if (posixly_correct || *options == '+')
376  		flags &= ~FLAG_PERMUTE;
377  	else if (*options == '-')
378  		flags |= FLAG_ALLARGS;
379  #endif
380  	if (*options == '+' || *options == '-')
381  		options++;
382  
383  	/*
384  	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
385  	 * XXX using optreset.  Work around this braindamage.
386  	 */
387  	if (optind == 0)
388  		optind = 1;
389  
390  	optarg = NULL;
391  start:
392  	if (!*place) {				/* update scanning pointer */
393  		if (optind >= nargc) {          /* end of argument vector */
394  			place = EMSG;
395  			if (nonopt_end != -1) {
396  				/* do permutation, if we have to */
397  				permute_args(nonopt_start, nonopt_end,
398  				    optind, nargv);
399  				optind -= nonopt_end - nonopt_start;
400  			}
401  			else if (nonopt_start != -1) {
402  				/*
403  				 * If we skipped non-options, set optind
404  				 * to the first of them.
405  				 */
406  				optind = nonopt_start;
407  			}
408  			nonopt_start = nonopt_end = -1;
409  			return (-1);
410  		}
411  		if (*(place = nargv[optind]) != '-' ||
412  #ifdef GNU_COMPATIBLE
413  		    place[1] == '\0') {
414  #else
415  		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
416  #endif
417  			place = EMSG;		/* found non-option */
418  			if (flags & FLAG_ALLARGS) {
419  				/*
420  				 * GNU extension:
421  				 * return non-option as argument to option 1
422  				 */
423  				optarg = nargv[optind++];
424  				return (INORDER);
425  			}
426  			if (!(flags & FLAG_PERMUTE)) {
427  				/*
428  				 * If no permutation wanted, stop parsing
429  				 * at first non-option.
430  				 */
431  				return (-1);
432  			}
433  			/* do permutation */
434  			if (nonopt_start == -1)
435  				nonopt_start = optind;
436  			else if (nonopt_end != -1) {
437  				permute_args(nonopt_start, nonopt_end,
438  				    optind, nargv);
439  				nonopt_start = optind -
440  				    (nonopt_end - nonopt_start);
441  				nonopt_end = -1;
442  			}
443  			optind++;
444  			/* process next argument */
445  			goto start;
446  		}
447  		if (nonopt_start != -1 && nonopt_end == -1)
448  			nonopt_end = optind;
449  
450  		/*
451  		 * If we have "-" do nothing, if "--" we are done.
452  		 */
453  		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
454  			optind++;
455  			place = EMSG;
456  			/*
457  			 * We found an option (--), so if we skipped
458  			 * non-options, we have to permute.
459  			 */
460  			if (nonopt_end != -1) {
461  				permute_args(nonopt_start, nonopt_end,
462  				    optind, nargv);
463  				optind -= nonopt_end - nonopt_start;
464  			}
465  			nonopt_start = nonopt_end = -1;
466  			return (-1);
467  		}
468  	}
469  
470  	/*
471  	 * Check long options if:
472  	 *  1) we were passed some
473  	 *  2) the arg is not just "-"
474  	 *  3) either the arg starts with -- we are getopt_long_only()
475  	 */
476  	if (long_options != NULL && place != nargv[optind] &&
477  	    (*place == '-' || (flags & FLAG_LONGONLY))) {
478  		short_too = 0;
479  #ifdef GNU_COMPATIBLE
480  		dash_prefix = D_PREFIX;
481  #endif
482  		if (*place == '-') {
483  			place++;		/* --foo long option */
484  #ifdef GNU_COMPATIBLE
485  			dash_prefix = DD_PREFIX;
486  #endif
487  		} else if (*place != ':' && strchr(options, *place) != NULL)
488  			short_too = 1;		/* could be short option too */
489  
490  		optchar = parse_long_options(nargv, options, long_options,
491  		    idx, short_too, flags);
492  		if (optchar != -1) {
493  			place = EMSG;
494  			return (optchar);
495  		}
496  	}
497  
498  	if ((optchar = (int)*place++) == (int)':' ||
499  	    (optchar == (int)'-' && *place != '\0') ||
500  	    (oli = strchr(options, optchar)) == NULL) {
501  		/*
502  		 * If the user specified "-" and  '-' isn't listed in
503  		 * options, return -1 (non-option) as per POSIX.
504  		 * Otherwise, it is an unknown option character (or ':').
505  		 */
506  		if (optchar == (int)'-' && *place == '\0')
507  			return (-1);
508  		if (!*place)
509  			++optind;
510  #ifdef GNU_COMPATIBLE
511  		if (PRINT_ERROR)
512  			warnx(posixly_correct ? illoptchar : gnuoptchar,
513  			      optchar);
514  #else
515  		if (PRINT_ERROR)
516  			warnx(illoptchar, optchar);
517  #endif
518  		optopt = optchar;
519  		return (BADCH);
520  	}
521  	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
522  		/* -W long-option */
523  		if (*place)			/* no space */
524  			/* NOTHING */;
525  		else if (++optind >= nargc) {	/* no arg */
526  			place = EMSG;
527  			if (PRINT_ERROR)
528  				warnx(recargchar, optchar);
529  			optopt = optchar;
530  			return (BADARG);
531  		} else				/* white space */
532  			place = nargv[optind];
533  #ifdef GNU_COMPATIBLE
534  		dash_prefix = W_PREFIX;
535  #endif
536  		optchar = parse_long_options(nargv, options, long_options,
537  		    idx, 0, flags);
538  		place = EMSG;
539  		return (optchar);
540  	}
541  	if (*++oli != ':') {			/* doesn't take argument */
542  		if (!*place)
543  			++optind;
544  	} else {				/* takes (optional) argument */
545  		optarg = NULL;
546  		if (*place)			/* no white space */
547  			optarg = (char *)place;
548  		else if (oli[1] != ':') {	/* arg not optional */
549  			if (++optind >= nargc) {	/* no arg */
550  				place = EMSG;
551  				if (PRINT_ERROR)
552  					warnx(recargchar, optchar);
553  				optopt = optchar;
554  				return (BADARG);
555  			} else
556  				optarg = nargv[optind];
557  		}
558  		place = EMSG;
559  		++optind;
560  	}
561  	/* dump back option letter */
562  	return (optchar);
563  }
564  
565  #ifdef REPLACE_GETOPT
566  /*
567   * getopt --
568   *	Parse argc/argv argument vector.
569   *
570   * [eventually this will replace the BSD getopt]
571   */
572  int
573  getopt(int nargc, char * const *nargv, const char *options)
574  {
575  
576  	/*
577  	 * We don't pass FLAG_PERMUTE to getopt_internal() since
578  	 * the BSD getopt(3) (unlike GNU) has never done this.
579  	 *
580  	 * Furthermore, since many privileged programs call getopt()
581  	 * before dropping privileges it makes sense to keep things
582  	 * as simple (and bug-free) as possible.
583  	 */
584  	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
585  }
586  #endif /* REPLACE_GETOPT */
587  
588  /*
589   * getopt_long --
590   *	Parse argc/argv argument vector.
591   */
592  int
593  getopt_long(int nargc, char * const *nargv, const char *options,
594  	const struct option *long_options, int *idx)
595  {
596  
597  	return (getopt_internal(nargc, nargv, options, long_options, idx,
598  	    FLAG_PERMUTE));
599  }
600  
601  /*
602   * getopt_long_only --
603   *	Parse argc/argv argument vector.
604   */
605  int
606  getopt_long_only(int nargc, char * const *nargv, const char *options,
607  	const struct option *long_options, int *idx)
608  {
609  
610  	return (getopt_internal(nargc, nargv, options, long_options, idx,
611  	    FLAG_PERMUTE|FLAG_LONGONLY));
612  }
613