static alias_Vector(struct engine_Variable *) _variable_all = ALIAS_VECTOR_INIT;
static alias_Vector(uint32_t) _variable_by_name = ALIAS_VECTOR_INIT;
static int _variable_by_name_compare(const void *ap, const void *bp, void *ud) {
uint32_t a = *(uint32_t *)ap;
uint32_t b = *(uint32_t *)bp;
return strcmp(_variable_all.data[a]->name, _variable_all.data[b]->name);
}
static struct engine_Variable ** _variable_find(const char * name) {
if(_variable_all.length == 0) {
return NULL;
}
struct engine_Variable _search_variable = {.name = name};
uint32_t key = _variable_all.length;
_variable_all.data[key] = &_search_variable;
uint32_t *found = bsearch(&key, _variable_by_name.data, _variable_by_name.length, sizeof(*_variable_by_name.data), _variable_by_name_compare, NULL);
if(found == NULL) {
return NULL;
}
return &_variable_all.data[*found];
}
static void _variable_insert(struct engine_Variable *var) {
uint32_t index = _variable_all.length;
alias_Vector_space_for(&_variable_all, NULL, 2);
alias_Vector_space_for(&_variable_by_name, NULL, 2);
*alias_Vector_push(&_variable_all) = var;
*alias_Vector_push(&_variable_by_name) = index;
qsort(_variable_by_name.data, _variable_by_name.length, sizeof(*_variable_by_name.data), _variable_by_name_compare, NULL);
}
static const char * _variable_get_string(struct engine_Variable *var) {
switch(var->tag) {
case engine_Variable_undefined:
default:
engine_error("invalid variable type");
return alias_str_clone(NULL, "");
case engine_Variable_string:
return alias_str_clone(NULL, *var->_string.ptr);
case engine_Variable_boolean:
return alias_str_clone(NULL, *var->_boolean.ptr ? "true" : "false");
case engine_Variable_integer:
return alias_str_format(NULL, "%ill", *var->_integer.ptr);
case engine_Variable_real:
return alias_str_format(NULL, "%d", *var->_real.ptr);
case engine_Variable_enumerator:
for(uint32_t i = 0; var->_enumerator.options[i].name; i++) {
if(*var->_enumerator.ptr == var->_enumerator.options[i].value) {
return alias_str_clone(NULL, var->_enumerator.options[i].name);
}
}
return alias_str_format(NULL, "%ill", *var->_enumerator.ptr);
}
}
static void _variable_set_string(struct engine_Variable *var, const char *string) {
uint32_t i;
switch(var->tag) {
case engine_Variable_undefined:
alias_str_free(NULL, var->_value);
var->_value = alias_str_clone(NULL, string);
break;
case engine_Variable_string:
if(var->_string.is_cloned) {
alias_str_free(NULL, *var->_string.ptr);
}
*var->_string.ptr = alias_str_clone(NULL, string);
var->_value = *var->_string.ptr;
var->_string.is_cloned = true;
break;
case engine_Variable_boolean:
if(strcmp(string, "false") == 0 || atoi(string) == 0) {
*var->_boolean.ptr = false;
} else {
*var->_boolean.ptr = true;
}
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_integer:
*var->_integer.ptr = atoi(string);
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_real:
*var->_real.ptr = atof(string);
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_enumerator:
for(i = 0; var->_enumerator.options[i].name; i++) {
if(strcmp(var->_enumerator.options[i].name, string) == 0) {
*var->_enumerator.ptr = var->_enumerator.options[i].value;
break;
}
}
if(var->_enumerator.options[i].name == NULL) {
*var->_enumerator.ptr = atoi(string);
}
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
default:
engine_error("invalid variable type");
break;
}
}
static void _variable_set_integer(struct engine_Variable *var, int64_t integer) {
switch(var->tag) {
case engine_Variable_undefined:
alias_str_free(NULL, var->_value);
var->_value = alias_str_format(NULL, "%ull", integer);
break;
case engine_Variable_string:
if(var->_string.is_cloned) {
alias_str_free(NULL, *var->_string.ptr);
}
*var->_string.ptr = alias_str_format(NULL, "%ull", integer);
var->_value = *var->_string.ptr;
var->_string.is_cloned = true;
break;
case engine_Variable_boolean:
if(integer == 0) {
*var->_boolean.ptr = false;
} else {
*var->_boolean.ptr = true;
}
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_integer:
*var->_integer.ptr = integer;
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_real:
*var->_real.ptr = integer;
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_enumerator:
*var->_enumerator.ptr = integer;
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
default:
engine_error("invalid variable type");
break;
}
}
static void _variable_set_real(struct engine_Variable *var, double real) {
switch(var->tag) {
case engine_Variable_undefined:
alias_str_free(NULL, var->_value);
var->_value = alias_str_format(NULL, "%d", real);
break;
case engine_Variable_string:
if(var->_string.is_cloned) {
alias_str_free(NULL, *var->_string.ptr);
}
*var->_string.ptr = alias_str_format(NULL, "%d", real);
var->_value = *var->_string.ptr;
var->_string.is_cloned = true;
break;
case engine_Variable_boolean:
if(real == 0) {
*var->_boolean.ptr = false;
} else {
*var->_boolean.ptr = true;
}
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_integer:
*var->_integer.ptr = real;
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_real:
*var->_real.ptr = real;
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
case engine_Variable_enumerator:
*var->_enumerator.ptr = real;
alias_str_free(NULL, var->_value);
var->_value = _variable_get_string(var);
break;
default:
engine_error("invalid variable type");
break;
}
}
void engine_variable_register(struct engine_Variable *var) {
struct engine_Variable **var_ptr = _variable_find(var->name);
if(var_ptr == NULL) {
var->_value = _variable_get_string(var);
_variable_insert(var);
} else if((*var_ptr)->tag == engine_Variable_undefined) {
alias_str_free(NULL, (*var_ptr)->name);
_variable_set_string(var, (*var_ptr)->_value);
alias_str_free(NULL, (*var_ptr)->_value);
alias_free(NULL, *var_ptr, sizeof(**var_ptr), alignof(**var_ptr));
*var_ptr = var;
} else {
engine_error("attempting to register engine variable %s more than once", var->name);
return;
}
}
const char *engine_variable_get(const char *name, const char *_default) {
struct engine_Variable **var_ptr = _variable_find(name);
if(var_ptr == NULL) {
return _default;
}
return (*var_ptr)->_value;
}
int64_t engine_variable_getInteger(const char *name, int64_t _default) {
struct engine_Variable **var_ptr = _variable_find(name);
if(var_ptr == NULL) {
return _default;
}
switch((*var_ptr)->tag) {
case engine_Variable_boolean:
return *(*var_ptr)->_boolean.ptr;
case engine_Variable_integer:
return *(*var_ptr)->_integer.ptr;
case engine_Variable_real:
return *(*var_ptr)->_real.ptr;
case engine_Variable_enumerator:
return *(*var_ptr)->_enumerator.ptr;
default:
return atoi((*var_ptr)->_value);
}
}
double engine_variable_getReal(const char *name, double _default) {
struct engine_Variable **var_ptr = _variable_find(name);
if(var_ptr == NULL) {
return _default;
}
switch((*var_ptr)->tag) {
case engine_Variable_boolean:
return *(*var_ptr)->_boolean.ptr;
case engine_Variable_integer:
return *(*var_ptr)->_integer.ptr;
case engine_Variable_real:
return *(*var_ptr)->_real.ptr;
case engine_Variable_enumerator:
return *(*var_ptr)->_enumerator.ptr;
default:
return atoi((*var_ptr)->_value);
}
}
static void _variable_undefined(const char *name, const char *value) {
struct engine_Variable *var = alias_malloc(NULL, sizeof(*var), alignof(*var));
var->tag = engine_Variable_undefined;
var->name = alias_str_clone(NULL, name);
var->_value = alias_str_clone(NULL, value);
_variable_insert(var);
}
void engine_variable_set(const char *name, const char *value) {
struct engine_Variable **var_ptr = _variable_find(name);
if(var_ptr == NULL) {
_variable_undefined(name, value);
} else {
_variable_set_string(*var_ptr, value);
}
}
void engine_variable_setInteger(const char *name, int64_t value) {
struct engine_Variable **var_ptr = _variable_find(name);
if(var_ptr == NULL) {
const char *value_as_string;
alias_str_stack_format(value_as_string, PRIi64, value);
_variable_undefined(name, value_as_string);
} else {
_variable_set_integer(*var_ptr, value);
}
}
void engine_variable_setReal(const char *name, double value) {
struct engine_Variable **var_ptr = _variable_find(name);
if(var_ptr == NULL) {
const char *value_as_string;
alias_str_stack_format(value_as_string, "%f", value);
_variable_undefined(name, value_as_string);
} else {
_variable_set_real(*var_ptr, value);
}
}
ENGINE_COMMAND(set, "set <name> <value>") {
if(argc < 3) {
engine_info("missing arguments");
return;
}
engine_variable_set(argv[1], argv[2]);
}