mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-25 23:07:18 +00:00
[common] rework option API to allow for custom types
This commit is contained in:
parent
5b199d8f25
commit
538a6dc08e
@ -25,34 +25,31 @@ enum OptionType
|
|||||||
OPTION_TYPE_NONE = 0,
|
OPTION_TYPE_NONE = 0,
|
||||||
OPTION_TYPE_INT,
|
OPTION_TYPE_INT,
|
||||||
OPTION_TYPE_STRING,
|
OPTION_TYPE_STRING,
|
||||||
OPTION_TYPE_BOOL
|
OPTION_TYPE_BOOL,
|
||||||
|
OPTION_TYPE_CUSTOM
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OptionState;
|
struct Option;
|
||||||
|
|
||||||
struct OptionValue
|
|
||||||
{
|
|
||||||
enum OptionType type;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int x_int;
|
|
||||||
char * x_string;
|
|
||||||
bool x_bool;
|
|
||||||
}
|
|
||||||
v;
|
|
||||||
|
|
||||||
// internal state
|
|
||||||
struct OptionState * state;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Option
|
struct Option
|
||||||
{
|
{
|
||||||
const char * module;
|
const char * module;
|
||||||
const char * name;
|
const char * name;
|
||||||
const char * description;
|
const char * description;
|
||||||
struct OptionValue value;
|
|
||||||
|
|
||||||
bool (*validator)(struct OptionValue * value, const char ** error);
|
enum OptionType type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int x_int;
|
||||||
|
char * x_string;
|
||||||
|
bool x_bool;
|
||||||
|
void * x_custom;
|
||||||
|
}
|
||||||
|
value;
|
||||||
|
|
||||||
|
bool (*parser )(struct Option * opt, const char * str);
|
||||||
|
bool (*validator)(struct Option * opt, const char ** error);
|
||||||
|
char * (*toString )(struct Option * opt);
|
||||||
void (*printHelp)();
|
void (*printHelp)();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,7 +57,7 @@ struct Option
|
|||||||
bool option_register(struct Option options[]);
|
bool option_register(struct Option options[]);
|
||||||
|
|
||||||
// lookup the value of an option
|
// lookup the value of an option
|
||||||
struct OptionValue * option_get (const char * module, const char * name);
|
struct Option * option_get (const char * module, const char * name);
|
||||||
int option_get_int (const char * module, const char * name);
|
int option_get_int (const char * module, const char * name);
|
||||||
const char * option_get_string(const char * module, const char * name);
|
const char * option_get_string(const char * module, const char * name);
|
||||||
bool option_get_bool (const char * module, const char * name);
|
bool option_get_bool (const char * module, const char * name);
|
||||||
|
@ -52,10 +52,51 @@ static struct State state =
|
|||||||
.gCount = 0
|
.gCount = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool int_parser(struct Option * opt, const char * str)
|
||||||
|
{
|
||||||
|
opt->value.x_int = atol(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool bool_parser(struct Option * opt, const char * str)
|
||||||
|
{
|
||||||
|
opt->value.x_bool =
|
||||||
|
strcmp(str, "1" ) == 0 ||
|
||||||
|
strcmp(str, "on" ) == 0 ||
|
||||||
|
strcmp(str, "yes" ) == 0 ||
|
||||||
|
strcmp(str, "true") == 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool string_parser(struct Option * opt, const char * str)
|
||||||
|
{
|
||||||
|
free(opt->value.x_string);
|
||||||
|
opt->value.x_string = strdup(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * int_toString(struct Option * opt)
|
||||||
|
{
|
||||||
|
int len = snprintf(NULL, 0, "%d", opt->value.x_int);
|
||||||
|
char * ret = malloc(len + 1);
|
||||||
|
sprintf(ret, "%d", opt->value.x_int);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * bool_toString(struct Option * opt)
|
||||||
|
{
|
||||||
|
return strdup(opt->value.x_bool ? "yes" : "no");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * string_toString(struct Option * opt)
|
||||||
|
{
|
||||||
|
return strdup(opt->value.x_string);
|
||||||
|
}
|
||||||
|
|
||||||
bool option_register(struct Option options[])
|
bool option_register(struct Option options[])
|
||||||
{
|
{
|
||||||
int new = 0;
|
int new = 0;
|
||||||
for(int i = 0; options[i].value.type != OPTION_TYPE_NONE; ++i)
|
for(int i = 0; options[i].type != OPTION_TYPE_NONE; ++i)
|
||||||
++new;
|
++new;
|
||||||
|
|
||||||
state.options = realloc(
|
state.options = realloc(
|
||||||
@ -63,14 +104,58 @@ bool option_register(struct Option options[])
|
|||||||
sizeof(struct Option) * (state.oCount + new)
|
sizeof(struct Option) * (state.oCount + new)
|
||||||
);
|
);
|
||||||
|
|
||||||
for(int i = 0; options[i].value.type != OPTION_TYPE_NONE; ++i)
|
for(int i = 0; options[i].type != OPTION_TYPE_NONE; ++i)
|
||||||
{
|
{
|
||||||
struct Option * o = &state.options[state.oCount + i];
|
struct Option * o = &state.options[state.oCount + i];
|
||||||
memcpy(o, &options[i], sizeof(struct Option));
|
memcpy(o, &options[i], sizeof(struct Option));
|
||||||
|
|
||||||
|
if (!o->parser)
|
||||||
|
{
|
||||||
|
switch(o->type)
|
||||||
|
{
|
||||||
|
case OPTION_TYPE_INT:
|
||||||
|
o->parser = int_parser;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPTION_TYPE_STRING:
|
||||||
|
o->parser = string_parser;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPTION_TYPE_BOOL:
|
||||||
|
o->parser = bool_parser;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_ERROR("BUG: Non int/string/bool option types must have a parser");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!o->toString)
|
||||||
|
{
|
||||||
|
switch(o->type)
|
||||||
|
{
|
||||||
|
case OPTION_TYPE_INT:
|
||||||
|
o->toString = int_toString;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPTION_TYPE_STRING:
|
||||||
|
o->toString = string_toString;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPTION_TYPE_BOOL:
|
||||||
|
o->toString = bool_toString;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_ERROR("BUG: Non int/string/bool option types must implement toString");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ensure the string is locally allocated
|
// ensure the string is locally allocated
|
||||||
if (o->value.type == OPTION_TYPE_STRING)
|
if (o->type == OPTION_TYPE_STRING)
|
||||||
o->value.v.x_string = strdup(o->value.v.x_string);
|
o->value.x_string = strdup(o->value.x_string);
|
||||||
|
|
||||||
// add the option to the correct group for help printout
|
// add the option to the correct group for help printout
|
||||||
bool found = false;
|
bool found = false;
|
||||||
@ -121,8 +206,8 @@ void option_free()
|
|||||||
for(int i = 0; i < state.oCount; ++i)
|
for(int i = 0; i < state.oCount; ++i)
|
||||||
{
|
{
|
||||||
struct Option * o = &state.options[i];
|
struct Option * o = &state.options[i];
|
||||||
if (o->value.type == OPTION_TYPE_STRING)
|
if (o->type == OPTION_TYPE_STRING)
|
||||||
free(o->value.v.x_string);
|
free(o->value.x_string);
|
||||||
}
|
}
|
||||||
free(state.options);
|
free(state.options);
|
||||||
state.options = NULL;
|
state.options = NULL;
|
||||||
@ -133,33 +218,9 @@ void option_free()
|
|||||||
state.gCount = 0;
|
state.gCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool option_set(struct OptionValue * v, const char * value)
|
static bool option_set(struct Option * opt, const char * value)
|
||||||
{
|
{
|
||||||
switch(v->type)
|
return opt->parser(opt, value);
|
||||||
{
|
|
||||||
case OPTION_TYPE_INT:
|
|
||||||
v->v.x_int = atol(value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPTION_TYPE_STRING:
|
|
||||||
free(v->v.x_string);
|
|
||||||
v->v.x_string = strdup(value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPTION_TYPE_BOOL:
|
|
||||||
v->v.x_bool =
|
|
||||||
strcmp(value, "1" ) == 0 ||
|
|
||||||
strcmp(value, "yes" ) == 0 ||
|
|
||||||
strcmp(value, "true") == 0 ||
|
|
||||||
strcmp(value, "on" ) == 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DEBUG_ERROR("BUG: Invalid option type, this should never happen");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool option_parse(int argc, char * argv[])
|
bool option_parse(int argc, char * argv[])
|
||||||
@ -203,7 +264,7 @@ bool option_parse(int argc, char * argv[])
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!option_set(&o->value, value))
|
if (!option_set(o, value))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to set the option value");
|
DEBUG_ERROR("Failed to set the option value");
|
||||||
free(arg);
|
free(arg);
|
||||||
@ -306,7 +367,7 @@ bool option_load(const char * filename)
|
|||||||
case '\n':
|
case '\n':
|
||||||
if (name)
|
if (name)
|
||||||
{
|
{
|
||||||
struct OptionValue * o = option_get(module, name);
|
struct Option * o = option_get(module, name);
|
||||||
if (!o)
|
if (!o)
|
||||||
DEBUG_WARN("Ignored unknown option %s:%s", module, name);
|
DEBUG_WARN("Ignored unknown option %s:%s", module, name);
|
||||||
else
|
else
|
||||||
@ -396,7 +457,7 @@ bool option_validate()
|
|||||||
struct Option * o = &state.options[i];
|
struct Option * o = &state.options[i];
|
||||||
const char * error = NULL;
|
const char * error = NULL;
|
||||||
if (o->validator)
|
if (o->validator)
|
||||||
if (!o->validator(&o->value, &error))
|
if (!o->validator(o, &error))
|
||||||
{
|
{
|
||||||
printf("\nInvalid value provided to the option: %s:%s\n", o->module, o->name);
|
printf("\nInvalid value provided to the option: %s:%s\n", o->module, o->name);
|
||||||
|
|
||||||
@ -430,66 +491,48 @@ void option_print()
|
|||||||
for(int i = 0; i < state.groups[g].count; ++i)
|
for(int i = 0; i < state.groups[g].count; ++i)
|
||||||
{
|
{
|
||||||
struct Option * o = state.groups[g].options[i];
|
struct Option * o = state.groups[g].options[i];
|
||||||
printf(" %s:%-*s - %s [", o->module, state.groups[g].pad, o->name, o->description);
|
char * value = o->toString(o);
|
||||||
|
printf(" %s:%-*s - %s [%s]\n", o->module, state.groups[g].pad, o->name, o->description, value);
|
||||||
switch(o->value.type)
|
free(value);
|
||||||
{
|
|
||||||
case OPTION_TYPE_INT:
|
|
||||||
printf("%d]\n", o->value.v.x_int);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPTION_TYPE_STRING:
|
|
||||||
printf("%s]\n", o->value.v.x_string);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPTION_TYPE_BOOL:
|
|
||||||
printf("%s]\n", o->value.v.x_bool ? "yes" : "no");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DEBUG_ERROR("BUG: Invalid option type, this should never happen");
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OptionValue * option_get(const char * module, const char * name)
|
struct Option * option_get(const char * module, const char * name)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < state.oCount; ++i)
|
for(int i = 0; i < state.oCount; ++i)
|
||||||
{
|
{
|
||||||
struct Option * o = &state.options[i];
|
struct Option * o = &state.options[i];
|
||||||
if ((strcmp(o->module, module) == 0) && (strcmp(o->name, name) == 0))
|
if ((strcmp(o->module, module) == 0) && (strcmp(o->name, name) == 0))
|
||||||
return &o->value;
|
return o;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int option_get_int(const char * module, const char * name)
|
int option_get_int(const char * module, const char * name)
|
||||||
{
|
{
|
||||||
struct OptionValue * o = option_get(module, name);
|
struct Option * o = option_get(module, name);
|
||||||
if (!o)
|
if (!o)
|
||||||
return -1;
|
return -1;
|
||||||
assert(o->type == OPTION_TYPE_INT);
|
assert(o->type == OPTION_TYPE_INT);
|
||||||
return o->v.x_int;
|
return o->value.x_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * option_get_string(const char * module, const char * name)
|
const char * option_get_string(const char * module, const char * name)
|
||||||
{
|
{
|
||||||
struct OptionValue * o = option_get(module, name);
|
struct Option * o = option_get(module, name);
|
||||||
if (!o)
|
if (!o)
|
||||||
return NULL;
|
return NULL;
|
||||||
assert(o->type == OPTION_TYPE_STRING);
|
assert(o->type == OPTION_TYPE_STRING);
|
||||||
return o->v.x_string;
|
return o->value.x_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool option_get_bool(const char * module, const char * name)
|
bool option_get_bool(const char * module, const char * name)
|
||||||
{
|
{
|
||||||
struct OptionValue * o = option_get(module, name);
|
struct Option * o = option_get(module, name);
|
||||||
if (!o)
|
if (!o)
|
||||||
return false;
|
return false;
|
||||||
assert(o->type == OPTION_TYPE_BOOL);
|
assert(o->type == OPTION_TYPE_BOOL);
|
||||||
return o->v.x_bool;
|
return o->value.x_bool;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user