// SPDX-License-Identifier: LGPL-3.0-only

#include <rz_analysis.h>
#include <rz_util.h>
#include <rz_list.h>
#include <rz_io.h>
#include <config.h>

RZ_LIB_VERSION(rz_analysis);

static RzAnalysisPlugin *analysis_static_plugins[] = {
	RZ_ANALYSIS_STATIC_PLUGINS
};

RZ_API void rz_analysis_set_limits(RzAnalysis *analysis, ut64 from, ut64 to) {
	free(analysis->limit);
	analysis->limit = RZ_NEW0(RzAnalysisRange);
	if (analysis->limit) {
		analysis->limit->from = from;
		analysis->limit->to = to;
	}
}

RZ_API void rz_analysis_unset_limits(RzAnalysis *analysis) {
	RZ_FREE(analysis->limit);
}

static void meta_unset_for(RzEvent *ev, int type, void *user, void *data) {
	RzSpaces *s = (RzSpaces *)ev->user;
	RzAnalysis *analysis = container_of(s, RzAnalysis, meta_spaces);
	RzSpaceEvent *se = (RzSpaceEvent *)data;
	rz_meta_space_unset_for(analysis, se->data.unset.space);
}

static void meta_count_for(RzEvent *ev, int type, void *user, void *data) {
	RzSpaces *s = (RzSpaces *)ev->user;
	RzAnalysis *analysis = container_of(s, RzAnalysis, meta_spaces);
	RzSpaceEvent *se = (RzSpaceEvent *)data;
	se->res = rz_meta_space_count_for(analysis, se->data.count.space);
}

static void zign_unset_for(RzEvent *ev, int type, void *user, void *data) {
	RzSpaces *s = (RzSpaces *)ev->user;
	RzAnalysis *analysis = container_of(s, RzAnalysis, zign_spaces);
	RzSpaceEvent *se = (RzSpaceEvent *)data;
	rz_sign_space_unset_for(analysis, se->data.unset.space);
}

static void zign_count_for(RzEvent *ev, int type, void *user, void *data) {
	RzSpaces *s = (RzSpaces *)ev->user;
	RzAnalysis *analysis = container_of(s, RzAnalysis, zign_spaces);
	RzSpaceEvent *se = (RzSpaceEvent *)data;
	se->res = rz_sign_space_count_for(analysis, se->data.count.space);
}

static void zign_rename_for(RzEvent *ev, int type, void *user, void *data) {
	RzSpaces *s = (RzSpaces *)ev->user;
	RzAnalysis *analysis = container_of(s, RzAnalysis, zign_spaces);
	RzSpaceEvent *se = (RzSpaceEvent *)data;
	rz_sign_space_rename_for(analysis, se->data.rename.space,
		se->data.rename.oldname, se->data.rename.newname);
}

void rz_analysis_hint_storage_init(RzAnalysis *a);
void rz_analysis_hint_storage_fini(RzAnalysis *a);

static void rz_meta_item_fini(RzAnalysisMetaItem *item) {
	free(item->str);
}

static void rz_meta_item_free(void *_item) {
	if (_item) {
		RzAnalysisMetaItem *item = _item;
		rz_meta_item_fini(item);
		free(item);
	}
}

RZ_API RzAnalysis *rz_analysis_new(void) {
	int i;
	RzAnalysis *analysis = RZ_NEW0(RzAnalysis);
	if (!analysis) {
		return NULL;
	}
	if (!rz_str_constpool_init(&analysis->constpool)) {
		free(analysis);
		return NULL;
	}
	analysis->bb_tree = NULL;
	analysis->ht_addr_fun = ht_up_new0();
	analysis->ht_name_fun = ht_pp_new0();
	analysis->os = strdup(RZ_SYS_OS);
	analysis->esil_goto_limit = RZ_ANALYSIS_ESIL_GOTO_LIMIT;
	analysis->opt.nopskip = true; // skip nops in code analysis
	analysis->opt.hpskip = false; // skip `mov reg,reg` and `lea reg,[reg]`
	analysis->gp = 0LL;
	analysis->sdb = sdb_new0();
	analysis->cpp_abi = RZ_ANALYSIS_CPP_ABI_ITANIUM;
	analysis->opt.depth = 32;
	analysis->opt.noncode = false; // do not analyze data by default
	rz_spaces_init(&analysis->meta_spaces, "CS");
	rz_event_hook(analysis->meta_spaces.event, RZ_SPACE_EVENT_UNSET, meta_unset_for, NULL);
	rz_event_hook(analysis->meta_spaces.event, RZ_SPACE_EVENT_COUNT, meta_count_for, NULL);

	rz_spaces_init(&analysis->zign_spaces, "zs");
	rz_event_hook(analysis->zign_spaces.event, RZ_SPACE_EVENT_UNSET, zign_unset_for, NULL);
	rz_event_hook(analysis->zign_spaces.event, RZ_SPACE_EVENT_COUNT, zign_count_for, NULL);
	rz_event_hook(analysis->zign_spaces.event, RZ_SPACE_EVENT_RENAME, zign_rename_for, NULL);
	rz_analysis_hint_storage_init(analysis);
	rz_interval_tree_init(&analysis->meta, rz_meta_item_free);
	analysis->sdb_types = sdb_ns(analysis->sdb, "types", 1);
	analysis->sdb_fmts = sdb_ns(analysis->sdb, "spec", 1);
	analysis->sdb_cc = sdb_ns(analysis->sdb, "cc", 1);
	analysis->sdb_zigns = sdb_ns(analysis->sdb, "zigns", 1);
	analysis->sdb_classes = sdb_ns(analysis->sdb, "classes", 1);
	analysis->sdb_classes_attrs = sdb_ns(analysis->sdb_classes, "attrs", 1);
	analysis->zign_path = strdup("");
	analysis->cb_printf = (PrintfCallback)printf;
	(void)rz_analysis_pin_init(analysis);
	(void)rz_analysis_xrefs_init(analysis);
	analysis->diff_thbb = RZ_ANALYSIS_THRESHOLDBB;
	analysis->diff_thfcn = RZ_ANALYSIS_THRESHOLDFCN;
	analysis->syscall = rz_syscall_new();
	rz_io_bind_init(analysis->iob);
	rz_flag_bind_init(analysis->flb);
	analysis->reg = rz_reg_new();
	analysis->last_disasm_reg = NULL;
	analysis->stackptr = 0;
	analysis->lineswidth = 0;
	analysis->fcns = rz_list_newf(rz_analysis_function_free);
	analysis->leaddrs = NULL;
	analysis->imports = rz_list_newf(free);
	rz_analysis_set_bits(analysis, 32);
	analysis->plugins = rz_list_newf((RzListFree)rz_analysis_plugin_free);
	if (analysis->plugins) {
		for (i = 0; analysis_static_plugins[i]; i++) {
			rz_analysis_add(analysis, analysis_static_plugins[i]);
		}
	}
	return analysis;
}

RZ_API void rz_analysis_plugin_free(RzAnalysisPlugin *p) {
	if (p && p->fini) {
		p->fini(NULL);
	}
}

void __block_free_rb(RBNode *node, void *user);

RZ_API RzAnalysis *rz_analysis_free(RzAnalysis *a) {
	if (!a) {
		return NULL;
	}
	/* TODO: Free anals here */
	rz_list_free(a->fcns);
	ht_up_free(a->ht_addr_fun);
	ht_pp_free(a->ht_name_fun);
	set_u_free(a->visited);
	rz_analysis_hint_storage_fini(a);
	rz_interval_tree_fini(&a->meta);
	free(a->cpu);
	free(a->os);
	free(a->zign_path);
	rz_list_free(a->plugins);
	rz_rbtree_free(a->bb_tree, __block_free_rb, NULL);
	rz_spaces_fini(&a->meta_spaces);
	rz_spaces_fini(&a->zign_spaces);
	rz_analysis_pin_fini(a);
	rz_syscall_free(a->syscall);
	rz_reg_free(a->reg);
	ht_up_free(a->dict_refs);
	ht_up_free(a->dict_xrefs);
	rz_list_free(a->leaddrs);
	sdb_free(a->sdb);
	if (a->esil) {
		rz_analysis_esil_free(a->esil);
		a->esil = NULL;
	}
	free(a->last_disasm_reg);
	rz_list_free(a->imports);
	rz_str_constpool_fini(&a->constpool);
	free(a);
	return NULL;
}

RZ_API void rz_analysis_set_user_ptr(RzAnalysis *analysis, void *user) {
	analysis->user = user;
}

RZ_API int rz_analysis_add(RzAnalysis *analysis, RzAnalysisPlugin *foo) {
	if (foo->init) {
		foo->init(analysis->user);
	}
	rz_list_append(analysis->plugins, foo);
	return true;
}

RZ_API bool rz_analysis_use(RzAnalysis *analysis, const char *name) {
	RzListIter *it;
	RzAnalysisPlugin *h;

	if (analysis) {
		rz_list_foreach (analysis->plugins, it, h) {
			if (!h->name || strcmp(h->name, name)) {
				continue;
			}
#if 0
			// regression happening here for asm.emu
			if (analysis->cur && analysis->cur == h) {
				return true;
			}
#endif
			analysis->cur = h;
			rz_analysis_set_reg_profile(analysis);
			return true;
		}
	}
	return false;
}

RZ_API char *rz_analysis_get_reg_profile(RzAnalysis *analysis) {
	return (analysis && analysis->cur && analysis->cur->get_reg_profile)
		? analysis->cur->get_reg_profile(analysis)
		: NULL;
}

// deprecate.. or at least reuse get_reg_profile...
RZ_API bool rz_analysis_set_reg_profile(RzAnalysis *analysis) {
	bool ret = false;
	if (analysis && analysis->cur && analysis->cur->set_reg_profile) {
		ret = analysis->cur->set_reg_profile(analysis);
	} else {
		char *p = rz_analysis_get_reg_profile(analysis);
		if (p && *p) {
			rz_reg_set_profile_string(analysis->reg, p);
			ret = true;
		}
		free(p);
	}
	return ret;
}

RZ_API bool rz_analysis_set_triplet(RzAnalysis *analysis, const char *os, const char *arch, int bits) {
	rz_return_val_if_fail(analysis, false);
	if (!os || !*os) {
		os = RZ_SYS_OS;
	}
	if (!arch || !*arch) {
		arch = analysis->cur ? analysis->cur->arch : RZ_SYS_ARCH;
	}
	if (bits < 1) {
		bits = analysis->bits;
	}
	free(analysis->os);
	analysis->os = strdup(os);
	rz_analysis_set_bits(analysis, bits);
	return rz_analysis_use(analysis, arch);
}

// copypasta from core/cbin.c
static void sdb_concat_by_path(Sdb *s, const char *path) {
	Sdb *db = sdb_new(0, path, 0);
	sdb_merge(s, db);
	sdb_close(db);
	sdb_free(db);
}

RZ_API bool rz_analysis_set_os(RzAnalysis *analysis, const char *os) {
	Sdb *types = analysis->sdb_types;
	const char *dir_prefix = rz_sys_prefix(NULL);
	const char *dbpath = sdb_fmt(RZ_JOIN_3_PATHS("%s", RZ_SDB_FCNSIGN, "types-%s.sdb"),
		dir_prefix, os);
	if (rz_file_exists(dbpath)) {
		sdb_concat_by_path(types, dbpath);
	}
	return rz_analysis_set_triplet(analysis, os, NULL, -1);
}

RZ_API bool rz_analysis_set_bits(RzAnalysis *analysis, int bits) {
	switch (bits) {
	case 8:
	case 16:
	case 27:
	case 32:
	case 64:
		if (analysis->bits != bits) {
			analysis->bits = bits;
			rz_analysis_set_reg_profile(analysis);
		}
		return true;
	}
	return false;
}

RZ_API void rz_analysis_set_cpu(RzAnalysis *analysis, const char *cpu) {
	free(analysis->cpu);
	analysis->cpu = cpu ? strdup(cpu) : NULL;
	int v = rz_analysis_archinfo(analysis, RZ_ANALYSIS_ARCHINFO_ALIGN);
	if (v != -1) {
		analysis->pcalign = v;
	}
}

RZ_API int rz_analysis_set_big_endian(RzAnalysis *analysis, int bigend) {
	analysis->big_endian = bigend;
	analysis->reg->big_endian = bigend;
	return true;
}

RZ_API ut8 *rz_analysis_mask(RzAnalysis *analysis, int size, const ut8 *data, ut64 at) {
	RzAnalysisOp *op = NULL;
	ut8 *ret = NULL;
	int oplen, idx = 0;

	if (!data) {
		return NULL;
	}

	if (analysis->cur && analysis->cur->analysis_mask) {
		return analysis->cur->analysis_mask(analysis, size, data, at);
	}

	if (!(op = rz_analysis_op_new())) {
		return NULL;
	}

	if (!(ret = malloc(size))) {
		rz_analysis_op_free(op);
		return NULL;
	}

	memset(ret, 0xff, size);

	while (idx < size) {
		if ((oplen = rz_analysis_op(analysis, op, at, data + idx, size - idx, RZ_ANALYSIS_OP_MASK_BASIC)) < 1) {
			break;
		}
		if ((op->ptr != UT64_MAX || op->jump != UT64_MAX) && op->nopcode != 0) {
			memset(ret + idx + op->nopcode, 0, oplen - op->nopcode);
		}
		idx += oplen;
		at += oplen;
	}

	rz_analysis_op_free(op);

	return ret;
}

RZ_API void rz_analysis_trace_bb(RzAnalysis *analysis, ut64 addr) {
	RzAnalysisBlock *bbi;
	RzAnalysisFunction *fcni;
	RzListIter *iter2;
	fcni = rz_analysis_get_fcn_in(analysis, addr, 0);
	if (fcni) {
		rz_list_foreach (fcni->bbs, iter2, bbi) {
			if (addr >= bbi->addr && addr < (bbi->addr + bbi->size)) {
				bbi->traced = true;
				break;
			}
		}
	}
}

RZ_API RzList *rz_analysis_get_fcns(RzAnalysis *analysis) {
	// avoid received to free this thing
	analysis->fcns->free = NULL;
	return analysis->fcns;
}

RZ_API RzAnalysisOp *rz_analysis_op_hexstr(RzAnalysis *analysis, ut64 addr, const char *str) {
	RzAnalysisOp *op = RZ_NEW0(RzAnalysisOp);
	if (!op) {
		return NULL;
	}
	ut8 *buf = calloc(1, strlen(str) + 1);
	if (!buf) {
		free(op);
		return NULL;
	}
	int len = rz_hex_str2bin(str, buf);
	rz_analysis_op(analysis, op, addr, buf, len, RZ_ANALYSIS_OP_MASK_BASIC);
	free(buf);
	return op;
}

RZ_API bool rz_analysis_op_is_eob(RzAnalysisOp *op) {
	if (op->eob) {
		return true;
	}
	switch (op->type) {
	case RZ_ANALYSIS_OP_TYPE_JMP:
	case RZ_ANALYSIS_OP_TYPE_UJMP:
	case RZ_ANALYSIS_OP_TYPE_RJMP:
	case RZ_ANALYSIS_OP_TYPE_IJMP:
	case RZ_ANALYSIS_OP_TYPE_IRJMP:
	case RZ_ANALYSIS_OP_TYPE_CJMP:
	case RZ_ANALYSIS_OP_TYPE_RET:
	case RZ_ANALYSIS_OP_TYPE_TRAP:
		return true;
	default:
		return false;
	}
}

RZ_API void rz_analysis_purge(RzAnalysis *analysis) {
	rz_analysis_hint_clear(analysis);
	rz_interval_tree_fini(&analysis->meta);
	rz_interval_tree_init(&analysis->meta, rz_meta_item_free);
	sdb_reset(analysis->sdb_types);
	sdb_reset(analysis->sdb_zigns);
	sdb_reset(analysis->sdb_classes);
	sdb_reset(analysis->sdb_classes_attrs);
	rz_analysis_pin_fini(analysis);
	rz_analysis_pin_init(analysis);
	sdb_reset(analysis->sdb_cc);
	rz_list_free(analysis->fcns);
	analysis->fcns = rz_list_newf(rz_analysis_function_free);
	rz_analysis_purge_imports(analysis);
}

RZ_API int rz_analysis_archinfo(RzAnalysis *analysis, int query) {
	rz_return_val_if_fail(analysis, -1);
	switch (query) {
	case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE:
	case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE:
	case RZ_ANALYSIS_ARCHINFO_ALIGN:
		if (analysis->cur && analysis->cur->archinfo) {
			return analysis->cur->archinfo(analysis, query);
		}
		break;
	}
	return -1;
}

static bool __nonreturn_print_commands(void *p, const char *k, const char *v) {
	RzAnalysis *analysis = (RzAnalysis *)p;
	if (!strncmp(v, "func", strlen("func") + 1)) {
		char *query = sdb_fmt("func.%s.noreturn", k);
		if (sdb_bool_get(analysis->sdb_types, query, NULL)) {
			analysis->cb_printf("tnn %s\n", k);
		}
	}
	if (!strncmp(k, "addr.", 5)) {
		analysis->cb_printf("tna 0x%s %s\n", k + 5, v);
	}
	return true;
}

static bool __nonreturn_print(void *p, const char *k, const char *v) {
	RzAnalysis *analysis = (RzAnalysis *)p;
	if (!strncmp(k, "func.", 5) && strstr(k, ".noreturn")) {
		char *s = strdup(k + 5);
		char *d = strchr(s, '.');
		if (d) {
			*d = 0;
		}
		analysis->cb_printf("%s\n", s);
		free(s);
	}
	if (!strncmp(k, "addr.", 5)) {
		char *off;
		if (!(off = strdup(k + 5))) {
			return 1;
		}
		char *ptr = strstr(off, ".noreturn");
		if (ptr) {
			*ptr = 0;
			analysis->cb_printf("0x%s\n", off);
		}
		free(off);
	}
	return true;
}

RZ_API void rz_analysis_noreturn_list(RzAnalysis *analysis, int mode) {
	switch (mode) {
	case 1:
	case '*':
	case 'r':
		sdb_foreach(analysis->sdb_types, __nonreturn_print_commands, analysis);
		break;
	default:
		sdb_foreach(analysis->sdb_types, __nonreturn_print, analysis);
		break;
	}
}

#define K_NORET_ADDR(x) sdb_fmt("addr.%" PFMT64x ".noreturn", x)
#define K_NORET_FUNC(x) sdb_fmt("func.%s.noreturn", x)

RZ_API bool rz_analysis_noreturn_add(RzAnalysis *analysis, const char *name, ut64 addr) {
	const char *tmp_name = NULL;
	Sdb *TDB = analysis->sdb_types;
	char *fnl_name = NULL;
	if (addr != UT64_MAX) {
		if (sdb_bool_set(TDB, K_NORET_ADDR(addr), true, 0)) {
			RzAnalysisFunction *fcn = rz_analysis_get_function_at(analysis, addr);
			if (fcn) {
				fcn->is_noreturn = true;
			}
			return true;
		}
	}
	if (name && *name) {
		tmp_name = name;
	} else {
		RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(analysis, addr, -1);
		RzFlagItem *fi = analysis->flb.get_at(analysis->flb.f, addr, false);
		if (!fcn && !fi) {
			eprintf("Can't find Function at given address\n");
			return false;
		}
		tmp_name = fcn ? fcn->name : fi->name;
		if (fcn) {
			fcn->is_noreturn = true;
		}
	}
	if (rz_type_func_exist(TDB, tmp_name)) {
		fnl_name = strdup(tmp_name);
	} else if (!(fnl_name = rz_type_func_guess(TDB, (char *)tmp_name))) {
		if (addr == UT64_MAX) {
			if (name) {
				sdb_bool_set(TDB, K_NORET_FUNC(name), true, 0);
			} else {
				eprintf("Can't find prototype for: %s\n", tmp_name);
			}
		} else {
			eprintf("Can't find prototype for: %s\n", tmp_name);
		}
		//return false;
	}
	if (fnl_name) {
		sdb_bool_set(TDB, K_NORET_FUNC(fnl_name), true, 0);
		free(fnl_name);
	}
	return true;
}

RZ_API bool rz_analysis_noreturn_drop(RzAnalysis *analysis, const char *expr) {
	Sdb *TDB = analysis->sdb_types;
	expr = rz_str_trim_head_ro(expr);
	const char *fcnname = NULL;
	if (!strncmp(expr, "0x", 2)) {
		ut64 n = rz_num_math(NULL, expr);
		sdb_unset(TDB, K_NORET_ADDR(n), 0);
		RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(analysis, n, -1);
		if (!fcn) {
			// eprintf ("can't find function at 0x%"PFMT64x"\n", n);
			return false;
		}
		fcnname = fcn->name;
	} else {
		fcnname = expr;
	}
	sdb_unset(TDB, K_NORET_FUNC(fcnname), 0);
#if 0
	char *tmp;
	// unnsecessary checks, imho the noreturn db should be pretty simple to allow forward and custom declarations without having to define the function prototype before
	if (rz_type_func_exist (TDB, fcnname)) {
		sdb_unset (TDB, K_NORET_FUNC (fcnname), 0);
		return true;
	} else if ((tmp = rz_type_func_guess (TDB, (char *)fcnname))) {
		sdb_unset (TDB, K_NORET_FUNC (fcnname), 0);
		free (tmp);
		return true;
	}
	eprintf ("Can't find prototype for %s in types database", fcnname);
#endif
	return false;
}

static bool rz_analysis_noreturn_at_name(RzAnalysis *analysis, const char *name) {
	if (sdb_bool_get(analysis->sdb_types, K_NORET_FUNC(name), NULL)) {
		return true;
	}
	char *tmp = rz_type_func_guess(analysis->sdb_types, (char *)name);
	if (tmp) {
		if (sdb_bool_get(analysis->sdb_types, K_NORET_FUNC(tmp), NULL)) {
			free(tmp);
			return true;
		}
		free(tmp);
	}
	if (rz_str_startswith(name, "reloc.")) {
		return rz_analysis_noreturn_at_name(analysis, name + 6);
	}
	return false;
}

RZ_API bool rz_analysis_noreturn_at_addr(RzAnalysis *analysis, ut64 addr) {
	return sdb_bool_get(analysis->sdb_types, K_NORET_ADDR(addr), NULL);
}

static bool noreturn_recurse(RzAnalysis *analysis, ut64 addr) {
	RzAnalysisOp op = { 0 };
	ut8 bbuf[0x10] = { 0 };
	ut64 recurse_addr = UT64_MAX;
	if (!analysis->iob.read_at(analysis->iob.io, addr, bbuf, sizeof(bbuf))) {
		eprintf("Couldn't read buffer\n");
		return false;
	}
	if (rz_analysis_op(analysis, &op, addr, bbuf, sizeof(bbuf), RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_VAL) < 1) {
		return false;
	}
	switch (op.type & RZ_ANALYSIS_OP_TYPE_MASK) {
	case RZ_ANALYSIS_OP_TYPE_JMP:
		if (op.jump == UT64_MAX) {
			recurse_addr = op.ptr;
		} else {
			recurse_addr = op.jump;
		}
		break;
	case RZ_ANALYSIS_OP_TYPE_UCALL:
	case RZ_ANALYSIS_OP_TYPE_RCALL:
	case RZ_ANALYSIS_OP_TYPE_ICALL:
	case RZ_ANALYSIS_OP_TYPE_IRCALL:
		recurse_addr = op.ptr;
		break;
	case RZ_ANALYSIS_OP_TYPE_CCALL:
	case RZ_ANALYSIS_OP_TYPE_CALL:
		recurse_addr = op.jump;
		break;
	}
	if (recurse_addr == UT64_MAX || recurse_addr == addr) {
		return false;
	}
	return rz_analysis_noreturn_at(analysis, recurse_addr);
}

RZ_API bool rz_analysis_noreturn_at(RzAnalysis *analysis, ut64 addr) {
	if (!addr || addr == UT64_MAX) {
		return false;
	}
	if (rz_analysis_noreturn_at_addr(analysis, addr)) {
		return true;
	}
	/* XXX this is very slow */
	RzAnalysisFunction *f = rz_analysis_get_function_at(analysis, addr);
	if (f) {
		if (rz_analysis_noreturn_at_name(analysis, f->name)) {
			return true;
		}
	}
	RzFlagItem *fi = analysis->flag_get(analysis->flb.f, addr);
	if (fi) {
		if (rz_analysis_noreturn_at_name(analysis, fi->realname ? fi->realname : fi->name)) {
			return true;
		}
	}
	if (analysis->recursive_noreturn) {
		return noreturn_recurse(analysis, addr);
	}
	return false;
}

RZ_API void rz_analysis_bind(RzAnalysis *analysis, RzAnalysisBind *b) {
	if (b) {
		b->analysis = analysis;
		b->get_fcn_in = rz_analysis_get_fcn_in;
		b->get_hint = rz_analysis_hint_get;
	}
}

RZ_API RzList *rz_analysis_preludes(RzAnalysis *analysis) {
	if (analysis->cur && analysis->cur->preludes) {
		return analysis->cur->preludes(analysis);
	}
	return NULL;
}

RZ_API bool rz_analysis_is_prelude(RzAnalysis *analysis, const ut8 *data, int len) {
	RzList *l = rz_analysis_preludes(analysis);
	if (l) {
		RzSearchKeyword *kw;
		RzListIter *iter;
		rz_list_foreach (l, iter, kw) {
			int ks = kw->keyword_length;
			if (len >= ks && !memcmp(data, kw->bin_keyword, ks)) {
				rz_list_free(l);
				return true;
			}
		}
		rz_list_free(l);
	}
	return false;
}

RZ_API void rz_analysis_add_import(RzAnalysis *analysis, const char *imp) {
	RzListIter *it;
	const char *eimp;
	rz_list_foreach (analysis->imports, it, eimp) {
		if (!strcmp(eimp, imp)) {
			return;
		}
	}
	char *cimp = strdup(imp);
	if (!cimp) {
		return;
	}
	rz_list_push(analysis->imports, cimp);
}

RZ_API void rz_analysis_remove_import(RzAnalysis *analysis, const char *imp) {
	RzListIter *it;
	const char *eimp;
	rz_list_foreach (analysis->imports, it, eimp) {
		if (!strcmp(eimp, imp)) {
			rz_list_delete(analysis->imports, it);
			return;
		}
	}
}

RZ_API void rz_analysis_purge_imports(RzAnalysis *analysis) {
	rz_list_purge(analysis->imports);
}
