* Refactor del_edges to use RzGraphStatus. * Refactor del_edge() to use RzGraphStatus. * Refactor update_edge() to use RzGraphStatus. * Refactor has_edge() to use RzGraphStatus. * Refactor add_edge() to use RzGraphStatus. * Fix leak of b * Fix leak of xref list * Fix leaks of analysis ops * Address review comments * Inlcude clean up * Fix invalid free * Add tests with node and edge data. * Extend tests
280 lines
8.1 KiB
C
280 lines
8.1 KiB
C
// SPDX-FileCopyrightText: 2014-2020 pancake <pancake@nopcode.org>
|
|
// SPDX-FileCopyrightText: 2014-2021 ret2libc <sirmy15@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_core.h>
|
|
#include "core_private.h"
|
|
|
|
RZ_IPI void rz_core_agraph_reset(RzCore *core) {
|
|
rz_agraph_reset(core->graph);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_add_node(RzCore *core, const char *title, const char *body) {
|
|
char *b = rz_str_dup(body);
|
|
if (rz_str_startswith(b, "base64:")) {
|
|
char *newbody = rz_str_dup(b);
|
|
free(b);
|
|
if (!newbody) {
|
|
return;
|
|
}
|
|
char *newbody_no_nl = rz_str_replace(newbody, "\\n", "", true);
|
|
b = (char *)rz_base64_decode_dyn(newbody_no_nl + strlen("base64:"), -1);
|
|
free(newbody_no_nl);
|
|
if (!b) {
|
|
return;
|
|
}
|
|
}
|
|
if (!RZ_STR_ISEMPTY(b)) {
|
|
b = rz_str_append(b, "\n");
|
|
}
|
|
rz_agraph_add_node(core->graph, title, b);
|
|
free(b);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_del_node(RzCore *core, const char *title) {
|
|
rz_agraph_del_node(core->graph, title);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_add_edge(RzCore *core, const char *un, const char *vn) {
|
|
RzANode *u = rz_agraph_get_node(core->graph, un);
|
|
RzANode *v = rz_agraph_get_node(core->graph, vn);
|
|
if (!u) {
|
|
rz_cons_printf("Node %s not found!\n", un);
|
|
return;
|
|
} else if (!v) {
|
|
rz_cons_printf("Node %s not found!\n", vn);
|
|
return;
|
|
}
|
|
rz_agraph_add_edge(core->graph, u, v);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_del_edge(RzCore *core, const char *un, const char *vn) {
|
|
RzANode *u = rz_agraph_get_node(core->graph, un);
|
|
RzANode *v = rz_agraph_get_node(core->graph, vn);
|
|
if (!u) {
|
|
rz_cons_printf("Node %s not found!\n", un);
|
|
return;
|
|
} else if (!v) {
|
|
rz_cons_printf("Node %s not found!\n", vn);
|
|
return;
|
|
}
|
|
rz_agraph_del_edge(core->graph, u, v);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_print_ascii(RzCore *core) {
|
|
core->graph->can->linemode = rz_config_get_i(core->config, "graph.linemode");
|
|
core->graph->can->color = rz_config_get_i(core->config, "scr.color");
|
|
rz_agraph_set_title(core->graph, rz_config_get(core->config, "graph.title"));
|
|
rz_agraph_print(core->graph);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_print_sdb(RzCore *core) {
|
|
Sdb *db = rz_agraph_get_sdb(core->graph);
|
|
char *o = sdb_querys(db, "null", 0, "*");
|
|
rz_cons_print(o);
|
|
free(o);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_print_interactive(RzCore *core) {
|
|
RzANode *ran = rz_agraph_get_first_node(core->graph);
|
|
if (!ran) {
|
|
RZ_LOG_ERROR("core: this graph contains no nodes\n");
|
|
return;
|
|
}
|
|
|
|
ut64 oseek = core->offset;
|
|
rz_agraph_set_title(core->graph, rz_config_get(core->config, "graph.title"));
|
|
rz_agraph_set_curnode(core->graph, ran);
|
|
core->graph->force_update_seek = true;
|
|
core->graph->need_set_layout = true;
|
|
core->graph->layout = rz_config_get_i(core->config, "graph.layout");
|
|
bool ov = rz_cons_is_interactive();
|
|
core->graph->need_update_dim = true;
|
|
int update_seek = rz_core_visual_graph(core, core->graph, NULL, true);
|
|
rz_config_set_i(core->config, "scr.interactive", ov);
|
|
rz_cons_show_cursor(true);
|
|
rz_cons_enable_mouse(false);
|
|
if (update_seek != -1) {
|
|
rz_core_seek(core, oseek, false);
|
|
}
|
|
}
|
|
|
|
static void agraph_print_node_dot(RzANode *n, void *user) {
|
|
char *label = rz_str_dup(n->body);
|
|
// label = rz_str_replace (label, "\n", "\\l", 1);
|
|
|
|
if (!label || !*label) {
|
|
rz_cons_printf("\"%s\" [URL=\"%s\", color=\"lightgray\", label=\"%s\"]\n",
|
|
n->title, n->title, n->title);
|
|
} else {
|
|
rz_str_replace_ch(label, '\"', '\'', true);
|
|
rz_cons_printf("\"%s\" [URL=\"%s\", color=\"lightgray\", label=\"%s\\n%s\"]\n",
|
|
n->title, n->title, n->title, label);
|
|
}
|
|
free(label);
|
|
}
|
|
|
|
static void agraph_print_edge_dot(RzANode *from, RzANode *to, void *user) {
|
|
rz_cons_printf("\"%s\" -> \"%s\"\n", from->title, to->title);
|
|
}
|
|
|
|
static void agraph_print_edge(RzANode *from, RzANode *to, void *user) {
|
|
rz_cons_printf("age \"%s\" \"%s\"\n", from->title, to->title);
|
|
}
|
|
|
|
static void agraph_print_node(RzANode *n, void *user) {
|
|
char *cmd;
|
|
int len = strlen(n->body);
|
|
|
|
if (len > 0 && n->body[len - 1] == '\n') {
|
|
len--;
|
|
}
|
|
if (RZ_STR_ISEMPTY(n->body)) {
|
|
cmd = rz_str_newf("agn \"%s\"\n", n->title);
|
|
} else {
|
|
char *encbody = rz_base64_encode_dyn((const ut8 *)n->body, len);
|
|
cmd = rz_str_newf("agn \"%s\" base64:%s\n", n->title, encbody);
|
|
free(encbody);
|
|
}
|
|
rz_cons_print(cmd);
|
|
free(cmd);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_print_dot(RzCore *core) {
|
|
const char *font = rz_config_get(core->config, "graph.font");
|
|
rz_cons_printf("digraph code {\nrankdir=LR;\noutputorder=edgesfirst\ngraph [bgcolor=azure];\n"
|
|
"edge [arrowhead=normal, color=\"#3030c0\" style=bold weight=2];\n"
|
|
"node [fillcolor=white, style=filled shape=box "
|
|
"fontname=\"%s\" fontsize=\"8\"];\n",
|
|
font);
|
|
rz_agraph_foreach(core->graph, agraph_print_node_dot, NULL);
|
|
rz_agraph_foreach_edge(core->graph, agraph_print_edge_dot, NULL);
|
|
rz_cons_printf("}\n");
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_print_rizin(RzCore *core) {
|
|
rz_agraph_foreach(core->graph, agraph_print_node, NULL);
|
|
rz_agraph_foreach_edge(core->graph, agraph_print_edge, NULL);
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_print_json(RzCore *core) {
|
|
PJ *pj = pj_new();
|
|
if (!pj) {
|
|
return;
|
|
}
|
|
pj_o(pj);
|
|
pj_k(pj, "nodes");
|
|
pj_a(pj);
|
|
rz_agraph_print_json(core->graph, pj);
|
|
pj_end(pj);
|
|
pj_end(pj);
|
|
rz_cons_println(pj_string(pj));
|
|
pj_free(pj);
|
|
}
|
|
|
|
static void agraph_print_node_gml(RzANode *n, void *user) {
|
|
rz_cons_printf(" node [\n"
|
|
" id %d\n"
|
|
" label \"%s\"\n"
|
|
" ]\n",
|
|
(int)rz_graph_node_get_vec_id(n->gnode), n->title);
|
|
}
|
|
|
|
static void agraph_print_edge_gml(RzANode *from, RzANode *to, void *user) {
|
|
rz_cons_printf(" edge [\n"
|
|
" source %d\n"
|
|
" target %d\n"
|
|
" ]\n",
|
|
(int)rz_graph_node_get_vec_id(from->gnode), (int)rz_graph_node_get_vec_id(to->gnode));
|
|
}
|
|
|
|
RZ_IPI void rz_core_agraph_print_gml(RzCore *core) {
|
|
rz_cons_printf("graph\n[\n"
|
|
"hierarchic 1\n"
|
|
"label \"\"\n"
|
|
"directed 1\n");
|
|
rz_agraph_foreach(core->graph, agraph_print_node_gml, NULL);
|
|
rz_agraph_foreach_edge(core->graph, agraph_print_edge_gml, NULL);
|
|
rz_cons_print("]\n");
|
|
}
|
|
|
|
RZ_IPI bool rz_core_agraph_print(RzCore *core, RzCoreGraphFormat format) {
|
|
switch (format) {
|
|
case RZ_CORE_GRAPH_FORMAT_ASCII_ART:
|
|
rz_core_agraph_print_ascii(core);
|
|
break;
|
|
break;
|
|
case RZ_CORE_GRAPH_FORMAT_SDB:
|
|
rz_core_agraph_print_sdb(core);
|
|
break;
|
|
case RZ_CORE_GRAPH_FORMAT_VISUAL:
|
|
rz_core_agraph_print_interactive(core);
|
|
break;
|
|
case RZ_CORE_GRAPH_FORMAT_DOT:
|
|
rz_core_agraph_print_dot(core);
|
|
break;
|
|
case RZ_CORE_GRAPH_FORMAT_CMD:
|
|
rz_core_agraph_print_rizin(core);
|
|
break;
|
|
case RZ_CORE_GRAPH_FORMAT_JSON:
|
|
/* fall-thru */
|
|
case RZ_CORE_GRAPH_FORMAT_JSON_DISASM:
|
|
rz_core_agraph_print_json(core);
|
|
break;
|
|
case RZ_CORE_GRAPH_FORMAT_GML:
|
|
rz_core_agraph_print_gml(core);
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RZ_IPI bool rz_core_agraph_is_shortcuts(RzCore *core, RzAGraph *g) {
|
|
rz_return_val_if_fail(g && core, false);
|
|
return g->is_interactive && rz_config_get_i(core->config, "graph.nodejmps");
|
|
}
|
|
|
|
RZ_IPI bool rz_core_agraph_add_shortcut(RzCore *core, RzAGraph *g, RzANode *an, ut64 addr, char *title) {
|
|
rz_return_val_if_fail(core && g && an && title, false);
|
|
char *shortcut = rz_core_add_asmqjmp(core, addr);
|
|
if (!shortcut) {
|
|
return false;
|
|
}
|
|
char *key = rz_str_newf("agraph.nodes.%s.shortcut", title);
|
|
sdb_set(g->db, key, shortcut);
|
|
free(key);
|
|
// title + "[o{shortcut}]", so w + 3 ?
|
|
an->shortcut_w = strlen(shortcut) + 3;
|
|
free(shortcut);
|
|
return true;
|
|
}
|
|
|
|
RZ_IPI bool rz_core_add_shortcuts(RzCore *core, RzAGraph *ag) {
|
|
rz_return_val_if_fail(core && ag, false);
|
|
RzIterator *it = rz_graph_get_nodes(ag->graph);
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
RzGraphNode *gn;
|
|
rz_iterator_foreach(it, gn) {
|
|
RzANode *an = rz_graph_node_get_data_mut(gn);
|
|
rz_core_agraph_add_shortcut(core, ag, an, an->offset, an->title);
|
|
}
|
|
rz_iterator_free(it);
|
|
return true;
|
|
}
|
|
|
|
RZ_IPI bool rz_core_agraph_apply(RzCore *core, RzGraph /*<RzGraphNodeInfo *, NULL *>*/ *graph) {
|
|
if (!(core && core->graph && graph)) {
|
|
return false;
|
|
}
|
|
if (!rz_core_create_agraph_from_graph_at(core->graph, graph, false, rz_config_get_b(core->config, "scr.utf8"))) {
|
|
return false;
|
|
}
|
|
if (rz_core_agraph_is_shortcuts(core, core->graph)) {
|
|
rz_core_add_shortcuts(core, core->graph);
|
|
}
|
|
return true;
|
|
}
|