RZ_API bool rz_core_analysis_graph(RzCore *core, ut64 addr, int opts) {
	ut64 from = rz_config_get_i(core->config, "graph.from");
	ut64 to = rz_config_get_i(core->config, "graph.to");
	const char *font = rz_config_get(core->config, "graph.font");
	int is_html = rz_cons_singleton()->is_html;
	int is_json = opts & RZ_CORE_ANALYSIS_JSON;
	int is_json_format_disasm = opts & RZ_CORE_ANALYSIS_JSON_FORMAT_DISASM;
	int is_keva = opts & RZ_CORE_ANALYSIS_KEYVALUE;
	int is_star = opts & RZ_CORE_ANALYSIS_STAR;
	RzConfigHold *hc;
	RzAnalysisFunction *fcni;
	RzListIter *iter;
	int nodes = 0;
	PJ *pj = NULL;

	if (!addr) {
		addr = core->offset;
	}
	if (rz_list_empty(core->analysis->fcns)) {
		return false;
	}
	hc = rz_config_hold_new(core->config);
	if (!hc) {
		return false;
	}

	rz_config_hold_i(hc, "asm.lines", "asm.bytes", "asm.dwarf", NULL);
	//opts |= RZ_CORE_ANALYSIS_GRAPHBODY;
	rz_config_set_i(core->config, "asm.lines", 0);
	rz_config_set_i(core->config, "asm.dwarf", 0);
	if (!is_json_format_disasm) {
		rz_config_hold_i(hc, "asm.bytes", NULL);
		rz_config_set_i(core->config, "asm.bytes", 0);
	}
	if (!is_html && !is_json && !is_keva && !is_star) {
		const char *gv_edge = rz_config_get(core->config, "graph.gv.edge");
		const char *gv_node = rz_config_get(core->config, "graph.gv.node");
		const char *gv_spline = rz_config_get(core->config, "graph.gv.spline");
		if (!gv_edge || !*gv_edge) {
			gv_edge = "arrowhead=\"normal\"";
		}
		if (!gv_node || !*gv_node) {
			gv_node = "fillcolor=gray style=filled shape=box";
		}
		if (!gv_spline || !*gv_spline) {
			gv_spline = "splines=\"ortho\"";
		}
		rz_cons_printf("digraph code {\n"
			       "\tgraph [bgcolor=azure fontsize=8 fontname=\"%s\" %s];\n"
			       "\tnode [%s];\n"
			       "\tedge [%s];\n",
			font, gv_spline, gv_node, gv_edge);
	}
	if (is_json) {
		pj = pj_new();
		if (!pj) {
			rz_config_hold_restore(hc);
			rz_config_hold_free(hc);
			return false;
		}
		pj_a(pj);
	}
	rz_list_foreach (core->analysis->fcns, iter, fcni) {
		if (fcni->type & (RZ_ANALYSIS_FCN_TYPE_SYM | RZ_ANALYSIS_FCN_TYPE_FCN | RZ_ANALYSIS_FCN_TYPE_LOC) &&
			(addr == UT64_MAX || rz_analysis_get_fcn_in(core->analysis, addr, 0) == fcni)) {
			if (addr == UT64_MAX && (from != UT64_MAX && to != UT64_MAX)) {
				if (fcni->addr < from || fcni->addr > to) {
					continue;
				}
			}
			nodes += core_analysis_graph_nodes(core, fcni, opts, pj);
			if (addr != UT64_MAX) {
				break;
			}
		}
	}
	if (!nodes) {
		if (!is_html && !is_json && !is_keva) {
			RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
			if (is_star) {
				char *name = get_title(fcn ? fcn->addr : addr);
				rz_cons_printf("agn %s;", name);
			} else {
				rz_cons_printf("\t\"0x%08" PFMT64x "\";\n", fcn ? fcn->addr : addr);
			}
		}
	}
	if (!is_keva && !is_html && !is_json && !is_star && !is_json_format_disasm) {
		rz_cons_printf("}\n");
	}
	if (is_json) {
		pj_end(pj);
		rz_cons_printf("%s\n", pj_string(pj));
		pj_free(pj);
	}
	rz_config_hold_restore(hc);
	rz_config_hold_free(hc);
	return true;
}
RZ_API bool rz_core_analysis_graph(RzCore *core, ut64 addr, int opts) {
	ut64 from = rz_config_get_i(core->config, "graph.from");
	ut64 to = rz_config_get_i(core->config, "graph.to");
	const char *font = rz_config_get(core->config, "graph.font");
	int is_html = rz_cons_singleton()->is_html;
	int is_json = opts & RZ_CORE_ANALYSIS_JSON;
	int is_json_format_disasm = opts & RZ_CORE_ANALYSIS_JSON_FORMAT_DISASM;
	int is_keva = opts & RZ_CORE_ANALYSIS_KEYVALUE;
	int is_star = opts & RZ_CORE_ANALYSIS_STAR;
	RzConfigHold *hc;
	RzAnalysisFunction *fcni;
	RzListIter *iter;
	int nodes = 0;
	PJ *pj = NULL;

	if (!addr) {
		addr = core->offset;
	}
	if (rz_list_empty(core->analysis->fcns)) {
		return false;
	}
	hc = rz_config_hold_new(core->config);
	if (!hc) {
		return false;
	}

	rz_config_hold_i(hc, "asm.lines", "asm.bytes", "asm.dwarf", NULL);
	 
	rz_config_set_i(core->config, "asm.lines", 0);
	rz_config_set_i(core->config, "asm.dwarf", 0);
	if (!is_json_format_disasm) {
		rz_config_hold_i(hc, "asm.bytes", NULL);
		rz_config_set_i(core->config, "asm.bytes", 0);
	}
	if (!is_html && !is_json && !is_keva && !is_star) {
		const char *gv_edge = rz_config_get(core->config, "graph.gv.edge");
		const char *gv_node = rz_config_get(core->config, "graph.gv.node");
		const char *gv_spline = rz_config_get(core->config, "graph.gv.spline");
		if (!gv_edge || !*gv_edge) {
			gv_edge = "arrowhead=\"normal\"";
		}
		if (!gv_node || !*gv_node) {
			gv_node = "fillcolor=gray style=filled shape=box";
		}
		if (!gv_spline || !*gv_spline) {
			gv_spline = "splines=\"ortho\"";
		}
		rz_cons_printf("digraph code {\n"
			       "\tgraph [bgcolor=azure fontsize=8 fontname=\"%s\" %s];\n"
			       "\tnode [%s];\n"
			       "\tedge [%s];\n",
			font, gv_spline, gv_node, gv_edge);
	}
	if (is_json) {
		pj = pj_new();
		if (!pj) {
			rz_config_hold_restore(hc);
			rz_config_hold_free(hc);
			return false;
		}
		pj_a(pj);
	}
	rz_list_foreach (core->analysis->fcns, iter, fcni) {
		if (fcni->type & (RZ_ANALYSIS_FCN_TYPE_SYM | RZ_ANALYSIS_FCN_TYPE_FCN | RZ_ANALYSIS_FCN_TYPE_LOC) &&
			(addr == UT64_MAX || rz_analysis_get_fcn_in(core->analysis, addr, 0) == fcni)) {
			if (addr == UT64_MAX && (from != UT64_MAX && to != UT64_MAX)) {
				if (fcni->addr < from || fcni->addr > to) {
					continue;
				}
			}
			nodes += core_analysis_graph_nodes(core, fcni, opts, pj);
			if (addr != UT64_MAX) {
				break;
			}
		}
	}
	if (!nodes) {
		if (!is_html && !is_json && !is_keva) {
			RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
			if (is_star) {
				char *name = get_title(fcn ? fcn->addr : addr);
				rz_cons_printf("agn %s;", name);
			} else {
				rz_cons_printf("\t\"0x%08" PFMT64x "\";\n", fcn ? fcn->addr : addr);
			}
		}
	}
	if (!is_keva && !is_html && !is_json && !is_star && !is_json_format_disasm) {
		rz_cons_printf("}\n");
	}
	if (is_json) {
		pj_end(pj);
		rz_cons_printf("%s\n", pj_string(pj));
		pj_free(pj);
	}
	rz_config_hold_restore(hc);
	rz_config_hold_free(hc);
	return true;
}