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");
+}
+