rizin/librz/config/config.c

1710 lines
53 KiB
C

// SPDX-FileCopyrightText: 2026 deroad <deroad@kumo.xn--q9jyb4c>
// SPDX-License-Identifier: LGPL-3.0-only
#include "config_internal.h"
#define config_var_assert_return(expr, name, val) \
do { \
if (RZ_UNLIKELY(!(expr))) { \
RZ_LOG_WARN("%s: assertion '%s' failed (line %d); variable %s\n", RZ_FUNCTION, #expr, __LINE__, name); \
return (val); \
} \
} while (0)
static void config_var_fini(RzConfigVar *var) {
if (!var) {
return;
}
free(var->name);
free(var->desc);
rz_list_free(var->options);
if (RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND)) {
// if is bind, we do not own the value.
return;
}
if (RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_STR)) {
free(var->value.string);
} else if (RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_LIST)) {
rz_list_free(var->value.list);
}
}
static void config_entry_fini(RzConfigEntry *entry) {
if (!entry) {
return;
}
if (!entry->is_variable) {
rz_config_node_fini(&entry->node);
} else {
config_var_fini(&entry->var);
}
}
static inline bool config_var_is_bind_without_set(const RzConfigVar *var) {
return var->flags & RZ_CONFIG_VAR_FLAG_BIND && !var->bind.set_value;
}
static inline bool config_var_set_readonly(RzConfigVar *var, bool read_only) {
if (config_var_is_bind_without_set(var) && !read_only) {
// if set_value is not set is always considered RO
RZ_LOG_ERROR("config: cannot unset '%s' as is an hardcoded read-only variable\n", var->name);
return false;
} else if (read_only) {
ut32 flags = ~RZ_CONFIG_VAR_FLAG_WRITABLE;
var->flags &= flags;
} else {
var->flags |= RZ_CONFIG_VAR_FLAG_WRITABLE;
}
return true;
}
static bool config_var_args_to_list(va_list argp, RzList /*<char *>*/ *list) {
const char *value = va_arg(argp, const char *);
while (value) {
char *copy = rz_str_dup(value);
if (!copy || !rz_list_append(list, copy)) {
free(copy);
return false;
}
value = va_arg(argp, const char *);
}
return true;
}
RZ_IPI RZ_OWN RzList /*<char *>*/ *rz_config_dup_list(RZ_NULLABLE const RzList /*<const char *>*/ *list) {
RzListIter *it;
const char *elem;
RzList *safe_list = rz_list_newf(free);
if (!safe_list) {
return NULL;
}
rz_list_foreach (list, it, elem) {
rz_list_append(safe_list, rz_str_dup(elem));
}
return safe_list;
}
/**
* \brief Allocates and initialize a new RzConfig
*
* \param user Pointer to the user data (not owned & deprecated)
*
* \return On success returns a valid pointer, otherwise NULL
*/
RZ_API RZ_OWN RzConfig *rz_config_new(RZ_DEPRECATE RZ_BORROW void *user) {
RzConfig *cfg = RZ_NEW0(RzConfig);
if (!cfg) {
return NULL;
}
rz_vector_init(&cfg->sorted_vars, sizeof(RzConfigEntry), (RzVectorFree)config_entry_fini, NULL);
// DEPRECATED
cfg->user = user;
return cfg;
}
/**
* \brief Frees RzConfig structure
*
* \param cfg The RzConfig to be freed
*/
RZ_API void rz_config_free(RZ_NULLABLE RzConfig *cfg) {
if (!cfg) {
return;
}
rz_vector_fini(&cfg->sorted_vars);
free(cfg);
}
/**
* \brief Sets a given variable to readonly or writable mode.
*
* \param cfg The RzConfig to modify
* \param name The name of the variable to set as RO/RW
* \param[in] read_only When true sets the variable to readonly mode otherwise allows writing.
*
* \return Returns true if the key is readonly, otherwise false.
*/
RZ_API bool rz_config_set_readonly(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, bool read_only) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return rz_config_node_set_readonly(&entry->node, read_only);
}
return config_var_set_readonly(&entry->var, read_only);
}
/**
* \brief Returns boolean describing if a given variable is readonly or not.
*
* \param cfg The configuration
* \param[in] name The name
*
* \return When true, the queried variable is readonly.
*/
RZ_API bool rz_config_is_readonly(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return rz_config_node_is_ro(&entry->node);
}
return rz_config_var_is_readonly(&entry->var);
}
/**
* \brief Iterates over all the variables set in a give RzConfig struct
*
* \param[in] cfg The configuration to iterate over.
* \param[in] iterator The iterator to call.
* \param user The user pointer passed to the iterator
*/
RZ_API void rz_config_iterate_over(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL RzConfigIterator iterator, RZ_NULLABLE void *user) {
rz_return_if_fail(cfg && iterator);
const RzConfigEntry *entry = NULL;
rz_vector_foreach (&cfg->sorted_vars, entry) {
if (!iterator(entry, user)) {
return;
}
}
}
static inline bool config_init_var_bool(RzConfigVar *var, const char *name, const char *desc, bool value) {
var->name = rz_str_dup(name);
if (!name) {
return false;
}
var->desc = rz_str_dup(desc);
var->flags = RZ_CONFIG_VAR_FLAG_WRITABLE | RZ_CONFIG_VAR_TYPE_BOOL;
var->value.boolean = value;
return true;
}
static inline bool config_init_var_integer(RzConfigVar *var, const char *name, const char *desc, ut64 value) {
var->name = rz_str_dup(name);
if (!name) {
return false;
}
var->desc = rz_str_dup(desc);
var->flags = RZ_CONFIG_VAR_FLAG_WRITABLE | RZ_CONFIG_VAR_TYPE_INT;
var->value.integer = value;
return true;
}
static inline bool config_init_var_string(RzConfigVar *var, const char *name, const char *desc, RZ_NULLABLE const char *value) {
var->name = rz_str_dup(name);
var->value.string = rz_str_dup(value);
if (!var->name) {
return false;
}
var->desc = rz_str_dup(desc);
var->flags = RZ_CONFIG_VAR_FLAG_WRITABLE | RZ_CONFIG_VAR_TYPE_STR;
if (!var->value.string && value) {
return false;
}
return true;
}
static inline bool config_init_var_list(RzConfigVar *var, const char *name, const char *desc, RZ_OWN RzList /*<char *>*/ *value) {
var->name = rz_str_dup(name);
var->value.list = value;
if (!var->name || !var->value.list) {
return false;
}
var->desc = rz_str_dup(desc);
var->flags = RZ_CONFIG_VAR_FLAG_WRITABLE | RZ_CONFIG_VAR_TYPE_LIST;
return true;
}
static inline bool config_init_var_itv(RzConfigVar *var, const char *name, const char *desc, ut64 from, ut64 to) {
if (from > to) {
RZ_LOG_ERROR("config: cannot add '%s' when from > to (0x%08" PFMT64x " > 0x%08" PFMT64x ").\n", name, from, to);
return false;
}
var->name = rz_str_dup(name);
if (!name) {
return false;
}
var->desc = rz_str_dup(desc);
var->flags = RZ_CONFIG_VAR_FLAG_WRITABLE | RZ_CONFIG_VAR_TYPE_ITV;
var->value.interval.addr = from;
var->value.interval.size = to - from;
return true;
}
static inline bool config_var_bind_set_options(RzConfigVar *var) {
if (!var->bind.get_options) {
return true;
}
RzList *options = NULL;
if (!var->bind.get_options(var->bind.user, &options)) {
return false;
}
rz_list_free(var->options);
var->options = options;
return true;
}
static inline bool config_init_var_bind(RzConfigVar *var, const char *name, const char *desc, ut32 flags, RzConfigBindGet get, RzConfigBindSet set, RzConfigBindOpts opts, void *user) {
var->name = rz_str_dup(name);
if (!var->name) {
return false;
}
var->desc = rz_str_dup(desc);
var->flags = RZ_CONFIG_VAR_FLAG_WRITABLE | RZ_CONFIG_VAR_FLAG_BIND | flags;
var->bind.user = user;
var->bind.get_value = get;
var->bind.set_value = set;
var->bind.get_options = opts;
return config_var_bind_set_options(var);
}
/**
* \brief Adds a boolean variable if doesn't exists.
*
* \param cfg The configuration where to add the variable
* \param[in] name The name of the variable to add
* \param[in] desc The description of the variable (optional)
* \param[in] value The value to set this variable to
*
* \return On success returns true, otherwise false
*/
RZ_API bool rz_config_add_bool(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *desc, bool value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry new_entry = { 0 };
if (config_find_entry(cfg, name)) {
RZ_LOG_ERROR("config: variable '%s' already exists.\n", name);
return false;
} else if (!config_init_var_bool(&new_entry.var, name, desc, value)) {
RZ_LOG_ERROR("config: failed to initialize '%s'.\n", name);
config_var_fini(&new_entry.var);
return false;
}
config_add_entry(cfg, name, &new_entry, true);
return true;
}
/**
* \brief Adds an unsigned integer variable if doesn't exists.
*
* \param cfg The configuration where to add the variable
* \param[in] name The name of the variable to add
* \param[in] desc The description of the variable (optional)
* \param[in] value The value to set this variable to
*
* \return On success returns true, otherwise false
*/
RZ_API bool rz_config_add_integer(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *desc, ut64 value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry new_entry = { 0 };
if (config_find_entry(cfg, name)) {
RZ_LOG_ERROR("config: variable '%s' already exists.\n", name);
return false;
} else if (!config_init_var_integer(&new_entry.var, name, desc, value)) {
RZ_LOG_ERROR("config: failed to initialize '%s'.\n", name);
config_var_fini(&new_entry.var);
return false;
}
config_add_entry(cfg, name, &new_entry, true);
return true;
}
/**
* \brief Adds a string variable if doesn't exists.
*
* \param cfg The configuration where to add the variable
* \param[in] name The name of the variable to add
* \param[in] desc The description of the variable (optional)
* \param[in] value The value to set this variable to
*
* \return On success returns true, otherwise false
*/
RZ_API bool rz_config_add_string(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *desc, RZ_NULLABLE const char *value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry new_entry = { 0 };
if (config_find_entry(cfg, name)) {
RZ_LOG_ERROR("config: variable '%s' already exists.\n", name);
return false;
} else if (!config_init_var_string(&new_entry.var, name, desc, value)) {
RZ_LOG_ERROR("config: failed to initialize '%s'.\n", name);
config_var_fini(&new_entry.var);
return false;
}
config_add_entry(cfg, name, &new_entry, true);
return true;
}
/**
* \brief Adds a string variable which accepts only one of specified options
*
* \param cfg The configuration where to add the variable
* \param[in] name The name of the variable to add
* \param[in] desc The description of the variable (optional)
* \param[in] ... The options to set for this variable (the first option is the default value, terminated by NULL)
*
* \return On success returns true, otherwise false
*/
RZ_API bool rz_config_add_options(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *desc, ...) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry new_entry = { 0 };
if (config_find_entry(cfg, name)) {
RZ_LOG_ERROR("config: variable '%s' already exists.\n", name);
return false;
}
va_list argp;
RzList *list = rz_list_newf(free);
if (!list) {
RZ_LOG_ERROR("config: failed to initialize options list for '%s'.\n", name);
config_var_fini(&new_entry.var);
return false;
}
va_start(argp, desc);
bool ok = config_var_args_to_list(argp, list);
va_end(argp);
if (!ok) {
RZ_LOG_ERROR("config: failed to initialize options list for '%s'.\n", name);
rz_list_free(list);
config_var_fini(&new_entry.var);
return false;
}
const char *first = rz_list_first_val(list);
if (!config_init_var_string(&new_entry.var, name, desc, first)) {
RZ_LOG_ERROR("config: failed to initialize '%s'.\n", name);
config_var_fini(&new_entry.var);
return false;
}
new_entry.var.options = list;
config_add_entry(cfg, name, &new_entry, true);
return true;
}
/**
* \brief Adds a list variable if doesn't exists.
*
* \param cfg The configuration where to add the variable
* \param[in] name The name of the variable to add
* \param[in] desc The description of the variable (optional)
* \param[in] ... The values to set this variable to (terminated by a NULL)
*
* \return On success returns true, otherwise false
*/
RZ_API bool rz_config_add_list(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *desc, ...) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry new_entry = { 0 };
if (config_find_entry(cfg, name)) {
RZ_LOG_ERROR("config: variable '%s' already exists.\n", name);
return false;
}
va_list argp;
RzList *list = rz_list_newf(free);
if (!list) {
return false;
}
va_start(argp, desc);
bool ok = config_var_args_to_list(argp, list);
va_end(argp);
if (!ok) {
rz_list_free(list);
return false;
} else if (!config_init_var_list(&new_entry.var, name, desc, list)) {
RZ_LOG_ERROR("config: failed to initialize '%s'.\n", name);
config_var_fini(&new_entry.var);
return false;
}
config_add_entry(cfg, name, &new_entry, true);
return true;
}
/**
* \brief Adds a interval variable if doesn't exists.
*
* \param cfg The configuration where to add the variable
* \param[in] name The name of the variable to add
* \param[in] desc The description of the variable (optional)
* \param[in] from The begin value to set (inclusive, and must be less or equal than "to")
* \param[in] to The end value to set (inclusive, and must be greater or equal than "from")
*
* \return On success returns true, otherwise false
*/
RZ_API bool rz_config_add_interval(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *desc, ut64 from, ut64 to) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry new_entry = { 0 };
if (config_find_entry(cfg, name)) {
RZ_LOG_ERROR("config: variable '%s' already exists.\n", name);
return false;
} else if (!config_init_var_itv(&new_entry.var, name, desc, from, to)) {
RZ_LOG_ERROR("config: failed to initialize '%s'.\n", name);
config_var_fini(&new_entry.var);
return false;
}
config_add_entry(cfg, name, &new_entry, true);
return true;
}
/**
* \brief Adds a string variable (not owned by RzConfig) bound to an external struct, if doesn't exists.
*
* \param cfg The configuration where to add the variable
* \param[in] name The name of the variable to add
* \param[in] desc The description of the variable (optional)
* \param[in] type The type of the variable (see RzConfigVarFlags)
* \param[in] get The callback used to get the value
* \param[in] set The callback used to set the value
* \param[in] opts The callback used to get the options value (optional)
* \param[in] user The pointer to a user set variable (not owned by RzConfig)
*
* \return On success returns true, otherwise false
*/
RZ_API bool rz_config_add_bind(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *desc, ut32 type, RZ_NONNULL RzConfigBindGet get, RZ_NULLABLE RzConfigBindSet set, RZ_NULLABLE RzConfigBindOpts opts, RZ_NULLABLE void *user) {
type &= RZ_CONFIG_VAR_TYPE_MASK;
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name) && get && type > 0, false);
RzConfigEntry new_entry = { 0 };
if (config_find_entry(cfg, name)) {
RZ_LOG_ERROR("config: variable '%s' already exists.\n", name);
return false;
} else if (!config_init_var_bind(&new_entry.var, name, desc, type, get, set, opts, user)) {
RZ_LOG_ERROR("config: failed to initialize '%s'.\n", name);
config_var_fini(&new_entry.var);
return false;
}
config_add_entry(cfg, name, &new_entry, true);
return true;
}
/**
* \brief Converts a given RzConfigVar to json.
*
* \param[in] var The variable
* \param pj PJ structure to write to
* \param[in] key The key name to assign to the value
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_config_var_as_json(RZ_NONNULL const RzConfigVar *var, RZ_NONNULL PJ *pj, RZ_NONNULL const char *key) {
rz_return_val_if_fail(var && pj && key, false);
if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_BOOL)) {
pj_kb(pj, key, rz_config_var_get_bool(var));
} else if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_INT)) {
pj_kn(pj, key, rz_config_var_get_integer(var));
} else if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_STR)) {
pj_ks(pj, key, rz_config_var_get_string(var));
} else if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_LIST)) {
const char *value;
RzListIter *it;
RzList *list = rz_config_var_get_list(var);
pj_ka(pj, key);
rz_list_foreach (list, it, value) {
pj_s(pj, value);
}
pj_end(pj);
rz_list_free(list);
} else if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_ITV)) {
RzInterval itv = rz_config_var_get_interval(var);
pj_ko(pj, key);
pj_kn(pj, "addr", rz_itv_begin(itv));
pj_kn(pj, "size", rz_itv_size(itv));
pj_end(pj);
}
return true;
}
/**
* \brief Returns an owned string that describes the variable value
*
* \param[in] var The RzConfigVar to stringify
*
* \return On success returns a valid pointer, otherwise NULL.
*/
RZ_API RZ_OWN char *rz_config_var_as_string(RZ_NONNULL const RzConfigVar *var) {
rz_return_val_if_fail(var, NULL);
if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_BOOL)) {
bool b = rz_config_var_get_bool(var);
return rz_str_dup(rz_str_bool(b));
} else if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_INT)) {
ut64 i64 = rz_config_var_get_integer(var);
return rz_str_newf("%" PFMT64u, i64);
} else if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_STR)) {
const char *str = rz_config_var_get_string(var);
return rz_str_dup(str);
} else if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_LIST)) {
RzList *list = rz_config_var_get_list(var);
char *value = rz_list_to_str(list, ',', false);
rz_list_free(list);
return value;
} else if (rz_config_var_has_type(var, RZ_CONFIG_VAR_TYPE_ITV)) {
RzInterval itv = rz_config_var_get_interval(var);
return rz_str_newf("0x%08" PFMT64x ",0x%08" PFMT64x, rz_itv_begin(itv), rz_itv_end(itv));
}
return NULL;
}
/**
* \brief Returns boolean describing if a given variable is readonly or not.
*
* \param var The RzConfigVar to use
*
* \return When true, the variable is readonly.
*/
RZ_API bool rz_config_var_is_readonly(RZ_NONNULL const RzConfigVar *var) {
rz_return_val_if_fail(var, false);
if (config_var_is_bind_without_set(var)) {
// if set_value is not set is always considered RO
return true;
}
return !RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_WRITABLE);
}
static inline bool config_var_bind_get_value(const RzConfigVar *var, void *value) {
return var->bind.get_value(var->bind.user, value);
}
static inline bool config_var_bind_set_value(const RzConfigVar *var, const void *value) {
return var->bind.set_value(var->bind.user, value);
}
/**
* \brief Returns the boolean held by the RzConfigVar (must be a boolean type)
*
* \param[in] var The RzConfigVar to use
*
* \return Returns the value held by the RzConfigVar
*/
RZ_API bool rz_config_var_get_bool(RZ_NONNULL const RzConfigVar *var) {
config_var_assert_return(var && RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_BOOL), var ? var->name : "(null)", false);
if (!(RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND))) {
return var->value.boolean;
}
bool value = false;
if (!config_var_bind_get_value(var, &value)) {
return false;
}
return value;
}
/**
* \brief Returns the unsigned integer held by the RzConfigVar (must be an integer type)
*
* \param[in] var The RzConfigVar to use
*
* \return Returns the value held by the RzConfigVar
*/
RZ_API ut64 rz_config_var_get_integer(RZ_NONNULL const RzConfigVar *var) {
config_var_assert_return(var && RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_INT), var ? var->name : "(null)", 0);
if (!(RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND))) {
return var->value.integer;
}
ut64 value = 0;
if (!config_var_bind_get_value(var, &value)) {
return 0;
}
return value;
}
/**
* \brief Returns the string held by the RzConfigVar (must be a string type)
*
* \param[in] var The RzConfigVar to use
*
* \return Returns the value held by the RzConfigVar
*/
RZ_API const char *rz_config_var_get_string(RZ_NONNULL const RzConfigVar *var) {
config_var_assert_return(var && RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_STR), var ? var->name : "(null)", NULL);
if (!(RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND))) {
return rz_str_get(var->value.string);
}
const char *value = NULL;
if (!config_var_bind_get_value(var, &value) || !value) {
return "";
}
return value;
}
/**
* \brief Returns the list held by the RzConfigVar (must be a list type)
*
* \param[in] var The RzConfigVar to use
*
* \return Returns the value held by the RzConfigVar
*/
RZ_API RZ_OWN RzList /*<const char *>*/ *rz_config_var_get_list(RZ_NONNULL const RzConfigVar *var) {
config_var_assert_return(var && RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_LIST), var ? var->name : "(null)", false);
if (!(RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND))) {
return rz_list_clone(var->value.list);
}
RzList /*<char *>*/ *value = NULL;
if (!config_var_bind_get_value(var, &value) || !value) {
return rz_list_new();
}
return value;
}
/**
* \brief Returns the interval held by the RzConfigVar (must be a interval type)
*
* \param[in] var The RzConfigVar to use
*
* \return Returns the value held by the RzConfigVar
*/
RZ_API RzInterval rz_config_var_get_interval(RZ_NONNULL const RzConfigVar *var) {
RzInterval value = { 0 };
config_var_assert_return(var && RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_ITV), var ? var->name : "(null)", value);
if (!(RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND))) {
return var->value.interval;
}
if (!config_var_bind_get_value(var, &value)) {
memset(&value, 0, sizeof(value));
return value;
}
return value;
}
/**
* \brief Checks if the expected type matches the one set in the RzConfigVar struct
*
* \param[in] var The variable to check
* \param[in] etype The type to expect
*
* \return Returns true if the expected type matches the one set in the RzConfigVar struct
*/
RZ_API bool rz_config_var_has_type(RZ_NONNULL const RzConfigVar *var, ut32 etype) {
rz_return_val_if_fail(var, false);
return RZ_CONFIG_VAR_IS_TYPE(var->flags, etype);
}
/**
* \brief Checks if the expected flags are set in the RzConfigVar struct
*
* \param[in] var The variable to check
* \param[in] eflags The flags to expect
*
* \return Returns true if the expected flags are set in the RzConfigVar struct
*/
RZ_API bool rz_config_var_has_flags(RZ_NONNULL const RzConfigVar *var, ut32 eflags) {
rz_return_val_if_fail(var, false);
return RZ_CONFIG_VAR_HAS_FLAG(var->flags, eflags) == eflags;
}
/**
* \brief Returns the encoded flags and type set in RzConfigVar (see RzConfigVarFlags)
*
* \param[in] RzConfigVar The RzConfigVar to use
*
* \return The variable encoded flags and type
*/
RZ_API ut32 rz_config_var_get_flags(RZ_NONNULL const RzConfigVar *var) {
rz_return_val_if_fail(var, 0);
return var->flags;
}
/**
* \brief Returns the name set in RzConfigVar
*
* \param[in] RzConfigVar The RzConfigVar to use
*
* \return The variable name (guaranteed to be always non NULL)
*/
RZ_API const char *rz_config_var_get_name(RZ_NONNULL const RzConfigVar *var) {
rz_return_val_if_fail(var, "");
return rz_str_get(var->name);
}
/**
* \brief Returns the description set in RzConfigVar
*
* \param[in] RzConfigVar The RzConfigVar to use
*
* \return The variable description (guaranteed to be always non NULL)
*/
RZ_API const char *rz_config_var_get_desc(RZ_NONNULL const RzConfigVar *var) {
rz_return_val_if_fail(var, "");
return rz_str_get(var->desc);
}
/**
* \brief Returns the options if set in the RzConfigVar struct
*
* \param[in] RzConfigVar The RzConfigVar to use
*
* \return The options (can be NULL)
*/
RZ_API const RzList /*<char *>*/ *rz_config_var_get_options(RZ_NONNULL const RzConfigVar *var) {
rz_return_val_if_fail(var, NULL);
return var->options;
}
/**
* \brief Converts a given encoded to string (see RzConfigVarFlags)
*
* \param[in] flags The flags to stringify
*
* \return On success returns a valid pointer, otherwise NULL.
*/
RZ_API RZ_OWN char *rz_config_var_flags_as_string(ut32 flags) {
RzStrBuf sb;
rz_strbuf_init(&sb);
ut32 types = flags & RZ_CONFIG_VAR_TYPE_MASK;
flags &= RZ_CONFIG_VAR_FLAGS_MASK;
switch (types) {
case RZ_CONFIG_VAR_TYPE_BOOL:
rz_strbuf_append(&sb, "bool");
break;
case RZ_CONFIG_VAR_TYPE_INT:
rz_strbuf_append(&sb, "integer");
break;
case RZ_CONFIG_VAR_TYPE_STR:
rz_strbuf_append(&sb, "string");
break;
case RZ_CONFIG_VAR_TYPE_LIST:
rz_strbuf_append(&sb, "list");
break;
case RZ_CONFIG_VAR_TYPE_ITV:
rz_strbuf_append(&sb, "interval");
break;
default:
rz_strbuf_appendf(&sb, "type_%u", types);
break;
}
if (flags & RZ_CONFIG_VAR_FLAG_BIND) {
if (rz_strbuf_length(&sb) > 0) {
rz_strbuf_append(&sb, ",");
}
rz_strbuf_append(&sb, "bind");
}
if (!(flags & RZ_CONFIG_VAR_FLAG_WRITABLE)) {
if (rz_strbuf_length(&sb) > 0) {
rz_strbuf_append(&sb, ",");
}
rz_strbuf_append(&sb, "readonly");
}
return rz_strbuf_drain_nofree(&sb);
}
static bool config_var_is_valid_value(const RzConfigVar *var, const void *value) {
if (!var->value.validator) {
return true;
}
return var->value.validator(var->value.validator_user, value);
}
RZ_IPI bool rz_config_var_set_bool(RzConfigVar *var, bool value) {
config_var_assert_return(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_BOOL), var->name, false);
if (rz_config_var_is_readonly(var)) {
RZ_LOG_ERROR("config: '%s' is a read only variable\n", var->name);
return false;
}
if (RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND)) {
return config_var_bind_set_value(var, &value);
} else if (!config_var_is_valid_value(var, &value)) {
return false;
}
var->value.boolean = value;
return true;
}
RZ_IPI bool rz_config_var_set_integer(RzConfigVar *var, ut64 value) {
config_var_assert_return(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_INT), var->name, false);
if (rz_config_var_is_readonly(var)) {
RZ_LOG_ERROR("config: '%s' is a read only variable\n", var->name);
return false;
}
if (RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND)) {
return config_var_bind_set_value(var, &value);
} else if (!config_var_is_valid_value(var, &value)) {
return false;
}
var->value.integer = value;
return true;
}
static bool config_var_has_option(RzConfigVar *var, const char *value) {
if (rz_list_length(var->options) < 1) {
// there are no options so the value is always valid.
return true;
}
// we consider NULL as "" (empty string)
value = rz_str_get(value);
const char *opt;
RzListIter *it;
rz_list_foreach (var->options, it, opt) {
if (RZ_STR_EQ(opt, value)) {
return true;
}
}
return false;
}
RZ_IPI bool rz_config_var_set_string(RzConfigVar *var, const char *value) {
config_var_assert_return(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_STR), var->name, false);
if (rz_config_var_is_readonly(var)) {
RZ_LOG_ERROR("config: '%s' is a read only variable\n", var->name);
return false;
} else if (!config_var_has_option(var, value)) {
value = rz_str_get(value);
RZ_LOG_ERROR("config: '%s' does not support option '%s'\n", var->name, value);
return false;
}
if (RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND)) {
return config_var_bind_set_value(var, value);
} else if (!config_var_is_valid_value(var, value)) {
return false;
}
free(var->value.string);
var->value.string = rz_str_dup(value);
return true;
}
RZ_IPI bool rz_config_var_set_list(RzConfigVar *var, const RzList /*<const char *>*/ *value) {
config_var_assert_return(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_LIST), var->name, false);
if (rz_config_var_is_readonly(var)) {
RZ_LOG_ERROR("config: '%s' is a read only variable\n", var->name);
return false;
}
if (RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND)) {
return config_var_bind_set_value(var, value);
} else if (!config_var_is_valid_value(var, value)) {
return false;
}
rz_list_free(var->value.list);
var->value.list = rz_config_dup_list(value);
return true;
}
RZ_IPI bool rz_config_var_set_list2(RzConfigVar *var, RZ_OWN RzList /*<char *>*/ *value) {
config_var_assert_return(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_LIST), var->name, false);
if (rz_config_var_is_readonly(var)) {
RZ_LOG_ERROR("config: '%s' is a read only variable\n", var->name);
return false;
}
if (RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND)) {
bool ret = config_var_bind_set_value(var, value);
rz_list_free(value);
return ret;
} else if (!config_var_is_valid_value(var, value)) {
rz_list_free(value);
return false;
}
rz_list_free(var->value.list);
var->value.list = value;
return true;
}
RZ_IPI bool rz_config_var_set_interval(RzConfigVar *var, RzInterval value) {
config_var_assert_return(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_ITV), var->name, false);
if (rz_config_var_is_readonly(var)) {
RZ_LOG_ERROR("config: '%s' is a read only variable\n", var->name);
return false;
}
if (RZ_CONFIG_VAR_HAS_FLAG(var->flags, RZ_CONFIG_VAR_FLAG_BIND)) {
return config_var_bind_set_value(var, &value);
} else if (!config_var_is_valid_value(var, &value)) {
return false;
}
var->value.interval = value;
return true;
}
static bool config_set_var_list_from_string(RzConfigVar *var, const char *value) {
config_var_assert_return(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_LIST), var->name, false);
RzList *list = NULL;
if (value) {
list = rz_str_split_duplist(value, ",", true);
} else {
list = rz_list_new();
}
return rz_config_var_set_list2(var, list);
}
static bool config_set_var_interval_from_string(RzConfigVar *var, const char *value) {
config_var_assert_return(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_ITV), var->name, false);
RzInterval itv = { 0 };
RzList *list = rz_str_split_duplist(value, ",", true);
if (rz_list_length(list) == 2) {
ut64 from = rz_num_get(NULL, rz_list_first_val(list));
ut64 to = rz_num_get(NULL, rz_list_last_val(list));
if (to < from) {
RZ_LOG_ERROR("config: cannot set '%s' when from > to (0x%08" PFMT64x " > 0x%08" PFMT64x ").\n", var->name, from, to);
rz_list_free(list);
return false;
}
itv.addr = from;
itv.size = to - from;
}
rz_list_free(list);
return rz_config_var_set_interval(var, itv);
}
RZ_IPI bool rz_config_var_set_any(RzConfigVar *var, const char *value) {
if (RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_BOOL)) {
bool bvalue = value ? rz_str_is_true(value) : false;
return rz_config_var_set_bool(var, bvalue);
} else if (RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_INT)) {
ut64 ivalue = value ? rz_num_get(NULL, value) : 0;
return rz_config_var_set_integer(var, ivalue);
} else if (RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_STR)) {
return rz_config_var_set_string(var, value);
} else if (RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_LIST)) {
return config_set_var_list_from_string(var, value);
} else if (RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_ITV)) {
return config_set_var_interval_from_string(var, value);
}
return false;
}
RZ_IPI bool rz_config_toggle_var_bool(RzConfigVar *var) {
if (!(RZ_CONFIG_VAR_IS_TYPE(var->flags, RZ_CONFIG_VAR_TYPE_BOOL))) {
RZ_LOG_ERROR("config: variable '%s' is not a boolean.\n", var->name);
return false;
}
bool bvalue = rz_config_var_get_bool(var);
return rz_config_var_set_bool(var, !bvalue);
}
/**
* \brief Inverts the value of a held boolean variable (if was true, becomes false, or viceversa)
*
* \param cfg The configuration to use
* \param[in] name The name of the boolean variable to invert
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_toggle_bool(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name) {
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
return rz_config_toggle_var_bool(&entry->var);
}
/**
* \brief Sets the value of a given string variable (must be string type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] value The value to set
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_string(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
rz_config_set(cfg, name, value);
return true;
}
return rz_config_var_set_string(&entry->var, value);
}
/**
* \brief Sets the value of a given unsigned integer variable (must be integer type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] value The value to set
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_integer(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, ut64 value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
rz_config_set_i(cfg, name, value);
return true;
}
return rz_config_var_set_integer(&entry->var, value);
}
/**
* \brief Sets the value of a given boolean variable (must be boolean type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] value The value to set
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_bool(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, bool value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
rz_config_set_b(cfg, name, value);
return true;
}
return rz_config_var_set_bool(&entry->var, value);
}
/**
* \brief Sets the value of a given list variable (must be list type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] value The value to set
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_list(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const RzList /*<const char *>*/ *value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return false;
}
return rz_config_var_set_list(&entry->var, value);
}
/**
* \brief Sets the value of a given list variable (must be list type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] ... The list of values to set (terminated by a NULL)
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_list2(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, ...) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return false;
}
va_list argp;
RzList *list = rz_list_newf(free);
if (!list) {
return false;
}
va_start(argp, name);
bool ok = config_var_args_to_list(argp, list);
va_end(argp);
if (!ok) {
RZ_LOG_ERROR("config: failed to initialize list for '%s'.\n", name);
rz_list_free(list);
return false;
}
return rz_config_var_set_list2(&entry->var, list);
}
/**
* \brief Sets the value of a given list variable (must be list type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] comma_list The value to set as comma separated (example: `a,b,c,d`)
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_list3(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *comma_list) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return false;
}
return config_set_var_list_from_string(&entry->var, comma_list);
}
/**
* \brief Sets the value of a given interval variable (must be interval type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] value The value to set
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_interval(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RzInterval value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return false;
}
return rz_config_var_set_interval(&entry->var, value);
}
/**
* \brief Sets the value of a given interval variable (must be interval type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] from The begin value to set (inclusive, and must be less or equal than "to")
* \param[in] to The end value to set (inclusive, and must be greater or equal than "from")
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_interval2(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, ut64 from, ut64 to) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return false;
}
if (to < from) {
RZ_LOG_ERROR("config: cannot set '%s' when from > to (0x%08" PFMT64x " > 0x%08" PFMT64x ").\n", name, from, to);
return false;
}
RzInterval value = { .addr = from, .size = to - from };
return rz_config_var_set_interval(&entry->var, value);
}
/**
* \brief Sets the value of a given interval variable (must be interval type)
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] comma_itv The value to set as comma separated (example: `0x1000,0x5fff`, `<from>,<to>` are inclusive and from <= to)
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_interval3(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *comma_itv) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return false;
}
return config_set_var_interval_from_string(&entry->var, comma_itv);
}
/**
* \brief Sets the value of a given variable (the string value is converted based on the variable type)
*
* When type is:
* - bool, the value `true` is set if `rz_str_is_true` returns true, otherwise is false (on NULL is set to false).
* - integer, the value is parsed via RzNum (on NULL is set to 0)
* - string, the value is kept as is (on NULL is set to "")
* - list, the expected value is a comma separated list like `a,b,c` (on NULL is set to empty list)
* - interval, the expected value is a comma separated range (inclusive) like `from,to` where from <= to (on NULL is set to [0,0])
*
* \param cfg The configuration to use
* \param[in] name The name of the variable to change
* \param[in] value The value to convert before being set in the variable (can be NULL)
*
* \return On success return true, otherwise false
*/
RZ_API bool rz_config_set_any(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE const char *value) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return rz_config_set(cfg, name, value) != NULL;
}
return rz_config_var_set_any(&entry->var, value);
}
/**
* \brief Returns the boolean held by the RzConfig (must be a boolean type)
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the boolean variable
*
* \return Returns the value held by the RzConfig
*/
RZ_API bool rz_config_get_bool(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL const char *name) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
const RzConfigEntry *entry = config_find_entry_ro(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
if (!entry->is_variable) {
return rz_config_get_b((RzConfig *)cfg, name);
}
return rz_config_var_get_bool(&entry->var);
}
/**
* \brief Returns the unsigned integer held by the RzConfig (must be a integer type)
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the integer variable
*
* \return Returns the value held by the RzConfig
*/
RZ_API ut64 rz_config_get_integer(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL const char *name) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), 0);
const RzConfigEntry *entry = config_find_entry_ro(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return 0;
}
if (!entry->is_variable) {
return rz_config_get_i((RzConfig *)cfg, name);
}
return rz_config_var_get_integer(&entry->var);
}
/**
* \brief Returns the string held by the RzConfig (must be a string type)
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the string variable
*
* \return Returns the value held by the RzConfig
*/
RZ_API const char *rz_config_get_string(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL const char *name) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
const RzConfigEntry *entry = config_find_entry_ro(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return NULL;
}
if (!entry->is_variable) {
return rz_config_get((RzConfig *)cfg, name);
}
return rz_config_var_get_string(&entry->var);
}
/**
* \brief Returns the list held by the RzConfig (must be a list type)
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the list variable
*
* \return Returns the value held by the RzConfig
*/
RZ_API RZ_OWN RzList /*<const char *>*/ *rz_config_get_list(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL const char *name) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
const RzConfigEntry *entry = config_find_entry_ro(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return NULL;
}
if (!entry->is_variable) {
rz_warn_if_reached();
return NULL;
}
return rz_config_var_get_list(&entry->var);
}
/**
* \brief Returns the interval held by the RzConfig (must be a interval type)
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the interval variable
*
* \return Returns the value held by the RzConfig
*/
RZ_API RzInterval rz_config_get_interval(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL const char *name) {
RzInterval zero = { 0 };
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), zero);
const RzConfigEntry *entry = config_find_entry_ro(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return zero;
}
if (!entry->is_variable) {
rz_warn_if_reached();
return zero;
}
return rz_config_var_get_interval(&entry->var);
}
/**
* \brief Returns an owned string that describes the queried variable value
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the variable to stringify
*
* \return On success returns a valid pointer, otherwise NULL.
*/
RZ_API RZ_OWN char *rz_config_get_as_string(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL const char *name) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
const RzConfigEntry *entry = config_find_entry_ro(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return NULL;
}
if (entry->is_variable) {
return rz_config_var_as_string(&entry->var);
}
const char *value = entry->node.value;
if (rz_config_node_is_bool(&entry->node)) {
bool b = rz_str_is_true(value);
value = rz_str_bool(b);
}
return rz_str_dup(value);
}
/**
* \brief Returns the options held by the queried variable value
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the variable to stringify
*
* \return On success returns a pointer, can be NULL.
*/
RZ_API const RzList /*<char *>*/ *rz_config_get_options(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL const char *name) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), NULL);
const RzConfigEntry *entry = config_find_entry_ro(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return NULL;
}
if (entry->is_variable) {
return rz_config_var_get_options(&entry->var);
}
return entry->node.options;
}
static ut32 config_entry_get_flags(const RzConfigEntry *entry) {
if (entry->is_variable) {
return rz_config_var_get_flags(&entry->var);
}
return rz_config_node_get_var_flags(&entry->node);
}
/**
* \brief Returns the encoded flags and type of the queried variable value (see RzConfigVarFlags)
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the variable to stringify
*
* \return On success returns a valid pointer, otherwise NULL.
*/
RZ_API ut32 rz_config_get_flags(RZ_NONNULL const RzConfig *cfg, RZ_NONNULL const char *name) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), 0);
const RzConfigEntry *entry = config_find_entry_ro(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return 0;
}
if (entry->is_variable) {
return rz_config_var_get_flags(&entry->var);
}
return rz_config_node_get_var_flags(&entry->node);
}
static bool config_entry_set_options(RzConfigEntry *entry, RZ_NULLABLE RZ_OWN RzList /*<char *>*/ *options) {
ut32 flags = config_entry_get_flags(entry);
if (!(RZ_CONFIG_VAR_IS_TYPE(flags, RZ_CONFIG_VAR_TYPE_STR))) {
const char *name = rz_config_entry_get_name(entry);
RZ_LOG_ERROR("config: variable '%s' is not a string.\n", name);
rz_list_free(options);
return false;
}
if (entry->is_variable) {
rz_list_free(entry->var.options);
entry->var.options = options;
} else {
rz_list_free(entry->node.options);
entry->node.options = options;
}
return true;
}
/**
* \brief Sets the options held by the string type variable
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the string type variable to update
* \param[in] options The options to set
*
* \return On success returns a true, otherwise false.
*/
RZ_API bool rz_config_set_options(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE RZ_OWN RzList /*<char *>*/ *options) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
rz_list_free(options);
return false;
}
return config_entry_set_options(entry, options);
}
/**
* \brief Sets the options held by the string type variable
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the string type variable to update
* \param[in] ... The list of options to set (NULL terminated)
*
* \return On success returns a true, otherwise false.
*/
RZ_API bool rz_config_set_options2(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, ...) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
}
va_list argp;
RzList *options = rz_list_newf(free);
if (!options) {
return false;
}
va_start(argp, name);
bool ok = config_var_args_to_list(argp, options);
va_end(argp);
if (!ok) {
rz_list_free(options);
return false;
}
return config_entry_set_options(entry, options);
}
/**
* \brief Sets the validator callback for a given RzConfigVar (owned types).
*
* \param[in] cfg The RzConfig to use
* \param[in] name The name of the string type variable to update
* \param[in] validator The validator callback to set (can be NULL)
*
* \return On success returns a true, otherwise false.
*/
RZ_API bool rz_config_set_validator(RZ_NONNULL RzConfig *cfg, RZ_NONNULL const char *name, RZ_NULLABLE RzConfigBindSet validator, RZ_NULLABLE void *user) {
rz_return_val_if_fail(cfg && RZ_STR_ISNOTEMPTY(name), false);
RzConfigEntry *entry = config_find_entry(cfg, name);
if (!entry) {
RZ_LOG_ERROR("config: variable '%s' does not exists.\n", name);
return false;
} else if (!entry->is_variable) {
RZ_LOG_ERROR("config: variable '%s' is not an RzConfigVar.\n", name);
return false;
} else if (rz_config_var_has_flags(&entry->var, RZ_CONFIG_VAR_FLAG_BIND)) {
RZ_LOG_ERROR("config: cannot set validator when variable '%s' is a bind.\n", name);
return false;
} else if (rz_config_var_get_options(&entry->var)) {
RZ_LOG_ERROR("config: cannot set validator for '%s' when options are set.\n", name);
return false;
}
entry->var.value.validator = validator;
entry->var.value.validator_user = user;
return true;
}
/**
* \brief DEPRECATED: returns the RzConfigEntry name
*
* \param[in] entry The RzConfigEntry to use
*
* \return On success returns a valid pointer (guaranteed to not be NULL)
*/
RZ_API const char *rz_config_entry_get_name(RZ_NONNULL const RzConfigEntry *entry) {
rz_return_val_if_fail(entry, "");
if (entry->is_variable) {
return rz_str_get(entry->var.name);
}
return rz_str_get(entry->node.name);
}
/**
* \brief DEPRECATED: returns the RzConfigEntry description
*
* \param[in] entry The RzConfigEntry to use
*
* \return On success returns a valid pointer (guaranteed to not be NULL)
*/
RZ_API const char *rz_config_entry_get_desc(RZ_NONNULL const RzConfigEntry *entry) {
rz_return_val_if_fail(entry, "");
if (entry->is_variable) {
return rz_str_get(entry->var.desc);
}
return rz_str_get(entry->node.desc);
}
/**
* \brief DEPRECATED: returns the RzConfigEntry variable held unsigned integer
*
* \param[in] entry The RzConfigEntry to use
*
* \return Returns the value held by the RzConfigEntry struct
*/
RZ_API ut64 rz_config_entry_get_integer(RZ_NONNULL const RzConfigEntry *entry) {
rz_return_val_if_fail(entry, 0);
if (entry->is_variable) {
return rz_config_var_get_integer(&entry->var);
}
return entry->node.i_value;
}
/**
* \brief DEPRECATED: returns the RzConfigEntry variable held boolean
*
* \param[in] entry The RzConfigEntry to use
*
* \return Returns the value held by the RzConfigEntry struct
*/
RZ_API bool rz_config_entry_get_bool(RZ_NONNULL const RzConfigEntry *entry) {
rz_return_val_if_fail(entry, false);
if (entry->is_variable) {
return rz_config_var_get_bool(&entry->var);
}
return rz_str_is_true(rz_str_get(entry->node.value));
}
/**
* \brief DEPRECATED: returns the RzConfigEntry variable held string
*
* \param[in] entry The RzConfigEntry to use
*
* \return Returns the value held by the RzConfigEntry struct
*/
RZ_API const char *rz_config_entry_get_string(RZ_NONNULL const RzConfigEntry *entry) {
rz_return_val_if_fail(entry, NULL);
if (entry->is_variable) {
return rz_config_var_get_string(&entry->var);
}
return rz_str_get(entry->node.value);
}
/**
* \brief DEPRECATED: returns the RzConfigEntry variable as owned string
*
* \param[in] entry The RzConfigEntry to stringify
*
* \return On success returns a valid pointer, otherwise NULL.
*/
RZ_API RZ_OWN char *rz_config_entry_get_as_string(RZ_NONNULL const RzConfigEntry *entry) {
rz_return_val_if_fail(entry, NULL);
if (entry->is_variable) {
return rz_config_var_as_string(&entry->var);
}
const char *value = rz_str_get(entry->node.value);
return rz_str_dup(value);
}