| /* $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; |
| } |
| } |
| } |