Blame tools/shhopt.c

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