radare2/libr/config/config.c
2016-08-07 17:38:34 +02:00

474 lines
11 KiB
C

/* radare - LGPL - Copyright 2006-2016 - pancake */
#include "r_config.h"
#include "r_util.h" // r_str_hash, r_str_chop, ...
R_API RConfigNode *r_config_node_new(const char *name, const char *value) {
RConfigNode *node;
if (!name || !*name)
return NULL;
node = R_NEW0 (RConfigNode);
if (!node) return NULL;
node->name = strdup (name);
node->hash = r_str_hash (name);
node->value = strdup (value? value: "");
node->flags = CN_RW | CN_STR;
node->i_value = r_num_get (NULL, value);
return node;
}
R_API RConfigNode *r_config_node_clone(RConfigNode *n) {
RConfigNode *cn = R_NEW0 (RConfigNode);
if (!cn) return NULL;
cn->name = strdup (n->name);
cn->desc = n->desc? strdup (n->desc): NULL;
cn->hash = n->hash;
cn->value = strdup (n->value? n->value: "");
cn->i_value = n->i_value;
cn->flags = n->flags;
cn->setter = n->setter;
return cn;
}
R_API void r_config_node_free(void *n) {
RConfigNode *node = (RConfigNode *)n;
if (!node) return;
free (node->name);
free (node->desc);
free (node->value);
free (node);
}
R_API void r_config_list(RConfig *cfg, const char *str, int rad) {
RConfigNode *node;
RListIter *iter;
const char *sfx = "";
const char *pfx = "";
int len = 0;
if (!strnull (str)) {
str = r_str_chop_ro (str);
len = strlen (str);
}
switch (rad) {
case 1:
pfx = "\"e ";
sfx = "\"";
case 0:
r_list_foreach (cfg->nodes, iter, node) {
if (!str || (str && (!strncmp (str, node->name, len))))
cfg->cb_printf ("%s%s = %s%s\n", pfx,
node->name, node->value, sfx);
}
break;
case 2:
r_list_foreach (cfg->nodes, iter, node) {
if (!str || (str && (!strncmp (str, node->name, len))))
if (!str || !strncmp (str, node->name, len))
cfg->cb_printf ("%20s: %s\n", node->name,
node->desc? node->desc: "");
}
break;
case 'j':
cfg->cb_printf ("{");
r_list_foreach (cfg->nodes, iter, node) {
if (!str || (str && (!strncmp (str, node->name, len))))
if (!str || !strncmp (str, node->name, len)) {
const char *val = node->value;
if (node->flags & CN_BOOL || node->flags & CN_INT || node->flags & CN_OFFT) {
if (!val) val = "0";
cfg->cb_printf ("\"%s\":%s",
node->name, val);
} else cfg->cb_printf ("\"%s\":\"%s\"",
node->name, val);
if (iter->n)
cfg->cb_printf (",");
}
}
cfg->cb_printf ("}\n");
break;
}
}
R_API RConfigNode *r_config_node_get(RConfig *cfg, const char *name) {
if (strnull (name))
return NULL;
return r_hashtable_lookup (cfg->ht, r_str_hash (name));
}
R_API int r_config_set_getter(RConfig *cfg, const char *key, RConfigCallback cb) {
RConfigNode *node = r_config_node_get (cfg, key);
if (node) {
node->getter = cb;
return 1;
}
return 0;
}
R_API int r_config_set_setter(RConfig *cfg, const char *key, RConfigCallback cb) {
RConfigNode *node = r_config_node_get (cfg, key);
if (node) {
node->setter = cb;
return 1;
}
return 0;
}
static bool is_true(const char *s) {
return !strcasecmp ("true", s) || !strcasecmp ("1", s);
}
static bool is_bool(const char *s) {
return !strcasecmp ("true", s) || !strcasecmp ("false", s);
}
R_API const char *r_config_get(RConfig *cfg, const char *name) {
RConfigNode *node = r_config_node_get (cfg, name);
if (node) {
if (node->getter) node->getter (cfg->user, node);
cfg->last_notfound = 0;
if (node->flags & CN_BOOL) {
return r_str_bool (is_true (node->value));
}
return node->value;
} else {
eprintf ("r_config_get: variable '%s' not found\n", name);
}
cfg->last_notfound = 1;
return NULL;
}
R_API int r_config_toggle(RConfig *cfg, const char *name) {
RConfigNode *node = r_config_node_get (cfg, name);
if (node && node->flags & CN_BOOL) {
r_config_set_i (cfg, name, !node->i_value);
return true;
}
return false;
}
R_API ut64 r_config_get_i(RConfig *cfg, const char *name) {
RConfigNode *node = r_config_node_get (cfg, name);
if (node) {
if (node->getter)
node->getter (cfg->user, node);
if (node->i_value != 0 || !strcmp (node->value, "false"))
return node->i_value;
return (ut64)r_num_math (cfg->num, node->value);
}
return (ut64)0LL;
}
R_API const char *r_config_node_type(RConfigNode *node) {
if (node) {
int f = node->flags;
if (f & CN_BOOL) return "bool";
if (f & CN_STR) return "str";
if (f & CN_OFFT || f & CN_INT) {
if (!strncmp (node->value, "0x", 2)) {
return "addr";
}
return "int";
}
}
return "";
}
R_API RConfigNode *r_config_set_cb(RConfig *cfg, const char *name, const char *value, RConfigCallback cb) {
RConfigNode *node = r_config_set (cfg, name, value);
if (node && (node->setter = cb))
if (!cb (cfg->user, node))
return NULL;
return node;
}
R_API RConfigNode *r_config_set_i_cb(RConfig *cfg, const char *name, int ivalue, RConfigCallback cb) {
RConfigNode *node = r_config_set_i (cfg, name, ivalue);
if (node && (node->setter = cb))
if (!node->setter (cfg->user, node))
return NULL;
return node;
}
/* TODO: reduce number of strdups here */
R_API RConfigNode *r_config_set(RConfig *cfg, const char *name, const char *value) {
RConfigNode *node;
char *ov = NULL;
ut64 oi;
if (!cfg || strnull (name)) return NULL;
node = r_config_node_get (cfg, name);
if (node) {
if (node->flags & CN_RO) {
eprintf ("(error: '%s' config key is read only)\n", name);
return node;
}
oi = node->i_value;
if (node->value) {
ov = strdup (node->value);
} else {
free (node->value);
node->value = strdup ("");
}
if (node->flags & CN_BOOL) {
bool b = is_true (value);
node->i_value = (ut64) b? 1: 0;
free (node->value);
node->value = strdup (r_str_bool (b));
} else {
if (value == NULL) {
free (node->value);
node->value = strdup ("");
node->i_value = 0;
} else {
free (node->value);
node->value = strdup (value);
if (*value >= '0' && *value <= '9') {
if (strchr (value, '/')) {
node->i_value = r_num_get (cfg->num, value);
} else {
node->i_value = r_num_math (cfg->num, value);
}
} else {
node->i_value = 0;
}
node->flags |= CN_INT;
}
}
} else { // Create a new RConfigNode
oi = UT64_MAX;
if (!cfg->lock) {
node = r_config_node_new (name, value);
if (node) {
if (value && is_bool (value)) {
node->flags |= CN_BOOL;
node->i_value = is_true (value)? 1: 0;
}
if (cfg->ht) {
r_hashtable_insert (cfg->ht, node->hash, node);
r_list_append (cfg->nodes, node);
cfg->n_nodes++;
}
} else {
eprintf ("r_config_set: unable to create a new RConfigNode\n");
}
} else {
eprintf ("r_config_set: variable '%s' not found\n", name);
}
}
if (node && node->setter) {
int ret = node->setter (cfg->user, node);
if (ret == false) {
if (oi != UT64_MAX) node->i_value = oi;
free (node->value);
node->value = strdup (ov? ov: "");
free (ov);
return NULL;
}
}
free (ov);
return node;
}
/* r_config_desc takes a RConfig and a name,
* r_config_node_desc takes a RConfigNode
* Both set and return node->desc */
R_API const char *r_config_desc(RConfig *cfg, const char *name, const char *desc) {
RConfigNode *node = r_config_node_get (cfg, name);
return r_config_node_desc (node, desc);
}
R_API const char *r_config_node_desc(RConfigNode *node, const char *desc) {
if (node) {
if (desc) {
free (node->desc);
node->desc = strdup (desc);
}
return node->desc;
}
return NULL;
}
R_API int r_config_rm(RConfig *cfg, const char *name) {
RConfigNode *node = r_config_node_get (cfg, name);
if (node) {
r_hashtable_remove (cfg->ht, node->hash);
r_list_delete_data (cfg->nodes, node);
cfg->n_nodes--;
return true;
}
return false;
}
R_API RConfigNode *r_config_set_i(RConfig *cfg, const char *name, const ut64 i) {
char buf[128], *ov = NULL;
RConfigNode *node;
if (!cfg || !name)
return NULL;
node = r_config_node_get (cfg, name);
if (node) {
if (node->flags & CN_RO)
return NULL;
if (node->value) {
ov = strdup (node->value);
free (node->value);
}
if (node->flags & CN_BOOL) {
node->value = strdup (r_str_bool (i));
} else {
snprintf (buf, sizeof (buf) - 1, "%" PFMT64d, i);
node->value = strdup (buf);
}
//node->flags = CN_RW | CN_INT;
node->i_value = i;
} else {
if (!cfg->lock) {
if (i < 1024) {
snprintf (buf, sizeof (buf), "%" PFMT64d "", i);
} else {
snprintf (buf, sizeof (buf), "0x%08" PFMT64x "", i);
}
node = r_config_node_new (name, buf);
if (!node) {
return NULL;
}
node->flags = CN_RW | CN_OFFT;
node->i_value = i;
if (cfg->ht) {
r_hashtable_insert (cfg->ht, node->hash, node);
}
if (cfg->nodes) {
r_list_append (cfg->nodes, node);
cfg->n_nodes++;
}
} else eprintf ("(locked: no new keys can be created (%s))\n", name);
}
if (node && node->setter) {
ut64 oi = node->i_value;
int ret = node->setter (cfg->user, node);
if (!ret) {
node->i_value = oi;
free (node->value);
node->value = strdup (ov? ov: "");
}
}
free (ov);
return node;
}
R_API int r_config_eval(RConfig *cfg, const char *str) {
char *ptr, *a, *b, name[1024];
unsigned int len;
if (!str || !cfg) return false;
len = strlen (str) + 1;
if (len >= sizeof (name)) return false;
memcpy (name, str, len);
str = r_str_chop (name);
if (str == NULL) return false;
if (str[0] == '\0' || !strcmp (str, "help")) {
r_config_list (cfg, NULL, 0);
return false;
}
if (str[0] == '-') {
r_config_rm (cfg, str + 1);
return false;
}
ptr = strchr (str, '=');
if (ptr) {
/* set */
ptr[0] = '\0';
a = r_str_chop (name);
b = r_str_chop (ptr + 1);
r_config_set (cfg, a, b);
} else {
char *foo = r_str_chop (name);
if (foo[strlen (foo) - 1] == '.') {
r_config_list (cfg, name, 0);
return false;
} else {
/* get */
const char *str = r_config_get (cfg, foo);
if (str) cfg->cb_printf ("%s\n",
(((int)(size_t)str) == 1)? "true": str);
}
}
return true;
}
static int cmp(RConfigNode *a, RConfigNode *b) {
return strcmp (a->name, b->name);
}
R_API void r_config_lock(RConfig *cfg, int l) {
r_list_sort (cfg->nodes, (RListComparator)cmp);
cfg->lock = l;
}
R_API int r_config_readonly(RConfig *cfg, const char *key) {
RConfigNode *n = r_config_node_get (cfg, key);
if (!n) return false;
n->flags |= CN_RO;
return true;
}
R_API RConfig *r_config_new(void *user) {
RConfig *cfg = R_NEW0 (RConfig);
if (!cfg) return NULL;
cfg->ht = r_hashtable_new ();
cfg->nodes = r_list_new ();
if (!cfg->nodes) {
R_FREE (cfg);
return NULL;
}
cfg->nodes->free = r_config_node_free;
cfg->user = user;
cfg->num = NULL;
cfg->n_nodes = 0;
cfg->lock = 0;
cfg->cb_printf = (void *)printf;
return cfg;
}
R_API RConfig *r_config_clone(RConfig *cfg) {
RListIter *iter;
RConfigNode *node;
RConfig *c = r_config_new (cfg->user);
if (!c) return NULL;
r_list_foreach (cfg->nodes, iter, node) {
RConfigNode *nn = r_config_node_clone (node);
r_hashtable_insert (c->ht, node->hash, nn);
r_list_append (c->nodes, nn);
c->n_nodes++;
}
c->lock = cfg->lock;
c->cb_printf = cfg->cb_printf;
return c;
}
R_API int r_config_free(RConfig *cfg) {
if (!cfg) return 0;
cfg->nodes->free = r_config_node_free; // damn
r_list_free (cfg->nodes);
r_hashtable_free (cfg->ht);
free (cfg);
return 0;
}
R_API void r_config_visual_hit_i(RConfig *cfg, const char *name, int delta) {
RConfigNode *node = r_config_node_get (cfg, name);
if (node && (node->flags & CN_INT || node->flags & CN_OFFT))
r_config_set_i (cfg, name, r_config_get_i (cfg, name) + delta);
}
R_API void r_config_bump(RConfig *cfg, const char *key) {
char *orig = strdup (r_config_get (cfg, key));
r_config_set (cfg, key, orig);
free (orig);
}