initial load of upstream version 1.06.32
[xmlrpc-c] / lib / util / cmdline_parser.c
diff --git a/lib/util/cmdline_parser.c b/lib/util/cmdline_parser.c
new file mode 100644 (file)
index 0000000..a2c19ab
--- /dev/null
@@ -0,0 +1,449 @@
+#include "xmlrpc_config.h"  /* prereq for mallocvar.h -- defines __inline__ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "bool.h"
+#include "mallocvar.h"
+#include "casprintf.h"
+#include "getoptx.h"
+
+#include "cmdline_parser.h"
+
+#define MAXOPTS 100
+
+struct optionDesc {
+    const char *    name;
+    enum optiontype type;
+    bool            present;
+    union {
+        unsigned int u;
+        int          i;
+        const char * s;
+    } value;
+};
+
+
+
+struct cmdlineParserCtl {
+    struct optionDesc * optionDescArray;
+    unsigned int        numOptions;
+    const char **       argumentArray;
+    unsigned int        numArguments;
+};
+
+
+
+static struct optionx *
+createLongOptsArray(struct optionDesc * const optionDescArray,
+                    unsigned int        const numOptions) {
+
+    struct optionx * longopts; 
+
+    MALLOCARRAY(longopts, numOptions+1);
+    if (longopts != NULL) {
+        unsigned int i;
+
+        for (i = 0; i < numOptions; ++i) {
+            longopts[i].name = optionDescArray[i].name;
+            /* If the option takes a value, we say it is optional even
+               though it never is.  That's because if we say it is
+               mandatory, getopt_long_only() pretends it doesn't even
+               recognize the option if the user doesn't give a value.
+               We prefer to generate a meaningful error message when
+               the user omits a required option value.
+            */
+            longopts[i].has_arg = 
+                optionDescArray[i].type == OPTTYPE_FLAG ? 
+                no_argument : optional_argument;
+            longopts[i].flag = NULL;
+            longopts[i].val = i;
+        }
+        longopts[numOptions].name = 0;
+        longopts[numOptions].has_arg = 0;
+        longopts[numOptions].flag = 0;
+        longopts[numOptions].val = 0;
+    }
+    return longopts;
+}
+
+
+
+static void
+parseOptionValue(const char *        const optarg, 
+                 struct optionDesc * const optionP,
+                 const char **       const errorP) {
+    
+    switch (optionP->type) {
+    case OPTTYPE_UINT: 
+    case OPTTYPE_INT: {
+        if (optarg == NULL)
+            casprintf(errorP, "Option requires a value");
+        else if (strlen(optarg) == 0)
+            casprintf(errorP, "Numeric option value is null string");
+        else {
+            char * tailptr;
+            long const longvalue = strtol(optarg, &tailptr, 10);
+            if (*tailptr != '\0')
+                casprintf(errorP, "Non-numeric value "
+                         "for numeric option value: '%s'", optarg);
+            else if (errno == ERANGE || longvalue > INT_MAX)
+                casprintf(errorP, "Numeric value out of range: %s", optarg);
+            else { 
+                if (optionP->type == OPTTYPE_UINT) {
+                    if (longvalue < 0)
+                        casprintf(errorP, "Unsigned numeric value is "
+                                  "negative: %ld", longvalue);
+                    else {
+                        *errorP = NULL;
+                        optionP->value.u = (unsigned int) longvalue;
+                    }
+                } else {
+                    *errorP = NULL;
+                    optionP->value.u = (int) longvalue;
+                }
+            }
+        }
+    }
+    break;
+    case OPTTYPE_STRING:
+        if (optarg == NULL)
+            casprintf(errorP, "Option requires a value");
+        else {
+            *errorP = NULL;
+            optionP->value.s = strdup(optarg);
+        }
+        break;
+    case OPTTYPE_FLAG:
+        *errorP = NULL;
+        break;
+    }
+}
+
+
+
+static void
+processOption(struct optionDesc * const optionP,
+              const char *        const optarg,
+              const char **       const errorP) {
+
+    const char * error;
+    
+    parseOptionValue(optarg, optionP, &error);
+    if (error)
+        casprintf(errorP, "Error in '%s' option: %s", optionP->name, error);
+    else
+        optionP->present = true;
+}
+
+
+
+static void
+extractArguments(struct cmdlineParserCtl * const cpP,
+                 unsigned int              const argc,
+                 const char **             const argv) {
+    
+    cpP->numArguments = argc - getopt_argstart();
+    MALLOCARRAY(cpP->argumentArray, cpP->numArguments);
+
+    if (cpP->argumentArray == NULL) {
+        fprintf(stderr, "Unable to allocate memory for argument array\n");
+        abort();
+    } else {
+        unsigned int i;
+
+        for (i = 0; i < cpP->numArguments; ++i) {
+            cpP->argumentArray[i] = strdup(argv[getopt_argstart() + i]);
+            if (cpP->argumentArray[i] == NULL) {
+                fprintf(stderr, "Unable to allocate memory for Argument %u\n",
+                        i);
+                abort();
+            }
+        }
+    }
+}
+
+
+
+void
+cmd_processOptions(cmdlineParser   const cpP,
+                   int             const argc,
+                   const char **   const argv, 
+                   const char **   const errorP) {
+
+    struct optionx * longopts;
+
+    longopts = createLongOptsArray(cpP->optionDescArray, cpP->numOptions);
+
+    if (longopts == NULL) 
+        casprintf(errorP, "Unable to get memory for longopts array");
+    else {
+        int endOfOptions;
+        unsigned int i;
+
+        *errorP = NULL;
+
+        /* Set up initial assumption:  No options present */
+
+        for (i = 0; i < cpP->numOptions; ++i)
+            cpP->optionDescArray[i].present = false;
+
+        endOfOptions = false;  /* initial value */
+            
+        while (!endOfOptions && !*errorP) {
+            int const opterr0 = 0;
+                /* Don't let getopt_long_only() print an error message */
+            unsigned int longoptsIndex;
+            const char * unrecognizedOption;
+            const char * optarg;
+            
+            getopt_long_onlyx(argc, (char**) argv, "", longopts, 
+                              &longoptsIndex, opterr0,
+                              &endOfOptions, &optarg, &unrecognizedOption);
+                              
+            if (unrecognizedOption)
+                casprintf(errorP, "Unrecognized option: '%s'", 
+                          unrecognizedOption);
+            else {
+                if (!endOfOptions)
+                    processOption(&cpP->optionDescArray[longoptsIndex], optarg,
+                                  errorP);
+            }
+        }
+        if (!*errorP)
+            extractArguments(cpP, argc, argv);
+
+        free(longopts);
+    }
+}
+
+
+
+cmdlineParser
+cmd_createOptionParser(void) {
+
+    struct cmdlineParserCtl * cpP;
+
+    MALLOCVAR(cpP);
+
+    if (cpP != NULL) {
+        struct optionDesc * optionDescArray;
+
+        cpP->numOptions = 0;
+        MALLOCARRAY(optionDescArray, MAXOPTS);
+        if (optionDescArray == NULL) {
+            free(cpP);
+            cpP = NULL;
+        } else 
+            cpP->optionDescArray = optionDescArray;
+    }
+    return cpP;
+}
+
+
+
+void
+cmd_destroyOptionParser(cmdlineParser const cpP) {
+    
+    unsigned int i;
+
+    for (i = 0; i < cpP->numOptions; ++i) {
+        struct optionDesc const option = cpP->optionDescArray[i];
+        if (option.type == OPTTYPE_STRING && option.present)
+            strfree(option.value.s);
+        strfree(option.name);
+    }
+
+    for (i = 0; i < cpP->numArguments; ++i)
+        strfree(cpP->argumentArray[i]);
+
+    free(cpP->optionDescArray);
+    free(cpP);
+}
+
+
+
+void
+cmd_defineOption(cmdlineParser   const cpP,
+                 const char *    const name, 
+                 enum optiontype const type) {
+    
+    if (cpP->numOptions < MAXOPTS) {
+        cpP->optionDescArray[cpP->numOptions].name = strdup(name);
+        cpP->optionDescArray[cpP->numOptions].type = type;
+        
+        ++cpP->numOptions;
+    }
+}
+
+
+
+static struct optionDesc *
+findOptionDesc(struct cmdlineParserCtl * const cpP,
+               const char *              const name) {
+
+    struct optionDesc * retval;
+    unsigned int i;
+
+    retval = NULL;
+
+    for (i = 0; i < cpP->numOptions && !retval; ++i)
+        if (strcmp(cpP->optionDescArray[i].name, name) == 0)
+            retval = &cpP->optionDescArray[i];
+
+    return retval;
+}
+
+
+
+int
+cmd_optionIsPresent(cmdlineParser const cpP,
+                    const char *  const name) {
+
+    struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
+
+    bool present;
+
+    if (!optionDescP) {
+        fprintf(stderr, "cmdlineParser called incorrectly.  "
+                "optionIsPresent() called for undefined option '%s'\n",
+                name);
+        abort();
+    } else 
+        present = optionDescP->present;
+
+    return present;
+}
+
+
+
+unsigned int
+cmd_getOptionValueUint(cmdlineParser const cpP,
+                       const char *  const name) {
+
+    struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
+
+    unsigned int retval;
+
+    if (!optionDescP) {
+        fprintf(stderr, "cmdlineParser called incorrectly.  "
+                "cmd_getOptionValueUint() called for undefined option '%s'\n",
+                name);
+        abort();
+    } else {
+        if (optionDescP->type != OPTTYPE_UINT) {
+            fprintf(stderr, "cmdlineParser called incorrectly.  "
+                    "cmd_getOptionValueUint() called for non-unsigned integer "
+                    "option '%s'\n", optionDescP->name);
+            abort();
+        } else {
+            if (optionDescP->present) 
+                retval = optionDescP->value.u;
+            else
+                retval = 0;
+        }
+    }
+    return retval;
+}
+
+
+
+int
+cmd_getOptionValueInt(cmdlineParser const cpP,
+                      const char *  const name) {
+
+    struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
+
+    int retval;
+
+    if (!optionDescP) {
+        fprintf(stderr, "cmdlineParser called incorrectly.  "
+                "cmd_getOptionValueInt() called for undefined option '%s'\n",
+                name);
+        abort();
+    } else {
+        if (optionDescP->type != OPTTYPE_INT) {
+            fprintf(stderr, "cmdlineParser called incorrectly.  "
+                    "cmd_getOptionValueInt() called for non-integer "
+                    "option '%s'\n", optionDescP->name);
+            abort();
+        } else {
+            if (optionDescP->present) 
+                retval = optionDescP->value.i;
+            else
+                retval = 0;
+        }
+    }
+
+    return retval;
+}
+
+
+
+const char *
+cmd_getOptionValueString(cmdlineParser const cpP,
+                         const char *  const name) {
+
+    struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
+
+    const char * retval;
+
+    if (!optionDescP) {
+        fprintf(stderr, "cmdlineParser called incorrectly.  "
+                "cmd_getOptionValueString() called for " 
+                "undefined option '%s'\n",
+                name);
+        abort();
+    } else {
+        if (optionDescP->type != OPTTYPE_STRING) {
+            fprintf(stderr, "cmdlineParser called incorrectly.  "
+                    "getOptionValueString() called for non-string "
+                    "option '%s'\n", optionDescP->name);
+            abort();
+        } else {
+            if (optionDescP->present) {
+                retval = strdup(optionDescP->value.s);
+                if (retval == NULL) {
+                    fprintf(stderr, 
+                            "out of memory in cmd_getOptionValueString()\n");
+                    abort();
+                }
+            } else
+                retval = NULL;
+        }
+    }
+    return retval;
+}
+
+
+
+unsigned int 
+cmd_argumentCount(cmdlineParser const cpP) {
+
+    return cpP->numArguments;
+
+}
+                  
+
+
+const char * 
+cmd_getArgument(cmdlineParser const cpP, 
+                unsigned int  const argNumber) { 
+
+    const char * retval;
+    if (argNumber >= cpP->numArguments)
+        retval = NULL;
+    else {
+        retval = strdup(cpP->argumentArray[argNumber]);
+
+        if (retval == NULL) {
+            fprintf(stderr, 
+                    "out of memory in cmd_getArgument()\n");
+            abort();
+        }
+    }
+    return retval;
+}