freeswitch/libs/xmlrpc-c/lib/util/cmdline_parser.c

588 lines
15 KiB
C

#include "xmlrpc_config.h" /* prereq for mallocvar.h -- defines __inline__ */
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include "bool.h"
#include "int.h"
#include "mallocvar.h"
#include "casprintf.h"
#include "getoptx.h"
#include "string_parser.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;
uint64_t llu;
double d;
} 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
parseInt(enum optiontype const type,
const char * const optarg,
unsigned int * const valueUintP,
int * const valueIntP,
const char ** const errorP) {
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 (type == OPTTYPE_UINT) {
if (longvalue < 0)
casprintf(errorP, "Unsigned numeric value is "
"negative: %ld", longvalue);
else {
*errorP = NULL;
*valueUintP = (unsigned int) longvalue;
}
} else {
*errorP = NULL;
*valueIntP = (int) longvalue;
}
}
}
}
static void
parseBinUint(const char * const optarg,
uint64_t * const valueP,
const char ** const errorP) {
if (optarg == NULL)
casprintf(errorP, "Option requires a value");
else if (strlen(optarg) == 0)
casprintf(errorP, "Numeric option value is null string");
else {
const char * error;
interpretBinUint(optarg, valueP, &error);
if (error) {
casprintf(errorP, "Invalid numeric option value '%s'. %s",
optarg, error);
strfree(error);
}
}
}
static void
parseFloat(const char * const optarg,
double * const valueP,
const char ** const errorP) {
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;
double const doublevalue = strtod(optarg, &tailptr);
if (*tailptr != '\0')
casprintf(errorP, "Non-numeric value "
"for numeric option value: '%s'", optarg);
else if (errno == ERANGE)
casprintf(errorP, "Numeric value out of range: %s", optarg);
else {
*errorP = NULL;
*valueP = doublevalue;
}
}
}
static void
parseOptionValue(const char * const optarg,
struct optionDesc * const optionP,
const char ** const errorP) {
switch (optionP->type) {
case OPTTYPE_FLAG:
*errorP = NULL;
break;
case OPTTYPE_INT:
case OPTTYPE_UINT:
parseInt(optionP->type, optarg, &optionP->value.u, &optionP->value.i,
errorP);
break;
case OPTTYPE_STRING:
if (optarg == NULL)
casprintf(errorP, "Option requires a value");
else {
*errorP = NULL;
optionP->value.s = strdup(optarg);
}
break;
case OPTTYPE_BINUINT:
parseBinUint(optarg, &optionP->value.llu, errorP);
break;
case OPTTYPE_FLOAT:
parseFloat(optarg, &optionP->value.d, errorP);
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 "
"(%u arguments)\n", cpP->numArguments);
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;
}
uint64_t
cmd_getOptionValueBinUint(cmdlineParser const cpP,
const char * const name) {
struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
uint64_t retval;
if (!optionDescP) {
fprintf(stderr, "cmdlineParser called incorrectly. "
"cmd_getOptionValueUint() called for undefined option '%s'\n",
name);
abort();
} else {
if (optionDescP->type != OPTTYPE_BINUINT) {
fprintf(stderr, "cmdlineParser called incorrectly. "
"cmd_getOptionValueBinUint() called for "
"non-OPTTYPE_BINUINT "
"option '%s'\n", optionDescP->name);
abort();
} else {
if (optionDescP->present)
retval = optionDescP->value.llu;
else
retval = 0;
}
}
return retval;
}
double
cmd_getOptionValueFloat(cmdlineParser const cpP,
const char * const name) {
struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
double retval;
if (!optionDescP) {
fprintf(stderr, "cmdlineParser called incorrectly. "
"cmd_getOptionValueInt() called for undefined option '%s'\n",
name);
abort();
} else {
if (optionDescP->type != OPTTYPE_FLOAT) {
fprintf(stderr, "cmdlineParser called incorrectly. "
"cmd_getOptionValueInt() called for non-float "
"option '%s'\n", optionDescP->name);
abort();
} else {
if (optionDescP->present)
retval = optionDescP->value.d;
else
retval = 0.0;
}
}
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;
}