mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
588 lines
15 KiB
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;
|
|
}
|