Mercurial > hg > nnchat
diff th_args.c @ 0:728243125263
Import.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 20 Mar 2008 00:15:03 +0000 |
parents | |
children | ecfa4e3597e3 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/th_args.c Thu Mar 20 00:15:03 2008 +0000 @@ -0,0 +1,387 @@ +/* + * Simple commandline argument processing + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2002-2008 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +/* +Description +=========== +Structures and functions for simple commandline argument processing, +option argument handling (short and long forms supported). + +Output function for printing out short on-line help for said options +and program commandline usage. + + +Format for typical commandline: + +$ program -a -b -c --long-d -e argument-for-e --long-f="argument for f" + +where -a, -b and -c are short options without required arguments; +--long-d is a long option without argument; -e is short option with +argument and finally --long-f is long option with argument. + + +Introduction +============ +Handling of commandline options in th_args_process() has various +options, which effect how an option is handled. Also the error +situations (unknown option/no required argument) can be handled +in different ways. + +Typically th_args_process() is called as follows: + +BOOL optionHandlerCallback(int optionNumber, char *optionArgument, char *optionName) +{ + if (option is OK) + return TRUE; + else + return FALSE; +} + +BOOL nonoptionHandlerCallback(char *argumentString) +{ + // return value same as in optionHandlerCallback +} + + +int main(int argc, char *argv[]) +{ + if (th_args_process(argc, argv, optList, optListN, + optionHandlerCallback, nonoptionHandlerCallback)) { + ... arguments OK ... + } else { + ... arguments invalid or required arguments missing ... + } +} + + +NOTICE! +------- +The return value from handler callbacks affects the return value of +th_args_process(). Additionally, a failure in callback (returns FALSE) +effects the argument processing if bailOut argument of th_args_process() +is TRUE! + +If bailOut is TRUE, any error/failure in argument processing (including +callbacks) immediately stops the argument processing and FALSE is +returned from th_args_process(). + +If bailOut is FALSE, most errors are "ignored", but FALSE is still returned +if any errors occured. + + +NOTICE #2! +---------- +A small temporary buffer of N*sizeof(BOOL) (where N is number of +options in optList[]) is allocated for processing required options. +If this allocation fails, the program is immediately exited with +code 128. + + +Examples +======== +Example of different options, in a fictional optionlist struct: + +optarg_t optList[] = { + // Option without arguments + { 0, '?', "help", "Show this help", OPT_NONE }, + + // Option with a required argument + { 1, 'o', "output", "Output file name", OPT_ARGREQ }, + + // Option with optional argument + { 2, 'f', "foobar", "Use foobar, with optional string", OPT_ARGOPT }, + + // This option is required to be given, though without other flags + // it may not make much sense. + { 4, 'S', "stupid", "You must give this option", OPT_REQUIRED }, + + // The flags can be combined with OR operator: this option is both + // required to be specified, and also requires argument (the filename) + { 5, 'i', "input", "Input file name", OPT_REQUIRED | OPT_ARGREQ }, + + + // Option with only long form + { 0, 0, "only-long", "Long option", OPT_NONE }, + + // Option with only short form + { 0, 's', NULL, "Short option", OPT_NONE }, + +}; + +const DINT optListN = (sizeof(optList) / sizeof(t_opt)); + + +*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "th_util.h" +#include "th_args.h" +#include "th_string.h" + + +/* Check if option requires an argument + */ +static BOOL th_args_check_arg(optarg_t *o, char *optArg) +{ + if ((o->optFlags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL) { + if (o->optShort != 0 && o->optLong != NULL) { + THERR("Option '-%c ARG' (--%s=ARG) requires an argument!\n", + o->optShort, o->optLong); + } else if (o->optShort != 0) { + THERR("Option '-%c ARG' requires an argument!\n", o->optShort); + } else if (o->optLong != NULL) { + THERR("Option --%s=ARG requires an argument!\n", o->optLong); + } + + return FALSE; + } else + return TRUE; + +} + + +/* Handle short options + */ +static BOOL th_args_process_short(char *currArg, int *newArgIndex, + BOOL *wasGiven, int argc, char *argv[], + optarg_t optList[], int optListN, + BOOL (*handleOpt)(int, char *, char *)) +{ + char *tmpArg = currArg, *optArg; + int optN; + BOOL isFound; + + /* Short options can be combined: -a -b -c == -abc */ + while (*tmpArg) { + + for (optN = 0, isFound = FALSE; (optN < optListN) && !isFound; optN++) + if (*tmpArg == optList[optN].optShort) { + /* Get possible option argument, if needed */ + if ((optList[optN].optFlags & OPT_ARGMASK) != 0 && (++(*newArgIndex) < argc)) + optArg = argv[*newArgIndex]; + else + optArg = NULL; + + /* Check if option argument is required */ + if (!th_args_check_arg(&optList[optN], optArg)) + return FALSE; + else { + char tmpStr[2] = { 0, 0 }; + + /* Option was given succesfully, try to handle it */ + wasGiven[optN] = TRUE; + + tmpStr[0] = *tmpArg; + + if (!handleOpt(optList[optN].optID, optArg, tmpStr)) + return FALSE; + } + + isFound = TRUE; + } + + if (!isFound) { + THERR("Unknown short option '%c' in argument '-%s'\n", + *tmpArg, currArg); + return FALSE; + } + + tmpArg++; + } + + return TRUE; +} + + +/* Handle long options + */ +static BOOL th_args_process_long(char *currArg, int *newArgIndex, + BOOL *wasGiven, int argc, char *argv[], + optarg_t optList[], int optListN, + BOOL (*handleOpt)(int, char *, char *)) +{ + int optN, optLen, i; + char *optArg; + + (void) argc; (void) argv; (void) newArgIndex; + + /* Long option */ + for (optN = -1, optLen = i = 0; (i < optListN) && (optN < 0); i++) + if (optList[i].optLong) { + optLen = th_strlen(optList[i].optLong); + if (th_strncmp(currArg, optList[i].optLong, optLen) == 0) + optN = i; + } + + /* Get possible option argument, if needed */ + if (optN >= 0) { + if ((optList[optN].optFlags & OPT_ARGMASK) != 0) { + if (currArg[optLen] == '=') + optArg = &currArg[optLen + 1]; + else + optArg = NULL; + } else + optArg = NULL; + + /* Check if option argument is required */ + if (!th_args_check_arg(&optList[optN], optArg)) + return FALSE; + else { + /* Option was given succesfully, try to handle it */ + wasGiven[optN] = TRUE; + if (!handleOpt(optList[optN].optID, optArg, currArg)) + return FALSE; + } + } else { + THERR("Unknown long option '--%s'\n", currArg); + return FALSE; + } + + return TRUE; +} + + +/* Process arguments, handling short and long options by + * calling the given callback functions. + */ +BOOL th_args_process(int argc, char *argv[], + optarg_t optList[], int optListN, + BOOL (*handleOpt) (int, char *, char *), + BOOL (*handleNonOption) (char *), BOOL bailOut) +{ + BOOL endOptions, optionsOK; + int argIndex, newArgIndex, i; + char *currArg; + BOOL *wasGiven; + + /* Allocate wasGiven */ + wasGiven = (BOOL *) th_calloc(optListN, sizeof(BOOL)); + if (!wasGiven) { + THERR("FATAL ERROR! Could not allocate wasGiven in th_args_process()!\n"); + exit(128); + } + + /* Parse arguments */ + argIndex = 1; + optionsOK = TRUE; + endOptions = FALSE; + while (argIndex < argc) { + currArg = argv[argIndex]; + if ((currArg[0] == '-') && !endOptions) { + newArgIndex = argIndex; + currArg++; + if (*currArg == '-') { + /* Check for "--", which ends the options-list */ + currArg++; + if (*currArg == 0) { + endOptions = TRUE; + continue; + } + + /* Long options */ + if (!th_args_process_long(currArg, &newArgIndex, + wasGiven, argc, argv, optList, optListN, + handleOpt)) + optionsOK = FALSE; + } else { + /* Short options */ + if (!th_args_process_short(currArg, &newArgIndex, + wasGiven, argc, argv, optList, optListN, + handleOpt)) + optionsOK = FALSE; + } + + argIndex = newArgIndex; + } else { + /* Was not option argument */ + if (!handleNonOption || (handleNonOption && !handleNonOption(currArg))) { + THERR("Invalid argument '%s'\n", currArg); + optionsOK = FALSE; + } + } + + /* Check if we bail out on invalid argument */ + if (!optionsOK && bailOut) { + th_free(wasGiven); + return FALSE; + } + + argIndex++; + } + + /* Check wasGiven by isRequired */ + for (i = 0; i < optListN; i++) + if ((optList[i].optFlags & OPT_REQUIRED) != 0 && !wasGiven[i]) { + THERR("Option -%s (--%s) is required.\n", + optList[i].optShort, optList[i].optLong); + + optionsOK = FALSE; + if (bailOut) break; + } + + th_free(wasGiven); + return optionsOK; +} + + +/* Print help for commandline arguments/options + */ +void th_args_help(FILE * outFile, + optarg_t optList[], int optListN, + char * progName, char * progUsage) +{ + int i, nrequired; + + fprintf(outFile, + "\n%s v%s (%s)\n" + "%s\n" + "%s\n" + "Usage: %s %s\n", + th_prog_name, th_prog_version, th_prog_fullname, + th_prog_author, th_prog_license, progName, progUsage); + + + for (i = nrequired = 0; i < optListN; i++) { + optarg_t *o = &optList[i]; + + /* Print short option */ + if (o->optShort != 0) + fprintf(outFile, " -%c, ", o->optShort); + else + fprintf(outFile, " "); + + /* Print long option */ + if (o->optLong) { + char tmpStr[64], *p; + + if ((o->optFlags & OPT_ARGMASK) == OPT_ARGOPT) { + snprintf(tmpStr, sizeof(tmpStr), "%s[=ARG]", optList[i].optLong); + p = tmpStr; + } else if ((o->optFlags & OPT_ARGMASK) == OPT_ARGREQ) { + snprintf(tmpStr, sizeof(tmpStr), "%s=ARG", optList[i].optLong); + p = tmpStr; + } else + p = o->optLong; + + fprintf(outFile, "--%-15s", p); + } else + fprintf(outFile, " "); + + fprintf(outFile, " %s.", optList[i].optDesc); + + if (o->optFlags & OPT_REQUIRED) { + fprintf(outFile, " [*]\n"); + nrequired++; + } else + fprintf(outFile, "\n"); + } + + if (nrequired > 0) + fprintf(outFile, "(Options marked with [*] are required)\n"); +} +