blob: 6d31225c8b350a208f8aed70f947efb5a655f9a3 [file] [log] [blame]
/* $Id: shhopt.c,v 2.2 1997/07/06 23:11:55 aebr Exp $ */
/**************************************************************************
*
* FILE shhopt.c
*
* DESCRIPTION Functions for parsing command line arguments. Values
* of miscellaneous types may be stored in variables,
* or passed to functions as specified.
*
* REQUIREMENTS Some systems lack the ANSI C -function strtoul. If your
* system is one of those, you'll ned to write one yourself,
* or get the GNU liberty-library (from prep.ai.mit.edu).
*
* WRITTEN BY Sverre H. Huseby <sverrehu@ifi.uio.no>
*
**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include "shhopt.h"
/**************************************************************************
* *
* P R I V A T E D A T A *
* *
**************************************************************************/
static void optFatalFunc(const char *, ...);
static void (*optFatal)(const char *format, ...) = optFatalFunc;
/**************************************************************************
* *
* P R I V A T E F U N C T I O N S *
* *
**************************************************************************/
/*-------------------------------------------------------------------------
*
* NAME optFatalFunc
*
* FUNCTION Show given message and abort the program.
*
* INPUT format, ...
* Arguments used as with printf().
*
* RETURNS Never returns. The program is aborted.
*
*/
void optFatalFunc(const char *format, ...)
{
va_list ap;
fflush(stdout);
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
exit(99);
}
/*-------------------------------------------------------------------------
*
* NAME optStructCount
*
* FUNCTION Get number of options in a optStruct.
*
* INPUT opt array of possible options.
*
* RETURNS Number of options in the given array.
*
* DESCRIPTION Count elements in an optStruct-array. The strcture must
* be ended using an element of type OPT_END.
*
*/
static int optStructCount(const optStruct opt[])
{
int ret = 0;
while (opt[ret].type != OPT_END)
++ret;
return ret;
}
/*-------------------------------------------------------------------------
*
* NAME optMatch
*
* FUNCTION Find a matching option.
*
* INPUT opt array of possible options.
* s string to match, without `-' or `--'.
* lng match long option, otherwise short.
*
* RETURNS Index to the option if found, -1 if not found.
*
* DESCRIPTION Short options are matched from the first character in
* the given string.
*
*/
static int optMatch(const optStruct opt[], const char *s, int lng)
{
int nopt, q, matchlen = 0;
char *p;
nopt = optStructCount(opt);
if (lng) {
if ((p = strchr(s, '=')) != NULL)
matchlen = p - s;
else
matchlen = strlen(s);
}
for (q = 0; q < nopt; q++) {
if (lng) {
if (!opt[q].longName)
continue;
if (strncmp(s, opt[q].longName, matchlen) == 0)
return q;
} else {
if (!opt[q].shortName)
continue;
if (*s == opt[q].shortName)
return q;
}
}
return -1;
}
/*-------------------------------------------------------------------------
*
* NAME optString
*
* FUNCTION Return a (static) string with the option name.
*
* INPUT opt the option to stringify.
* lng is it a long option?
*
* RETURNS Pointer to static string.
*
*/
static char *optString(const optStruct *opt, int lng)
{
static char ret[31];
if (lng) {
strcpy(ret, "--");
strncpy(ret + 2, opt->longName, 28);
} else {
ret[0] = '-';
ret[1] = opt->shortName;
ret[2] = '\0';
}
return ret;
}
/*-------------------------------------------------------------------------
*
* NAME optNeedsArgument
*
* FUNCTION Check if an option requires an argument.
*
* INPUT opt the option to check.
*
* RETURNS Boolean value.
*
*/
static int optNeedsArgument(const optStruct *opt)
{
return opt->type == OPT_STRING
|| opt->type == OPT_INT
|| opt->type == OPT_UINT
|| opt->type == OPT_LONG
|| opt->type == OPT_ULONG;
}
/*-------------------------------------------------------------------------
*
* NAME argvRemove
*
* FUNCTION Remove an entry from an argv-array.
*
* INPUT argc pointer to number of options.
* argv array of option-/argument-strings.
* i index of option to remove.
*
* OUTPUT argc new argument count.
* argv array with given argument removed.
*
*/
static void argvRemove(int *argc, char *argv[], int i)
{
if (i >= *argc)
return;
while (i++ < *argc)
argv[i - 1] = argv[i];
--*argc;
}
/*-------------------------------------------------------------------------
*
* NAME optExecute
*
* FUNCTION Perform the action of an option.
*
* INPUT opt array of possible options.
* arg argument to option, if it applies.
* lng was the option given as a long option?
*
* RETURNS Nothing. Aborts in case of error.
*
*/
void optExecute(const optStruct *opt, char *arg, int lng)
{
switch (opt->type) {
case OPT_FLAG:
if (opt->flags & OPT_CALLFUNC)
((void (*)(void)) opt->arg)();
else
*((int *) opt->arg) = 1;
break;
case OPT_STRING:
if (opt->flags & OPT_CALLFUNC)
((void (*)(char *)) opt->arg)(arg);
else
*((char **) opt->arg) = arg;
break;
case OPT_INT:
case OPT_LONG: {
long tmp;
char *e;
tmp = strtol(arg, &e, 10);
if (*e)
optFatal("invalid number `%s'\n", arg);
if (errno == ERANGE
|| (opt->type == OPT_INT && (tmp > INT_MAX || tmp < INT_MIN)))
optFatal("number `%s' to `%s' out of range\n",
arg, optString(opt, lng));
if (opt->type == OPT_INT) {
if (opt->flags & OPT_CALLFUNC)
((void (*)(int)) opt->arg)((int) tmp);
else
*((int *) opt->arg) = (int) tmp;
} else /* OPT_LONG */ {
if (opt->flags & OPT_CALLFUNC)
((void (*)(long)) opt->arg)(tmp);
else
*((long *) opt->arg) = tmp;
}
break;
}
case OPT_UINT:
case OPT_ULONG: {
unsigned long tmp;
char *e;
tmp = strtoul(arg, &e, 10);
if (*e)
optFatal("invalid number `%s'\n", arg);
if (errno == ERANGE
|| (opt->type == OPT_UINT && tmp > UINT_MAX))
optFatal("number `%s' to `%s' out of range\n",
arg, optString(opt, lng));
if (opt->type == OPT_UINT) {
if (opt->flags & OPT_CALLFUNC)
((void (*)(unsigned)) opt->arg)((unsigned) tmp);
else
*((unsigned *) opt->arg) = (unsigned) tmp;
} else /* OPT_ULONG */ {
if (opt->flags & OPT_CALLFUNC)
((void (*)(unsigned long)) opt->arg)(tmp);
else
*((unsigned long *) opt->arg) = tmp;
}
break;
}
default:
break;
}
}
/**************************************************************************
* *
* P U B L I C F U N C T I O N S *
* *
**************************************************************************/
/*-------------------------------------------------------------------------
*
* NAME optSetFatalFunc
*
* FUNCTION Set function used to display error message and exit.
*
* SYNOPSIS #include "shhmsg.h"
* void optSetFatalFunc(void (*f)(const char *, ...));
*
* INPUT f function accepting printf()'like parameters,
* that _must_ abort the program.
*
*/
void optSetFatalFunc(void (*f)(const char *, ...))
{
optFatal = f;
}
/*-------------------------------------------------------------------------
*
* NAME optParseOptions
*
* FUNCTION Parse commandline options.
*
* SYNOPSIS #include "shhopt.h"
* void optParseOptions(int *argc, char *argv[],
* const optStruct opt[], int allowNegNum);
*
* INPUT argc Pointer to number of options.
* argv Array of option-/argument-strings.
* opt Array of possible options.
* allowNegNum
* a negative number is not to be taken as
* an option.
*
* OUTPUT argc new argument count.
* argv array with arguments removed.
*
* RETURNS Nothing. Aborts in case of error.
*
* DESCRIPTION This function checks each option in the argv-array
* against strings in the opt-array, and `executes' any
* matching action. Any arguments to the options are
* extracted and stored in the variables or passed to
* functions pointed to by entries in opt.
*
* Options and arguments used are removed from the argv-
* array, and argc is decreased accordingly.
*
* Any error leads to program abortion.
*
*/
void optParseOptions(int *argc, char *argv[],
const optStruct opt[], int allowNegNum)
{
int ai, /* argv index. */
optarg, /* argv index of option argument, or -1 if none. */
mi, /* Match index in opt. */
done;
char *arg, /* Pointer to argument to an option. */
*o, /* pointer to an option character */
*p;
/*
* Loop through all arguments.
*/
for (ai = 0; ai < *argc; ) {
/*
* "--" indicates that the rest of the argv-array does not
* contain options.
*/
if (strcmp(argv[ai], "--") == 0) {
argvRemove(argc, argv, ai);
break;
}
if (allowNegNum && argv[ai][0] == '-' && isdigit(argv[ai][1])) {
++ai;
continue;
} else if (strncmp(argv[ai], "--", 2) == 0) {
/* long option */
/* find matching option */
if ((mi = optMatch(opt, argv[ai] + 2, 1)) < 0)
optFatal("unrecognized option `%s'\n", argv[ai]);
/* possibly locate the argument to this option. */
arg = NULL;
if ((p = strchr(argv[ai], '=')) != NULL)
arg = p + 1;
/* does this option take an argument? */
optarg = -1;
if (optNeedsArgument(&opt[mi])) {
/* option needs an argument. find it. */
if (!arg) {
if ((optarg = ai + 1) == *argc)
optFatal("option `%s' requires an argument\n",
optString(&opt[mi], 1));
arg = argv[optarg];
}
} else {
if (arg)
optFatal("option `%s' doesn't allow an argument\n",
optString(&opt[mi], 1));
}
/* perform the action of this option. */
optExecute(&opt[mi], arg, 1);
/* remove option and any argument from the argv-array. */
if (optarg >= 0)
argvRemove(argc, argv, ai);
argvRemove(argc, argv, ai);
} else if (*argv[ai] == '-') {
/* A dash by itself is not considered an option. */
if (argv[ai][1] == '\0') {
++ai;
continue;
}
/* Short option(s) following */
o = argv[ai] + 1;
done = 0;
optarg = -1;
while (*o && !done) {
/* find matching option */
if ((mi = optMatch(opt, o, 0)) < 0)
optFatal("unrecognized option `-%c'\n", *o);
/* does this option take an argument? */
optarg = -1;
arg = NULL;
if (optNeedsArgument(&opt[mi])) {
/* option needs an argument. find it. */
arg = o + 1;
if (!*arg) {
if ((optarg = ai + 1) == *argc)
optFatal("option `%s' requires an argument\n",
optString(&opt[mi], 0));
arg = argv[optarg];
}
done = 1;
}
/* perform the action of this option. */
optExecute(&opt[mi], arg, 0);
++o;
}
/* remove option and any argument from the argv-array. */
if (optarg >= 0)
argvRemove(argc, argv, ai);
argvRemove(argc, argv, ai);
} else {
/* a non-option argument */
++ai;
}
}
}