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

#include <rz_core.h>

typedef enum bb_type {
	TRAP,
	NORMAL,
	JUMP,
	FAIL,
	CALL,
	END,
} bb_type_t;

typedef struct bb {
	ut64 start;
	ut64 end;
	ut64 jump;
	ut64 fail;
	int score;
	int called;
	int reached;
	bb_type_t type;
} bb_t;

typedef struct fcn {
	ut64 addr;
	ut64 size;
	RzList *bbs;
	st64 score;
	ut64 ends;
} fcn_t;

static bool __is_data_block_cb(RzAnalysisBlock *block, void *user) {
	bool *block_exists = user;
	*block_exists = true;
	return false;
}

static int __isdata(RzCore *core, ut64 addr) {
	if (!rz_io_is_valid_offset(core->io, addr, false)) {
		// eprintf ("Warning: Invalid memory address at 0x%08"PFMT64x"\n", addr);
		return 4;
	}

	bool block_exists = false;
	// This will just set block_exists = true if there is any basic block at this addr
	rz_analysis_blocks_foreach_in(core->analysis, addr, __is_data_block_cb, &block_exists);
	if (block_exists) {
		return 1;
	}

	RzPVector *list = rz_meta_get_all_in(core->analysis, addr, RZ_META_TYPE_ANY);
	void **it;
	int result = 0;
	rz_pvector_foreach (list, it) {
		RzIntervalNode *node = *it;
		RzAnalysisMetaItem *meta = node->data;
		switch (meta->type) {
		case RZ_META_TYPE_DATA:
		case RZ_META_TYPE_STRING:
		case RZ_META_TYPE_FORMAT:
			result = node->end - addr + 1;
			goto exit;
		default:
			break;
		}
	}
exit:
	rz_pvector_free(list);
	return result;
}

static bool fcnAddBB(fcn_t *fcn, bb_t *block) {
	if (!fcn) {
		eprintf("No function given to add a basic block\n");
		return false;
	}
	fcn->score += block->score;
	fcn->size += block->end - block->start;
	if (block->type == END) {
		fcn->ends++;
	}
	if (!fcn->bbs) {
		eprintf("Block list not initialized\n");
		return false;
	}
	rz_list_append(fcn->bbs, block);
	return true;
}

static fcn_t *fcnNew(bb_t *block) {
	fcn_t *fcn = RZ_NEW0(fcn_t);
	if (!fcn) {
		eprintf("Failed to allocate memory for function\n");
		return NULL;
	}
	fcn->addr = block->start;
	fcn->bbs = rz_list_new();
	if (!fcnAddBB(fcn, block)) {
		eprintf("Failed to add block to function\n");
	}
	return fcn;
}

static void fcnFree(fcn_t *fcn) {
	rz_list_free(fcn->bbs);
	free(fcn);
}

static int bbCMP(void *_a, void *_b) {
	bb_t *a = (bb_t *)_a;
	bb_t *b = (bb_t *)_b;
	return b->start - a->start;
}

static void initBB(bb_t *bb, ut64 start, ut64 end, ut64 jump, ut64 fail, bb_type_t type, int score, int reached, int called) {
	if (bb) {
		bb->start = start;
		bb->end = end;
		bb->jump = jump;
		bb->fail = fail;
		bb->type = type;
		bb->score = score;
		bb->reached = reached;
		bb->called = called;
	}
}

static bool addBB(RzList *block_list, ut64 start, ut64 end, ut64 jump, ut64 fail, bb_type_t type, int score) {
	bb_t *bb = (bb_t *)RZ_NEW0(bb_t);
	if (!bb) {
		eprintf("Failed to calloc mem for new basic block!\n");
		return false;
	}
	initBB(bb, start, end, jump, fail, type, score, 0, 0);
	if (jump < UT64_MAX) {
		bb_t *jump_bb = (bb_t *)RZ_NEW0(bb_t);
		if (!jump_bb) {
			eprintf("Failed to allocate memory for jump block\n");
			free(bb);
			return false;
		}
		if (type == CALL) {
			initBB(jump_bb, jump, UT64_MAX, UT64_MAX, UT64_MAX, CALL, 0, 1, 1);
		} else {
			initBB(jump_bb, jump, UT64_MAX, UT64_MAX, UT64_MAX, JUMP, 0, 1, 0);
		}
		rz_list_append(block_list, jump_bb);
	}
	if (fail < UT64_MAX) {
		bb_t *fail_bb = (bb_t *)RZ_NEW0(bb_t);
		if (!fail_bb) {
			eprintf("Failed to allocate memory for fail block\n");
			free(bb);
			return false;
		}
		initBB(fail_bb, fail, UT64_MAX, UT64_MAX, UT64_MAX, FAIL, 0, 1, 0);
		rz_list_append(block_list, fail_bb);
	}
	rz_list_append(block_list, bb);
	return true;
}

void dump_block(bb_t *block) {
	eprintf("s: 0x%" PFMT64x " e: 0x%" PFMT64x " j: 0x%" PFMT64x " f: 0x%" PFMT64x " t: %d\n", block->start, block->end, block->jump, block->fail, block->type);
}

void dump_blocks(RzList *list) {
	RzListIter *iter;
	bb_t *block = NULL;
	rz_list_foreach (list, iter, block) {
		dump_block(block);
	}
}

static bool checkFunction(fcn_t *fcn) {
	if (fcn && fcn->ends > 0 && fcn->size > 0) {
		return true;
	}

	return false;
}

static void printFunctionCommands(RzCore *core, fcn_t *fcn, const char *name) {
	if (!fcn) {
		eprintf("No function given to print\n");
		return;
	}

	RzListIter *fcn_iter;
	bb_t *cur = NULL;
	const char *pfx = rz_config_get(core->config, "analysis.fcnprefix");
	if (!pfx) {
		pfx = "fcn";
	}

	char *_name = name ? (char *)name : rz_str_newf("%s.%" PFMT64x, pfx, fcn->addr);
	rz_cons_printf("af+ 0x%08" PFMT64x " %s\n", fcn->addr, _name);
	if (!name) {
		free(_name);
	}

	rz_list_foreach (fcn->bbs, fcn_iter, cur) {
		rz_cons_printf("afb+ 0x%08" PFMT64x " 0x%08" PFMT64x " %llu 0x%08" PFMT64x " 0x%08" PFMT64x "\n", fcn->addr, cur->start, cur->end - cur->start, cur->jump, cur->fail);
	}
}

static void createFunction(RzCore *core, fcn_t *fcn, const char *name) {
	if (!fcn) {
		eprintf("No function given to create\n");
		return;
	}

	RzListIter *fcn_iter;
	bb_t *cur = NULL;
	const char *pfx = rz_config_get(core->config, "analysis.fcnprefix");
	if (!pfx) {
		pfx = "fcn";
	}

	RzAnalysisFunction *f = rz_analysis_function_new(core->analysis);
	if (!f) {
		eprintf("Failed to create new function\n");
		return;
	}

	f->name = name ? strdup(name) : rz_str_newf("%s.%" PFMT64x, pfx, fcn->addr);
	f->addr = fcn->addr;
	f->bits = core->analysis->bits;
	f->cc = rz_str_constpool_get(&core->analysis->constpool, rz_analysis_cc_default(core->analysis));
	f->type = RZ_ANALYSIS_FCN_TYPE_FCN;

	rz_list_foreach (fcn->bbs, fcn_iter, cur) {
		if (__isdata(core, cur->start)) {
			continue;
		}
		rz_analysis_fcn_add_bb(core->analysis, f, cur->start, (cur->end - cur->start), cur->jump, cur->fail, NULL);
	}
	if (!rz_analysis_add_function(core->analysis, f)) {
		// eprintf ("Failed to insert function\n");
		rz_analysis_function_free(f);
		return;
	}
}

#define Fhandled(x) sdb_fmt("handled.%" PFMT64x "", x)
RZ_API bool core_analysis_bbs(RzCore *core, const char *input) {
	if (!rz_io_is_valid_offset(core->io, core->offset, false)) {
		eprintf("No valid offset given to analyze\n");
		return false;
	}

	Sdb *sdb = NULL;
	const ut64 start = core->offset;
	ut64 size = input[0] ? rz_num_math(core->num, input + 1) : core->blocksize;
	ut64 b_start = start;
	RzListIter *iter;
	int block_score = 0;
	RzList *block_list;
	bb_t *block = NULL;
	int invalid_instruction_barrier = -20000;
	bool debug = rz_config_get_i(core->config, "cfg.debug");
	bool nopskip = rz_config_get_i(core->config, "analysis.nopskip");

	block_list = rz_list_new();
	if (!block_list) {
		eprintf("Failed to create block_list\n");
	}

	if (debug) {
		eprintf("Analyzing [0x%08" PFMT64x "-0x%08" PFMT64x "]\n", start, start + size);
		eprintf("Creating basic blocks\b");
	}
	ut64 cur = 0, base = 0;
	while (cur >= base && cur < size) {
		if (rz_cons_is_breaked()) {
			break;
		}
		// magic number to fix huge section of invalid code fuzz files
		if (block_score < invalid_instruction_barrier) {
			break;
		}
		const ut64 dst = start + cur;
		if (dst < start) {
			// fix underflow issue
			break;
		}
		base = cur;
		int dsize = __isdata(core, dst);
		if (dsize > 0) {
			cur += dsize;
			continue;
		}
		RzAnalysisOp *const op = rz_core_analysis_op(core, dst, RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_DISASM);

		if (!op || !op->mnemonic) {
			block_score -= 10;
			cur++;
			continue;
		}

		if (op->mnemonic[0] == '?') {
			eprintf("? Bad op at: 0x%08" PFMT64x "\n", dst);
			eprintf("Cannot analyze opcode at 0x%" PFMT64x "\n", dst);
			block_score -= 10;
			cur++;
			continue;
		}
		switch (op->type) {
		case RZ_ANALYSIS_OP_TYPE_NOP:
			if (nopskip && b_start == dst) {
				b_start = dst + op->size;
			}
			break;
		case RZ_ANALYSIS_OP_TYPE_CALL:
			if (rz_analysis_noreturn_at(core->analysis, op->jump)) {
				addBB(block_list, b_start, dst + op->size, UT64_MAX, UT64_MAX, END, block_score);
				b_start = dst + op->size;
				block_score = 0;
			} else {
				addBB(block_list, op->jump, UT64_MAX, UT64_MAX, UT64_MAX, CALL, block_score);
			}
			break;
		case RZ_ANALYSIS_OP_TYPE_JMP:
			addBB(block_list, b_start, dst + op->size, op->jump, UT64_MAX, END, block_score);
			b_start = dst + op->size;
			block_score = 0;
			break;
		case RZ_ANALYSIS_OP_TYPE_TRAP:
			// we don't want to add trap stuff
			if (b_start < dst) {
				addBB(block_list, b_start, dst, UT64_MAX, UT64_MAX, NORMAL, block_score);
			}
			b_start = dst + op->size;
			block_score = 0;
			break;
		case RZ_ANALYSIS_OP_TYPE_RET:
			addBB(block_list, b_start, dst + op->size, UT64_MAX, UT64_MAX, END, block_score);
			b_start = dst + op->size;
			block_score = 0;
			break;
		case RZ_ANALYSIS_OP_TYPE_CJMP:
			addBB(block_list, b_start, dst + op->size, op->jump, dst + op->size, NORMAL, block_score);
			b_start = dst + op->size;
			block_score = 0;
			break;
		case RZ_ANALYSIS_OP_TYPE_UNK:
		case RZ_ANALYSIS_OP_TYPE_ILL:
			block_score -= 10;
			break;
		default:
			break;
		}
		cur += op->size;
		rz_analysis_op_free(op);
	}

	if (debug) {
		eprintf("Found %d basic blocks\n", block_list->length);
	}

	RzList *result = rz_list_newf(free);
	if (!result) {
		rz_list_free(block_list);
		eprintf("Failed to create resulting list\n");
		return false;
	}

	sdb = sdb_new0();
	if (!sdb) {
		eprintf("Failed to initialize sdb db\n");
		rz_list_free(block_list);
		rz_list_free(result);
		return false;
	}

	rz_list_sort(block_list, (RzListComparator)bbCMP);

	if (debug) {
		eprintf("Sorting all blocks done\n");
		eprintf("Creating the complete graph\n");
	}

	while (block_list->length > 0) {
		block = rz_list_pop(block_list);
		if (!block) {
			eprintf("Failed to get next block from list\n");
			continue;
		}
		if (rz_cons_is_breaked()) {
			break;
		}

		if (block_list->length > 0) {
			bb_t *next_block = (bb_t *)rz_list_iter_get_data(block_list->tail);
			if (!next_block) {
				eprintf("No next block to compare with!\n");
			}

			// current block is just a split block
			if (block->start == next_block->start && block->end == UT64_MAX) {
				if (block->type != CALL && next_block->type != CALL) {
					next_block->reached = block->reached + 1;
				}
				free(block);
				continue;
			}

			// block and next_block share the same start so we copy the
			// contenct of the block into the next_block and skip the current one
			if (block->start == next_block->start && next_block->end == UT64_MAX) {
				if (next_block->type != CALL) {
					block->reached += 1;
				}
				*next_block = *block;
				free(block);
				continue;
			}

			if (block->end < UT64_MAX && next_block->start < block->end && next_block->start > block->start) {
				if (next_block->jump == UT64_MAX) {
					next_block->jump = block->jump;
				}

				if (next_block->fail == UT64_MAX) {
					next_block->fail = block->fail;
				}

				next_block->end = block->end;
				block->end = next_block->start;
				block->jump = next_block->start;
				block->fail = UT64_MAX;
				next_block->type = block->type;
				if (next_block->type != CALL) {
					next_block->reached += 1;
				}
			}
		}

		sdb_ptr_set(sdb, sdb_fmt("bb.0x%08" PFMT64x, block->start), block, 0);
		rz_list_append(result, block);
	}

	// finally search for functions
	// we simply assume that non reached blocks or called blocks
	// are functions
	if (debug) {
		eprintf("Trying to create functions\n");
	}

	rz_list_foreach (result, iter, block) {
		if (rz_cons_is_breaked()) {
			break;
		}
		if (block && (block->reached == 0 || block->called >= 1)) {
			fcn_t *current_function = fcnNew(block);
			RzStack *stack = rz_stack_new(100);
			bb_t *jump = NULL;
			bb_t *fail = NULL;
			bb_t *cur = NULL;

			if (!rz_stack_push(stack, (void *)block)) {
				eprintf("Failed to push initial block\n");
			}

			while (!rz_stack_is_empty(stack)) {
				cur = (bb_t *)rz_stack_pop(stack);
				if (!cur) {
					continue;
				}
				sdb_num_set(sdb, Fhandled(cur->start), 1, 0);
				if (cur->score < 0) {
					fcnFree(current_function);
					current_function = NULL;
					break;
				}
				// we ignore negative blocks
				if ((st64)(cur->end - cur->start) < 0) {
					break;
				}

				fcnAddBB(current_function, cur);

				if (cur->jump < UT64_MAX && !sdb_num_get(sdb, Fhandled(cur->jump), NULL)) {
					jump = sdb_ptr_get(sdb, sdb_fmt("bb.0x%08" PFMT64x, cur->jump), NULL);
					if (!jump) {
						eprintf("Failed to get jump block at 0x%" PFMT64x "\n", cur->jump);
						continue;
					}
					if (!rz_stack_push(stack, (void *)jump)) {
						eprintf("Failed to push jump block to stack\n");
					}
				}

				if (cur->fail < UT64_MAX && !sdb_num_get(sdb, Fhandled(cur->fail), NULL)) {
					fail = sdb_ptr_get(sdb, sdb_fmt("bb.0x%08" PFMT64x, cur->fail), NULL);
					if (!fail) {
						eprintf("Failed to get fail block at 0x%" PFMT64x "\n", cur->fail);
						continue;
					}
					if (!rz_stack_push(stack, (void *)fail)) {
						eprintf("Failed to push jump block to stack\n");
					}
				}
			}

			// function creation complete
			if (current_function) {
				if (checkFunction(current_function)) {
					if (input[0] == '*') {
						printFunctionCommands(core, current_function, NULL);
					} else {
						createFunction(core, current_function, NULL);
					}
				}
				fcnFree(current_function);
			}

			rz_stack_free(stack);
		}
	}

	sdb_free(sdb);
	rz_list_free(result);
	rz_list_free(block_list);
	return true;
}

RZ_API bool core_analysis_bbs_range(RzCore *core, const char *input) {
	if (!rz_io_is_valid_offset(core->io, core->offset, false)) {
		eprintf("No valid offset given to analyze\n");
		return false;
	}

	Sdb *sdb = NULL;
	ut64 cur = 0;
	ut64 start = core->offset;
	ut64 size = input[0] ? rz_num_math(core->num, input + 1) : core->blocksize;
	ut64 b_start = start;
	RzAnalysisOp *op;
	RzListIter *iter;
	int block_score = 0;
	RzList *block_list;
	bb_t *block = NULL;
	int invalid_instruction_barrier = -20000;
	bool debug = rz_config_get_i(core->config, "cfg.debug");
	ut64 lista[1024] = { 0 };
	int idx = 0;
	int x;

	block_list = rz_list_new();
	if (!block_list) {
		eprintf("Failed to create block_list\n");
	}
	if (debug) {
		eprintf("Analyzing [0x%08" PFMT64x "-0x%08" PFMT64x "]\n", start, start + size);
		eprintf("Creating basic blocks\b");
	}
	lista[idx++] = b_start;
	for (x = 0; x < 1024; x++) {
		if (lista[x] != 0) {
			cur = 0;
			b_start = lista[x];
			lista[x] = 0;
			while (cur < size) {
				if (rz_cons_is_breaked()) {
					break;
				}
				// magic number to fix huge section of invalid code fuzz files
				if (block_score < invalid_instruction_barrier) {
					break;
				}

				bool bFound = false;
				// check if offset don't have into block_list, to end branch analisys
				rz_list_foreach (block_list, iter, block) {
					if ((block->type == END || block->type == NORMAL) && b_start + cur == block->start) {
						bFound = true;
						break;
					}
				}

				if (!bFound) {
					op = rz_core_analysis_op(core, b_start + cur, RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_DISASM);

					if (!op || !op->mnemonic) {
						block_score -= 10;
						cur++;
						continue;
					}

					if (op->mnemonic[0] == '?') {
						eprintf("? Bad op at: 0x%08" PFMT64x "\n", cur + b_start);
						eprintf("Cannot analyze opcode at %" PFMT64x "\n", b_start + cur);
						block_score -= 10;
						cur++;
						continue;
					}
					//eprintf ("0x%08"PFMT64x" %s\n", b_start + cur, op->mnemonic);
					switch (op->type) {
					case RZ_ANALYSIS_OP_TYPE_RET:
						addBB(block_list, b_start, b_start + cur + op->size, UT64_MAX, UT64_MAX, END, block_score);
						cur = size;
						break;
					case RZ_ANALYSIS_OP_TYPE_UJMP:
					case RZ_ANALYSIS_OP_TYPE_IRJMP:
						addBB(block_list, b_start, b_start + cur + op->size, op->jump, UT64_MAX, END, block_score);
						cur = size;
						break;
					case RZ_ANALYSIS_OP_TYPE_JMP:
						addBB(block_list, b_start, b_start + cur + op->size, op->jump, UT64_MAX, END, block_score);
						b_start = op->jump;
						cur = 0;
						block_score = 0;
						break;
					case RZ_ANALYSIS_OP_TYPE_CJMP:
						//eprintf ("bb_b  0x%08"PFMT64x" - 0x%08"PFMT64x"\n", b_start, b_start + cur + op->size);
						addBB(block_list, b_start, b_start + cur + op->size, op->jump, b_start + cur + op->size, NORMAL, block_score);
						b_start = b_start + cur + op->size;
						cur = 0;
						if (idx < 1024) {
							lista[idx++] = op->jump;
						}
						block_score = 0;
						break;
					case RZ_ANALYSIS_OP_TYPE_TRAP:
					case RZ_ANALYSIS_OP_TYPE_UNK:
					case RZ_ANALYSIS_OP_TYPE_ILL:
						block_score -= 10;
						cur += op->size;
						break;
					default:
						cur += op->size;
						break;
					}
					rz_analysis_op_free(op);
					op = NULL;
				} else {
					// we have this offset into previous analyzed block, exit from this path flow.
					break;
				}
			}
		}
	}
	if (debug) {
		eprintf("Found %d basic blocks\n", block_list->length);
	}

	RzList *result = rz_list_newf(free);
	if (!result) {
		rz_list_free(block_list);
		eprintf("Failed to create resulting list\n");
		return false;
	}

	sdb = sdb_new0();
	if (!sdb) {
		eprintf("Failed to initialize sdb db\n");
		rz_list_free(block_list);
		rz_list_free(result);
		return false;
	}

	rz_list_sort(block_list, (RzListComparator)bbCMP);

	if (debug) {
		eprintf("Sorting all blocks done\n");
		eprintf("Creating the complete graph\n");
	}

	while (block_list->length > 0) {
		block = rz_list_pop(block_list);
		if (!block) {
			eprintf("Failed to get next block from list\n");
			continue;
		}
		if (rz_cons_is_breaked()) {
			break;
		}

		if (block_list->length > 0) {
			bb_t *next_block = (bb_t *)rz_list_iter_get_data(block_list->tail);
			if (!next_block) {
				eprintf("No next block to compare with!\n");
			}

			// current block is just a split block
			if (block->start == next_block->start && block->end == UT64_MAX) {
				if (block->type != CALL && next_block->type != CALL) {
					next_block->reached = block->reached + 1;
				}
				free(block);
				continue;
			}

			// block and next_block share the same start so we copy the
			// contenct of the block into the next_block and skip the current one
			if (block->start == next_block->start && next_block->end == UT64_MAX) {
				if (next_block->type != CALL) {
					block->reached += 1;
				}
				*next_block = *block;
				free(block);
				continue;
			}

			if (block->end < UT64_MAX && next_block->start < block->end && next_block->start > block->start) {
				if (next_block->jump == UT64_MAX) {
					next_block->jump = block->jump;
				}

				if (next_block->fail == UT64_MAX) {
					next_block->fail = block->fail;
				}

				next_block->end = block->end;
				block->end = next_block->start;
				block->jump = next_block->start;
				block->fail = UT64_MAX;
				next_block->type = block->type;
				if (next_block->type != CALL) {
					next_block->reached += 1;
				}
			}
		}

		sdb_ptr_set(sdb, sdb_fmt("bb.0x%08" PFMT64x, block->start), block, 0);
		rz_list_append(result, block);
	}

	// finally add bb to function
	// we simply assume that non reached blocks
	// don't are part of the created function
	if (debug) {
		eprintf("Trying to create functions\n");
	}

	rz_list_foreach (result, iter, block) {
		if (rz_cons_is_breaked()) {
			break;
		}
		if (block && (block->reached == 0)) {
			fcn_t *current_function = fcnNew(block);
			RzStack *stack = rz_stack_new(100);
			bb_t *jump = NULL;
			bb_t *fail = NULL;
			bb_t *cur = NULL;

			if (!rz_stack_push(stack, (void *)block)) {
				eprintf("Failed to push initial block\n");
			}

			while (!rz_stack_is_empty(stack)) {
				cur = (bb_t *)rz_stack_pop(stack);
				if (!cur) {
					continue;
				}
				sdb_num_set(sdb, Fhandled(cur->start), 1, 0);
				if (cur->score < 0) {
					fcnFree(current_function);
					current_function = NULL;
					break;
				}
				// we ignore negative blocks
				if ((st64)(cur->end - cur->start) < 0) {
					break;
				}

				fcnAddBB(current_function, cur);

				if (cur->jump < UT64_MAX && !sdb_num_get(sdb, Fhandled(cur->jump), NULL)) {
					jump = sdb_ptr_get(sdb, sdb_fmt("bb.0x%08" PFMT64x, cur->jump), NULL);
					if (!jump) {
						eprintf("Failed to get jump block at 0x%" PFMT64x "\n", cur->jump);
						continue;
					}
					if (!rz_stack_push(stack, (void *)jump)) {
						eprintf("Failed to push jump block to stack\n");
					}
				}

				if (cur->fail < UT64_MAX && !sdb_num_get(sdb, Fhandled(cur->fail), NULL)) {
					fail = sdb_ptr_get(sdb, sdb_fmt("bb.0x%08" PFMT64x, cur->fail), NULL);
					if (!fail) {
						eprintf("Failed to get fail block at 0x%" PFMT64x "\n", cur->fail);
						continue;
					}
					if (!rz_stack_push(stack, (void *)fail)) {
						eprintf("Failed to push jump block to stack\n");
					}
				}
			}

			// function creation complete
			if (current_function) {
				// check for supply function address match with current block
				if (current_function->addr == start) {
					// set supply function size
					current_function->size = size;
					if (checkFunction(current_function)) {
						if (input[0] == '*') {
							printFunctionCommands(core, current_function, NULL);
						} else {
							createFunction(core, current_function, NULL);
						}
						fcnFree(current_function);
						rz_stack_free(stack);
						break;
					}
				}
				fcnFree(current_function);
			}
			rz_stack_free(stack);
		}
	}

	sdb_free(sdb);
	rz_list_free(result);
	rz_list_free(block_list);
	return true;
}
