/* $Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */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 $ */34/*5* Copyright (c) 2002 Todd C. Miller <[email protected]>6*7* Permission to use, copy, modify, and distribute this software for any8* purpose with or without fee is hereby granted, provided that the above9* copyright notice and this permission notice appear in all copies.10*11* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES12* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF13* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR14* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES15* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN16* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF17* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.18*19* Sponsored in part by the Defense Advanced Research Projects20* Agency (DARPA) and Air Force Research Laboratory, Air Force21* Materiel Command, USAF, under agreement number F39502-99-1-0512.22*/2324#ifndef lint25static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $";26#endif /* lint */27/*-28* Copyright (c) 2000 The NetBSD Foundation, Inc.29* All rights reserved.30*31* This code is derived from software contributed to The NetBSD Foundation32* by Dieter Baron and Thomas Klausner.33*34* Redistribution and use in source and binary forms, with or without35* modification, are permitted provided that the following conditions36* are met:37* 1. Redistributions of source code must retain the above copyright38* notice, this list of conditions and the following disclaimer.39* 2. Redistributions in binary form must reproduce the above copyright40* notice, this list of conditions and the following disclaimer in the41* documentation and/or other materials provided with the distribution.42*43* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS44* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED45* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR46* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS47* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR48* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF49* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS50* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN51* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)52* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE53* POSSIBILITY OF SUCH DAMAGE.54*/5556#if 057#include <err.h>58#endif59#include <errno.h>60#include <getopt.h>61#include <stdlib.h>62#include <string.h>6364#ifdef _WIN326566/* Windows needs warnx(). We change the definition though:67* 1. (another) global is defined, opterrmsg, which holds the error message68* 2. errors are always printed out on stderr w/o the program name69* Note that opterrmsg always gets set no matter what opterr is set to. The70* error message will not be printed if opterr is 0 as usual.71*/7273#include <stdio.h>74#include <stdarg.h>75#include <stdarg.h>7677char opterrmsg[128]; /* last error message is stored here */7879static void warnx(const char *fmt, ...)80{81va_list ap;82va_start(ap, fmt);83if (fmt != NULL)84_vsnprintf(opterrmsg, 128, fmt, ap);85else86opterrmsg[0]='\0';87va_end(ap);88fprintf(stderr, opterrmsg);89fprintf(stderr, "\n");90}9192#endif /*_WIN32*/9394#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */9596#ifdef REPLACE_GETOPT97int opterr = 1; /* if error message should be printed */98int optind = 1; /* index into parent argv vector */99int optopt = '?'; /* character checked for validity */100int optreset; /* reset getopt */101char *optarg; /* argument associated with option */102#endif103104#define PRINT_ERROR ((opterr) && (*options != ':'))105106#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */107#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */108#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */109110/* return values */111#define BADCH (int)'?'112#define BADARG ((*options == ':') ? (int)':' : (int)'?')113#define INORDER (int)1114115#define EMSG ""116117static int getopt_internal(int, char * const *, const char *,118const struct option *, int *, int);119static int parse_long_options(char * const *, const char *,120const struct option *, int *, int);121static int gcd(int, int);122static void permute_args(int, int, int, char * const *);123124static char *place = EMSG; /* option letter processing */125126/* XXX: set optreset to 1 rather than these two */127static int nonopt_start = -1; /* first non option argument (for permute) */128static int nonopt_end = -1; /* first option after non options (for permute) */129130/* Error messages */131static const char recargchar[] = "option requires an argument -- %c";132static const char recargstring[] = "option requires an argument -- %s";133static const char ambig[] = "ambiguous option -- %.*s";134static const char noarg[] = "option doesn't take an argument -- %.*s";135static const char illoptchar[] = "unknown option -- %c";136static const char illoptstring[] = "unknown option -- %s";137138/*139* Compute the greatest common divisor of a and b.140*/141static int142gcd(int a, int b)143{144int c;145146c = a % b;147while (c != 0) {148a = b;149b = c;150c = a % b;151}152153return (b);154}155156/*157* Exchange the block from nonopt_start to nonopt_end with the block158* from nonopt_end to opt_end (keeping the same order of arguments159* in each block).160*/161static void162permute_args(int panonopt_start, int panonopt_end, int opt_end,163char * const *nargv)164{165int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;166char *swap;167168/*169* compute lengths of blocks and number and size of cycles170*/171nnonopts = panonopt_end - panonopt_start;172nopts = opt_end - panonopt_end;173ncycle = gcd(nnonopts, nopts);174cyclelen = (opt_end - panonopt_start) / ncycle;175176for (i = 0; i < ncycle; i++) {177cstart = panonopt_end+i;178pos = cstart;179for (j = 0; j < cyclelen; j++) {180if (pos >= panonopt_end)181pos -= nnonopts;182else183pos += nopts;184swap = nargv[pos];185/* LINTED const cast */186((char **) nargv)[pos] = nargv[cstart];187/* LINTED const cast */188((char **)nargv)[cstart] = swap;189}190}191}192193/*194* parse_long_options --195* Parse long options in argc/argv argument vector.196* Returns -1 if short_too is set and the option does not match long_options.197*/198static int199parse_long_options(char * const *nargv, const char *options,200const struct option *long_options, int *idx, int short_too)201{202char *current_argv, *has_equal;203size_t current_argv_len;204int i, match;205206current_argv = place;207match = -1;208209optind++;210211if ((has_equal = strchr(current_argv, '=')) != NULL) {212/* argument found (--option=arg) */213current_argv_len = has_equal - current_argv;214has_equal++;215} else216current_argv_len = strlen(current_argv);217218for (i = 0; long_options[i].name; i++) {219/* find matching long option */220if (strncmp(current_argv, long_options[i].name,221current_argv_len))222continue;223224if (strlen(long_options[i].name) == current_argv_len) {225/* exact match */226match = i;227break;228}229/*230* If this is a known short option, don't allow231* a partial match of a single character.232*/233if (short_too && current_argv_len == 1)234continue;235236if (match == -1) /* partial match */237match = i;238else {239/* ambiguous abbreviation */240if (PRINT_ERROR)241warnx(ambig, (int)current_argv_len,242current_argv);243optopt = 0;244return (BADCH);245}246}247if (match != -1) { /* option found */248if (long_options[match].has_arg == no_argument249&& has_equal) {250if (PRINT_ERROR)251warnx(noarg, (int)current_argv_len,252current_argv);253/*254* XXX: GNU sets optopt to val regardless of flag255*/256if (long_options[match].flag == NULL)257optopt = long_options[match].val;258else259optopt = 0;260return (BADARG);261}262if (long_options[match].has_arg == required_argument ||263long_options[match].has_arg == optional_argument) {264if (has_equal)265optarg = has_equal;266else if (long_options[match].has_arg ==267required_argument) {268/*269* optional argument doesn't use next nargv270*/271optarg = nargv[optind++];272}273}274if ((long_options[match].has_arg == required_argument)275&& (optarg == NULL)) {276/*277* Missing argument; leading ':' indicates no error278* should be generated.279*/280if (PRINT_ERROR)281warnx(recargstring,282current_argv);283/*284* XXX: GNU sets optopt to val regardless of flag285*/286if (long_options[match].flag == NULL)287optopt = long_options[match].val;288else289optopt = 0;290--optind;291return (BADARG);292}293} else { /* unknown option */294if (short_too) {295--optind;296return (-1);297}298if (PRINT_ERROR)299warnx(illoptstring, current_argv);300optopt = 0;301return (BADCH);302}303if (idx)304*idx = match;305if (long_options[match].flag) {306*long_options[match].flag = long_options[match].val;307return (0);308} else309return (long_options[match].val);310}311312/*313* getopt_internal --314* Parse argc/argv argument vector. Called by user level routines.315*/316static int317getopt_internal(int nargc, char * const *nargv, const char *options,318const struct option *long_options, int *idx, int flags)319{320char *oli; /* option letter list index */321int optchar, short_too;322static int posixly_correct = -1;323324if (options == NULL)325return (-1);326327/*328* Disable GNU extensions if POSIXLY_CORRECT is set or options329* string begins with a '+'.330*/331if (posixly_correct == -1)332posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);333if (posixly_correct || *options == '+')334flags &= ~FLAG_PERMUTE;335else if (*options == '-')336flags |= FLAG_ALLARGS;337if (*options == '+' || *options == '-')338options++;339340/*341* XXX Some GNU programs (like cvs) set optind to 0 instead of342* XXX using optreset. Work around this braindamage.343*/344if (optind == 0)345optind = optreset = 1;346347optarg = NULL;348if (optreset)349nonopt_start = nonopt_end = -1;350start:351if (optreset || !*place) { /* update scanning pointer */352optreset = 0;353if (optind >= nargc) { /* end of argument vector */354place = EMSG;355if (nonopt_end != -1) {356/* do permutation, if we have to */357permute_args(nonopt_start, nonopt_end,358optind, nargv);359optind -= nonopt_end - nonopt_start;360}361else if (nonopt_start != -1) {362/*363* If we skipped non-options, set optind364* to the first of them.365*/366optind = nonopt_start;367}368nonopt_start = nonopt_end = -1;369return (-1);370}371if (*(place = nargv[optind]) != '-' ||372(place[1] == '\0' && strchr(options, '-') == NULL)) {373place = EMSG; /* found non-option */374if (flags & FLAG_ALLARGS) {375/*376* GNU extension:377* return non-option as argument to option 1378*/379optarg = nargv[optind++];380return (INORDER);381}382if (!(flags & FLAG_PERMUTE)) {383/*384* If no permutation wanted, stop parsing385* at first non-option.386*/387return (-1);388}389/* do permutation */390if (nonopt_start == -1)391nonopt_start = optind;392else if (nonopt_end != -1) {393permute_args(nonopt_start, nonopt_end,394optind, nargv);395nonopt_start = optind -396(nonopt_end - nonopt_start);397nonopt_end = -1;398}399optind++;400/* process next argument */401goto start;402}403if (nonopt_start != -1 && nonopt_end == -1)404nonopt_end = optind;405406/*407* If we have "-" do nothing, if "--" we are done.408*/409if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {410optind++;411place = EMSG;412/*413* We found an option (--), so if we skipped414* non-options, we have to permute.415*/416if (nonopt_end != -1) {417permute_args(nonopt_start, nonopt_end,418optind, nargv);419optind -= nonopt_end - nonopt_start;420}421nonopt_start = nonopt_end = -1;422return (-1);423}424}425426/*427* Check long options if:428* 1) we were passed some429* 2) the arg is not just "-"430* 3) either the arg starts with -- we are getopt_long_only()431*/432if (long_options != NULL && place != nargv[optind] &&433(*place == '-' || (flags & FLAG_LONGONLY))) {434short_too = 0;435if (*place == '-')436place++; /* --foo long option */437else if (*place != ':' && strchr(options, *place) != NULL)438short_too = 1; /* could be short option too */439440optchar = parse_long_options(nargv, options, long_options,441idx, short_too);442if (optchar != -1) {443place = EMSG;444return (optchar);445}446}447448if ((optchar = (int)*place++) == (int)':' ||449(optchar == (int)'-' && *place != '\0') ||450(oli = strchr(options, optchar)) == NULL) {451/*452* If the user specified "-" and '-' isn't listed in453* options, return -1 (non-option) as per POSIX.454* Otherwise, it is an unknown option character (or ':').455*/456if (optchar == (int)'-' && *place == '\0')457return (-1);458if (!*place)459++optind;460if (PRINT_ERROR)461warnx(illoptchar, optchar);462optopt = optchar;463return (BADCH);464}465if (long_options != NULL && optchar == 'W' && oli[1] == ';') {466/* -W long-option */467if (*place) /* no space */468/* NOTHING */;469else if (++optind >= nargc) { /* no arg */470place = EMSG;471if (PRINT_ERROR)472warnx(recargchar, optchar);473optopt = optchar;474return (BADARG);475} else /* white space */476place = nargv[optind];477optchar = parse_long_options(nargv, options, long_options,478idx, 0);479place = EMSG;480return (optchar);481}482if (*++oli != ':') { /* doesn't take argument */483if (!*place)484++optind;485} else { /* takes (optional) argument */486optarg = NULL;487if (*place) /* no white space */488optarg = place;489else if (oli[1] != ':') { /* arg not optional */490if (++optind >= nargc) { /* no arg */491place = EMSG;492if (PRINT_ERROR)493warnx(recargchar, optchar);494optopt = optchar;495return (BADARG);496} else497optarg = nargv[optind];498}499place = EMSG;500++optind;501}502/* dump back option letter */503return (optchar);504}505506#ifdef REPLACE_GETOPT507/*508* getopt --509* Parse argc/argv argument vector.510*511* [eventually this will replace the BSD getopt]512*/513int514getopt(int nargc, char * const *nargv, const char *options)515{516517/*518* We don't pass FLAG_PERMUTE to getopt_internal() since519* the BSD getopt(3) (unlike GNU) has never done this.520*521* Furthermore, since many privileged programs call getopt()522* before dropping privileges it makes sense to keep things523* as simple (and bug-free) as possible.524*/525return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));526}527#endif /* REPLACE_GETOPT */528529/*530* getopt_long --531* Parse argc/argv argument vector.532*/533int534getopt_long(int nargc, char * const *nargv, const char *options,535const struct option *long_options, int *idx)536{537538return (getopt_internal(nargc, nargv, options, long_options, idx,539FLAG_PERMUTE));540}541542/*543* getopt_long_only --544* Parse argc/argv argument vector.545*/546int547getopt_long_only(int nargc, char * const *nargv, const char *options,548const struct option *long_options, int *idx)549{550551return (getopt_internal(nargc, nargv, options, long_options, idx,552FLAG_PERMUTE|FLAG_LONGONLY));553}554555556