|
|
#define _XOPEN_SOURCE 600 |
|
|
|
|
|
#include "xmlrpc_config.h" |
|
|
|
|
|
#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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cpP->numOptions; ++i) |
|
|
cpP->optionDescArray[i].present = false; |
|
|
|
|
|
endOfOptions = false; |
|
|
|
|
|
while (!endOfOptions && !*errorP) { |
|
|
int const opterr0 = 0; |
|
|
|
|
|
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; |
|
|
} |
|
|
|