/* $Id: shhopt.c 1418 2002-07-22 17:06:19Z schoenw $ */ /*------------------------------------------------------------------------ | 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 +----------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include "shhopt.h" /*-----------------------------------------------------------------------+ | PRIVATE DATA | +-----------------------------------------------------------------------*/ static void optFatalFunc(const char *, ...); static void (*optFatal)(const char *format, ...) = optFatalFunc; /*-----------------------------------------------------------------------+ | PRIVATE FUNCTIONS | +-----------------------------------------------------------------------*/ /*------------------------------------------------------------------------ | 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(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(optStruct opt[], const char *s, int lng) { int nopt, q; const char *p; size_t matchlen = 0; 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(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(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. */ static void optExecute(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; } } /*-----------------------------------------------------------------------+ | PUBLIC FUNCTIONS | +-----------------------------------------------------------------------*/ /*------------------------------------------------------------------------ | NAME optSetFatalFunc | | FUNCTION Set function used to display error message and exit. | | SYNOPSIS #include "shhopt.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[], | 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[], 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; } } }