You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

526 lines
16 KiB
C++

/*
getopt.c - implementation of getopt(3) and getopt_long(3)
Copyright Keristor Systems and Chris Croughton 1997
Internet: swdev@keristor.org
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define XGETOPT_NAMES
#include "XGetopt.h"
/*
* This is the published interface for the GNU getopt module
*/
char *optarg = NULL;
int optind = 0;
int opterr = 1;
int optopt = 0;
int getopt(int argc, char * const argv[], const char *optstring);
int getopt_long(int argc, char * const argv[], const char *optstring,
const struct option *longopts, int *longindex);
int getopt_long_only(int argc, char * const argv[], const char *optstring,
const struct option *longopts, int *longindex);
/************************************************************************
*
* Everything from here down is a rewrite without looking at the GNU
* implementation; it is therefore free of GPL and LGPL contraints, see
* above for licencing information.
*
************************************************************************/
#define true 1
#define false 0
#define bool char
enum action
{
E_PERMUTE,
E_POSIX,
E_INPLACE,
E_NUM_OF_ACTIONS
};
static bool initialised = false;
static bool endargs = false;
static char *nextchar = NULL;
/* Note that the lookup routines use and modify the global variables - not
* nice, but better than passing everything as reference parameters.
*/
static int
lookup_shortopt(int argc, char **argv, const char *opts)
{
int c = *optarg++;
/* remember the Alamo - a dodgy character if ever there was one */
nextchar = optarg;
/* look at the options - like any you see? */
while (*opts)
{
if (c == *opts++)
{
/* OK, it's a fair (c)op, guv */
if (*opts == ':')
{
/* we can do arguments, but there will be nothing left of this arg */
nextchar = NULL;
if (*++opts == ':')
{
/* but it's optional, so either a 5 minute argument or nothing */
if (!*optarg)
optarg = NULL;
return c;
}
else
{
/* this argument's not optional, you get the full half hour */
if (*optarg)
{
/* We've got an argument on our hands! */
return c;
}
else
{
/* try the next door down */
if (++optind < argc)
{
/* OK, we've got it ("Oh no you haven't!") */
optarg = argv[optind];
return c;
}
else
{
/* Th-Th-Th-That's All, Folks! */
optopt = c;
if (opterr)
fprintf(stderr, "required parameter for -%c missing\n", c);
/* we've got an option, but it's dud */
return '?';
}
}
}
}
else
{
/* no arguments please, we're British */
nextchar = optarg;
optarg = NULL;
return c;
}
}
}
/* return to sender, address unknown */
optopt = c;
optarg = NULL;
return -1;
}
static int
lookup_longopt(int argc, char **argv,
const struct option *longopts,
int *longindex)
{
char *eqp;
bool ambig = false;
int match = -1;
int exact = -1;
int min;
int len;
int i;
/* if we have no long options, any we get are wrong */
if (!longopts)
{
if (opterr)
fprintf(stderr, "long options not supported\n");
return '?';
}
/* look for equality or fraternity */
eqp = strchr(optarg, '=');
min = (eqp ? eqp - optarg : strlen(optarg));
for (i = 0; longopts[i].name; i++)
{
len = strlen(longopts[i].name);
/* is it exact? */
if (len == min && strncmp(longopts[i].name, optarg, len) == 0)
{
/* an exact match - go no further, we can't get better than this */
exact = i;
break;
}
/* a match as far as the argument goes? */
if (strncmp(longopts[i].name, optarg, min) == 0)
{
/* if we've already found one of those, it's ambiguous */
if (match < 0)
match = i;
else
ambig = true;
}
}
/* we've looked at everything, now what? */
if (exact < 0)
{
/* no exact match, if it's ambiguous then fail othersise use what
* (if anything) we have */
if (ambig)
{
if (opterr)
fprintf(stderr, "option --%s is ambiguous\n", optarg);
return '?';
}
else
exact = match;
}
/* if we don't have anything, return -1 (/not/ fail, this could be a test */
if (exact < 0)
return -1;
switch (longopts[exact].has_arg)
{
case no_argument:
/* we have nothing to fight about... */
if (eqp)
{
/* ... except equality */
if (opterr)
fprintf(stderr, "option --%s does not take an argument\n", optarg);
return '?';
}
optarg = NULL;
break;
case required_argument:
/* you have to fight, so there! */
if (eqp)
optarg = eqp + 1;
else if (++optind < argc)
optarg = argv[optind];
else
{
if (opterr)
fprintf(stderr, "missing argument for option --%s\n", optarg);
return '?';
}
break;
case optional_argument:
/* you don't have to fight if you don't want to */
optarg = (eqp ? eqp + 1 : NULL);
break;
}
/* tell the caller what he's got */
if (longindex)
*longindex = exact;
if (longopts[exact].flag)
{
/* set a flag rather than returning the value */
*longopts[exact].flag = longopts[exact].val;
return 0;
}
return longopts[exact].val;
}
/*
* Note that this internal function drops the const on the arg list. That's
* because optarg etc. are defined without const.
*/
static int
getnextopt(int argc, char **argv, const char *optstring,
const struct option *longopts, int *longindex,
bool longonly, enum action act)
{
static int thisind = 0;
static int oldind = 0;
int c = 0;
/* if we haven't started, make sure we're not in Denmark */
if (!initialised)
{
initialised = true;
nextchar = NULL;
endargs = false;
thisind = 0;
oldind = 0;
optind = 0;
}
/* check for previous sequence of short options */
if (nextchar && *nextchar)
{
optarg = nextchar;
c = lookup_shortopt(argc, argv, optstring);
/* now shuffle the arguments if necessary */
if (act == E_PERMUTE && oldind != thisind && (!nextchar || !*nextchar))
{
int optlen = optind - thisind; /* the length of the option */
int optnum = thisind - oldind; /* number of args in between */
memmove(argv+oldind+optlen+1, argv+oldind, sizeof(*argv) * optnum);
optind = oldind + optlen;
}
return c;
}
/* check for end of argument list */
if (++optind >= argc || !argv[optind])
{
optarg = NULL;
return EOF; /* no arguments left */
}
/* get next argument from list */
optarg = argv[optind];
/* if we've had "--" then treat as normal argument */
if (endargs)
return (act == E_INPLACE ? '\1' : EOF);
/* if we're permuting the arguments, look for the next option */
if (act == E_PERMUTE)
{
int i;
/* save where we are, we'll need this later */
oldind = optind;
for (i = optind; i < argc; i++)
{
char *p = argv[i];
/* remember that "--" ends options? Ignore anything past it... */
if (strcmp(p, "--") == 0)
{
int optnum = i - oldind; /* number of args in between */
if (optnum > 0)
{
/* shuffle the arguments up over it */
memmove(argv+oldind+1, argv+oldind, sizeof(*argv) * optnum);
optind = ++oldind;
optarg = argv[optind];
}
break;
}
if (*p == '-' && strcmp(p, "-"))
{
/* we found a honest-to-goodness option! Set up shop here... */
optind = i;
optarg = p;
break;
}
}
}
/* test for "--" meaning "end of options" - everything after that is an
* argument not an option */
if (strcmp(optarg, "--") == 0)
{
endargs = true;
if (++optind < argc)
{
optarg = argv[optind];
return (act == E_INPLACE ? '\1' : EOF);
}
else
{
optarg = NULL;
return EOF;
}
}
/* an argument of "-" or not starting with '-'is not an option */
if (strcmp(optarg, "-") == 0 || *optarg != '-')
{
/* if we're being POSIXLY_CORRECT (ugh!) then the first non-option ends
* the options. I don't like it but that's what the spec. says... */
if (act == E_POSIX)
endargs = true;
return (act == E_INPLACE ? '\1' : EOF);
}
thisind = optind; /* remember this position, lookup changes it */
/* OK, it's some sort of option, junk the first '-' */
optarg++;
/* prime for "option not found" */
c = -1;
/* test the next character - another '-' means it's a long option */
if (longopts && *optarg == '-')
{
/* it's a long option, skip the next '-' */
optarg++;
c = lookup_longopt(argc, argv, longopts, longindex);
if (c < 0)
{
fprintf(stderr, "unknown option --%s\n", optarg);
c = '?';
}
}
else if (longopts && longonly)
{
int tmperr = opterr;
opterr = 0;
/* try a long option first, if that doesn't work try a short one */
c = lookup_longopt(argc, argv, longopts, longindex);
opterr = tmperr;
if (c < 0)
c = lookup_shortopt(argc, argv, optstring);
}
else
{
/* just try short options */
c = lookup_shortopt(argc, argv, optstring);
}
/* if not found give (optional) error and return '?' */
if (c < 0)
{
if (opterr)
fprintf(stderr, "unknown option -%c\n", optopt);
optarg = 0;
c = '?';
}
/* now shuffle the arguments if necessary - I told you we'd need to save
* that old index! (I wonder if there's a more elegant way to do this?) */
if (act == E_PERMUTE && oldind != thisind && (!nextchar || !*nextchar))
{
int optlen = optind - thisind; /* the length of the option */
int optnum = thisind - oldind; /* number of args in between */
memmove(argv+oldind+optlen+1, argv+oldind, sizeof(*argv) * optnum);
optind = oldind + optlen;
oldind = 0;
thisind = 0;
}
return c;
}
/***********************************************************************
* At last, the real routines we wanted to call!
**********************************************************************/
/**
* Scan the command-line parameters for options in the form \b -x.
*/
int getopt(int argc, char * const argv[], const char *optstring)
{
return getnextopt(argc, (char**)argv, optstring, NULL, NULL,
false, E_PERMUTE);
}
/**
* Scan the command-line parameters for options, allowing both the short
* (single character) options and long (string) options.
*/
int getopt_long(int argc, char * const argv[], const char *optstring,
const struct option *longopts, int *longindex)
{
return getnextopt(argc, (char**)argv, optstring, longopts, longindex,
false, E_PERMUTE);
}
/**
* Scan the command-line parameters for long options only
*/
int getopt_long_only(int argc, char * const argv[], const char *optstring,
const struct option *longopts, int *longindex)
{
return getnextopt(argc, (char**)argv, optstring, longopts, longindex,
true, E_PERMUTE);
}
/***********************************************************************
* Just in case you wanted to test this stuff (or as an example of how
* not to use and abuse it), some code to do call it with arguments...
**********************************************************************/
#ifdef TEST_GETOPT
const char *shortopts = "abcr:o::";
char *Args[] =
{
"arg0",
"arg1",
"-abc",
"-rargr1",
"-r", "argr2",
"-oargo",
"-o",
"arg2",
"--",
"arg3",
"-arg4",
"arg5",
NULL
};
int heckle = 'x';
struct option longopts[] =
{
{ "help", no_argument, NULL, 'h' },
{ "heckle", no_argument, &heckle, 'k' },
{ "speck", no_argument, &heckle, 's' },
{ "arg", required_argument, NULL, 'g' },
{ "opt", optional_argument, NULL, 'z' },
{ 0, 0, 0, 0 }
};
int
main(int argc, char **argv)
{
int i;
int c;
if (argc <= 1)
{
argv = Args;
argc = (sizeof(Args) / sizeof(Args[0])) - 1;
}
else
shortopts="hksg:z::";
for (i = 0; i < argc; i++)
printf("%2d <%s>\n", i, argv[i]);
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF)
{
printf("option %d '%c' (%s)\n", optind, c, optarg);
#if DEBUG
for (i = 0; i < argc; i++)
printf("%2d <%s>\n", i, argv[i]);
#endif
}
for (i = optind; i < argc; i++)
printf("param %2d <%s>\n", i, argv[i]);
printf("flag = '%c'\n", heckle);
return 0;
}
#endif