diff src/dmargs.c @ 1742:ddec147d1f90

Bring in changes from the th-libs version of commandline argument handling.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 09 Jun 2018 17:56:07 +0300
parents 985225a93aeb
children bcdea45a14cb
line wrap: on
line diff
--- a/src/dmargs.c	Sat Jun 09 17:04:33 2018 +0300
+++ b/src/dmargs.c	Sat Jun 09 17:56:07 2018 +0300
@@ -1,12 +1,279 @@
+/*
+ * Simple commandline argument processing
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2018 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+/// @file
+/// @brief Simple commandline argument processing functions
 #include "dmargs.h"
-#define TH_EXTERNAL 1
-#define th_optarg_t DMOptArg
-#define th_args_process dmArgsProcess
-#define th_args_help dmArgsPrintHelp
+
+
+/**
+ * Parse and optionally handle the given long or short option argument.
+ * @param currArg current argument string
+ * @param argIndex pointer to index of current argument in argv[]
+ * @param argc number of arguments
+ * @param argv argument string array
+ * @param opts options list array
+ * @param nopts number of elements in options list array
+ * @param handle_option function pointer to callback that handles option arguments
+ * @param doProcess if TRUE, actually handle the argument, aka call the handle_option() function. if FALSE, only validity of options are checked.
+ * @param isLong TRUE if the option is a --long-format one
+ */
+static BOOL dmArgsProcessOpt(
+    char *currArg, int *argIndex,
+    int argc, char *argv[],
+    const DMOptArg opts[], int nopts,
+    BOOL (*handle_option)(int id, char *, char *),
+    BOOL doProcess, BOOL isLong)
+{
+    const DMOptArg *opt = NULL;
+    char *optArg = NULL;
+    int optIndex;
+
+    for (optIndex = 0; optIndex < nopts; optIndex++)
+    {
+        const DMOptArg *node = &opts[optIndex];
+        if (isLong && node->o_long != NULL)
+        {
+            if (strcmp(currArg, node->o_long) == 0)
+            {
+                opt = node;
+                optArg = NULL;
+                break;
+            }
+
+            size_t len = strlen(node->o_long);
+            if (strncmp(currArg, node->o_long, len) == 0 &&
+                currArg[len] == '=')
+            {
+                opt = node;
+                optArg = (&currArg[len+1] != 0) ? &currArg[len+1] : NULL;
+                break;
+            }
+        }
+        else
+        if (!isLong && node->o_short != 0)
+        {
+            if (*currArg == node->o_short)
+            {
+                opt = node;
+                optArg = (currArg[1] != 0) ? &currArg[1] : NULL;
+            }
+        }
+    }
+
+    if (opt != NULL)
+    {
+        // Check for the possible option argument
+        if ((opt->flags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL)
+        {
+            if (*argIndex < argc)
+            {
+                (*argIndex)++;
+                optArg = argv[*argIndex];
+            }
+
+            if (optArg == NULL)
+            {
+                dmErrorMsg("Option '%s%s' requires an argument.\n",
+                    isLong ? "--" : "-",
+                    currArg);
+                return FALSE;
+            }
+        }
+
+        // Option was given succesfully, try to process it
+        if (doProcess && !handle_option(opt->id, optArg, currArg))
+            return FALSE;
+    }
+    else
+    {
+        dmErrorMsg("Unknown %s option '%s%s'\n",
+            isLong ? "long" : "short",
+            isLong ? "--" : "-",
+            currArg);
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+/**
+ * Process given array of commandline arguments, handling short
+ * and long options by calling the respective callback functions.
+ *
+ * @param argc number of arguments
+ * @param argv argument list
+ * @param opts supported option list array
+ * @param nopts number of elements in the option list array
+ * @param handle_option callback function
+ * @param handle_other callback function
+ * @param flags processing flags
+ * @return return TRUE if all is well
+ */
+BOOL dmArgsProcess(int argc, char *argv[],
+     const DMOptArg *opts, const int nopts,
+     BOOL(*handle_option)(int id, char *, char *),
+     BOOL(*handle_other)(char *), const int flags)
+{
+    int argIndex, handleFlags = flags & OPTH_ONLY_MASK;
+    BOOL optionsOK = TRUE, endOfOptions = FALSE;
+
+    for (argIndex = 1; argIndex < argc; argIndex++)
+    {
+        char *str = argv[argIndex];
+        if (*str == '-' && !endOfOptions)
+        {
+            // Should we process options?
+            BOOL doProcess = (handleFlags & OPTH_ONLY_OPTS) || handleFlags == 0;
+            BOOL isLong;
 
-#define THERR dmErrorMsg
-#define th_calloc dmCalloc
-#define th_malloc dmMalloc
-#define th_free dmFree
+            str++;
+            if (*str == '-')
+            {
+                // Check for "--", which ends the options-list
+                str++;
+                if (*str == 0)
+                {
+                    endOfOptions = TRUE;
+                    continue;
+                }
+
+                // We have a long option
+                isLong = TRUE;
+            }
+            else
+                isLong = FALSE;
+
+            if (!dmArgsProcessOpt(str, &argIndex, argc, argv,
+                opts, nopts, handle_option, doProcess, isLong))
+                optionsOK = FALSE;
+        }
+        else
+        if (handleFlags == OPTH_ONLY_OTHER || handleFlags == 0)
+        {
+            // Was not option argument
+            if (handle_other == NULL ||
+                (handle_other != NULL && !handle_other(str)))
+            {
+                dmErrorMsg("Invalid argument '%s'\n", str);
+                optionsOK = FALSE;
+            }
+        }
+
+        // Check if we bail out on invalid argument
+        if (!optionsOK && (flags & OPTH_BAILOUT))
+            return FALSE;
+    }
+
+    return optionsOK;
+}
+
+
+static void dmPad(FILE *outFile, int count)
+{
+    while (count--)
+        fputc(' ', outFile);
+}
+
+
+static void dmPrintWrap(FILE *fh, const char *str, int spad, int rpad, int width)
+{
+    size_t pos = 0;
+    BOOL first = TRUE;
+
+    while (str[pos])
+    {
+        // Pre-pad line
+        int linelen = first ? spad : rpad;
+        dmPad(fh, first ? 0 : rpad);
+        first = FALSE;
+
+        // Skip whitespace at line start
+        while (isspace(str[pos]) || str[pos] == '\n') pos++;
 
-#include "dmargs_int.c"
+        // Handle each word
+        while (str[pos] && str[pos] != '\n')
+        {
+            size_t next;
+            int wlen;
+            for (wlen = 0, next = pos; str[next] && !isspace(str[next]) && str[next] != '\n'; next++, wlen++);
+//            fprintf(stdout, "X '%c', %d .. linelen=%d/%d, wlen=%d\n", str[pos], pos, linelen, width, wlen);
+            if (linelen + wlen < width)
+            {
+                for (;pos < next; pos++, linelen++)
+                    fputc(str[pos], fh);
+
+                if (str[next] == '\n' || str[next] == 0)
+                {
+                    fprintf(fh, "\n");
+                    break;
+                }
+                else
+                {
+                    fputc(str[pos], fh);
+                    pos++;
+                    linelen++;
+                }
+            }
+            else
+            {
+                fprintf(fh, "\n");
+                break;
+            }
+        }
+    }
+}
+
+
+/**
+ * Print help for commandline arguments/options
+ * @param fh stdio file handle to output to
+ * @param opts options list array
+ * @param nopts number of elements in options list array
+ * @param flags flags (currently unused)
+ */
+void dmArgsPrintHelp(FILE *fh,
+    const DMOptArg *opts, const int nopts,
+    const int flags)
+{
+    int index;
+    (void) flags;
+
+    // Print out option list
+    for (index = 0; index < nopts; index++)
+    {
+        const DMOptArg *opt = &opts[index];
+        char tmpStr[128];
+
+        // Print short option
+        if (opt->o_short != 0)
+        {
+            snprintf(tmpStr, sizeof(tmpStr),
+                "-%c,", opt->o_short);
+        }
+        else
+            tmpStr[0] = 0;
+
+        fprintf(fh, " %-5s", tmpStr);
+
+        // Print long option
+        if (opt->o_long != NULL)
+        {
+            snprintf(tmpStr, sizeof(tmpStr), "--%s%s",
+                opt->o_long,
+                (opt->flags & OPT_ARGREQ) ? "=ARG" : "");
+        }
+        else
+            tmpStr[0] = 0;
+
+        fprintf(fh, "%-20s", tmpStr);
+
+        dmPrintWrap(fh, opt->desc, 26, 26, 79);
+    }
+}