rizin/librz/flag/flag.c

984 lines
25 KiB
C

// SPDX-FileCopyrightText: 2007-2020 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2007-2020 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <sdb.h>
#include <rz_flag.h>
#include <rz_util.h>
#include <rz_cons.h>
#include <stdio.h>
RZ_LIB_VERSION(rz_flag);
#define IS_FI_NOTIN_SPACE(f, i) (rz_flag_space_cur(f) && (i)->space != rz_flag_space_cur(f))
#define IS_FI_IN_SPACE(fi, sp) (!(sp) || (fi)->space == (sp))
#define STRDUP_OR_NULL(s) (!RZ_STR_ISEMPTY(s) ? strdup(s) : NULL)
static const char *str_callback(RzNum *user, ut64 off, int *ok) {
RzFlag *f = (RzFlag *)user;
if (ok) {
*ok = 0;
}
if (f) {
const RzList *list = rz_flag_get_list(f, off);
RzFlagItem *item = rz_list_last_val(list);
if (item) {
if (ok) {
*ok = true;
}
return item->name;
}
}
return NULL;
}
static void flag_skiplist_free(void *data) {
RzFlagsAtOffset *item = (RzFlagsAtOffset *)data;
rz_list_free(item->flags);
free(data);
}
static int flag_skiplist_cmp(const void *va, const void *vb, void *user) {
const RzFlagsAtOffset *a = (RzFlagsAtOffset *)va, *b = (RzFlagsAtOffset *)vb;
if (a->off == b->off) {
return 0;
}
return a->off < b->off ? -1 : 1;
}
static ut64 num_callback(RzNum *user, const char *name, int *ok) {
RzFlag *f = (RzFlag *)user;
if (ok) {
*ok = 0;
}
RzFlagItem *item = ht_sp_find(f->ht_name, name, NULL);
if (item) {
// NOTE: to avoid warning infinite loop here we avoid recursivity
if (item->alias) {
return 0LL;
}
if (ok) {
*ok = 1;
}
return item->offset;
}
return 0LL;
}
static void free_item_realname(RzFlagItem *item) {
if (item->name != item->realname) {
free(item->realname);
}
}
static void free_item_name(RzFlagItem *item) {
if (item->name != item->realname) {
free(item->name);
}
}
/* return the list of flag at the nearest position.
dir == -1 -> result <= off
dir == 0 -> result == off
dir == 1 -> result >= off*/
static RzFlagsAtOffset *rz_flag_get_nearest_list(RzFlag *f, ut64 off, int dir) {
RzFlagsAtOffset key = { .off = off };
RzFlagsAtOffset *flags = (dir >= 0)
? rz_skiplist_get_geq(f->by_off, &key)
: rz_skiplist_get_leq(f->by_off, &key);
return (dir == 0 && flags && flags->off != off) ? NULL : flags;
}
static void remove_offsetmap(RzFlag *f, RzFlagItem *item) {
rz_return_if_fail(f && item);
RzFlagsAtOffset *flags = rz_flag_get_nearest_list(f, item->offset, 0);
if (flags) {
rz_list_delete_data(flags->flags, item);
if (rz_list_empty(flags->flags)) {
rz_skiplist_delete(f->by_off, flags);
}
}
}
static RzFlagsAtOffset *flags_at_offset(RzFlag *f, ut64 off) {
RzFlagsAtOffset *res = rz_flag_get_nearest_list(f, off, 0);
if (res) {
return res;
}
// there is no existing flagsAtOffset, we create one now
res = RZ_NEW(RzFlagsAtOffset);
if (!res) {
return NULL;
}
res->flags = rz_list_new();
if (!res->flags) {
free(res);
return NULL;
}
res->off = off;
rz_skiplist_insert(f->by_off, res);
return res;
}
static char *filter_item_name(const char *name) {
char *res = rz_str_dup(name);
if (!res) {
return NULL;
}
rz_str_trim(res);
rz_name_filter(res, 0, true);
return res;
}
static void set_name(RzFlagItem *item, char *name) {
free_item_name(item);
item->name = name;
free_item_realname(item);
item->realname = item->name;
}
static bool update_flag_item_offset(RzFlag *f, RzFlagItem *item, ut64 newoff, bool is_new, bool force) {
if (item->offset != newoff || force) {
if (!is_new) {
remove_offsetmap(f, item);
}
item->offset = newoff;
RzFlagsAtOffset *flagsAtOffset = flags_at_offset(f, newoff);
if (!flagsAtOffset) {
return false;
}
rz_list_append(flagsAtOffset->flags, item);
return true;
}
return false;
}
static bool update_flag_item_name(RzFlag *f, RzFlagItem *item, const char *newname, bool force) {
if (!f || !item || !newname) {
return false;
}
if (!force && (item->name == newname || (item->name && !strcmp(item->name, newname)))) {
return false;
}
char *fname = filter_item_name(newname);
if (!fname) {
return false;
}
bool res = (item->name)
? ht_sp_update_key(f->ht_name, item->name, fname)
: ht_sp_insert(f->ht_name, fname, item);
if (res) {
set_name(item, fname);
return true;
}
free(fname);
return false;
}
static bool count_flags(RzFlagItem *fi, void *user) {
int *count = (int *)user;
(*count)++;
return true;
}
static bool unset_flags_space(RzFlagItem *fi, void *user) {
fi->space = NULL;
return true;
}
static void count_flags_in_space(RzEvent *ev, int type, void *user, void *data) {
RzSpaces *sp = (RzSpaces *)ev->user;
RzFlag *f = container_of(sp, RzFlag, spaces);
RzSpaceEvent *spe = (RzSpaceEvent *)data;
rz_flag_foreach_space(f, spe->data.count.space, count_flags, &spe->res);
}
static void unset_flagspace(RzEvent *ev, int type, void *user, void *data) {
RzSpaces *sp = (RzSpaces *)ev->user;
RzFlag *f = container_of(sp, RzFlag, spaces);
const RzSpaceEvent *spe = (const RzSpaceEvent *)data;
rz_flag_foreach_space(f, spe->data.unset.space, unset_flags_space, NULL);
}
static void new_spaces(RzFlag *f) {
rz_spaces_init(&f->spaces, "fs");
rz_event_hook(f->spaces.event, RZ_SPACE_EVENT_COUNT, count_flags_in_space, NULL);
rz_event_hook(f->spaces.event, RZ_SPACE_EVENT_UNSET, unset_flagspace, NULL);
}
RZ_API RzFlag *rz_flag_new(void) {
RzFlag *f = RZ_NEW0(RzFlag);
if (!f) {
return NULL;
}
f->num = rz_num_new(&num_callback, &str_callback, f);
if (!f->num) {
rz_flag_free(f);
return NULL;
}
f->zones = NULL;
f->tags = sdb_new0();
f->ht_name = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_flag_item_free);
f->by_off = rz_skiplist_new(flag_skiplist_free, flag_skiplist_cmp);
rz_list_free(f->zones);
new_spaces(f);
return f;
}
RZ_API RzFlagItem *rz_flag_item_clone(RzFlagItem *item) {
rz_return_val_if_fail(item, NULL);
RzFlagItem *n = RZ_NEW0(RzFlagItem);
if (!n) {
return NULL;
}
n->color = STRDUP_OR_NULL(item->color);
n->comment = STRDUP_OR_NULL(item->comment);
n->alias = STRDUP_OR_NULL(item->alias);
n->name = STRDUP_OR_NULL(item->name);
n->realname = STRDUP_OR_NULL(item->realname);
n->offset = item->offset;
n->size = item->size;
n->space = item->space;
return n;
}
RZ_API void rz_flag_item_free(RzFlagItem *item) {
if (!item) {
return;
}
free(item->color);
free(item->comment);
free(item->alias);
/* release only one of the two pointers if they are the same */
free_item_name(item);
free(item->realname);
free(item);
}
RZ_API RzFlag *rz_flag_free(RzFlag *f) {
rz_return_val_if_fail(f, NULL);
rz_skiplist_free(f->by_off);
ht_sp_free(f->ht_name);
sdb_free(f->tags);
rz_spaces_fini(&f->spaces);
rz_num_free(f->num);
rz_list_free(f->zones);
free(f);
return NULL;
}
static RzFlagItem *evalFlag(RzFlag *f, RzFlagItem *item) {
rz_return_val_if_fail(f && item, NULL);
if (item->alias) {
item->offset = rz_num_math(f->num, item->alias);
}
return item;
}
/* return true if flag.* exist at offset. Otherwise, false is returned.
* For example (f, "sym", 3, 0x1000)*/
RZ_API bool rz_flag_exist_at(RzFlag *f, const char *flag_prefix, ut16 fp_size, ut64 off) {
rz_return_val_if_fail(f && flag_prefix, false);
RzListIter *iter = NULL;
RzFlagItem *item = NULL;
const RzList *list = rz_flag_get_list(f, off);
if (list) {
rz_list_foreach (list, iter, item) {
if (item->name && !strncmp(item->name, flag_prefix, fp_size)) {
return true;
}
}
}
return false;
}
/* return the flag item with name "name" in the RzFlag "f", if it exists.
* Otherwise, NULL is returned. */
RZ_API RzFlagItem *rz_flag_get(RzFlag *f, const char *name) {
rz_return_val_if_fail(f, NULL);
RzFlagItem *r = ht_sp_find(f->ht_name, name, NULL);
return r ? evalFlag(f, r) : NULL;
}
/**
* \brief Resets all flag spaces associated with object files.
* These are:
* - classes
* - imports
* - relocs
* - sections
* - segments
* - strings
* - symbols
* - globals
* - maps
*
* \param flags The RzFlag instance to use.
* \param backup_filename If not NULL, the RzFlag instance will
* be backed up into the file before the reset.
*
* \return True on success, false othewise.
*/
RZ_API bool rz_flag_reset_obj_flags(RZ_NONNULL RZ_BORROW RzFlag *flags, RZ_NULLABLE const char *backup_filename) {
rz_return_val_if_fail(flags, false);
bool backup_succeeded = !backup_filename;
if (backup_filename) {
Sdb *sdb = sdb_new0();
if (!sdb) {
return false;
}
rz_serialize_flag_save(sdb, flags);
backup_succeeded = sdb_text_save(sdb, backup_filename, false);
}
if (!backup_succeeded) {
RZ_LOG_WARN("Could not backup RzFlag before resetting flag space. Abort flag space reset.\n");
return false;
}
rz_flag_unset_all_in_space(flags, "classes");
rz_flag_unset_all_in_space(flags, "imports");
rz_flag_unset_all_in_space(flags, "relocs");
rz_flag_unset_all_in_space(flags, "sections");
rz_flag_unset_all_in_space(flags, "segments");
rz_flag_unset_all_in_space(flags, "strings");
rz_flag_unset_all_in_space(flags, "symbols");
rz_flag_unset_all_in_space(flags, "globals");
rz_flag_unset_all_in_space(flags, "maps");
return true;
}
/* return the first flag item that can be found at offset "off", or NULL otherwise */
RZ_API RzFlagItem *rz_flag_get_i(RzFlag *f, ut64 off) {
rz_return_val_if_fail(f, NULL);
const RzList *list = rz_flag_get_list(f, off);
return list ? evalFlag(f, rz_list_last_val(list)) : NULL;
}
/* return the first flag that matches an offset ordered by the order of
* operands to the function.
* Pass in the name of each space, in order, followed by a NULL */
RZ_API RzFlagItem *rz_flag_get_by_spaces(RzFlag *f, ut64 off, ...) {
rz_return_val_if_fail(f, NULL);
const RzList *list = rz_flag_get_list(f, off);
RzFlagItem *ret = NULL;
const char *spacename;
RzSpace **spaces;
RzListIter *iter;
RzFlagItem *flg;
va_list ap, aq;
size_t n_spaces = 0, i;
va_start(ap, off);
// some quick checks for common cases
if (rz_list_empty(list)) {
goto beach;
}
if (rz_list_length(list) == 1) {
ret = rz_list_last_val(list);
goto beach;
}
// count spaces in the vaarg
va_copy(aq, ap);
spacename = va_arg(aq, const char *);
while (spacename) {
n_spaces++;
spacename = va_arg(aq, const char *);
}
va_end(aq);
// get RzSpaces from the names
i = 0;
spaces = RZ_NEWS(RzSpace *, n_spaces);
spacename = va_arg(ap, const char *);
while (spacename) {
RzSpace *space = rz_flag_space_get(f, spacename);
if (space) {
spaces[i++] = space;
}
spacename = va_arg(ap, const char *);
}
n_spaces = i;
ut64 min_space_i = n_spaces + 1;
rz_list_foreach (list, iter, flg) {
// get the "priority" of the flag flagspace and
// check if better than what we found so far
for (i = 0; i < n_spaces; i++) {
if (flg->space == spaces[i]) {
break;
}
if (i >= min_space_i) {
break;
}
}
if (i < min_space_i) {
min_space_i = i;
ret = flg;
}
if (!min_space_i) {
// this is the best flag we can find, let's stop immediately
break;
}
}
free(spaces);
beach:
va_end(ap);
return ret ? evalFlag(f, ret) : NULL;
}
static bool isFunctionFlag(const char *n) {
return (!strncmp(n, "sym.func.", 9) || !strncmp(n, "method.", 7) || !strncmp(n, "sym.", 4) || !strncmp(n, "func.", 5) || !strncmp(n, "fcn.0", 5));
}
/* returns the last flag item defined before or at the given offset.
* NULL is returned if such a item is not found. */
RZ_API RzFlagItem *rz_flag_get_at(RzFlag *f, ut64 off, bool closest) {
rz_return_val_if_fail(f, NULL);
RzFlagItem *nice = NULL;
RzListIter *iter;
const RzFlagsAtOffset *flags_at = rz_flag_get_nearest_list(f, off, -1);
if (!flags_at) {
return NULL;
}
if (flags_at->off == off) {
RzFlagItem *item;
rz_list_foreach (flags_at->flags, iter, item) {
if (IS_FI_NOTIN_SPACE(f, item)) {
continue;
}
if (nice) {
if (isFunctionFlag(nice->name)) {
nice = item;
}
} else {
nice = item;
}
}
if (nice) {
return evalFlag(f, nice);
}
}
if (!closest) {
return NULL;
}
while (!nice && flags_at) {
RzFlagItem *item;
rz_list_foreach (flags_at->flags, iter, item) {
if (IS_FI_NOTIN_SPACE(f, item)) {
continue;
}
if (item->offset == off) {
rz_warn_if_reached(); // corrupt skiplist
return NULL;
}
nice = item;
break;
}
if (!nice && flags_at->off) {
flags_at = rz_flag_get_nearest_list(f, flags_at->off - 1, -1);
} else {
flags_at = NULL;
}
}
return nice ? evalFlag(f, nice) : NULL;
}
static bool flag_space_is_in_list(RzList /*<RzSpace *>*/ *spaces, RzFlagItem *item) {
RzListIter *it;
RzSpace *space;
rz_list_foreach (spaces, it, space) {
if (IS_FI_IN_SPACE(item, space)) {
return true;
}
}
return false;
}
/**
* \brief Returns the last flag item close or at the specified offset within the given spaces.
*
* \param f RzFlag object to use
* \param[in] closest When true returns the first flag found at the closest offset
* \param[in] off Offset of the flag to search
* \param[in] ... Spaces to search into (must contain a NULL value).
*
* \return On success returns the flag item close or at the specified offset, otherwise NULL.
*/
RZ_API RZ_BORROW RzFlagItem *rz_flag_get_at_by_spaces(RZ_NONNULL RzFlag *f, bool closest, ut64 off, ...) {
va_list ap;
RzList *spaces = rz_list_new();
if (!spaces) {
return NULL;
}
va_start(ap, off);
const char *space_name = va_arg(ap, const char *);
while (space_name) {
RzSpace *space = rz_flag_space_get(f, space_name);
if (space) {
rz_list_append(spaces, space);
}
space_name = va_arg(ap, const char *);
}
va_end(ap);
RzFlagItem *nice = NULL;
RzListIter *iter;
const RzFlagsAtOffset *flags_at = rz_flag_get_nearest_list(f, off, -1);
if (!flags_at) {
return NULL;
}
if (flags_at->off == off) {
RzFlagItem *item;
rz_list_foreach (flags_at->flags, iter, item) {
if (!flag_space_is_in_list(spaces, item)) {
continue;
}
if (nice) {
if (isFunctionFlag(nice->name)) {
nice = item;
}
} else {
nice = item;
}
}
if (nice) {
return evalFlag(f, nice);
}
}
if (!closest) {
rz_list_free(spaces);
return NULL;
}
while (!nice && flags_at) {
RzFlagItem *item;
rz_list_foreach (flags_at->flags, iter, item) {
if (!flag_space_is_in_list(spaces, item)) {
continue;
}
if (item->offset == off) {
return evalFlag(f, item);
}
nice = item;
break;
}
if (!nice && flags_at->off) {
flags_at = rz_flag_get_nearest_list(f, flags_at->off - 1, -1);
} else {
flags_at = NULL;
}
}
rz_list_free(spaces);
return nice ? evalFlag(f, nice) : NULL;
}
static bool append_to_list(RzFlagItem *fi, void *user) {
RzList *ret = (RzList *)user;
rz_list_append(ret, fi);
return true;
}
RZ_API RzList /*<RzFlagItem *>*/ *rz_flag_all_list(RzFlag *f, bool by_space) {
RzList *ret = rz_list_new();
if (!ret) {
return NULL;
}
RzSpace *cur = by_space ? rz_flag_space_cur(f) : NULL;
rz_flag_foreach_space(f, cur, append_to_list, ret);
return ret;
}
/* return the list of flag items that are associated with a given offset */
RZ_API const RzList /*<RzFlagItem *>*/ *rz_flag_get_list(RzFlag *f, ut64 off) {
const RzFlagsAtOffset *item = rz_flag_get_nearest_list(f, off, 0);
return item ? item->flags : NULL;
}
RZ_API char *rz_flag_get_liststr(RzFlag *f, ut64 off) {
RzFlagItem *fi;
RzListIter *iter;
const RzList *list = rz_flag_get_list(f, off);
char *p = NULL;
rz_list_foreach (list, iter, fi) {
p = rz_str_appendf(p, "%s%s",
fi->realname, rz_list_iter_has_next(iter) ? "," : "");
}
return p;
}
/**
* Set a flag if there is not already a flag with the same name that does
* not match the given \p off and \p size.
* \return whether to stop searching for another name, using bool instead of returning
* a pointer to distinguish between existing name and failed malloc.
*/
bool try_set_flag(RzFlag *f, const char *name, ut64 off, ut32 size, RzFlagItem **r) {
RzFlagItem *fi = rz_flag_get(f, name);
if (fi) {
if (fi->offset == off && fi->size == size) {
*r = fi;
return true;
}
return false;
}
*r = rz_flag_set(f, name, off, size);
return true;
}
/**
* Set a new flag named \p name at \p off. If there's already a flag with
* the same name, slightly change the name by appending the address or ".%d" as suffix.
* If there is a flag at \p off of size \p size and a matching name, that flag is returned
* instead of creating a new one.
*/
RZ_API RzFlagItem *rz_flag_set_next(RzFlag *f, const char *name, ut64 off, ut32 size) {
rz_return_val_if_fail(f && name, NULL);
RzFlagItem *r = NULL;
if (try_set_flag(f, name, off, size, &r)) {
return r;
}
size_t name_len = strlen(name);
static const size_t suffix_size = 16 + 2; // max size of a 64bit addr + '.' + '\0'
char *new_name = malloc(name_len + suffix_size);
if (!new_name) {
return NULL;
}
memcpy(new_name, name, name_len);
snprintf(new_name + name_len, suffix_size, ".%" PFMT64x, off);
if (!try_set_flag(f, new_name, off, size, &r)) {
for (int i = 0; i < 1024 /* some upper bound to prevent unreasonable looping */; i++) {
snprintf(new_name + name_len, 17, ".%d", i);
if (try_set_flag(f, new_name, off, size, &r)) {
break;
}
}
}
free(new_name);
return r;
}
/* create or modify an existing flag item with the given name and parameters.
* The realname of the item will be the same as the name.
* NULL is returned in case of any errors during the process. */
RZ_API RzFlagItem *rz_flag_set(RzFlag *f, const char *name, ut64 off, ut32 size) {
rz_return_val_if_fail(f && name && *name, NULL);
bool is_new = false;
char *itemname = filter_item_name(name);
if (!itemname) {
return NULL;
}
RzFlagItem *item = rz_flag_get(f, itemname);
free(itemname);
if (item && item->offset == off) {
item->size = size;
return item;
}
if (!item) {
item = RZ_NEW0(RzFlagItem);
if (!item) {
goto err;
}
is_new = true;
}
item->space = rz_flag_space_cur(f);
item->size = size;
update_flag_item_offset(f, item, off, is_new, true);
update_flag_item_name(f, item, name, true);
return item;
err:
rz_flag_item_free(item);
return NULL;
}
/* add/replace/remove the alias of a flag item */
RZ_API void rz_flag_item_set_alias(RzFlagItem *item, const char *alias) {
rz_return_if_fail(item);
free(item->alias);
item->alias = RZ_STR_ISEMPTY(alias) ? NULL : rz_str_dup(alias);
}
/* add/replace/remove the comment of a flag item */
RZ_API void rz_flag_item_set_comment(RzFlagItem *item, const char *comment) {
rz_return_if_fail(item);
free(item->comment);
item->comment = RZ_STR_ISEMPTY(comment) ? NULL : rz_str_dup(comment);
}
/* add/replace/remove the realname of a flag item */
RZ_API void rz_flag_item_set_realname(RzFlagItem *item, const char *realname) {
rz_return_if_fail(item);
free_item_realname(item);
item->realname = RZ_STR_ISEMPTY(realname) ? NULL : rz_str_dup(realname);
}
/* add/replace/remove the color of a flag item */
RZ_API const char *rz_flag_item_set_color(RzFlagItem *item, const char *color) {
rz_return_val_if_fail(item, NULL);
free(item->color);
item->color = STRDUP_OR_NULL(color);
return item->color;
}
/* change the name of a flag item, if the new name is available.
* true is returned if everything works well, false otherwise */
RZ_API int rz_flag_rename(RzFlag *f, RzFlagItem *item, const char *name) {
rz_return_val_if_fail(f && item && name && *name, false);
return update_flag_item_name(f, item, name, false);
}
/* \brief unset the given flag \p item.
*
* return true if the item is successfully unset, false otherwise.
* NOTE: the item is freed.
*/
RZ_API bool rz_flag_unset(RzFlag *f, RzFlagItem *item) {
rz_return_val_if_fail(f && item, false);
remove_offsetmap(f, item);
ht_sp_delete(f->ht_name, item->name);
return true;
}
/* \brief unset the first flag item found at offset \p off.
*
* return true if such a flag is found and unset, false otherwise.
*/
RZ_API bool rz_flag_unset_off(RzFlag *f, ut64 off) {
rz_return_val_if_fail(f, false);
RzFlagItem *item = rz_flag_get_i(f, off);
if (item && rz_flag_unset(f, item)) {
return true;
}
return false;
}
struct unset_off_foreach_t {
RzFlag *f;
ut64 offset;
};
static bool unset_off_foreach(void *user, const char *k, const void *v) {
struct unset_off_foreach_t *u = (struct unset_off_foreach_t *)user;
RzFlagItem *fi = (RzFlagItem *)v;
if (u->offset == fi->offset) {
rz_flag_unset(u->f, fi);
}
return true;
}
/* \brief unset the all flag items found at offset \p off.
*
* return true if at least one flag is found and unset, false otherwise.
*/
RZ_API bool rz_flag_unset_all_off(RzFlag *f, ut64 off) {
rz_return_val_if_fail(f, false);
struct unset_off_foreach_t u = { f, off };
ht_sp_foreach(f->ht_name, unset_off_foreach, &u);
return true;
}
struct unset_foreach_t {
RzFlag *f;
int n;
};
static bool unset_foreach(RzFlagItem *fi, void *user) {
struct unset_foreach_t *u = (struct unset_foreach_t *)user;
if (IS_FI_NOTIN_SPACE(u->f, fi)) {
return true;
}
rz_flag_unset(u->f, fi);
u->n++;
return true;
}
/* unset all the flag items that satisfy the given glob.
* return the number of unset items. -1 on error */
// XXX This is O(n^n) because unset_globa iterates all flags and unset too.
RZ_API int rz_flag_unset_glob(RzFlag *f, const char *glob) {
rz_return_val_if_fail(f, -1);
struct unset_foreach_t u = { .f = f, .n = 0 };
rz_flag_foreach_glob(f, glob, unset_foreach, &u);
return u.n;
}
/* unset the flag item with the given name.
* returns true if the item is found and unset, false otherwise. */
RZ_API bool rz_flag_unset_name(RzFlag *f, const char *name) {
rz_return_val_if_fail(f, false);
RzFlagItem *item = ht_sp_find(f->ht_name, name, NULL);
return item && rz_flag_unset(f, item);
}
/* unset all flag items in the RzFlag f */
RZ_API void rz_flag_unset_all(RzFlag *f) {
rz_return_if_fail(f);
ht_sp_free(f->ht_name);
f->ht_name = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_flag_item_free);
rz_skiplist_purge(f->by_off);
rz_spaces_fini(&f->spaces);
new_spaces(f);
}
/**
* \brief Unset all flag items in the space with the given name
*
* \param f an RzFlag
* \param space_name name of the space
*/
RZ_API void rz_flag_unset_all_in_space(RzFlag *f, const char *space_name) {
rz_flag_space_push(f, space_name);
RzList *flags = rz_flag_all_list(f, true);
RzFlagItem *flag;
RzListIter *iter;
rz_list_foreach (flags, iter, flag) {
rz_flag_unset(f, flag);
}
rz_flag_space_pop(f);
rz_list_free(flags);
}
struct flag_relocate_t {
RzFlag *f;
ut64 off;
ut64 off_mask;
ut64 neg_mask;
ut64 to;
int n;
};
static bool flag_relocate_foreach(RzFlagItem *fi, void *user) {
struct flag_relocate_t *u = (struct flag_relocate_t *)user;
ut64 fn = fi->offset & u->neg_mask;
ut64 on = u->off & u->neg_mask;
if (fn == on) {
ut64 fm = fi->offset & u->off_mask;
ut64 om = u->to & u->off_mask;
update_flag_item_offset(u->f, fi, (u->to & u->neg_mask) + fm + om, false, false);
u->n++;
}
return true;
}
RZ_API int rz_flag_relocate(RzFlag *f, ut64 off, ut64 off_mask, ut64 to) {
rz_return_val_if_fail(f, -1);
struct flag_relocate_t u = {
.f = f,
.off = off,
.off_mask = off_mask,
.neg_mask = ~(off_mask),
.to = to,
.n = 0
};
rz_flag_foreach(f, flag_relocate_foreach, &u);
return u.n;
}
RZ_API bool rz_flag_move(RzFlag *f, ut64 at, ut64 to) {
rz_return_val_if_fail(f, false);
RzFlagItem *item = rz_flag_get_i(f, at);
if (item) {
rz_flag_set(f, item->name, to, item->size);
return true;
}
return false;
}
// BIND
RZ_API void rz_flag_bind(RzFlag *f, RzFlagBind *fb) {
rz_return_if_fail(f && fb);
fb->f = f;
fb->exist_at = rz_flag_exist_at;
fb->get = rz_flag_get;
fb->get_at = rz_flag_get_at;
fb->get_at_by_spaces = rz_flag_get_by_spaces;
fb->get_list = rz_flag_get_list;
fb->set = rz_flag_set;
fb->unset = rz_flag_unset;
fb->unset_name = rz_flag_unset_name;
fb->unset_off = rz_flag_unset_off;
fb->set_fs = rz_flag_space_set;
fb->push_fs = rz_flag_space_push;
fb->pop_fs = rz_flag_space_pop;
fb->rename = rz_flag_rename;
}
static bool flag_count_foreach(RzFlagItem *fi, void *user) {
int *count = (int *)user;
(*count)++;
return true;
}
RZ_API int rz_flag_count(RzFlag *f, const char *glob) {
int count = 0;
rz_return_val_if_fail(f, -1);
rz_flag_foreach_glob(f, glob, flag_count_foreach, &count);
return count;
}
#define FOREACH_BODY(condition) \
RzSkipListNode *it, *tmp; \
RzFlagsAtOffset *flags_at; \
RzListIter *it2, *tmp2; \
RzFlagItem *fi; \
rz_skiplist_foreach_safe(f->by_off, it, tmp, flags_at) { \
if (flags_at) { \
rz_list_foreach_safe (flags_at->flags, it2, tmp2, fi) { \
if (condition) { \
if (!cb(fi, user)) { \
return; \
} \
} \
} \
} \
}
RZ_API void rz_flag_foreach(RzFlag *f, RzFlagItemCb cb, void *user) {
FOREACH_BODY(true);
}
RZ_API void rz_flag_foreach_prefix(RzFlag *f, const char *pfx, int pfx_len, RzFlagItemCb cb, void *user) {
pfx_len = pfx_len < 0 ? strlen(pfx) : pfx_len;
FOREACH_BODY(!strncmp(fi->name, pfx, pfx_len));
}
/**
* \param from inclusive
* \param to inclusive
*/
RZ_API void rz_flag_foreach_range(RZ_NONNULL RzFlag *f, ut64 from, ut64 to, RzFlagItemCb cb, void *user) {
rz_return_if_fail(f);
FOREACH_BODY(fi->offset >= from && fi->offset <= to);
}
RZ_API void rz_flag_foreach_glob(RzFlag *f, const char *glob, RzFlagItemCb cb, void *user) {
FOREACH_BODY(!glob || rz_str_glob(fi->name, glob));
}
RZ_API void rz_flag_foreach_space_glob(RzFlag *f, const char *glob, const RzSpace *space, RzFlagItemCb cb, void *user) {
FOREACH_BODY(IS_FI_IN_SPACE(fi, space) && (!glob || rz_str_glob(fi->name, glob)));
}
RZ_API void rz_flag_foreach_space(RzFlag *f, const RzSpace *space, RzFlagItemCb cb, void *user) {
FOREACH_BODY(IS_FI_IN_SPACE(fi, space));
}