diff dmargs_int.c @ 0:32250b436bca

Initial re-import.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 28 Sep 2012 01:54:23 +0300
parents
children 4d6769bd8cfb
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmargs_int.c	Fri Sep 28 01:54:23 2012 +0300
@@ -0,0 +1,418 @@
+/*
+ * 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));
+
+
+*/
+#ifndef TH_EXTERNAL
+#include "th_util.h"
+#include "th_args.h"
+#include "th_string.h"
+#endif
+
+
+/* Check if option requires an argument
+ */
+static BOOL th_args_check_arg(const optarg_t *o, const char *optArg)
+{
+    if ((o->flags & 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].flags & 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].id, 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].flags & 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].id, 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].flags & 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)
+{
+    int i, nrequired;
+
+    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->flags & OPT_ARGMASK) == OPT_ARGOPT)
+            {
+                snprintf(tmpStr, sizeof(tmpStr), "%s[=ARG]",
+                         optList[i].optLong);
+                p = tmpStr;
+            }
+            else if ((o->flags & 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].desc);
+
+        if (o->flags & OPT_REQUIRED)
+        {
+            fprintf(outFile, " [*]\n");
+            nrequired++;
+        }
+        else
+            fprintf(outFile, "\n");
+    }
+
+    if (nrequired > 0)
+        fprintf(outFile, "(Options marked with [*] are required)\n");
+}