/ payloads / libpayload / libc / getopt_long.c
getopt_long.c
  1  /*	$OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl 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   * Copyright (c) 2008 coresystems GmbH
  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  /*
 54  #include <err.h>
 55  #include <errno.h>
 56  */
 57  #include <commonlib/bsd/gcd.h>
 58  #include <libpayload.h>
 59  #include <getopt.h>
 60  #define warnx(x...) printf(x)
 61  #include <stdlib.h>
 62  #include <string.h>
 63  #define	REPLACE_GETOPT		/* use this getopt as the system getopt(3) */
 64  
 65  #ifdef REPLACE_GETOPT
 66  int	opterr = 1;		/* if error message should be printed */
 67  int	optind = 1;		/* index into parent argv vector */
 68  int	optopt = '?';		/* character checked for validity */
 69  int	optreset;		/* reset getopt */
 70  char    *optarg;		/* argument associated with option */
 71  
 72  int posixly_correct = 0;
 73  #endif
 74  
 75  #define PRINT_ERROR	((opterr) && (*options != ':'))
 76  
 77  #define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
 78  #define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
 79  #define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
 80  
 81  /* return values */
 82  #define	BADCH		(int)'?'
 83  #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
 84  #define	INORDER 	(int)1
 85  
 86  #define	EMSG		(char*)""
 87  
 88  static int getopt_internal(int, char * const *, const char *,
 89  			   const struct option *, int *, int);
 90  static int parse_long_options(char * const *, const char *,
 91  			      const struct option *, int *, int);
 92  static void permute_args(int, int, int, char * const *);
 93  
 94  static char *place = EMSG; /* option letter processing */
 95  
 96  /* XXX: set optreset to 1 rather than these two */
 97  static int nonopt_start = -1; /* first non option argument (for permute) */
 98  static int nonopt_end = -1;   /* first option after non options (for permute) */
 99  
100  /* Error messages */
101  static const char recargchar[] = "option requires an argument -- %c";
102  static const char recargstring[] = "option requires an argument -- %s";
103  static const char ambig[] = "ambiguous option -- %.*s";
104  static const char noarg[] = "option doesn't take an argument -- %.*s";
105  static const char illoptchar[] = "unknown option -- %c";
106  static const char illoptstring[] = "unknown option -- %s";
107  
108  /*
109   * Exchange the block from nonopt_start to nonopt_end with the block
110   * from nonopt_end to opt_end (keeping the same order of arguments
111   * in each block).
112   */
113  static void
114  permute_args(int panonopt_start, int panonopt_end, int opt_end,
115  	char * const *nargv)
116  {
117  	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
118  	char *swap;
119  
120  	/*
121  	 * compute lengths of blocks and number and size of cycles
122  	 */
123  	nnonopts = panonopt_end - panonopt_start;
124  	nopts = opt_end - panonopt_end;
125  	ncycle = gcd(nnonopts, nopts);
126  	cyclelen = (opt_end - panonopt_start) / ncycle;
127  
128  	for (i = 0; i < ncycle; i++) {
129  		cstart = panonopt_end+i;
130  		pos = cstart;
131  		for (j = 0; j < cyclelen; j++) {
132  			if (pos >= panonopt_end)
133  				pos -= nnonopts;
134  			else
135  				pos += nopts;
136  			swap = nargv[pos];
137  			/* LINTED const cast */
138  			((char **) nargv)[pos] = nargv[cstart];
139  			/* LINTED const cast */
140  			((char **)nargv)[cstart] = swap;
141  		}
142  	}
143  }
144  
145  /*
146   * parse_long_options --
147   *	Parse long options in argc/argv argument vector.
148   * Returns -1 if short_too is set and the option does not match long_options.
149   */
150  static int
151  parse_long_options(char * const *nargv, const char *options,
152  	const struct option *long_options, int *idx, int short_too)
153  {
154  	char *current_argv, *has_equal;
155  	size_t current_argv_len;
156  	int i, match;
157  
158  	current_argv = place;
159  	match = -1;
160  
161  	optind++;
162  
163  	if ((has_equal = strchr(current_argv, '=')) != NULL) {
164  		/* argument found (--option=arg) */
165  		current_argv_len = has_equal - current_argv;
166  		has_equal++;
167  	} else
168  		current_argv_len = strlen(current_argv);
169  
170  	for (i = 0; long_options[i].name; i++) {
171  		/* find matching long option */
172  		if (strncmp(current_argv, long_options[i].name,
173  		    current_argv_len))
174  			continue;
175  
176  		if (strlen(long_options[i].name) == current_argv_len) {
177  			/* exact match */
178  			match = i;
179  			break;
180  		}
181  		/*
182  		 * If this is a known short option, don't allow
183  		 * a partial match of a single character.
184  		 */
185  		if (short_too && current_argv_len == 1)
186  			continue;
187  
188  		if (match == -1)	/* partial match */
189  			match = i;
190  		else {
191  			/* ambiguous abbreviation */
192  			if (PRINT_ERROR)
193  				warnx(ambig, (int)current_argv_len,
194  				     current_argv);
195  			optopt = 0;
196  			return (BADCH);
197  		}
198  	}
199  	if (match != -1) {		/* option found */
200  		if (long_options[match].has_arg == no_argument
201  		    && has_equal) {
202  			if (PRINT_ERROR)
203  				warnx(noarg, (int)current_argv_len,
204  				     current_argv);
205  			/*
206  			 * XXX: GNU sets optopt to val regardless of flag
207  			 */
208  			if (long_options[match].flag == NULL)
209  				optopt = long_options[match].val;
210  			else
211  				optopt = 0;
212  			return (BADARG);
213  		}
214  		if (long_options[match].has_arg == required_argument ||
215  		    long_options[match].has_arg == optional_argument) {
216  			if (has_equal)
217  				optarg = has_equal;
218  			else if (long_options[match].has_arg ==
219  			    required_argument) {
220  				/*
221  				 * optional argument doesn't use next nargv
222  				 */
223  				optarg = nargv[optind++];
224  			}
225  		}
226  		if ((long_options[match].has_arg == required_argument)
227  		    && (optarg == NULL)) {
228  			/*
229  			 * Missing argument; leading ':' indicates no error
230  			 * should be generated.
231  			 */
232  			if (PRINT_ERROR)
233  				warnx(recargstring,
234  				    current_argv);
235  			/*
236  			 * XXX: GNU sets optopt to val regardless of flag
237  			 */
238  			if (long_options[match].flag == NULL)
239  				optopt = long_options[match].val;
240  			else
241  				optopt = 0;
242  			--optind;
243  			return (BADARG);
244  		}
245  	} else {			/* unknown option */
246  		if (short_too) {
247  			--optind;
248  			return (-1);
249  		}
250  		if (PRINT_ERROR)
251  			warnx(illoptstring, current_argv);
252  		optopt = 0;
253  		return (BADCH);
254  	}
255  	if (idx)
256  		*idx = match;
257  	if (long_options[match].flag) {
258  		*long_options[match].flag = long_options[match].val;
259  		return (0);
260  	} else
261  		return (long_options[match].val);
262  }
263  
264  /*
265   * getopt_internal --
266   *	Parse argc/argv argument vector.  Called by user level routines.
267   */
268  static int
269  getopt_internal(int nargc, char * const *nargv, const char *options,
270  	const struct option *long_options, int *idx, int flags)
271  {
272  	char *oli;				/* option letter list index */
273  	int optchar, short_too;
274  
275  	if (options == NULL)
276  		return (-1);
277  
278  	/*
279  	 * Disable GNU extensions if posixly_correct is set or options
280  	 * string begins with a '+'.
281  	 */
282  	if (posixly_correct || *options == '+')
283  		flags &= ~FLAG_PERMUTE;
284  	else if (*options == '-')
285  		flags |= FLAG_ALLARGS;
286  	if (*options == '+' || *options == '-')
287  		options++;
288  
289  	/*
290  	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
291  	 * XXX using optreset.  Work around this braindamage.
292  	 */
293  	if (optind == 0)
294  		optind = optreset = 1;
295  
296  	optarg = NULL;
297  	if (optreset)
298  		nonopt_start = nonopt_end = -1;
299  start:
300  	if (optreset || !*place) {		/* update scanning pointer */
301  		optreset = 0;
302  		if (optind >= nargc) {          /* end of argument vector */
303  			place = EMSG;
304  			if (nonopt_end != -1) {
305  				/* do permutation, if we have to */
306  				permute_args(nonopt_start, nonopt_end,
307  				    optind, nargv);
308  				optind -= nonopt_end - nonopt_start;
309  			}
310  			else if (nonopt_start != -1) {
311  				/*
312  				 * If we skipped non-options, set optind
313  				 * to the first of them.
314  				 */
315  				optind = nonopt_start;
316  			}
317  			nonopt_start = nonopt_end = -1;
318  			return (-1);
319  		}
320  		if (*(place = nargv[optind]) != '-' ||
321  		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
322  			place = EMSG;		/* found non-option */
323  			if (flags & FLAG_ALLARGS) {
324  				/*
325  				 * GNU extension:
326  				 * return non-option as argument to option 1
327  				 */
328  				optarg = nargv[optind++];
329  				return (INORDER);
330  			}
331  			if (!(flags & FLAG_PERMUTE)) {
332  				/*
333  				 * If no permutation wanted, stop parsing
334  				 * at first non-option.
335  				 */
336  				return (-1);
337  			}
338  			/* do permutation */
339  			if (nonopt_start == -1)
340  				nonopt_start = optind;
341  			else if (nonopt_end != -1) {
342  				permute_args(nonopt_start, nonopt_end,
343  				    optind, nargv);
344  				nonopt_start = optind -
345  				    (nonopt_end - nonopt_start);
346  				nonopt_end = -1;
347  			}
348  			optind++;
349  			/* process next argument */
350  			goto start;
351  		}
352  		if (nonopt_start != -1 && nonopt_end == -1)
353  			nonopt_end = optind;
354  
355  		/*
356  		 * If we have "-" do nothing, if "--" we are done.
357  		 */
358  		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
359  			optind++;
360  			place = EMSG;
361  			/*
362  			 * We found an option (--), so if we skipped
363  			 * non-options, we have to permute.
364  			 */
365  			if (nonopt_end != -1) {
366  				permute_args(nonopt_start, nonopt_end,
367  				    optind, nargv);
368  				optind -= nonopt_end - nonopt_start;
369  			}
370  			nonopt_start = nonopt_end = -1;
371  			return (-1);
372  		}
373  	}
374  
375  	/*
376  	 * Check long options if:
377  	 *  1) we were passed some
378  	 *  2) the arg is not just "-"
379  	 *  3) either the arg starts with -- we are getopt_long_only()
380  	 */
381  	if (long_options != NULL && place != nargv[optind] &&
382  	    (*place == '-' || (flags & FLAG_LONGONLY))) {
383  		short_too = 0;
384  		if (*place == '-')
385  			place++;		/* --foo long option */
386  		else if (*place != ':' && strchr(options, *place) != NULL)
387  			short_too = 1;		/* could be short option too */
388  
389  		optchar = parse_long_options(nargv, options, long_options,
390  		    idx, short_too);
391  		if (optchar != -1) {
392  			place = EMSG;
393  			return (optchar);
394  		}
395  	}
396  
397  	if ((optchar = (int)*place++) == (int)':' ||
398  	    (optchar == (int)'-' && *place != '\0') ||
399  	    (oli = strchr(options, optchar)) == NULL) {
400  		/*
401  		 * If the user specified "-" and  '-' isn't listed in
402  		 * options, return -1 (non-option) as per POSIX.
403  		 * Otherwise, it is an unknown option character (or ':').
404  		 */
405  		if (optchar == (int)'-' && *place == '\0')
406  			return (-1);
407  		if (!*place)
408  			++optind;
409  		if (PRINT_ERROR)
410  			warnx(illoptchar, optchar);
411  		optopt = optchar;
412  		return (BADCH);
413  	}
414  	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
415  		/* -W long-option */
416  		if (*place)			/* no space */
417  			/* NOTHING */;
418  		else if (++optind >= nargc) {	/* no arg */
419  			place = EMSG;
420  			if (PRINT_ERROR)
421  				warnx(recargchar, optchar);
422  			optopt = optchar;
423  			return (BADARG);
424  		} else				/* white space */
425  			place = nargv[optind];
426  		optchar = parse_long_options(nargv, options, long_options,
427  		    idx, 0);
428  		place = EMSG;
429  		return (optchar);
430  	}
431  	if (*++oli != ':') {			/* doesn't take argument */
432  		if (!*place)
433  			++optind;
434  	} else {				/* takes (optional) argument */
435  		optarg = NULL;
436  		if (*place)			/* no white space */
437  			optarg = place;
438  		else if (oli[1] != ':') {	/* arg not optional */
439  			if (++optind >= nargc) {	/* no arg */
440  				place = EMSG;
441  				if (PRINT_ERROR)
442  					warnx(recargchar, optchar);
443  				optopt = optchar;
444  				return (BADARG);
445  			} else
446  				optarg = nargv[optind];
447  		}
448  		place = EMSG;
449  		++optind;
450  	}
451  	/* dump back option letter */
452  	return (optchar);
453  }
454  
455  #ifdef REPLACE_GETOPT
456  /*
457   * getopt --
458   *	Parse argc/argv argument vector.
459   *
460   * [eventually this will replace the BSD getopt]
461   */
462  int
463  getopt(int nargc, char * const *nargv, const char *options)
464  {
465  
466  	/*
467  	 * We don't pass FLAG_PERMUTE to getopt_internal() since
468  	 * the BSD getopt(3) (unlike GNU) has never done this.
469  	 *
470  	 * Furthermore, since many privileged programs call getopt()
471  	 * before dropping privileges it makes sense to keep things
472  	 * as simple (and bug-free) as possible.
473  	 */
474  	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
475  }
476  #endif /* REPLACE_GETOPT */
477  
478  /*
479   * getopt_long --
480   *	Parse argc/argv argument vector.
481   */
482  int
483  getopt_long(int nargc, char * const *nargv, const char *options,
484      const struct option *long_options, int *idx)
485  {
486  
487  	return (getopt_internal(nargc, nargv, options, long_options, idx,
488  	    FLAG_PERMUTE));
489  }
490  
491  /*
492   * getopt_long_only --
493   *	Parse argc/argv argument vector.
494   */
495  int
496  getopt_long_only(int nargc, char * const *nargv, const char *options,
497      const struct option *long_options, int *idx)
498  {
499  
500  	return (getopt_internal(nargc, nargv, options, long_options, idx,
501  	    FLAG_PERMUTE|FLAG_LONGONLY));
502  }