Mercurial > hg > nnchat
view th_args.c @ 322:b9c15c57dc8f
Clean up message functions, add new printMsgQ() helper function for messages that should not
go into the log file. Add skeleton help function, accessible via F1 key. And other cleanups.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 11 Jun 2011 09:48:26 +0300 |
parents | 69aed051f84d |
children | 9ad157feb99a |
line wrap: on
line source
/* * 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 int optListN = (sizeof(optList) / sizeof(optarg_t)); */ #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 = strlen(optList[i].optLong); if (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 == NULL || (handleNonOption != NULL && !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"); }